LCOV - code coverage report
Current view: top level - AR_WPNav - AR_WPNav.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 245 276 88.8 %
Date: 2026-09-09 11:02:08 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include <AP_AHRS/AP_AHRS.h>
      17             : #include <AP_Math/AP_Math.h>
      18             : #include <AP_HAL/AP_HAL.h>
      19             : #include "AR_WPNav.h"
      20             : #include <GCS_MAVLink/GCS.h>
      21             : #include <AP_InternalError/AP_InternalError.h>
      22             : 
      23             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      24             : #include <stdio.h>
      25             : #endif
      26             : 
      27             : extern const AP_HAL::HAL& hal;
      28             : 
      29             : #define AR_WPNAV_TIMEOUT_MS             100
      30             : #define AR_WPNAV_SPEED_DEFAULT          2.0f
      31             : #define AR_WPNAV_SPEED_MIN              0.05f   // minimum speed between waypoints in m/s
      32             : #define AR_WPNAV_SPEED_UPDATE_MIN_MS    500     // max speed cannot be updated more than once in this many milliseconds
      33             : #define AR_WPNAV_RADIUS_DEFAULT         2.0f
      34             : #define AR_WPNAV_OVERSPEED_RATIO_MAX    5.0f    // if _overspeed_enabled the vehicle may travel as quickly as 5x WP_SPEED
      35             : #define AR_WPNAV_SNAP_MAX               15.0f   // scurve snap (change in jerk) in m/s/s/s/s
      36             : #define AR_WPNAV_ACCEL_MAX              20.0    // acceleration used when user has specified no acceleration limit
      37             : 
      38             : const AP_Param::GroupInfo AR_WPNav::var_info[] = {
      39             : 
      40             :     // @Param: SPEED
      41             :     // @DisplayName: Waypoint speed default
      42             :     // @Description: Waypoint speed default
      43             :     // @Units: m/s
      44             :     // @Range: 0 100
      45             :     // @Increment: 0.1
      46             :     // @User: Standard
      47             :     AP_GROUPINFO("SPEED", 1, AR_WPNav, _speed_max, AR_WPNAV_SPEED_DEFAULT),
      48             : 
      49             :     // @Param: RADIUS
      50             :     // @DisplayName: Waypoint radius
      51             :     // @Description: The distance in meters from a waypoint when we consider the waypoint has been reached. This determines when the vehicle will turn toward the next waypoint.
      52             :     // @Units: m
      53             :     // @Range: 0 100
      54             :     // @Increment: 0.1
      55             :     // @User: Standard
      56             :     AP_GROUPINFO("RADIUS", 2, AR_WPNav, _radius, AR_WPNAV_RADIUS_DEFAULT),
      57             : 
      58             :     // 3 was OVERSHOOT
      59             : 
      60             :     // 4 was PIVOT_ANGLE
      61             :     // 5 was PIVOT_RATE
      62             :     // 6 was SPEED_MIN
      63             :     // 7 was PIVOT_DELAY
      64             : 
      65             :     // @Group: PIVOT_
      66             :     // @Path: AR_PivotTurn.cpp
      67             :     AP_SUBGROUPINFO(_pivot, "PIVOT_", 8, AR_WPNav, AR_PivotTurn),
      68             : 
      69             :     // @Param: ACCEL
      70             :     // @DisplayName: Waypoint acceleration
      71             :     // @Description: Waypoint acceleration.  If zero then ATC_ACCEL_MAX is used
      72             :     // @Units: m/s/s
      73             :     // @Range: 0 100
      74             :     // @Increment: 0.1
      75             :     // @User: Standard
      76             :     AP_GROUPINFO("ACCEL", 9, AR_WPNav, _accel_max, 0),
      77             : 
      78             :     // @Param: JERK
      79             :     // @DisplayName: Waypoint jerk
      80             :     // @Description: Waypoint jerk (change in acceleration).  If zero then jerk is same as acceleration
      81             :     // @Units: m/s/s/s
      82             :     // @Range: 0 100
      83             :     // @Increment: 0.1
      84             :     // @User: Standard
      85             :     AP_GROUPINFO("JERK", 10, AR_WPNav, _jerk_max, 0),
      86             : 
      87             :     AP_GROUPEND
      88             : };
      89             : 
      90         285 : AR_WPNav::AR_WPNav(AR_AttitudeControl& atc, AR_PosControl &pos_control) :
      91         285 :     _pivot(atc),
      92         285 :     _atc(atc),
      93         570 :     _pos_control(pos_control)
      94             : {
      95         285 :     AP_Param::setup_object_defaults(this, var_info);
      96         285 : }
      97             : 
      98             : // initialise waypoint controller.  speed_max should be set to the maximum speed in m/s (or left at zero to use the default speed)
      99         127 : void AR_WPNav::init(float speed_max)
     100             : {
     101             :     // determine max speed, acceleration and jerk
     102         127 :     if (is_positive(speed_max)) {
     103           5 :         _base_speed_max = speed_max;
     104             :     } else {
     105         122 :         _base_speed_max = _speed_max;
     106             :     }
     107         127 :     _base_speed_max = MAX(AR_WPNAV_SPEED_MIN, _base_speed_max);
     108         127 :     float atc_accel_max = MIN(_atc.get_accel_max(), _atc.get_decel_max());
     109         127 :     if (!is_positive(atc_accel_max)) {
     110             :         // accel_max of zero means no limit so use maximum acceleration
     111           0 :         atc_accel_max = AR_WPNAV_ACCEL_MAX;
     112             :     }
     113         127 :     const float accel_max = is_positive(_accel_max) ? MIN(_accel_max, atc_accel_max) : atc_accel_max;
     114         127 :     const float jerk_max = is_positive(_jerk_max) ? _jerk_max : accel_max;
     115             : 
     116             :     // initialise position controller
     117         127 :     _pos_control.set_limits(_base_speed_max, accel_max, _atc.get_turn_lat_accel_max(), jerk_max);
     118             : 
     119         127 :     _scurve_prev_leg.init();
     120         127 :     _scurve_this_leg.init();
     121         127 :     _scurve_next_leg.init();
     122         127 :     _track_scalar_dt = 1.0f;
     123             : 
     124             :     // init some flags
     125         127 :     _reached_destination = false;
     126         127 :     _fast_waypoint = false;
     127             : 
     128             :     // ensure pivot turns are deactivated
     129         127 :     _pivot.deactivate();
     130         127 :     _pivot_at_next_wp = false;
     131             : 
     132             :     // initialise origin and destination to stopping point
     133         127 :     _orig_and_dest_valid = false;
     134         127 :     set_origin_and_destination_to_stopping_point();
     135             : 
     136             :     // initialise nudge speed to zero
     137         127 :     set_nudge_speed_max(0);
     138         127 : }
     139             : 
     140             : // update navigation
     141      170371 : void AR_WPNav::update(float dt)
     142             : {
     143             :     // exit immediately if no current location, origin or destination
     144      170371 :     Location current_loc;
     145             :     float speed;
     146      170371 :     if (!hal.util->get_soft_armed() || !_orig_and_dest_valid || !AP::ahrs().get_location(current_loc) || !_atc.get_forward_speed(speed)) {
     147           0 :         _desired_speed_limited = _atc.get_desired_speed_accel_limited(0.0f, dt);
     148           0 :         _desired_lat_accel = 0.0f;
     149           0 :         _desired_turn_rate_rads = 0.0f;
     150           0 :         _cross_track_error = 0;
     151           0 :         return;
     152             :     }
     153             : 
     154             :     // if no recent calls initialise desired_speed_limited to current speed
     155      170371 :     if (!is_active()) {
     156          81 :         _desired_speed_limited = speed;
     157             :     }
     158      170371 :     _last_update_ms = AP_HAL::millis();
     159             : 
     160      170371 :     update_distance_and_bearing_to_destination();
     161             : 
     162             :     // handle change in max speed
     163      170371 :     update_speed_max();
     164             : 
     165             :     // advance target along path unless vehicle is pivoting
     166      170371 :     if (!_pivot.active()) {
     167      170371 :         switch (_nav_control_type) {
     168      130879 :         case NavControllerType::NAV_SCURVE:
     169      130879 :             advance_wp_target_along_track(current_loc, dt);
     170      130879 :             break;
     171       39492 :         case NavControllerType::NAV_PSC_INPUT_SHAPING:
     172       39492 :             update_psc_input_shaping(dt);
     173       39492 :             break;
     174             :         }
     175             :     }
     176             : 
     177             :     // update_steering_and_speed
     178      170371 :     update_steering_and_speed(current_loc, dt);
     179             : }
     180             : 
     181             : // set maximum speed in m/s.  returns true on success
     182             : // this should not be called at more than 3hz or else SCurve path planning may not advance properly
     183          17 : bool AR_WPNav::set_speed_max(float speed_max)
     184             : {
     185             :     // range check target speed
     186          17 :     if (speed_max < AR_WPNAV_SPEED_MIN) {
     187           0 :         return false;
     188             :     }
     189             : 
     190          17 :     _base_speed_max = speed_max;
     191          17 :     return true;
     192             : }
     193             : 
     194             : // set speed nudge in m/s.  this will have no effect unless nudge_speed_max > speed_max
     195             : // nudge_speed_max should always be positive regardless of whether the vehicle is travelling forward or reversing
     196      180988 : void AR_WPNav::set_nudge_speed_max(float nudge_speed_max)
     197             : {
     198      180988 :     _nudge_speed_max = nudge_speed_max;
     199      180988 : }
     200             : 
     201             : // set desired location and (optionally) next_destination
     202             : // next_destination should be provided if known to allow smooth cornering
     203         200 : bool AR_WPNav::set_desired_location(const Location& destination, Location next_destination)
     204             : {
     205             :     // re-initialise if inactive, previous destination has been interrupted or different controller was used
     206         200 :     if (!is_active() || !_reached_destination || (_nav_control_type != NavControllerType::NAV_SCURVE)) {
     207         109 :         if (!set_origin_and_destination_to_stopping_point()) {
     208           0 :             return false;
     209             :         }
     210             :         // clear scurves
     211         109 :         _scurve_prev_leg.init();
     212         109 :         _scurve_this_leg.init();
     213         109 :         _scurve_next_leg.init();
     214             :     }
     215             : 
     216             :     // shift this leg to previous leg
     217         200 :     _scurve_prev_leg = _scurve_this_leg;
     218             : 
     219             :     // initialise some variables
     220         200 :     _origin = _destination;
     221         200 :     _destination = destination;
     222         200 :     _orig_and_dest_valid = true;
     223         200 :     _reached_destination = false;
     224             : 
     225         200 :     update_distance_and_bearing_to_destination();
     226             : 
     227             :     // check if vehicle should pivot if vehicle stopped at previous waypoint
     228             :     // or journey to previous waypoint was interrupted or navigation has just started
     229         200 :     if (!_fast_waypoint) {
     230         108 :         _pivot.deactivate();
     231         108 :         _pivot.check_activation((_reversed ? wrap_360_cd(oa_wp_bearing_cd() + 18000) : oa_wp_bearing_cd()) * 0.01, _pivot_at_next_wp);
     232             :     }
     233             : 
     234             :     // convert origin and destination to offset from EKF origin
     235         200 :     Vector2f origin_NE;
     236         200 :     Vector2f destination_NE;
     237         400 :     if (!_origin.get_vector_xy_from_origin_NE_cm(origin_NE) ||
     238         200 :         !_destination.get_vector_xy_from_origin_NE_cm(destination_NE)) {
     239           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     240           0 :         return false;
     241             :     }
     242         200 :     origin_NE *= 0.01f;
     243         200 :     destination_NE *= 0.01f;
     244             : 
     245             :     // calculate track to destination
     246         200 :     if (_fast_waypoint && !_scurve_next_leg.finished()) {
     247             :         // skip recalculating this leg by simply shifting next leg
     248          89 :         _scurve_this_leg = _scurve_next_leg;
     249             :     } else {
     250         111 :         _scurve_this_leg.calculate_track(Vector3p{origin_NE.x, origin_NE.y, 0.0f},              // origin
     251         222 :                                          Vector3p{destination_NE.x, destination_NE.y, 0.0f},    // destination
     252             :                                          0.0, // arc length is zero for straight track
     253         111 :                                          _pos_control.get_speed_max(),
     254         111 :                                          _pos_control.get_speed_max(),  // speed up (not used)
     255         111 :                                          _pos_control.get_speed_max(),  // speed down (not used)
     256         111 :                                          _pos_control.get_accel_max(),  // forward back acceleration
     257         111 :                                          _pos_control.get_accel_max(),  // vertical accel (not used)
     258         111 :                                          _pos_control.get_accel_max(),  // corner acceleration
     259             :                                          AR_WPNAV_SNAP_MAX,             // snap
     260         111 :                                          _pos_control.get_jerk_max());
     261             :     }
     262             : 
     263             :     // handle next destination
     264         200 :     _scurve_next_leg.init();
     265         200 :     _fast_waypoint = false;
     266         200 :     _pivot_at_next_wp = false;
     267         200 :     if (next_destination.initialised()) {
     268             :         // check if vehicle should pivot at next waypoint
     269         112 :         const float next_wp_yaw_change = get_corner_angle(_origin, destination, next_destination);
     270         112 :         _pivot_at_next_wp = _pivot.would_activate(next_wp_yaw_change);
     271         112 :         if (!_pivot_at_next_wp) {
     272             :             // convert next_destination to offset from EKF origin
     273         112 :             Vector2f next_destination_NE;
     274         112 :             if (!next_destination.get_vector_xy_from_origin_NE_cm(next_destination_NE)) {
     275           0 :                 INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     276           0 :                 return false;
     277             :             }
     278         112 :             next_destination_NE *= 0.01f;
     279         112 :             _scurve_next_leg.calculate_track(Vector3p{destination_NE.x, destination_NE.y, 0.0f},
     280         112 :                                              Vector3p{next_destination_NE.x, next_destination_NE.y, 0.0f},
     281             :                                              0.0, // arc length is zero for straight track
     282         112 :                                              _pos_control.get_speed_max(),
     283         112 :                                              _pos_control.get_speed_max(),  // speed up (not used)
     284         112 :                                              _pos_control.get_speed_max(),  // speed down (not used)
     285         112 :                                              _pos_control.get_accel_max(),  // forward back acceleration
     286         112 :                                              _pos_control.get_accel_max(),  // vertical accel (not used)
     287         112 :                                              _pos_control.get_accel_max(),  // corner accel
     288             :                                              AR_WPNAV_SNAP_MAX,             // snap
     289         112 :                                              _pos_control.get_jerk_max());
     290             : 
     291             :             // next destination provided so fast waypoint
     292         112 :             _fast_waypoint = true;
     293             :         }
     294             :     }
     295             : 
     296             :     // scurves used for navigation to destination
     297         200 :     _nav_control_type = NavControllerType::NAV_SCURVE;
     298             : 
     299         200 :     update_distance_and_bearing_to_destination();
     300             : 
     301         200 :     return true;
     302             : }
     303             : 
     304             : // set desired location to a reasonable stopping point, return true on success
     305           3 : bool AR_WPNav::set_desired_location_to_stopping_location()
     306             : {
     307           3 :     Location stopping_loc;
     308           3 :     if (!get_stopping_location(stopping_loc)) {
     309           0 :         return false;
     310             :     }
     311           3 :     return set_desired_location(stopping_loc);
     312             : }
     313             : 
     314             : // set desired location as offset from the EKF origin, return true on success
     315           2 : bool AR_WPNav::set_desired_location_NED(const Vector3f& destination)
     316             : {
     317             :     // initialise destination to ekf origin
     318           2 :     Location destination_ned;
     319           2 :     if (!AP::ahrs().get_origin(destination_ned)) {
     320           0 :         return false;
     321             :     }
     322             : 
     323             :     // apply offset
     324           2 :     destination_ned.offset(destination.x, destination.y);
     325           2 :     return set_desired_location(destination_ned);
     326             : }
     327             : 
     328           5 : bool AR_WPNav::set_desired_location_NED(const Vector3f &destination, const Vector3f &next_destination)
     329             : {
     330             :     // initialise destination to ekf origin
     331           5 :     Location dest_loc, next_dest_loc;
     332           5 :     if (!AP::ahrs().get_origin(dest_loc)) {
     333           0 :         return false;
     334             :     }
     335           5 :     next_dest_loc = dest_loc;
     336             : 
     337             :     // apply offsets
     338           5 :     dest_loc.offset(destination.x, destination.y);
     339           5 :     next_dest_loc.offset(next_destination.x, next_destination.y);
     340           5 :     return set_desired_location(dest_loc, next_dest_loc);
     341             : }
     342             : 
     343             : // set desired location but expect the destination to be updated again in the near future
     344             : // position controller input shaping will be used for navigation instead of scurves
     345             : // Note: object avoidance is not supported if this method is used
     346         565 : bool AR_WPNav::set_desired_location_expect_fast_update(const Location &destination)
     347             : {
     348             :     // initialise if not active
     349         565 :     if (!is_active() || (_nav_control_type != NavControllerType::NAV_PSC_INPUT_SHAPING)) {
     350          29 :         if (!set_origin_and_destination_to_stopping_point()) {
     351           0 :             return false;
     352             :         }
     353             :     }
     354             : 
     355             :     // initialise some variables
     356         565 :     _origin = _destination;
     357         565 :     _destination = destination;
     358         565 :     _orig_and_dest_valid = true;
     359         565 :     _reached_destination = false;
     360             : 
     361         565 :     update_distance_and_bearing_to_destination();
     362             : 
     363             :     // check if vehicle should pivot
     364         565 :     _pivot.check_activation((_reversed ? wrap_360_cd(oa_wp_bearing_cd() + 18000) : oa_wp_bearing_cd()) * 0.01);
     365             : 
     366             :     // position controller input shaping used for navigation to destination
     367         565 :     _nav_control_type = NavControllerType::NAV_PSC_INPUT_SHAPING;
     368         565 :     return true;
     369             : }
     370             : 
     371             : // calculate vehicle stopping point using current location, velocity and maximum acceleration
     372         316 : bool AR_WPNav::get_stopping_location(Location& stopping_loc)
     373             : {
     374         316 :     Location current_loc;
     375         316 :     if (!AP::ahrs().get_location(current_loc)) {
     376          28 :         return false;
     377             :     }
     378             : 
     379             :     // get current velocity vector and speed
     380         288 :     const Vector2f velocity = AP::ahrs().groundspeed_vector();
     381         288 :     const float speed = velocity.length();
     382             : 
     383             :     // avoid divide by zero
     384         288 :     if (!is_positive(speed)) {
     385          15 :         stopping_loc = current_loc;
     386          15 :         return true;
     387             :     }
     388             : 
     389             :     // get stopping distance in meters
     390         273 :     const float stopping_dist = _atc.get_stopping_distance(speed);
     391             : 
     392             :     // calculate stopping position from current location in meters
     393         273 :     const Vector2f stopping_offset = velocity.normalized() * stopping_dist;
     394         273 :     stopping_loc = current_loc;
     395         273 :     stopping_loc.offset(stopping_offset.x, stopping_offset.y);
     396             : 
     397         273 :     return true;
     398             : }
     399             : 
     400             : // true if update has been called recently
     401      171136 : bool AR_WPNav::is_active() const
     402             : {
     403      171136 :     return ((AP_HAL::millis() - _last_update_ms) < AR_WPNAV_TIMEOUT_MS);
     404             : }
     405             : 
     406             : // move target location along track from origin to destination using SCurves navigation
     407      130879 : void AR_WPNav::advance_wp_target_along_track(const Location &current_loc, float dt)
     408             : {
     409             :     // exit immediately if no current location, destination or disarmed
     410      130879 :     Vector2f curr_pos_NE;
     411      130879 :     Vector3f curr_vel_NED;
     412      130879 :     if (!AP::ahrs().get_relative_position_NE_origin_float(curr_pos_NE) || !AP::ahrs().get_velocity_NED(curr_vel_NED)) {
     413           0 :         return;
     414             :     }
     415             : 
     416             :     // exit immediately if we can't convert waypoint origin to offset from ekf origin
     417      130879 :     Vector2f origin_NE;
     418      130879 :     if (!_origin.get_vector_xy_from_origin_NE_cm(origin_NE)) {
     419           0 :         return;
     420             :     }
     421             :     // convert from cm to meters
     422      130879 :     origin_NE *= 0.01f;
     423             : 
     424             :     // use _track_scalar_dt to slow down S-Curve time to prevent target moving too far in front of vehicle
     425      130879 :     Vector2f curr_target_vel = _pos_control.get_desired_velocity();
     426      130879 :     float track_scaler_dt = 1.0f;
     427      130879 :     if (is_positive(curr_target_vel.length())) {
     428      130659 :         Vector2f track_direction = curr_target_vel.normalized();
     429      130659 :         const float track_error = _pos_control.get_pos_error().tofloat().dot(track_direction);
     430      130659 :         float track_velocity = curr_vel_NED.xy().dot(track_direction);
     431             :         // set time scaler to be consistent with the achievable vehicle speed with a 5% buffer for short term variation.
     432      130659 :         const float time_scaler_dt_max = _overspeed_enabled ? AR_WPNAV_OVERSPEED_RATIO_MAX : 1.0f;
     433      130659 :         track_scaler_dt = constrain_float(0.05f + (track_velocity - _pos_control.get_pos_p().kP() * track_error) / curr_target_vel.length(), 0.0f, time_scaler_dt_max);
     434             :     }
     435             :     // change s-curve time speed with a time constant of maximum acceleration / maximum jerk
     436      130879 :     float track_scaler_tc = 1.0f;
     437      130879 :     if (is_positive(_pos_control.get_jerk_max())) {
     438      130879 :         track_scaler_tc = _pos_control.get_accel_max() / _pos_control.get_jerk_max();
     439             :     }
     440      130879 :     _track_scalar_dt += (track_scaler_dt - _track_scalar_dt) * (dt / track_scaler_tc);
     441             : 
     442             :     // target position, velocity and acceleration from straight line or spline calculators
     443      130879 :     Vector3p target_pos_3d{origin_NE.x, origin_NE.y, 0.0f};
     444      130879 :     Vector3f target_vel, target_accel;
     445             : 
     446             :     // update target position, velocity and acceleration
     447      130879 :     const float wp_radius = MAX(_radius, _turn_radius);
     448      130879 :     bool s_finished = _scurve_this_leg.advance_target_along_track(_scurve_prev_leg, _scurve_next_leg, wp_radius, _pos_control.get_lat_accel_max(), _fast_waypoint, _track_scalar_dt * dt, target_pos_3d, target_vel, target_accel);
     449             : 
     450             :     // pass new target to the position controller
     451      130879 :     init_pos_control_if_necessary();
     452      130879 :     Vector2p target_pos_ptype{target_pos_3d.x, target_pos_3d.y};
     453      130879 :     _pos_control.set_pos_vel_accel_target(target_pos_ptype, target_vel.xy(), target_accel.xy());
     454             : 
     455             :     // check if we've reached the waypoint
     456      130879 :     if (!_reached_destination && s_finished) {
     457             :         // "fast" waypoints are complete once the intermediate point reaches the destination
     458         138 :         if (_fast_waypoint) {
     459         104 :             _reached_destination = true;
     460             :         } else {
     461             :             // regular waypoints also require the vehicle to be within the waypoint radius or past the "finish line"
     462          34 :             const bool near_wp = current_loc.get_distance(_destination) <= _radius;
     463          34 :             const bool past_wp = current_loc.past_interval_finish_line(_origin, _destination);
     464          34 :             _reached_destination = near_wp || past_wp;
     465             :         }
     466             :     }
     467             : }
     468             : 
     469             : // update psc input shaping navigation controller
     470       39492 : void AR_WPNav::update_psc_input_shaping(float dt)
     471             : {
     472             :     // convert destination location to offset from EKF origin (in meters)
     473       39492 :     Vector2f pos_target_cm;
     474       39492 :     if (!_destination.get_vector_xy_from_origin_NE_cm(pos_target_cm)) {
     475           0 :         return;
     476             :     }
     477             : 
     478             :     // initialise position controller if not called recently
     479       39492 :     init_pos_control_if_necessary();
     480             : 
     481             :     // convert to meters and update target
     482       39492 :     const Vector2p pos_target = pos_target_cm.topostype() * 0.01;
     483       39492 :     _pos_control.input_pos_target(pos_target, dt);
     484             : 
     485             :     // update reached_destination
     486       39492 :     if (!_reached_destination) {
     487             :         // calculate position difference between destination and position controller input shaped target
     488       39492 :         Vector2p pos_target_diff = pos_target - _pos_control.get_pos_target();
     489             :         // vehicle has reached destination when the target is within 1cm of the destination and vehicle is within waypoint radius
     490       39492 :         _reached_destination = (pos_target_diff.length_squared() < sq(0.01)) && (_pos_control.get_pos_error().length_squared() < sq(_radius));
     491             :     }
     492             : }
     493             : 
     494             : // update straight-line distance and bearing from vehicle's current position to destination
     495      171336 : void AR_WPNav::update_distance_and_bearing_to_destination()
     496             : {
     497             :     // if no current location leave distance unchanged
     498      171336 :     Location current_loc;
     499      171336 :     if (!_orig_and_dest_valid || !AP::ahrs().get_location(current_loc)) {
     500           0 :         _distance_to_destination = 0.0f;
     501           0 :         _wp_bearing_cd = 0.0f;
     502           0 :         return;
     503             :     }
     504      171336 :     _distance_to_destination = current_loc.get_distance(_destination);
     505      171336 :     _wp_bearing_cd = current_loc.get_bearing_to(_destination);
     506             : }
     507             : 
     508             : // calculate steering and speed to drive along line from origin to destination waypoint
     509      170371 : void AR_WPNav::update_steering_and_speed(const Location &current_loc, float dt)
     510             : {
     511      170371 :     _cross_track_error = calc_crosstrack_error(current_loc);
     512             : 
     513             :     // update position controller
     514      170371 :     _pos_control.set_reversed(_reversed);
     515      170371 :     _pos_control.update(dt);
     516             : 
     517             :     // handle pivot turns
     518      170371 :     if (_pivot.active()) {
     519             :         // decelerate to zero
     520           0 :         _desired_speed_limited = _atc.get_desired_speed_accel_limited(0.0f, dt);
     521           0 :         _desired_heading_cd = _reversed ? wrap_360_cd(oa_wp_bearing_cd() + 18000) : oa_wp_bearing_cd();
     522           0 :         _desired_turn_rate_rads = is_zero(_desired_speed_limited) ? _pivot.get_turn_rate_rads(_desired_heading_cd * 0.01, dt) : 0;
     523           0 :         _desired_lat_accel = 0.0f;
     524             :     } else {
     525      170371 :         _desired_speed_limited = _pos_control.get_desired_speed();
     526      170371 :         _desired_turn_rate_rads = _pos_control.get_desired_turn_rate_rads();
     527      170371 :         _desired_lat_accel = _pos_control.get_desired_lat_accel();
     528             :     }
     529      170371 : }
     530             : 
     531             : // settor to allow vehicle code to provide turn related param values to this library (should be updated regularly)
     532       12526 : void AR_WPNav::set_turn_params(float turn_radius, bool pivot_possible)
     533             : {
     534       12526 :     _turn_radius = pivot_possible ? 0.0 : turn_radius;
     535       12526 :     _pivot.enable(pivot_possible);
     536       12526 : }
     537             : 
     538             : // calculate the crosstrack error
     539             : // value is negative when the vehicle is on the path's left side
     540      170371 : float AR_WPNav::calc_crosstrack_error(const Location& current_loc) const
     541             : {
     542      170371 :     if (!_orig_and_dest_valid) {
     543           0 :         return 0.0f;
     544             :     }
     545             : 
     546             :     // get object avoidance adjusted origin and destination
     547      170371 :     const Location &orig = get_oa_origin();
     548      170371 :     const Location &dest = get_oa_destination();
     549             : 
     550             :     // calculate the NE position of destination relative to origin
     551      170371 :     Vector2f dest_from_origin = orig.get_distance_NE(dest);
     552             : 
     553             :     // return distance to destination if length of track is very small
     554      170371 :     if (dest_from_origin.length() < 1.0e-6f) {
     555        2608 :         return current_loc.get_distance_NE(dest).length();
     556             :     }
     557             : 
     558             :     // convert to a vector indicating direction only
     559      167763 :     dest_from_origin.normalize();
     560             : 
     561             :     // calculate the NE position of the vehicle relative to origin
     562      167763 :     const Vector2f veh_from_origin = orig.get_distance_NE(current_loc);
     563             : 
     564             :     // calculate distance to target track, for reporting
     565      167763 :     return veh_from_origin % dest_from_origin;
     566             : }
     567             : 
     568             : // calculate yaw change at next waypoint in degrees
     569             : // returns zero if the angle cannot be calculated because some points are on top of others
     570         112 : float AR_WPNav::get_corner_angle(const Location& loc1, const Location& loc2, const Location& loc3) const
     571             : {
     572             :     // sanity check
     573         112 :     if (!loc1.initialised() || !loc2.initialised() || !loc3.initialised()) {
     574           0 :         return 0;
     575             :     }
     576         112 :     const float loc1_to_loc2_deg = loc1.get_bearing_to(loc2) * 0.01;
     577         112 :     const float loc2_to_loc3_deg = loc2.get_bearing_to(loc3) * 0.01;
     578         112 :     const float diff_yaw_deg = wrap_180(loc2_to_loc3_deg - loc1_to_loc2_deg);
     579         112 :     return diff_yaw_deg;
     580             : }
     581             : 
     582             : // helper function to initialise position controller if it hasn't been called recently
     583             : // this should be called before updating the position controller with new targets but after the EKF has a good position estimate
     584      170371 : void AR_WPNav::init_pos_control_if_necessary()
     585             : {
     586             :     // initialise position controller if not called recently
     587      170371 :     if (!_pos_control.is_active()) {
     588          80 :         if (!_pos_control.init()) {
     589             :             // this should never fail because we should always have a valid position estimate at this point
     590           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     591           0 :             return;
     592             :         }
     593             :     }
     594             : }
     595             : 
     596             : // set origin and destination to stopping point
     597         265 : bool AR_WPNav::set_origin_and_destination_to_stopping_point()
     598             : {
     599             :     // initialise origin and destination to stopping point
     600         265 :     Location stopping_loc;
     601         265 :     if (!get_stopping_location(stopping_loc)) {
     602           9 :         return false;
     603             :     }
     604         256 :     _origin = _destination = stopping_loc;
     605         256 :     _orig_and_dest_valid = true;
     606         256 :     return true;
     607             : }
     608             : 
     609             : // check for changes in _base_speed_max or _nudge_speed_max
     610             : // updates position controller limits and recalculate scurve path if required
     611      170371 : void AR_WPNav::update_speed_max()
     612             : {
     613      170371 :     const float speed_max = MAX(_base_speed_max, _nudge_speed_max);
     614             : 
     615             :     // ignore calls that do not change the speed
     616      170371 :     if (is_equal(speed_max, _pos_control.get_speed_max())) {
     617      170355 :         return;
     618             :     }
     619             : 
     620             :     // protect against rapid updates
     621          16 :     const uint32_t now_ms = AP_HAL::millis();
     622          16 :     if (now_ms - _last_speed_update_ms < AR_WPNAV_SPEED_UPDATE_MIN_MS) {
     623           0 :         return;
     624             :     }
     625          16 :     _last_speed_update_ms = now_ms;
     626             : 
     627             :     // update position controller max speed
     628          16 :     _pos_control.set_limits(speed_max, _pos_control.get_accel_max(), _pos_control.get_lat_accel_max(), _pos_control.get_jerk_max());
     629             : 
     630             :     // change track speed
     631          16 :     _scurve_this_leg.set_speed_max(_pos_control.get_speed_max(), _pos_control.get_speed_max(), _pos_control.get_speed_max());
     632          16 :     _scurve_next_leg.set_speed_max(_pos_control.get_speed_max(), _pos_control.get_speed_max(), _pos_control.get_speed_max());
     633             : }

Generated by: LCOV version 1.14