/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "AR_WPNav_OA.h" #include #include extern const AP_HAL::HAL& hal; // update navigation void AR_WPNav_OA::update(float dt) { // exit immediately if no current location, origin or destination Location current_loc; float speed; if (!hal.util->get_soft_armed() || !is_destination_valid() || !AP::ahrs().get_location(current_loc) || !_atc.get_forward_speed(speed)) { _desired_speed_limited = _atc.get_desired_speed_accel_limited(0.0f, dt); _desired_lat_accel = 0.0f; _desired_turn_rate_rads = 0.0f; _oa_active = false; return; } // run path planning around obstacles bool stop_vehicle = false; // backup _origin, _destination and _next_destination when not doing oa if (!_oa_active) { _origin_oabak = _origin; _destination_oabak = _destination; _next_destination_oabak = _next_destination; } AP_OAPathPlanner *oa = AP_OAPathPlanner::get_singleton(); if (oa != nullptr) { Location oa_origin_new, oa_destination_new, oa_next_destination_new; AP_OAPathPlanner::OAPathPlannerUsed path_planner_used; bool dest_to_next_dest_clear; const AP_OAPathPlanner::OA_RetState oa_retstate = oa->mission_avoidance(current_loc, _origin_oabak, _destination_oabak, _next_destination_oabak, oa_origin_new, oa_destination_new, oa_next_destination_new, dest_to_next_dest_clear, path_planner_used); switch (oa_retstate) { case AP_OAPathPlanner::OA_NOT_REQUIRED: if (_oa_active) { // object avoidance has become inactive so reset target to original destination if (!AR_WPNav::set_desired_location(_destination_oabak)) { // this should never happen because we should have an EKF origin and the destination must be valid INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control); stop_vehicle = true; } _oa_active = false; // ToDo: handle "if (oa->get_options() & AP_OAPathPlanner::OA_OPTION_WP_RESET)" } break; case AP_OAPathPlanner::OA_PROCESSING: case AP_OAPathPlanner::OA_ERROR: // during processing or in case of error, slow vehicle to a stop stop_vehicle = true; _oa_active = false; break; case AP_OAPathPlanner::OA_SUCCESS: // handling of returned destination depends upon path planner used switch (path_planner_used) { case AP_OAPathPlanner::OAPathPlannerUsed::None: case AP_OAPathPlanner::OAPathPlannerUsed::BendyRulerVertical: // this should never happen. this means the path planner has returned success but has returned an invalid planner INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control); _oa_active = false; stop_vehicle = true; return; case AP_OAPathPlanner::OAPathPlannerUsed::Dijkstras: // Dijkstra's. Action is only needed if path planner has just became active or the target destination's lat or lon has changed if (!_oa_active || !oa_destination_new.same_latlon_as(_oa_destination)) { if (AR_WPNav::set_desired_location(oa_destination_new)) { // if new target set successfully, update oa state and destination _oa_active = true; _oa_origin = oa_origin_new; _oa_destination = oa_destination_new; } else { // this should never happen INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control); stop_vehicle = true; } } break; case AP_OAPathPlanner::OAPathPlannerUsed::BendyRulerHorizontal: { // BendyRuler. Action is only needed if path planner has just became active or the target destination's lat or lon has changed if (!_oa_active || !oa_destination_new.same_latlon_as(_oa_destination)) { if (AR_WPNav::set_desired_location_expect_fast_update(oa_destination_new)) { // if new target set successfully, update oa state and destination _oa_active = true; _oa_origin = oa_origin_new; _oa_destination = oa_destination_new; } else { // this should never happen INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control); stop_vehicle = true; } } } break; } // switch (path_planner_used) { } // switch (oa_retstate) { } // if (oa != nullptr) { update_oa_distance_and_bearing_to_destination(); // handle stopping vehicle if avoidance has failed if (stop_vehicle) { // decelerate to speed to zero and set turn rate to zero _desired_speed_limited = _atc.get_desired_speed_accel_limited(0.0f, dt); _desired_lat_accel = 0.0f; _desired_turn_rate_rads = 0.0f; return; } // call parent update AR_WPNav::update(dt); } // set desired location and (optionally) next_destination // next_destination should be provided if known to allow smooth cornering bool AR_WPNav_OA::set_desired_location(const Location& destination, Location next_destination) { const bool ret = AR_WPNav::set_desired_location(destination, next_destination); if (ret) { // disable object avoidance, it will be re-enabled (if necessary) on next update _oa_active = false; } return ret; } // true if vehicle has reached desired location. defaults to true because this is normally used by missions and we do not want the mission to become stuck bool AR_WPNav_OA::reached_destination() const { // object avoidance should always be deactivated before reaching final destination if (_oa_active) { return false; } return AR_WPNav::reached_destination(); } // get object avoidance adjusted origin. Note: this is not guaranteed to be valid (i.e. _orig_and_dest_valid is not checked) const Location &AR_WPNav_OA::get_oa_origin() const { if (_oa_active) { return _oa_origin; } return _origin; } // get object avoidance adjusted destination. Note: this is not guaranteed to be valid (i.e. _orig_and_dest_valid is not checked) const Location &AR_WPNav_OA::get_oa_destination() const { if (_oa_active) { return _oa_destination; } return AR_WPNav::get_oa_destination(); } // return the heading (in centi-degrees) to the next waypoint accounting for OA, (used by sailboats) float AR_WPNav_OA::oa_wp_bearing_cd() const { if (_oa_active) { return _oa_wp_bearing_cd; } return AR_WPNav::oa_wp_bearing_cd(); } // update distance from vehicle's current position to destination void AR_WPNav_OA::update_oa_distance_and_bearing_to_destination() { // update OA adjusted values Location current_loc; if (_oa_active && AP::ahrs().get_location(current_loc)) { _oa_distance_to_destination = current_loc.get_distance(_oa_destination); _oa_wp_bearing_cd = current_loc.get_bearing_to(_oa_destination); } else { _oa_distance_to_destination = AR_WPNav::get_distance_to_destination(); _oa_wp_bearing_cd = AR_WPNav::wp_bearing_cd(); } }