Path: blob/master/libraries/AC_WPNav/AC_WPNav_OA.cpp
9660 views
#include "AC_WPNav_config.h"12#if AC_WPNAV_OA_ENABLED34#include <AP_Math/control.h>5#include <AP_InternalError/AP_InternalError.h>6#include "AC_WPNav_OA.h"78AC_WPNav_OA::AC_WPNav_OA(const AP_AHRS_View& ahrs, AC_PosControl& pos_control, const AC_AttitudeControl& attitude_control) :9AC_WPNav(ahrs, pos_control, attitude_control)10{11}1213// Returns the object-avoidance-adjusted waypoint location (in global coordinates).14// Falls back to original destination if OA is not active.15bool AC_WPNav_OA::get_oa_wp_destination(Location& destination) const16{17// Return unmodified global destination if OA is not active18if (_oa_state == AP_OAPathPlanner::OA_NOT_REQUIRED) {19return get_wp_destination_loc(destination);20}2122// OA is active — return path-planner-adjusted intermediate destination23destination = _oa_destination;24return true;25}2627// Sets the waypoint destination using NEU coordinates in centimeters.28// See set_wp_destination_NED_m() for full details.29bool AC_WPNav_OA::set_wp_destination_NEU_cm(const Vector3f& destination_neu_cm, bool is_terrain_alt)30{31// Convert input from NEU centimeters to NED meters and delegate to meter version32Vector3p destination_ned_m = Vector3p(destination_neu_cm.x, destination_neu_cm.y, -destination_neu_cm.z) * 0.01;33return set_wp_destination_NED_m(destination_ned_m, is_terrain_alt);34}3536// Sets the waypoint destination using NED coordinates in meters.37// - destination_ned_m: NED offset from EKF origin in meters.38// - is_terrain_alt: true if the destination_ned_m is relative to the terrain surface.39// - Resets OA state on success.40bool AC_WPNav_OA::set_wp_destination_NED_m(const Vector3p& destination_ned_m, bool is_terrain_alt, float arc_ang_rad)41{42// Call base implementation to set destination and terrain-altitude flag43const bool ret = AC_WPNav::set_wp_destination_NED_m(destination_ned_m, is_terrain_alt, arc_ang_rad);4445// If destination set successfully, reset OA state to inactive46if (ret) {47// reset object avoidance state48_oa_state = AP_OAPathPlanner::OA_NOT_REQUIRED;49}5051return ret;52}5354// Returns the horizontal distance to the final destination in centimeters.55// See get_wp_distance_to_destination_m() for full details.56float AC_WPNav_OA::get_wp_distance_to_destination_cm() const57{58// Convert horizontal distance from meters to centimeters59return get_wp_distance_to_destination_m() * 100.0;60}6162// Returns the horizontal distance to the final destination in meters.63// Ignores OA-adjusted targets and always measures to the original final destination.64float AC_WPNav_OA::get_wp_distance_to_destination_m() const65{66// Return horizontal distance to final destination (ignoring OA intermediate goals)67if (_oa_state == AP_OAPathPlanner::OA_NOT_REQUIRED) {68return AC_WPNav::get_wp_distance_to_destination_m();69}7071// Compute distance to original destination using backed-up NEU position72return get_horizontal_distance(_pos_control.get_pos_estimate_NED_m().xy(), _destination_oabak_ned_m.xy());73}7475// Returns the bearing to the final destination in centidegrees.76// See get_wp_bearing_to_destination_rad() for full details.77int32_t AC_WPNav_OA::get_wp_bearing_to_destination_cd() const78{79// Convert bearing to destination (in radians) to centidegrees80return rad_to_cd(get_wp_bearing_to_destination_rad());81}8283// Returns the bearing to the final destination in radians.84// Ignores OA-adjusted targets and always calculates from original final destination.85float AC_WPNav_OA::get_wp_bearing_to_destination_rad() const86{87// Use base class method if object avoidance is inactive88if (_oa_state == AP_OAPathPlanner::OA_NOT_REQUIRED) {89return AC_WPNav::get_wp_bearing_to_destination_rad();90}9192// Return bearing to the original destination, not the OA-adjusted one93return get_bearing_rad(_pos_control.get_pos_estimate_NED_m().xy().tofloat(), _destination_oabak_ned_m.xy().tofloat());94}9596// Returns true if the vehicle has reached the final destination within radius threshold.97// Ignores OA-adjusted intermediate destinations.98bool AC_WPNav_OA::reached_wp_destination() const99{100// Only consider the waypoint reached if OA is inactive and base class condition is met101return (_oa_state == AP_OAPathPlanner::OA_NOT_REQUIRED) && AC_WPNav::reached_wp_destination();102}103104// Runs the waypoint navigation update loop, including OA path planning logic.105// Delegates to parent class if OA is not active or not required.106bool AC_WPNav_OA::update_wpnav()107{108// Run path planning logic using the active OA planner109AP_OAPathPlanner *oa_ptr = AP_OAPathPlanner::get_singleton();110Location current_loc;111if ((oa_ptr != nullptr) && AP::ahrs().get_location(current_loc)) {112113// Backup current path state before OA modifies it114if (_oa_state == AP_OAPathPlanner::OA_NOT_REQUIRED) {115_origin_oabak_ned_m = _origin_ned_m;116_destination_oabak_ned_m = _destination_ned_m;117_is_terrain_alt_oabak = _is_terrain_alt;118_next_destination_oabak_ned_m = _next_destination_ned_m;119}120121// Convert backup path state to global Location objects for planner input122const Location origin_loc = Location::from_ekf_offset_NED_m(_origin_oabak_ned_m, _is_terrain_alt_oabak ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);123const Location destination_loc = Location::from_ekf_offset_NED_m(_destination_oabak_ned_m, _is_terrain_alt_oabak ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);124const Location next_destination_loc = Location::from_ekf_offset_NED_m(_next_destination_oabak_ned_m, _is_terrain_alt_oabak ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);125Location oa_origin_new, oa_destination_new, oa_next_destination_new;126bool dest_to_next_dest_clear = true;127AP_OAPathPlanner::OAPathPlannerUsed path_planner_used = AP_OAPathPlanner::OAPathPlannerUsed::None;128129// Request obstacle-avoidance-adjusted path from planner130const AP_OAPathPlanner::OA_RetState oa_retstate = oa_ptr->mission_avoidance(current_loc,131origin_loc,132destination_loc,133next_destination_loc,134oa_origin_new,135oa_destination_new,136oa_next_destination_new,137dest_to_next_dest_clear,138path_planner_used);139140switch (oa_retstate) {141142case AP_OAPathPlanner::OA_NOT_REQUIRED:143// OA is no longer needed — restore original destination and optionally set next144if (_oa_state != oa_retstate) {145// object avoidance has become inactive so reset target to original destination146if (!set_wp_destination_NED_m(_destination_oabak_ned_m, _is_terrain_alt_oabak)) {147// trigger terrain failsafe148return false;149}150151// if path from destination to next_destination is clear152if (dest_to_next_dest_clear && (oa_ptr->get_options() & AP_OAPathPlanner::OA_OPTION_FAST_WAYPOINTS)) {153// set next destination if non-zero154if (!_next_destination_oabak_ned_m.is_zero()) {155set_wp_destination_next_NED_m(_next_destination_oabak_ned_m);156}157}158_oa_state = oa_retstate;159}160161// Prevent transitioning past this waypoint if path to next is unclear162// Note that this check is run on every iteration even if the path planner is not active163if (!dest_to_next_dest_clear) {164force_stop_at_next_wp();165}166break;167168case AP_OAPathPlanner::OA_PROCESSING:169// Allow continued movement while OA path is processing if fast-waypointing is enabled170if (oa_ptr->get_options() & AP_OAPathPlanner::OA_OPTION_FAST_WAYPOINTS) {171// if fast waypoint option is set, proceed during processing172break;173}174FALLTHROUGH;175176case AP_OAPathPlanner::OA_ERROR:177// OA temporarily failing — stop vehicle at current position178if ((_oa_state != AP_OAPathPlanner::OA_PROCESSING) && (_oa_state != AP_OAPathPlanner::OA_ERROR)) {179// calculate stopping point180Vector3p stopping_point_ned_m;181get_wp_stopping_point_NED_m(stopping_point_ned_m);182_oa_destination = Location::from_ekf_offset_NED_m(stopping_point_ned_m, Location::AltFrame::ABOVE_ORIGIN);183_oa_next_destination.zero();184if (set_wp_destination_NED_m(stopping_point_ned_m, false)) {185_oa_state = oa_retstate;186}187}188break;189190case AP_OAPathPlanner::OA_SUCCESS:191192// Handle result differently depending on which OA planner was used193switch (path_planner_used) {194195case AP_OAPathPlanner::OAPathPlannerUsed::None:196// this should never happen. this means the path planner has returned success but has failed to set the path planner used197INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);198return false;199200case AP_OAPathPlanner::OAPathPlannerUsed::Dijkstras:201// Dijkstra's. Action is only needed if path planner has just became active or the target destination's lat or lon has changed202// Interpolate altitude and set new target if different or first OA success203if ((_oa_state != AP_OAPathPlanner::OA_SUCCESS) || !oa_destination_new.same_latlon_as(_oa_destination)) {204Location origin_oabak_loc = Location::from_ekf_offset_NED_m(_origin_oabak_ned_m, _is_terrain_alt_oabak ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);205Location destination_oabak_loc = Location::from_ekf_offset_NED_m(_destination_oabak_ned_m, _is_terrain_alt_oabak ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);206oa_destination_new.linearly_interpolate_alt(origin_oabak_loc, destination_oabak_loc);207208// set new OA adjusted destination209if (!set_wp_destination_loc(oa_destination_new)) {210// trigger terrain failsafe211return false;212}213// if new target set successfully, update oa state and destination214_oa_state = oa_retstate;215_oa_destination = oa_destination_new;216217// Set next destination if provided218if ((oa_ptr->get_options() & AP_OAPathPlanner::OA_OPTION_FAST_WAYPOINTS) && !oa_next_destination_new.is_zero()) {219// calculate oa_next_destination_new's altitude using linear interpolation between original origin and destination220// this "next destination" is still an intermediate point between the origin and destination221oa_next_destination_new.linearly_interpolate_alt(origin_oabak_loc, destination_oabak_loc);222if (set_wp_destination_next_loc(oa_next_destination_new)) {223_oa_next_destination = oa_next_destination_new;224}225}226}227break;228229case AP_OAPathPlanner::OAPathPlannerUsed::BendyRulerHorizontal: {230_oa_state = oa_retstate;231_oa_destination = oa_destination_new;232233// Adjust altitude based on current progress along the path234Location target_alt_loc = current_loc;235target_alt_loc.linearly_interpolate_alt(origin_loc, destination_loc);236237// Get terrain offset if needed238float terrain_d_m = 0;239if (_is_terrain_alt_oabak && !get_terrain_D_m(terrain_d_m)) {240// trigger terrain failsafe241return false;242}243244// Convert global destination to NEU vector and pass directly to position controller245Vector2f destination_ne_m;246if (!_oa_destination.get_vector_xy_from_origin_NE_m(destination_ne_m)) {247// this should never happen because we can only get here if we have an EKF origin248INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);249return false;250}251float target_alt_loc_alt_m = 0;252UNUSED_RESULT(target_alt_loc.get_alt_m(target_alt_loc.get_alt_frame(), target_alt_loc_alt_m));253Vector3p destination_ned_m{destination_ne_m.x, destination_ne_m.y, target_alt_loc_alt_m};254255// pass the desired position directly to the position controller256_pos_control.input_pos_NED_m(destination_ned_m, terrain_d_m, 10.0);257258// update horizontal position controller (vertical is updated in vehicle code)259_pos_control.NE_update_controller();260261// return success without calling parent AC_WPNav262return true;263}264265case AP_OAPathPlanner::OAPathPlannerUsed::BendyRulerVertical: {266_oa_state = oa_retstate;267_oa_destination = oa_destination_new;268269// Convert final destination to NEU offset and push to position controller270Vector3p destination_ned_m;271if (!_oa_destination.get_vector_from_origin_NED_m(destination_ned_m)) {272// this should never happen because we can only get here if we have an EKF origin273INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);274return false;275}276277// pass the desired position directly to the position controller as an offset from EKF origin in NEU278_pos_control.input_pos_NED_m(destination_ned_m, 0, 10.0);279280// update horizontal position controller (vertical is updated in vehicle code)281_pos_control.NE_update_controller();282283// return success without calling parent AC_WPNav284return true;285}286287}288}289}290291// Run standard waypoint update if OA was not active or handled above292return AC_WPNav::update_wpnav();293}294295#endif // Ac_WPNAV_OA_ENABLED296297298