Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AC_WPNav/AC_WPNav.cpp
Views: 1798
#include <AP_HAL/AP_HAL.h>1#include "AC_WPNav.h"23extern const AP_HAL::HAL& hal;45// maximum velocities and accelerations6#define WPNAV_WP_SPEED 1000.0f // default horizontal speed between waypoints in cm/s7#define WPNAV_WP_SPEED_MIN 10.0f // minimum horizontal speed between waypoints in cm/s8#define WPNAV_WP_RADIUS 200.0f // default waypoint radius in cm9#define WPNAV_WP_RADIUS_MIN 5.0f // minimum waypoint radius in cm10#define WPNAV_WP_SPEED_UP 250.0f // default maximum climb velocity11#define WPNAV_WP_SPEED_DOWN 150.0f // default maximum descent velocity12#define WPNAV_WP_ACCEL_Z_DEFAULT 100.0f // default vertical acceleration between waypoints in cm/s/s1314const AP_Param::GroupInfo AC_WPNav::var_info[] = {15// index 0 was used for the old orientation matrix1617// @Param: SPEED18// @DisplayName: Waypoint Horizontal Speed Target19// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain horizontally during a WP mission20// @Units: cm/s21// @Range: 10 200022// @Increment: 5023// @User: Standard24AP_GROUPINFO("SPEED", 0, AC_WPNav, _wp_speed_cms, WPNAV_WP_SPEED),2526// @Param: RADIUS27// @DisplayName: Waypoint Radius28// @Description: Defines the distance from a waypoint, that when crossed indicates the wp has been hit.29// @Units: cm30// @Range: 5 100031// @Increment: 132// @User: Standard33AP_GROUPINFO("RADIUS", 1, AC_WPNav, _wp_radius_cm, WPNAV_WP_RADIUS),3435// @Param: SPEED_UP36// @DisplayName: Waypoint Climb Speed Target37// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain while climbing during a WP mission38// @Units: cm/s39// @Range: 10 100040// @Increment: 5041// @User: Standard42AP_GROUPINFO("SPEED_UP", 2, AC_WPNav, _wp_speed_up_cms, WPNAV_WP_SPEED_UP),4344// @Param: SPEED_DN45// @DisplayName: Waypoint Descent Speed Target46// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain while descending during a WP mission47// @Units: cm/s48// @Range: 10 50049// @Increment: 1050// @User: Standard51AP_GROUPINFO("SPEED_DN", 3, AC_WPNav, _wp_speed_down_cms, WPNAV_WP_SPEED_DOWN),5253// @Param: ACCEL54// @DisplayName: Waypoint Acceleration55// @Description: Defines the horizontal acceleration in cm/s/s used during missions56// @Units: cm/s/s57// @Range: 50 50058// @Increment: 1059// @User: Standard60AP_GROUPINFO("ACCEL", 5, AC_WPNav, _wp_accel_cmss, WPNAV_ACCELERATION),6162// @Param: ACCEL_Z63// @DisplayName: Waypoint Vertical Acceleration64// @Description: Defines the vertical acceleration in cm/s/s used during missions65// @Units: cm/s/s66// @Range: 50 50067// @Increment: 1068// @User: Standard69AP_GROUPINFO("ACCEL_Z", 6, AC_WPNav, _wp_accel_z_cmss, WPNAV_WP_ACCEL_Z_DEFAULT),7071// @Param: RFND_USE72// @DisplayName: Waypoint missions use rangefinder for terrain following73// @Description: This controls if waypoint missions use rangefinder for terrain following74// @Values: 0:Disable,1:Enable75// @User: Advanced76AP_GROUPINFO("RFND_USE", 10, AC_WPNav, _rangefinder_use, 1),7778// @Param: JERK79// @DisplayName: Waypoint Jerk80// @Description: Defines the horizontal jerk in m/s/s used during missions81// @Units: m/s/s/s82// @Range: 1 2083// @User: Standard84AP_GROUPINFO("JERK", 11, AC_WPNav, _wp_jerk, 1.0f),8586// @Param: TER_MARGIN87// @DisplayName: Waypoint Terrain following altitude margin88// @Description: Waypoint Terrain following altitude margin. Vehicle will stop if distance from target altitude is larger than this margin (in meters)89// @Units: m90// @Range: 0.1 10091// @User: Advanced92AP_GROUPINFO("TER_MARGIN", 12, AC_WPNav, _terrain_margin, 10.0),9394// @Param: ACCEL_C95// @DisplayName: Waypoint Cornering Acceleration96// @Description: Defines the maximum cornering acceleration in cm/s/s used during missions. If zero uses 2x accel value.97// @Units: cm/s/s98// @Range: 0 50099// @Increment: 10100// @User: Standard101AP_GROUPINFO("ACCEL_C", 13, AC_WPNav, _wp_accel_c_cmss, 0.0),102103AP_GROUPEND104};105106// Default constructor.107// Note that the Vector/Matrix constructors already implicitly zero108// their values.109//110AC_WPNav::AC_WPNav(const AP_InertialNav& inav, const AP_AHRS_View& ahrs, AC_PosControl& pos_control, const AC_AttitudeControl& attitude_control) :111_inav(inav),112_ahrs(ahrs),113_pos_control(pos_control),114_attitude_control(attitude_control)115{116AP_Param::setup_object_defaults(this, var_info);117118// init flags119_flags.reached_destination = false;120_flags.fast_waypoint = false;121122// initialise old WPNAV_SPEED values123_last_wp_speed_cms = _wp_speed_cms;124_last_wp_speed_up_cms = _wp_speed_up_cms;125_last_wp_speed_down_cms = get_default_speed_down();126}127128// get expected source of terrain data if alt-above-terrain command is executed (used by Copter's ModeRTL)129AC_WPNav::TerrainSource AC_WPNav::get_terrain_source() const130{131// use range finder if connected132if (_rangefinder_available && _rangefinder_use) {133return AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER;134}135#if AP_TERRAIN_AVAILABLE136const AP_Terrain *terrain = AP::terrain();137if (terrain != nullptr && terrain->enabled()) {138return AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE;139} else {140return AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE;141}142#else143return AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE;144#endif145}146147///148/// waypoint navigation149///150151/// wp_and_spline_init - initialise straight line and spline waypoint controllers152/// speed_cms should be a positive value or left at zero to use the default speed153/// stopping_point should be the vehicle's stopping point (equal to the starting point of the next segment) if know or left as zero154/// should be called once before the waypoint controller is used but does not need to be called before subsequent updates to destination155void AC_WPNav::wp_and_spline_init(float speed_cms, Vector3f stopping_point)156{157// check _wp_radius_cm is reasonable158_wp_radius_cm.set_and_save_ifchanged(MAX(_wp_radius_cm, WPNAV_WP_RADIUS_MIN));159160// check _wp_speed161_wp_speed_cms.set_and_save_ifchanged(MAX(_wp_speed_cms, WPNAV_WP_SPEED_MIN));162163// initialise position controller164_pos_control.init_z_controller_stopping_point();165_pos_control.init_xy_controller_stopping_point();166167// initialize the desired wp speed168_check_wp_speed_change = !is_positive(speed_cms);169_wp_desired_speed_xy_cms = is_positive(speed_cms) ? speed_cms : _wp_speed_cms;170_wp_desired_speed_xy_cms = MAX(_wp_desired_speed_xy_cms, WPNAV_WP_SPEED_MIN);171172// initialise position controller speed and acceleration173_pos_control.set_max_speed_accel_xy(_wp_desired_speed_xy_cms, get_wp_acceleration());174_pos_control.set_correction_speed_accel_xy(_wp_desired_speed_xy_cms, get_wp_acceleration());175_pos_control.set_max_speed_accel_z(-get_default_speed_down(), _wp_speed_up_cms, _wp_accel_z_cmss);176_pos_control.set_correction_speed_accel_z(-get_default_speed_down(), _wp_speed_up_cms, _wp_accel_z_cmss);177178// calculate scurve jerk and jerk time179if (!is_positive(_wp_jerk)) {180_wp_jerk.set(get_wp_acceleration());181}182calc_scurve_jerk_and_snap();183184_scurve_prev_leg.init();185_scurve_this_leg.init();186_scurve_next_leg.init();187_track_scalar_dt = 1.0f;188189_flags.reached_destination = true;190_flags.fast_waypoint = false;191192// initialise origin and destination to stopping point193if (stopping_point.is_zero()) {194get_wp_stopping_point(stopping_point);195}196_origin = _destination = stopping_point;197_terrain_alt = false;198_this_leg_is_spline = false;199200// initialise the terrain velocity to the current maximum velocity201_offset_vel = _wp_desired_speed_xy_cms;202_offset_accel = 0.0;203_paused = false;204205// mark as active206_wp_last_update = AP_HAL::millis();207}208209/// set_speed_xy - allows main code to pass target horizontal velocity for wp navigation210void AC_WPNav::set_speed_xy(float speed_cms)211{212// range check target speed and protect against divide by zero213if (speed_cms >= WPNAV_WP_SPEED_MIN && is_positive(_wp_desired_speed_xy_cms)) {214// update horizontal velocity speed offset scalar215_offset_vel = speed_cms * _offset_vel / _wp_desired_speed_xy_cms;216217// initialize the desired wp speed218_wp_desired_speed_xy_cms = speed_cms;219220// update position controller speed and acceleration221_pos_control.set_max_speed_accel_xy(_wp_desired_speed_xy_cms, get_wp_acceleration());222_pos_control.set_correction_speed_accel_xy(_wp_desired_speed_xy_cms, get_wp_acceleration());223224// change track speed225update_track_with_speed_accel_limits();226}227}228229/// set current target climb rate during wp navigation230void AC_WPNav::set_speed_up(float speed_up_cms)231{232_pos_control.set_max_speed_accel_z(_pos_control.get_max_speed_down_cms(), speed_up_cms, _pos_control.get_max_accel_z_cmss());233update_track_with_speed_accel_limits();234}235236/// set current target descent rate during wp navigation237void AC_WPNav::set_speed_down(float speed_down_cms)238{239_pos_control.set_max_speed_accel_z(speed_down_cms, _pos_control.get_max_speed_up_cms(), _pos_control.get_max_accel_z_cmss());240update_track_with_speed_accel_limits();241}242243/// set_wp_destination waypoint using location class244/// returns false if conversion from location to vector from ekf origin cannot be calculated245bool AC_WPNav::set_wp_destination_loc(const Location& destination)246{247bool terr_alt;248Vector3f dest_neu;249250// convert destination location to vector251if (!get_vector_NEU(destination, dest_neu, terr_alt)) {252return false;253}254255// set target as vector from EKF origin256return set_wp_destination(dest_neu, terr_alt);257}258259/// set next destination using location class260/// returns false if conversion from location to vector from ekf origin cannot be calculated261bool AC_WPNav::set_wp_destination_next_loc(const Location& destination)262{263bool terr_alt;264Vector3f dest_neu;265266// convert destination location to vector267if (!get_vector_NEU(destination, dest_neu, terr_alt)) {268return false;269}270271// set target as vector from EKF origin272return set_wp_destination_next(dest_neu, terr_alt);273}274275// get destination as a location. Altitude frame will be above origin or above terrain276// returns false if unable to return a destination (for example if origin has not yet been set)277bool AC_WPNav::get_wp_destination_loc(Location& destination) const278{279if (!AP::ahrs().get_origin(destination)) {280return false;281}282283destination = Location{get_wp_destination(), _terrain_alt ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN};284return true;285}286287/// set_wp_destination - set destination waypoints using position vectors (distance from ekf origin in cm)288/// terrain_alt should be true if destination.z is an altitude above terrain (false if alt-above-ekf-origin)289/// returns false on failure (likely caused by missing terrain data)290bool AC_WPNav::set_wp_destination(const Vector3f& destination, bool terrain_alt)291{292// re-initialise if previous destination has been interrupted293if (!is_active() || !_flags.reached_destination) {294wp_and_spline_init(_wp_desired_speed_xy_cms);295}296297_scurve_prev_leg.init();298float origin_speed = 0.0f;299300// use previous destination as origin301_origin = _destination;302303if (terrain_alt == _terrain_alt) {304if (_this_leg_is_spline) {305// if previous leg was a spline we can use current target velocity vector for origin velocity vector306Vector3f curr_target_vel = _pos_control.get_vel_desired_cms();307curr_target_vel.z -= _pos_control.get_vel_offset_z_cms();308origin_speed = curr_target_vel.length();309} else {310// store previous leg311_scurve_prev_leg = _scurve_this_leg;312}313} else {314315// get current alt above terrain316float origin_terr_offset;317if (!get_terrain_offset(origin_terr_offset)) {318return false;319}320321// convert origin to alt-above-terrain if necessary322if (terrain_alt) {323// new destination is alt-above-terrain, previous destination was alt-above-ekf-origin324_origin.z -= origin_terr_offset;325_pos_control.init_pos_terrain_cm(origin_terr_offset);326} else {327// new destination is alt-above-ekf-origin, previous destination was alt-above-terrain328_origin.z += origin_terr_offset;329_pos_control.init_pos_terrain_cm(0.0);330}331}332333// update destination334_destination = destination;335_terrain_alt = terrain_alt;336337if (_flags.fast_waypoint && !_this_leg_is_spline && !_next_leg_is_spline && !_scurve_next_leg.finished()) {338_scurve_this_leg = _scurve_next_leg;339} else {340_scurve_this_leg.calculate_track(_origin, _destination,341_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),342get_wp_acceleration(), _wp_accel_z_cmss,343_scurve_snap * 100.0f, _scurve_jerk * 100.0f);344if (!is_zero(origin_speed)) {345// rebuild start of scurve if we have a non-zero origin speed346_scurve_this_leg.set_origin_speed_max(origin_speed);347}348}349350_this_leg_is_spline = false;351_scurve_next_leg.init();352_next_destination.zero(); // clear next destination353_flags.fast_waypoint = false; // default waypoint back to slow354_flags.reached_destination = false;355356return true;357}358359/// set next destination using position vector (distance from ekf origin in cm)360/// terrain_alt should be true if destination.z is a desired altitude above terrain361/// provide next_destination362bool AC_WPNav::set_wp_destination_next(const Vector3f& destination, bool terrain_alt)363{364// do not add next point if alt types don't match365if (terrain_alt != _terrain_alt) {366return true;367}368369_scurve_next_leg.calculate_track(_destination, destination,370_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),371get_wp_acceleration(), _wp_accel_z_cmss,372_scurve_snap * 100.0f, _scurve_jerk * 100.0);373if (_this_leg_is_spline) {374const float this_leg_dest_speed_max = _spline_this_leg.get_destination_speed_max();375const float next_leg_origin_speed_max = _scurve_next_leg.set_origin_speed_max(this_leg_dest_speed_max);376_spline_this_leg.set_destination_speed_max(next_leg_origin_speed_max);377}378_next_leg_is_spline = false;379380// next destination provided so fast waypoint381_flags.fast_waypoint = true;382383// record next destination384_next_destination = destination;385386return true;387}388389/// set waypoint destination using NED position vector from ekf origin in meters390bool AC_WPNav::set_wp_destination_NED(const Vector3f& destination_NED)391{392// convert NED to NEU and do not use terrain following393return set_wp_destination(Vector3f(destination_NED.x * 100.0f, destination_NED.y * 100.0f, -destination_NED.z * 100.0f), false);394}395396/// set waypoint destination using NED position vector from ekf origin in meters397bool AC_WPNav::set_wp_destination_next_NED(const Vector3f& destination_NED)398{399// convert NED to NEU and do not use terrain following400return set_wp_destination_next(Vector3f(destination_NED.x * 100.0f, destination_NED.y * 100.0f, -destination_NED.z * 100.0f), false);401}402403/// shifts the origin and destination horizontally to the current position404/// used to reset the track when taking off without horizontal position control405/// relies on set_wp_destination or set_wp_origin_and_destination having been called first406void AC_WPNav::shift_wp_origin_and_destination_to_current_pos_xy()407{408// Reset position controller to current location409_pos_control.init_xy_controller();410411// get current and target locations412const Vector2f& curr_pos = _inav.get_position_xy_cm();413414// shift origin and destination horizontally415_origin.xy() = curr_pos;416_destination.xy() = curr_pos;417}418419/// shifts the origin and destination horizontally to the achievable stopping point420/// used to reset the track when horizontal navigation is enabled after having been disabled (see Copter's wp_navalt_min)421/// relies on set_wp_destination or set_wp_origin_and_destination having been called first422void AC_WPNav::shift_wp_origin_and_destination_to_stopping_point_xy()423{424// relax position control in xy axis425// removing velocity error also impacts stopping point calculation426_pos_control.relax_velocity_controller_xy();427428// get current and target locations429Vector2f stopping_point;430get_wp_stopping_point_xy(stopping_point);431432// shift origin and destination horizontally433_origin.xy() = stopping_point;434_destination.xy() = stopping_point;435436// move pos controller target horizontally437_pos_control.set_pos_desired_xy_cm(stopping_point);438}439440/// get_wp_stopping_point_xy - returns vector to stopping point based on a horizontal position and velocity441void AC_WPNav::get_wp_stopping_point_xy(Vector2f& stopping_point) const442{443Vector2p stop;444_pos_control.get_stopping_point_xy_cm(stop);445stopping_point = stop.tofloat();446}447448/// get_wp_stopping_point - returns vector to stopping point based on 3D position and velocity449void AC_WPNav::get_wp_stopping_point(Vector3f& stopping_point) const450{451Vector3p stop;452_pos_control.get_stopping_point_xy_cm(stop.xy());453_pos_control.get_stopping_point_z_cm(stop.z);454stopping_point = stop.tofloat();455}456457/// advance_wp_target_along_track - move target location along track from origin to destination458bool AC_WPNav::advance_wp_target_along_track(float dt)459{460// calculate terrain adjustments461float terr_offset = 0.0f;462if (_terrain_alt && !get_terrain_offset(terr_offset)) {463return false;464}465const float offset_z_scaler = _pos_control.pos_offset_z_scaler(terr_offset, get_terrain_margin() * 100.0);466467// input shape the terrain offset468_pos_control.set_pos_terrain_target_cm(terr_offset);469470// get position controller's position offset (post input shaping) so it can be used in position error calculation471const Vector3p& psc_pos_offset = _pos_control.get_pos_offset_cm();472473// get current position and adjust altitude to origin and destination's frame (i.e. _frame)474Vector3f curr_pos = _inav.get_position_neu_cm() - psc_pos_offset.tofloat();475curr_pos.z -= terr_offset;476Vector3f curr_target_vel = _pos_control.get_vel_desired_cms();477curr_target_vel.z -= _pos_control.get_vel_offset_z_cms();478479// Use _track_scalar_dt to slow down progression of the position target moving too far in front of aircraft480// _track_scalar_dt does not scale the velocity or acceleration481float track_scaler_dt = 1.0f;482// check target velocity is non-zero483if (is_positive(curr_target_vel.length_squared())) {484Vector3f track_direction = curr_target_vel.normalized();485const float track_error = _pos_control.get_pos_error_cm().dot(track_direction);486const float track_velocity = _inav.get_velocity_neu_cms().dot(track_direction);487// set time scaler to be consistent with the achievable aircraft speed with a 5% buffer for short term variation.488track_scaler_dt = constrain_float(0.05f + (track_velocity - _pos_control.get_pos_xy_p().kP() * track_error) / curr_target_vel.length(), 0.0f, 1.0f);489}490491// Use vel_scaler_dt to slow down the trajectory time492// vel_scaler_dt scales the velocity and acceleration to be kinematically consistent493float vel_scaler_dt = 1.0;494if (is_positive(_wp_desired_speed_xy_cms)) {495update_vel_accel(_offset_vel, _offset_accel, dt, 0.0, 0.0);496const float vel_input = !_paused ? _wp_desired_speed_xy_cms * offset_z_scaler : 0.0;497shape_vel_accel(vel_input, 0.0, _offset_vel, _offset_accel, -get_wp_acceleration(), get_wp_acceleration(),498_pos_control.get_shaping_jerk_xy_cmsss(), dt, true);499vel_scaler_dt = _offset_vel / _wp_desired_speed_xy_cms;500}501502// change s-curve time speed with a time constant of maximum acceleration / maximum jerk503float track_scaler_tc = 1.0f;504if (!is_zero(_wp_jerk)) {505track_scaler_tc = 0.01f * get_wp_acceleration()/_wp_jerk;506}507_track_scalar_dt += (track_scaler_dt - _track_scalar_dt) * (dt / track_scaler_tc);508509// target position, velocity and acceleration from straight line or spline calculators510Vector3f target_pos, target_vel, target_accel;511512bool s_finished;513if (!_this_leg_is_spline) {514// update target position, velocity and acceleration515target_pos = _origin;516s_finished = _scurve_this_leg.advance_target_along_track(_scurve_prev_leg, _scurve_next_leg, _wp_radius_cm, get_corner_acceleration(), _flags.fast_waypoint, _track_scalar_dt * vel_scaler_dt * dt, target_pos, target_vel, target_accel);517} else {518// splinetarget_vel519target_vel = curr_target_vel;520_spline_this_leg.advance_target_along_track(_track_scalar_dt * vel_scaler_dt * dt, target_pos, target_vel);521s_finished = _spline_this_leg.reached_destination();522}523524Vector3f accel_offset;525if (is_positive(target_vel.length_squared())) {526Vector3f track_direction = target_vel.normalized();527accel_offset = track_direction * _offset_accel * target_vel.length() / _wp_desired_speed_xy_cms;528}529530target_vel *= vel_scaler_dt;531target_accel *= sq(vel_scaler_dt);532target_accel += accel_offset;533534// pass new target to the position controller535_pos_control.set_pos_vel_accel(target_pos.topostype(), target_vel, target_accel);536537// check if we've reached the waypoint538if (!_flags.reached_destination) {539if (s_finished) {540// "fast" waypoints are complete once the intermediate point reaches the destination541if (_flags.fast_waypoint) {542_flags.reached_destination = true;543} else {544// regular waypoints also require the copter to be within the waypoint radius545const Vector3f dist_to_dest = curr_pos - _destination;546if (dist_to_dest.length_squared() <= sq(_wp_radius_cm)) {547_flags.reached_destination = true;548}549}550}551}552553// successfully advanced along track554return true;555}556557/// recalculate path with update speed and/or acceleration limits558void AC_WPNav::update_track_with_speed_accel_limits()559{560// update this leg561if (_this_leg_is_spline) {562_spline_this_leg.set_speed_accel(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),563get_wp_acceleration(), _wp_accel_z_cmss);564} else {565_scurve_this_leg.set_speed_max(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms());566}567568// update next leg569if (_next_leg_is_spline) {570_spline_next_leg.set_speed_accel(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),571get_wp_acceleration(), _wp_accel_z_cmss);572} else {573_scurve_next_leg.set_speed_max(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms());574}575}576577/// get_wp_distance_to_destination - get horizontal distance to destination in cm578float AC_WPNav::get_wp_distance_to_destination() const579{580return get_horizontal_distance_cm(_inav.get_position_xy_cm(), _destination.xy());581}582583/// get_wp_bearing_to_destination - get bearing to next waypoint in centi-degrees584int32_t AC_WPNav::get_wp_bearing_to_destination() const585{586return get_bearing_cd(_inav.get_position_xy_cm(), _destination.xy());587}588589/// update_wpnav - run the wp controller - should be called at 100hz or higher590bool AC_WPNav::update_wpnav()591{592// check for changes in speed parameter values593if (_check_wp_speed_change) {594if (!is_equal(_wp_speed_cms.get(), _last_wp_speed_cms)) {595set_speed_xy(_wp_speed_cms);596_last_wp_speed_cms = _wp_speed_cms;597}598}599if (!is_equal(_wp_speed_up_cms.get(), _last_wp_speed_up_cms)) {600set_speed_up(_wp_speed_up_cms);601_last_wp_speed_up_cms = _wp_speed_up_cms;602}603if (!is_equal(_wp_speed_down_cms.get(), _last_wp_speed_down_cms)) {604set_speed_down(_wp_speed_down_cms);605_last_wp_speed_down_cms = _wp_speed_down_cms;606}607608// advance the target if necessary609bool ret = true;610if (!advance_wp_target_along_track(_pos_control.get_dt())) {611// To-Do: handle inability to advance along track (probably because of missing terrain data)612ret = false;613}614615_pos_control.update_xy_controller();616617_wp_last_update = AP_HAL::millis();618619return ret;620}621622// returns true if update_wpnav has been run very recently623bool AC_WPNav::is_active() const624{625return (AP_HAL::millis() - _wp_last_update) < 200;626}627628// force stopping at next waypoint. Used by Dijkstra's object avoidance when path from destination to next destination is not clear629// only affects regular (e.g. non-spline) waypoints630// returns true if this had any affect on the path631bool AC_WPNav::force_stop_at_next_wp()632{633// exit immediately if vehicle was going to stop anyway634if (!_flags.fast_waypoint) {635return false;636}637638_flags.fast_waypoint = false;639640// update this_leg's final velocity and next leg's initial velocity to zero641if (!_this_leg_is_spline) {642_scurve_this_leg.set_destination_speed_max(0);643}644if (!_next_leg_is_spline) {645_scurve_next_leg.init();646}647648return true;649}650651// get terrain's altitude (in cm above the ekf origin) at the current position (+ve means terrain below vehicle is above ekf origin's altitude)652bool AC_WPNav::get_terrain_offset(float& offset_cm)653{654// calculate offset based on source (rangefinder or terrain database)655switch (get_terrain_source()) {656case AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE:657return false;658case AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER:659if (_rangefinder_healthy) {660offset_cm = _rangefinder_terrain_offset_cm;661return true;662}663return false;664case AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE:665#if AP_TERRAIN_AVAILABLE666float terr_alt = 0.0f;667AP_Terrain *terrain = AP::terrain();668if (terrain != nullptr &&669terrain->height_above_terrain(terr_alt, true)) {670offset_cm = _inav.get_position_z_up_cm() - (terr_alt * 100.0);671return true;672}673#endif674return false;675}676677// we should never get here678return false;679}680681///682/// spline methods683///684685/// set_spline_destination waypoint using location class686/// returns false if conversion from location to vector from ekf origin cannot be calculated687/// next_destination should be the next segment's destination688/// next_is_spline should be true if path to next_destination should be a spline689bool AC_WPNav::set_spline_destination_loc(const Location& destination, const Location& next_destination, bool next_is_spline)690{691// convert destination location to vector692Vector3f dest_neu;693bool dest_terr_alt;694if (!get_vector_NEU(destination, dest_neu, dest_terr_alt)) {695return false;696}697698// convert next destination to vector699Vector3f next_dest_neu;700bool next_dest_terr_alt;701if (!get_vector_NEU(next_destination, next_dest_neu, next_dest_terr_alt)) {702return false;703}704705// set target as vector from EKF origin706return set_spline_destination(dest_neu, dest_terr_alt, next_dest_neu, next_dest_terr_alt, next_is_spline);707}708709/// set next destination (e.g. the one after the current destination) as a spline segment specified as a location710/// returns false if conversion from location to vector from ekf origin cannot be calculated711/// next_next_destination should be the next segment's destination712bool AC_WPNav::set_spline_destination_next_loc(const Location& next_destination, const Location& next_next_destination, bool next_next_is_spline)713{714// convert next_destination location to vector715Vector3f next_dest_neu;716bool next_dest_terr_alt;717if (!get_vector_NEU(next_destination, next_dest_neu, next_dest_terr_alt)) {718return false;719}720721// convert next_next_destination to vector722Vector3f next_next_dest_neu;723bool next_next_dest_terr_alt;724if (!get_vector_NEU(next_next_destination, next_next_dest_neu, next_next_dest_terr_alt)) {725return false;726}727728// set target as vector from EKF origin729return set_spline_destination_next(next_dest_neu, next_dest_terr_alt, next_next_dest_neu, next_next_dest_terr_alt, next_next_is_spline);730}731732/// set_spline_destination waypoint using position vector (distance from ekf origin in cm)733/// terrain_alt should be true if destination.z is a desired altitude above terrain (false if its desired altitudes above ekf origin)734/// next_destination should be set to the next segment's destination735/// next_terrain_alt should be true if next_destination.z is a desired altitude above terrain (false if its desired altitudes above ekf origin)736/// next_destination.z must be in the same "frame" as destination.z (i.e. if destination is a alt-above-terrain, next_destination should be too)737bool AC_WPNav::set_spline_destination(const Vector3f& destination, bool terrain_alt, const Vector3f& next_destination, bool next_terrain_alt, bool next_is_spline)738{739// re-initialise if previous destination has been interrupted740if (!is_active() || !_flags.reached_destination) {741wp_and_spline_init(_wp_desired_speed_xy_cms);742}743744// update spline calculators speeds and accelerations745_spline_this_leg.set_speed_accel(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),746_pos_control.get_max_accel_xy_cmss(), _pos_control.get_max_accel_z_cmss());747748// calculate origin and origin velocity vector749Vector3f origin_vector;750if (terrain_alt == _terrain_alt) {751if (_flags.fast_waypoint) {752// calculate origin vector753if (_this_leg_is_spline) {754// if previous leg was a spline we can use destination velocity vector for origin velocity vector755origin_vector = _spline_this_leg.get_destination_vel();756} else {757// use direction of the previous straight line segment758origin_vector = _destination - _origin;759}760}761762// use previous destination as origin763_origin = _destination;764} else {765766// use previous destination as origin767_origin = _destination;768769// get current alt above terrain770float origin_terr_offset;771if (!get_terrain_offset(origin_terr_offset)) {772return false;773}774775// convert origin to alt-above-terrain if necessary776if (terrain_alt) {777// new destination is alt-above-terrain, previous destination was alt-above-ekf-origin778_origin.z -= origin_terr_offset;779_pos_control.init_pos_terrain_cm(origin_terr_offset);780} else {781// new destination is alt-above-ekf-origin, previous destination was alt-above-terrain782_origin.z += origin_terr_offset;783_pos_control.init_pos_terrain_cm(0.0);784}785}786787// store destination location788_destination = destination;789_terrain_alt = terrain_alt;790791// calculate destination velocity vector792Vector3f destination_vector;793if (terrain_alt == next_terrain_alt) {794if (next_is_spline) {795// leave this segment moving parallel to vector from origin to next destination796destination_vector = next_destination - _origin;797} else {798// leave this segment moving parallel to next segment799destination_vector = next_destination - _destination;800}801}802_flags.fast_waypoint = !destination_vector.is_zero();803804// setup spline leg805_spline_this_leg.set_origin_and_destination(_origin, _destination, origin_vector, destination_vector);806_this_leg_is_spline = true;807_flags.reached_destination = false;808809return true;810}811812/// set next destination (e.g. the one after the current destination) as an offset (in cm, NEU frame) from the EKF origin813/// next_terrain_alt should be true if next_destination.z is a desired altitude above terrain (false if its desired altitudes above ekf origin)814/// next_next_destination should be set to the next segment's destination815/// next_next_terrain_alt should be true if next_next_destination.z is a desired altitude above terrain (false if it is desired altitude above ekf origin)816/// next_next_destination.z must be in the same "frame" as destination.z (i.e. if next_destination is a alt-above-terrain, next_next_destination should be too)817bool AC_WPNav::set_spline_destination_next(const Vector3f& next_destination, bool next_terrain_alt, const Vector3f& next_next_destination, bool next_next_terrain_alt, bool next_next_is_spline)818{819// do not add next point if alt types don't match820if (next_terrain_alt != _terrain_alt) {821return true;822}823824// calculate origin and origin velocity vector825Vector3f origin_vector;826if (_this_leg_is_spline) {827// if previous leg was a spline we can use destination velocity vector for origin velocity vector828origin_vector = _spline_this_leg.get_destination_vel();829} else {830// use the direction of the previous straight line segment831origin_vector = _destination - _origin;832}833834// calculate destination velocity vector835Vector3f destination_vector;836if (next_terrain_alt == next_next_terrain_alt) {837if (next_next_is_spline) {838// leave this segment moving parallel to vector from this leg's origin (i.e. prev leg's destination) to next next destination839destination_vector = next_next_destination - _destination;840} else {841// leave this segment moving parallel to next segment842destination_vector = next_next_destination - next_destination;843}844}845846// update spline calculators speeds and accelerations847_spline_next_leg.set_speed_accel(_pos_control.get_max_speed_xy_cms(), _pos_control.get_max_speed_up_cms(), _pos_control.get_max_speed_down_cms(),848_pos_control.get_max_accel_xy_cmss(), _pos_control.get_max_accel_z_cmss());849850// setup next spline leg. Note this could be made local851_spline_next_leg.set_origin_and_destination(_destination, next_destination, origin_vector, destination_vector);852_next_leg_is_spline = true;853854// next destination provided so fast waypoint855_flags.fast_waypoint = true;856857// update this_leg's final velocity to match next spline leg858if (!_this_leg_is_spline) {859_scurve_this_leg.set_destination_speed_max(_spline_next_leg.get_origin_speed_max());860} else {861_spline_this_leg.set_destination_speed_max(_spline_next_leg.get_origin_speed_max());862}863864return true;865}866867// convert location to vector from ekf origin. terrain_alt is set to true if resulting vector's z-axis should be treated as alt-above-terrain868// returns false if conversion failed (likely because terrain data was not available)869bool AC_WPNav::get_vector_NEU(const Location &loc, Vector3f &vec, bool &terrain_alt)870{871// convert location to NE vector2f872Vector2f res_vec;873if (!loc.get_vector_xy_from_origin_NE(res_vec)) {874return false;875}876877// convert altitude878if (loc.get_alt_frame() == Location::AltFrame::ABOVE_TERRAIN) {879int32_t terr_alt;880if (!loc.get_alt_cm(Location::AltFrame::ABOVE_TERRAIN, terr_alt)) {881return false;882}883vec.z = terr_alt;884terrain_alt = true;885} else {886terrain_alt = false;887int32_t temp_alt;888if (!loc.get_alt_cm(Location::AltFrame::ABOVE_ORIGIN, temp_alt)) {889return false;890}891vec.z = temp_alt;892terrain_alt = false;893}894895// copy xy (we do this to ensure we do not adjust vector unless the overall conversion is successful896vec.x = res_vec.x;897vec.y = res_vec.y;898899return true;900}901902// helper function to calculate scurve jerk and jerk_time values903// updates _scurve_jerk and _scurve_snap904void AC_WPNav::calc_scurve_jerk_and_snap()905{906// calculate jerk907_scurve_jerk = MIN(_attitude_control.get_ang_vel_roll_max_rads() * GRAVITY_MSS, _attitude_control.get_ang_vel_pitch_max_rads() * GRAVITY_MSS);908if (is_zero(_scurve_jerk)) {909_scurve_jerk = _wp_jerk;910} else {911_scurve_jerk = MIN(_scurve_jerk, _wp_jerk);912}913914// calculate maximum snap915// Snap (the rate of change of jerk) uses the attitude control input time constant because multicopters916// lean to accelerate. This means the change in angle is equivalent to the change in acceleration917_scurve_snap = (_scurve_jerk * M_PI) / (2.0 * MAX(_attitude_control.get_input_tc(), 0.1f));918const float snap = MIN(_attitude_control.get_accel_roll_max_radss(), _attitude_control.get_accel_pitch_max_radss()) * GRAVITY_MSS;919if (is_positive(snap)) {920_scurve_snap = MIN(_scurve_snap, snap);921}922// reduce maximum snap by a factor of two from what the aircraft is capable of923_scurve_snap *= 0.5;924}925926927