AC_Avoid: Added new OA type (Dijkstra + BendyRuler fusion)

This commit is contained in:
Rishabh 2020-08-24 12:34:35 +05:30 committed by Randy Mackay
parent 80bee19bc9
commit caf5bfed59
5 changed files with 93 additions and 28 deletions

View File

@ -79,7 +79,7 @@ AP_OABendyRuler::AP_OABendyRuler()
// run background task to find best path and update avoidance_results // run background task to find best path and update avoidance_results
// returns true and updates origin_new and destination_new if a best path has been found // returns true and updates origin_new and destination_new if a best path has been found
bool AP_OABendyRuler::update(const Location& current_loc, const Location& destination, const Vector2f &ground_speed_vec, Location &origin_new, Location &destination_new) bool AP_OABendyRuler::update(const Location& current_loc, const Location& destination, const Vector2f &ground_speed_vec, Location &origin_new, Location &destination_new, bool proximity_only)
{ {
// bendy ruler always sets origin to current_loc // bendy ruler always sets origin to current_loc
origin_new = current_loc; origin_new = current_loc;
@ -114,20 +114,20 @@ bool AP_OABendyRuler::update(const Location& current_loc, const Location& destin
switch (get_type()) { switch (get_type()) {
case OABendyType::OA_BENDY_VERTICAL: case OABendyType::OA_BENDY_VERTICAL:
#if VERTICAL_ENABLED #if VERTICAL_ENABLED
ret = search_vertical_path(current_loc, destination, destination_new, lookahead_step1_dist, lookahead_step2_dist, bearing_to_dest, distance_to_dest); ret = search_vertical_path(current_loc, destination, destination_new, lookahead_step1_dist, lookahead_step2_dist, bearing_to_dest, distance_to_dest, proximity_only);
break; break;
#endif #endif
case OABendyType::OA_BENDY_HORIZONTAL: case OABendyType::OA_BENDY_HORIZONTAL:
default: default:
ret = search_xy_path(current_loc, destination, ground_course_deg, destination_new, lookahead_step1_dist, lookahead_step2_dist, bearing_to_dest, distance_to_dest); ret = search_xy_path(current_loc, destination, ground_course_deg, destination_new, lookahead_step1_dist, lookahead_step2_dist, bearing_to_dest, distance_to_dest, proximity_only);
} }
return ret; return ret;
} }
// Search for path in the horizontal directions // Search for path in the horizontal directions
bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location& destination, float ground_course_deg, Location &destination_new, float lookahead_step1_dist, float lookahead_step2_dist, float bearing_to_dest, float distance_to_dest ) bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location& destination, float ground_course_deg, Location &destination_new, float lookahead_step1_dist, float lookahead_step2_dist, float bearing_to_dest, float distance_to_dest, bool proximity_only)
{ {
// check OA_BEARING_INC definition allows checking in all directions // check OA_BEARING_INC definition allows checking in all directions
static_assert(360 % OA_BENDYRULER_BEARING_INC_XY == 0, "check 360 is a multiple of OA_BEARING_INC"); static_assert(360 % OA_BENDYRULER_BEARING_INC_XY == 0, "check 360 is a multiple of OA_BEARING_INC");
@ -157,7 +157,7 @@ bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location
test_loc.offset_bearing(bearing_test, lookahead_step1_dist); test_loc.offset_bearing(bearing_test, lookahead_step1_dist);
// calculate margin from obstacles for this scenario // calculate margin from obstacles for this scenario
float margin = calc_avoidance_margin(current_loc, test_loc); float margin = calc_avoidance_margin(current_loc, test_loc, proximity_only);
if (margin > best_margin) { if (margin > best_margin) {
best_margin_bearing = bearing_test; best_margin_bearing = bearing_test;
best_margin = margin; best_margin = margin;
@ -184,7 +184,7 @@ bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location
test_loc2.offset_bearing(bearing_test2, distance2); test_loc2.offset_bearing(bearing_test2, distance2);
// calculate minimum margin to fence and obstacles for this scenario // calculate minimum margin to fence and obstacles for this scenario
float margin2 = calc_avoidance_margin(test_loc, test_loc2); float margin2 = calc_avoidance_margin(test_loc, test_loc2, proximity_only);
if (margin2 > _margin_max) { if (margin2 > _margin_max) {
// if the chosen direction is directly towards the destination avoidance can be turned off // if the chosen direction is directly towards the destination avoidance can be turned off
// i == 0 && j == 0 implies no deviation from bearing to destination // i == 0 && j == 0 implies no deviation from bearing to destination
@ -192,7 +192,7 @@ bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location
float final_bearing = bearing_test; float final_bearing = bearing_test;
float final_margin = margin; float final_margin = margin;
// check if we need ignore test_bearing and continue on previous bearing // check if we need ignore test_bearing and continue on previous bearing
const bool ignore_bearing_change = resist_bearing_change(destination, current_loc, active, bearing_test, lookahead_step1_dist, margin, _destination_prev,_bearing_prev, final_bearing, final_margin); const bool ignore_bearing_change = resist_bearing_change(destination, current_loc, active, bearing_test, lookahead_step1_dist, margin, _destination_prev,_bearing_prev, final_bearing, final_margin, proximity_only);
// all good, now project in the chosen direction by the full distance // all good, now project in the chosen direction by the full distance
destination_new = current_loc; destination_new = current_loc;
@ -230,7 +230,7 @@ bool AP_OABendyRuler::search_xy_path(const Location& current_loc, const Location
} }
// Search for path in the vertical directions // Search for path in the vertical directions
bool AP_OABendyRuler::search_vertical_path(const Location& current_loc, const Location& destination,Location &destination_new, const float &lookahead_step1_dist, const float &lookahead_step2_dist, const float &bearing_to_dest, const float &distance_to_dest) bool AP_OABendyRuler::search_vertical_path(const Location& current_loc, const Location& destination,Location &destination_new, const float &lookahead_step1_dist, const float &lookahead_step2_dist, const float &bearing_to_dest, const float &distance_to_dest, bool proximity_only)
{ {
// check OA_BEARING_INC_VERTICAL definition allows checking in all directions // check OA_BEARING_INC_VERTICAL definition allows checking in all directions
static_assert(360 % OA_BENDYRULER_BEARING_INC_VERTICAL == 0, "check 360 is a multiple of OA_BEARING_INC_VERTICAL"); static_assert(360 % OA_BENDYRULER_BEARING_INC_VERTICAL == 0, "check 360 is a multiple of OA_BEARING_INC_VERTICAL");
@ -254,7 +254,7 @@ bool AP_OABendyRuler::search_vertical_path(const Location& current_loc, const Lo
test_loc.offset_bearing_and_pitch(bearing_to_dest, pitch_delta, lookahead_step1_dist); test_loc.offset_bearing_and_pitch(bearing_to_dest, pitch_delta, lookahead_step1_dist);
// calculate margin from obstacles for this scenario // calculate margin from obstacles for this scenario
float margin = calc_avoidance_margin(current_loc, test_loc); float margin = calc_avoidance_margin(current_loc, test_loc, proximity_only);
if (margin > best_margin) { if (margin > best_margin) {
best_margin_pitch = pitch_delta; best_margin_pitch = pitch_delta;
@ -282,18 +282,18 @@ bool AP_OABendyRuler::search_vertical_path(const Location& current_loc, const Lo
test_loc2.offset_bearing_and_pitch(bearing_to_dest2, bearing_test2 ,distance2); test_loc2.offset_bearing_and_pitch(bearing_to_dest2, bearing_test2 ,distance2);
// calculate minimum margin to fence and obstacles for this scenario // calculate minimum margin to fence and obstacles for this scenario
float margin2 = calc_avoidance_margin(test_loc, test_loc2); float margin2 = calc_avoidance_margin(test_loc, test_loc2, proximity_only);
if (margin2 > _margin_max) { if (margin2 > _margin_max) {
// if the chosen direction is directly towards the destination we might turn off avoidance // if the chosen direction is directly towards the destination we might turn off avoidance
// i == 0 && j == 0 implies no deviation from bearing to destination // i == 0 && j == 0 implies no deviation from bearing to destination
bool active = (i != 0 || j != 0); bool active = (i != 0 || j != 0);
if (!active) { if (!active) {
// do a sub test to confirm if we should really turn of BendyRuler // do a sub test for proximity obstacles to confirm if we should really turn of BendyRuler
const float sub_test_pitch_step2[] {-90.0f, 90.0f}; const float sub_test_pitch_step2[] {-90.0f, 90.0f};
for (uint8_t k = 0; k < ARRAY_SIZE(sub_test_pitch_step2); k++) { for (uint8_t k = 0; k < ARRAY_SIZE(sub_test_pitch_step2); k++) {
Location test_loc_sub_test = test_loc; Location test_loc_sub_test = test_loc;
test_loc_sub_test.offset_bearing_and_pitch(bearing_to_dest2, sub_test_pitch_step2[k], _margin_max); test_loc_sub_test.offset_bearing_and_pitch(bearing_to_dest2, sub_test_pitch_step2[k], _margin_max);
float margin_sub_test = calc_avoidance_margin(test_loc, test_loc_sub_test); float margin_sub_test = calc_avoidance_margin(test_loc, test_loc_sub_test, true);
if (margin_sub_test < _margin_max) { if (margin_sub_test < _margin_max) {
// BendyRuler will remain active // BendyRuler will remain active
active = true; active = true;
@ -361,7 +361,7 @@ Therefore, this method attempts to avoid changing direction of the vehicle by mo
unless the new margin is atleast _bendy_ratio times better than the margin with previously calculated bearing. unless the new margin is atleast _bendy_ratio times better than the margin with previously calculated bearing.
We return true if we have resisted the change and will follow the last calculated bearing. We return true if we have resisted the change and will follow the last calculated bearing.
*/ */
bool AP_OABendyRuler::resist_bearing_change(const Location &destination, const Location &current_loc, bool active, float bearing_test, float lookahead_step1_dist, float margin, Location &prev_dest, float &prev_bearing, float &final_bearing, float &final_margin) const bool AP_OABendyRuler::resist_bearing_change(const Location &destination, const Location &current_loc, bool active, float bearing_test, float lookahead_step1_dist, float margin, Location &prev_dest, float &prev_bearing, float &final_bearing, float &final_margin, bool proximity_only) const
{ {
bool resisted_change = false; bool resisted_change = false;
// see if there was a change in destination, if so, do not resist changing bearing // see if there was a change in destination, if so, do not resist changing bearing
@ -378,7 +378,7 @@ bool AP_OABendyRuler::resist_bearing_change(const Location &destination, const L
// check margin in last bearing's direction // check margin in last bearing's direction
Location test_loc_previous_bearing = current_loc; Location test_loc_previous_bearing = current_loc;
test_loc_previous_bearing.offset_bearing(wrap_180(prev_bearing), lookahead_step1_dist); test_loc_previous_bearing.offset_bearing(wrap_180(prev_bearing), lookahead_step1_dist);
float previous_bearing_margin = calc_avoidance_margin(current_loc,test_loc_previous_bearing); float previous_bearing_margin = calc_avoidance_margin(current_loc,test_loc_previous_bearing, proximity_only);
if (margin < (_bendy_ratio * previous_bearing_margin)) { if (margin < (_bendy_ratio * previous_bearing_margin)) {
// don't change direction abruptly. If margin difference is not significant, follow the last direction // don't change direction abruptly. If margin difference is not significant, follow the last direction
@ -400,11 +400,21 @@ bool AP_OABendyRuler::resist_bearing_change(const Location &destination, const L
} }
// calculate minimum distance between a segment and any obstacle // calculate minimum distance between a segment and any obstacle
float AP_OABendyRuler::calc_avoidance_margin(const Location &start, const Location &end) const float AP_OABendyRuler::calc_avoidance_margin(const Location &start, const Location &end, bool proximity_only) const
{ {
float margin_min = FLT_MAX; float margin_min = FLT_MAX;
float latest_margin; float latest_margin;
if (calc_margin_from_object_database(start, end, latest_margin)) {
margin_min = MIN(margin_min, latest_margin);
}
if (proximity_only) {
// only need margin from proximity data
return margin_min;
}
if (calc_margin_from_circular_fence(start, end, latest_margin)) { if (calc_margin_from_circular_fence(start, end, latest_margin)) {
margin_min = MIN(margin_min, latest_margin); margin_min = MIN(margin_min, latest_margin);
} }
@ -418,10 +428,6 @@ float AP_OABendyRuler::calc_avoidance_margin(const Location &start, const Locati
} }
#endif #endif
if (calc_margin_from_object_database(start, end, latest_margin)) {
margin_min = MIN(margin_min, latest_margin);
}
if (calc_margin_from_inclusion_and_exclusion_polygons(start, end, latest_margin)) { if (calc_margin_from_inclusion_and_exclusion_polygons(start, end, latest_margin)) {
margin_min = MIN(margin_min, latest_margin); margin_min = MIN(margin_min, latest_margin);
} }
@ -649,6 +655,9 @@ bool AP_OABendyRuler::calc_margin_from_object_database(const Location &start, co
if (!start.get_vector_from_origin_NEU(start_NEU) || !end.get_vector_from_origin_NEU(end_NEU)) { if (!start.get_vector_from_origin_NEU(start_NEU) || !end.get_vector_from_origin_NEU(end_NEU)) {
return false; return false;
} }
if (start_NEU == end_NEU) {
return false;
}
// check each obstacle's distance from segment // check each obstacle's distance from segment
float smallest_margin = FLT_MAX; float smallest_margin = FLT_MAX;

View File

@ -21,7 +21,7 @@ public:
// run background task to find best path and update avoidance_results // run background task to find best path and update avoidance_results
// returns true and populates origin_new and destination_new if OA is required. returns false if OA is not required // returns true and populates origin_new and destination_new if OA is required. returns false if OA is not required
bool update(const Location& current_loc, const Location& destination, const Vector2f &ground_speed_vec, Location &origin_new, Location &destination_new); bool update(const Location& current_loc, const Location& destination, const Vector2f &ground_speed_vec, Location &origin_new, Location &destination_new, bool proximity_only);
enum class OABendyType { enum class OABendyType {
OA_BENDY_DISABLED = 0, OA_BENDY_DISABLED = 0,
@ -33,20 +33,20 @@ public:
OABendyType get_type() const; OABendyType get_type() const;
// search for path in XY direction // search for path in XY direction
bool search_xy_path(const Location& current_loc, const Location& destination, float ground_course_deg, Location &destination_new, float lookahead_step_1_dist, float lookahead_step_2_dist, float bearing_to_dest, float distance_to_dest); bool search_xy_path(const Location& current_loc, const Location& destination, float ground_course_deg, Location &destination_new, float lookahead_step_1_dist, float lookahead_step_2_dist, float bearing_to_dest, float distance_to_dest, bool proximity_only);
// search for path in the Vertical directions // search for path in the Vertical directions
bool search_vertical_path(const Location& current_loc, const Location& destination,Location &destination_new, const float &lookahead_step1_dist, const float &lookahead_step2_dist, const float &bearing_to_dest, const float &distance_to_dest); bool search_vertical_path(const Location& current_loc, const Location& destination,Location &destination_new, const float &lookahead_step1_dist, const float &lookahead_step2_dist, const float &bearing_to_dest, const float &distance_to_dest, bool proximity_only);
static const struct AP_Param::GroupInfo var_info[]; static const struct AP_Param::GroupInfo var_info[];
private: private:
// calculate minimum distance between a path and any obstacle // calculate minimum distance between a path and any obstacle
float calc_avoidance_margin(const Location &start, const Location &end) const; float calc_avoidance_margin(const Location &start, const Location &end, bool proximity_only) const;
// determine if BendyRuler should accept the new bearing or try and resist it. Returns true if bearing is not changed // determine if BendyRuler should accept the new bearing or try and resist it. Returns true if bearing is not changed
bool resist_bearing_change(const Location &destination, const Location &current_loc, bool active, float bearing_test, float lookahead_step1_dist, float margin, Location &prev_dest, float &prev_bearing, float &final_bearing, float &final_margin) const; bool resist_bearing_change(const Location &destination, const Location &current_loc, bool active, float bearing_test, float lookahead_step1_dist, float margin, Location &prev_dest, float &prev_bearing, float &final_bearing, float &final_margin, bool proximity_only) const;
// calculate minimum distance between a path and the circular fence (centered on home) // calculate minimum distance between a path and the circular fence (centered on home)
// on success returns true and updates margin // on success returns true and updates margin

View File

@ -22,6 +22,9 @@ public:
// set fence margin (in meters) used when creating "safe positions" within the polygon fence // set fence margin (in meters) used when creating "safe positions" within the polygon fence
void set_fence_margin(float margin) { _polyfence_margin = MAX(margin, 0.0f); } void set_fence_margin(float margin) { _polyfence_margin = MAX(margin, 0.0f); }
// trigger Dijkstra's to recalculate shortest path based on current location
void recalculate_path() { _shortest_path_ok = false; }
// update return status enum // update return status enum
enum AP_OADijkstra_State : uint8_t { enum AP_OADijkstra_State : uint8_t {
DIJKSTRA_STATE_NOT_REQUIRED = 0, DIJKSTRA_STATE_NOT_REQUIRED = 0,

View File

@ -37,7 +37,7 @@ const AP_Param::GroupInfo AP_OAPathPlanner::var_info[] = {
// @Param: TYPE // @Param: TYPE
// @DisplayName: Object Avoidance Path Planning algorithm to use // @DisplayName: Object Avoidance Path Planning algorithm to use
// @Description: Enabled/disable path planning around obstacles // @Description: Enabled/disable path planning around obstacles
// @Values: 0:Disabled,1:BendyRuler,2:Dijkstra // @Values: 0:Disabled,1:BendyRuler,2:Dijkstra,3:Dijkstra with BendyRuler
// @User: Standard // @User: Standard
AP_GROUPINFO_FLAGS("TYPE", 1, AP_OAPathPlanner, _type, OA_PATHPLAN_DISABLED, AP_PARAM_FLAG_ENABLE), AP_GROUPINFO_FLAGS("TYPE", 1, AP_OAPathPlanner, _type, OA_PATHPLAN_DISABLED, AP_PARAM_FLAG_ENABLE),
@ -101,6 +101,15 @@ void AP_OAPathPlanner::init()
_oadijkstra = new AP_OADijkstra(); _oadijkstra = new AP_OADijkstra();
} }
break; break;
case OA_PATHPLAN_DJIKSTRA_BENDYRULER:
if (_oadijkstra == nullptr) {
_oadijkstra = new AP_OADijkstra();
}
if (_oabendyruler == nullptr) {
_oabendyruler = new AP_OABendyRuler();
AP_Param::load_object_from_eeprom(_oabendyruler, AP_OABendyRuler::var_info);
}
break;
} }
_oadatabase.init(); _oadatabase.init();
@ -136,6 +145,12 @@ bool AP_OAPathPlanner::pre_arm_check(char *failure_msg, uint8_t failure_msg_len)
return false; return false;
} }
break; break;
case OA_PATHPLAN_DJIKSTRA_BENDYRULER:
if(_oadijkstra == nullptr || _oabendyruler == nullptr) {
hal.util->snprintf(failure_msg, failure_msg_len, "OA requires reboot");
return false;
}
break;
} }
return true; return true;
} }
@ -267,12 +282,12 @@ void AP_OAPathPlanner::avoidance_thread()
continue; continue;
} }
_oabendyruler->set_config(_margin_max); _oabendyruler->set_config(_margin_max);
if (_oabendyruler->update(avoidance_request2.current_loc, avoidance_request2.destination, avoidance_request2.ground_speed_vec, origin_new, destination_new)) { if (_oabendyruler->update(avoidance_request2.current_loc, avoidance_request2.destination, avoidance_request2.ground_speed_vec, origin_new, destination_new, false)) {
res = OA_SUCCESS; res = OA_SUCCESS;
} }
break; break;
case OA_PATHPLAN_DIJKSTRA: case OA_PATHPLAN_DIJKSTRA: {
if (_oadijkstra == nullptr) { if (_oadijkstra == nullptr) {
continue; continue;
} }
@ -292,6 +307,42 @@ void AP_OAPathPlanner::avoidance_thread()
break; break;
} }
case OA_PATHPLAN_DJIKSTRA_BENDYRULER: {
if ((_oabendyruler == nullptr) || _oadijkstra == nullptr) {
continue;
}
_oabendyruler->set_config(_margin_max);
if (_oabendyruler->update(avoidance_request2.current_loc, avoidance_request2.destination, avoidance_request2.ground_speed_vec, origin_new, destination_new, proximity_only)) {
// detected a obstacle by vehicle's proximity sensor. Switch avoidance to BendyRuler till obstacle is out of the way
proximity_only = false;
res = OA_SUCCESS;
break;
} else {
// cleared all obstacles, trigger Dijkstra's to calculate path based on current deviated position
if (proximity_only == false) {
_oadijkstra->recalculate_path();
}
// only use proximity avoidance now for BendyRuler
proximity_only = true;
}
_oadijkstra->set_fence_margin(_margin_max);
const AP_OADijkstra::AP_OADijkstra_State dijkstra_state = _oadijkstra->update(avoidance_request2.current_loc, avoidance_request2.destination, origin_new, destination_new);
switch (dijkstra_state) {
case AP_OADijkstra::DIJKSTRA_STATE_NOT_REQUIRED:
res = OA_NOT_REQUIRED;
break;
case AP_OADijkstra::DIJKSTRA_STATE_ERROR:
res = OA_ERROR;
break;
case AP_OADijkstra::DIJKSTRA_STATE_SUCCESS:
res = OA_SUCCESS;
break;
}
break;
}
}
{ {
// give the main thread the avoidance result // give the main thread the avoidance result
WITH_SEMAPHORE(_rsem); WITH_SEMAPHORE(_rsem);

View File

@ -51,7 +51,8 @@ public:
enum OAPathPlanTypes { enum OAPathPlanTypes {
OA_PATHPLAN_DISABLED = 0, OA_PATHPLAN_DISABLED = 0,
OA_PATHPLAN_BENDYRULER = 1, OA_PATHPLAN_BENDYRULER = 1,
OA_PATHPLAN_DIJKSTRA = 2 OA_PATHPLAN_DIJKSTRA = 2,
OA_PATHPLAN_DJIKSTRA_BENDYRULER = 3,
}; };
// enumeration for _OPTION parameter // enumeration for _OPTION parameter
@ -104,6 +105,7 @@ private:
AP_OADatabase _oadatabase; // Database of dynamic objects to avoid AP_OADatabase _oadatabase; // Database of dynamic objects to avoid
uint32_t avoidance_latest_ms; // last time Dijkstra's or BendyRuler algorithms ran uint32_t avoidance_latest_ms; // last time Dijkstra's or BendyRuler algorithms ran
bool proximity_only = true;
static AP_OAPathPlanner *_singleton; static AP_OAPathPlanner *_singleton;
}; };