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.
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.
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/>.
16 #include "AC_Avoidance_config.h"
18 #if AP_OAPATHPLANNER_ENABLED
20 #include "AP_OAPathPlanner.h"
21 #include <AP_Math/AP_Math.h>
22 #include <AP_AHRS/AP_AHRS.h>
23 #include <AC_Fence/AC_Fence.h>
24 #include <AP_Logger/AP_Logger.h>
25 #include "AP_OABendyRuler.h"
26 #include "AP_OADijkstra.h"
28 extern const AP_HAL::HAL
&hal
;
31 static constexpr float OA_MARGIN_MAX_DEFAULT
= 5;
32 static constexpr int16_t OA_OPTIONS_DEFAULT
= 1;
34 static constexpr int16_t OA_UPDATE_MS
= 1000; // path planning updates run at 1hz
35 static constexpr int16_t OA_TIMEOUT_MS
= 3000; // results over 3 seconds old are ignored
37 const AP_Param::GroupInfo
AP_OAPathPlanner::var_info
[] = {
40 // @DisplayName: Object Avoidance Path Planning algorithm to use
41 // @Description: Enabled/disable path planning around obstacles
42 // @Values: 0:Disabled,1:BendyRuler,2:Dijkstra,3:Dijkstra with BendyRuler
44 AP_GROUPINFO_FLAGS("TYPE", 1, AP_OAPathPlanner
, _type
, OA_PATHPLAN_DISABLED
, AP_PARAM_FLAG_ENABLE
),
46 // Note: Do not use Index "2" for any new parameter
47 // It was being used by _LOOKAHEAD which was later moved to AP_OABendyRuler
50 // @DisplayName: Object Avoidance wide margin distance
51 // @Description: Object Avoidance will ignore objects more than this many meters from vehicle
56 AP_GROUPINFO("MARGIN_MAX", 3, AP_OAPathPlanner
, _margin_max
, OA_MARGIN_MAX_DEFAULT
),
59 // @Path: AP_OADatabase.cpp
60 AP_SUBGROUPINFO(_oadatabase
, "DB_", 4, AP_OAPathPlanner
, AP_OADatabase
),
63 // @DisplayName: Options while recovering from Object Avoidance
64 // @Description: Bitmask which will govern vehicles behaviour while recovering from Obstacle Avoidance (i.e Avoidance is turned off after the path ahead is clear).
65 // @Bitmask{Rover}: 0: Reset the origin of the waypoint to the present location, 1: log Dijkstra points
66 // @Bitmask{Copter}: 1:log Dijkstra points, 2:Allow fast waypoints (Dijkastras only)
68 AP_GROUPINFO("OPTIONS", 5, AP_OAPathPlanner
, _options
, OA_OPTIONS_DEFAULT
),
71 // @Path: AP_OABendyRuler.cpp
72 AP_SUBGROUPPTR(_oabendyruler
, "BR_", 6, AP_OAPathPlanner
, AP_OABendyRuler
),
78 AP_OAPathPlanner::AP_OAPathPlanner()
82 AP_Param::setup_object_defaults(this, var_info
);
85 // perform any required initialisation
86 void AP_OAPathPlanner::init()
88 // run background task looking for best alternative destination
90 case OA_PATHPLAN_DISABLED
:
93 case OA_PATHPLAN_BENDYRULER
:
94 if (_oabendyruler
== nullptr) {
95 _oabendyruler
= NEW_NOTHROW
AP_OABendyRuler();
96 AP_Param::load_object_from_eeprom(_oabendyruler
, AP_OABendyRuler::var_info
);
99 case OA_PATHPLAN_DIJKSTRA
:
101 if (_oadijkstra
== nullptr) {
102 _oadijkstra
= NEW_NOTHROW
AP_OADijkstra(_options
);
106 case OA_PATHPLAN_DJIKSTRA_BENDYRULER
:
108 if (_oadijkstra
== nullptr) {
109 _oadijkstra
= NEW_NOTHROW
AP_OADijkstra(_options
);
112 if (_oabendyruler
== nullptr) {
113 _oabendyruler
= NEW_NOTHROW
AP_OABendyRuler();
114 AP_Param::load_object_from_eeprom(_oabendyruler
, AP_OABendyRuler::var_info
);
123 // pre-arm checks that algorithms have been initialised successfully
124 bool AP_OAPathPlanner::pre_arm_check(char *failure_msg
, uint8_t failure_msg_len
) const
126 // check if initialisation has succeeded
128 case OA_PATHPLAN_DISABLED
:
131 case OA_PATHPLAN_BENDYRULER
:
132 if (_oabendyruler
== nullptr) {
133 hal
.util
->snprintf(failure_msg
, failure_msg_len
, "BendyRuler OA requires reboot");
137 case OA_PATHPLAN_DIJKSTRA
:
138 if (_oadijkstra
== nullptr) {
139 hal
.util
->snprintf(failure_msg
, failure_msg_len
, "Dijkstra OA requires reboot");
143 case OA_PATHPLAN_DJIKSTRA_BENDYRULER
:
144 if(_oadijkstra
== nullptr || _oabendyruler
== nullptr) {
145 hal
.util
->snprintf(failure_msg
, failure_msg_len
, "OA requires reboot");
153 bool AP_OAPathPlanner::start_thread()
155 WITH_SEMAPHORE(_rsem
);
157 if (_thread_created
) {
160 if (_type
== OA_PATHPLAN_DISABLED
) {
164 // create the avoidance thread as low priority. It should soak
165 // up spare CPU cycles to fill in the avoidance_result structure based
166 // on requests in avoidance_request
167 if (!hal
.scheduler
->thread_create(FUNCTOR_BIND_MEMBER(&AP_OAPathPlanner::avoidance_thread
, void),
169 8192, AP_HAL::Scheduler::PRIORITY_IO
, -1)) {
172 _thread_created
= true;
176 // helper function to map OABendyType to OAPathPlannerUsed
177 AP_OAPathPlanner::OAPathPlannerUsed
AP_OAPathPlanner::map_bendytype_to_pathplannerused(AP_OABendyRuler::OABendyType bendy_type
)
179 switch (bendy_type
) {
180 case AP_OABendyRuler::OABendyType::OA_BENDY_HORIZONTAL
:
181 return OAPathPlannerUsed::BendyRulerHorizontal
;
183 case AP_OABendyRuler::OABendyType::OA_BENDY_VERTICAL
:
184 return OAPathPlannerUsed::BendyRulerVertical
;
187 case AP_OABendyRuler::OABendyType::OA_BENDY_DISABLED
:
188 return OAPathPlannerUsed::None
;
192 // provides an alternative target location if path planning around obstacles is required
193 // returns true and updates result_origin, result_destination, result_next_destination with an intermediate path
194 // result_dest_to_next_dest_clear is set to true if the path from result_destination to result_next_destination is clear (only supported by Dijkstras)
195 // path_planner_used updated with which path planner produced the result
196 AP_OAPathPlanner::OA_RetState
AP_OAPathPlanner::mission_avoidance(const Location
¤t_loc
,
197 const Location
&origin
,
198 const Location
&destination
,
199 const Location
&next_destination
,
200 Location
&result_origin
,
201 Location
&result_destination
,
202 Location
&result_next_destination
,
203 bool &result_dest_to_next_dest_clear
,
204 OAPathPlannerUsed
&path_planner_used
)
206 // exit immediately if disabled or thread is not running from a failed init
207 if (_type
== OA_PATHPLAN_DISABLED
|| !_thread_created
) {
208 return OA_NOT_REQUIRED
;
211 // check if just activated to avoid initial timeout error
212 const uint32_t now
= AP_HAL::millis();
213 if (now
- _last_update_ms
> 200) {
216 _last_update_ms
= now
;
218 WITH_SEMAPHORE(_rsem
);
220 // place new request for the thread to work on
221 avoidance_request
.current_loc
= current_loc
;
222 avoidance_request
.origin
= origin
;
223 avoidance_request
.destination
= destination
;
224 avoidance_request
.next_destination
= next_destination
;
225 avoidance_request
.ground_speed_vec
= AP::ahrs().groundspeed_vector();
226 avoidance_request
.request_time_ms
= now
;
228 // check result's destination and next_destination matches our request
229 // e.g. check this result was using our current inputs and not from an old request
230 const bool destination_matches
= destination
.same_latlon_as(avoidance_result
.destination
);
231 const bool next_destination_matches
= next_destination
.same_latlon_as(avoidance_result
.next_destination
);
233 // check results have not timed out
234 const bool timed_out
= (now
- avoidance_result
.result_time_ms
> OA_TIMEOUT_MS
) && (now
- _activated_ms
> OA_TIMEOUT_MS
);
236 // return results from background thread's latest checks
237 if (destination_matches
&& next_destination_matches
&& !timed_out
) {
238 // we have a result from the thread
239 result_origin
= avoidance_result
.origin_new
;
240 result_destination
= avoidance_result
.destination_new
;
241 result_next_destination
= avoidance_result
.next_destination_new
;
242 result_dest_to_next_dest_clear
= avoidance_result
.dest_to_next_dest_clear
;
243 path_planner_used
= avoidance_result
.path_planner_used
;
244 return avoidance_result
.ret_state
;
247 // if timeout then path planner is taking too long to respond
252 // background thread is working on a new destination
253 return OA_PROCESSING
;
256 // avoidance thread that continually updates the avoidance_result structure based on avoidance_request
257 void AP_OAPathPlanner::avoidance_thread()
259 // require ekf origin to have been set
260 bool origin_set
= false;
261 while (!origin_set
) {
262 hal
.scheduler
->delay(500);
263 Location ekf_origin
{};
265 WITH_SEMAPHORE(AP::ahrs().get_semaphore());
266 origin_set
= AP::ahrs().get_origin(ekf_origin
);
272 // if database queue needs attention, service it faster
273 if (_oadatabase
.process_queue()) {
274 hal
.scheduler
->delay(1);
276 hal
.scheduler
->delay(20);
279 const uint32_t now
= AP_HAL::millis();
280 if (now
- avoidance_latest_ms
< OA_UPDATE_MS
) {
283 avoidance_latest_ms
= now
;
285 _oadatabase
.update();
287 // values returned by path planners
289 Location destination_new
;
290 Location next_destination_new
;
291 bool dest_to_next_dest_clear
= false;
293 WITH_SEMAPHORE(_rsem
);
294 if (now
- avoidance_request
.request_time_ms
> OA_TIMEOUT_MS
) {
295 // this is a very old request, don't process it
299 // copy request to avoid conflict with main thread
300 avoidance_request2
= avoidance_request
;
302 // store passed in origin, destination and next_destination so we can return it if object avoidance is not required
303 origin_new
= avoidance_request
.origin
;
304 destination_new
= avoidance_request
.destination
;
305 next_destination_new
= avoidance_request
.next_destination
;
308 // run background task looking for best alternative destination
309 OA_RetState res
= OA_NOT_REQUIRED
;
310 OAPathPlannerUsed path_planner_used
= OAPathPlannerUsed::None
;
312 case OA_PATHPLAN_DISABLED
:
314 case OA_PATHPLAN_BENDYRULER
: {
315 if (_oabendyruler
== nullptr) {
318 _oabendyruler
->set_config(_margin_max
);
320 AP_OABendyRuler::OABendyType bendy_type
;
321 if (_oabendyruler
->update(avoidance_request2
.current_loc
, avoidance_request2
.destination
, avoidance_request2
.ground_speed_vec
, origin_new
, destination_new
, bendy_type
, false)) {
324 path_planner_used
= map_bendytype_to_pathplannerused(bendy_type
);
328 case OA_PATHPLAN_DIJKSTRA
: {
330 if (_oadijkstra
== nullptr) {
333 _oadijkstra
->set_fence_margin(_margin_max
);
334 const AP_OADijkstra::AP_OADijkstra_State dijkstra_state
= _oadijkstra
->update(avoidance_request2
.current_loc
,
335 avoidance_request2
.destination
,
336 avoidance_request2
.next_destination
,
339 next_destination_new
,
340 dest_to_next_dest_clear
);
341 switch (dijkstra_state
) {
342 case AP_OADijkstra::DIJKSTRA_STATE_NOT_REQUIRED
:
343 res
= OA_NOT_REQUIRED
;
345 case AP_OADijkstra::DIJKSTRA_STATE_ERROR
:
348 case AP_OADijkstra::DIJKSTRA_STATE_SUCCESS
:
352 path_planner_used
= OAPathPlannerUsed::Dijkstras
;
357 case OA_PATHPLAN_DJIKSTRA_BENDYRULER
: {
358 if ((_oabendyruler
== nullptr) || _oadijkstra
== nullptr) {
361 _oabendyruler
->set_config(_margin_max
);
362 AP_OABendyRuler::OABendyType bendy_type
;
363 if (_oabendyruler
->update(avoidance_request2
.current_loc
, avoidance_request2
.destination
, avoidance_request2
.ground_speed_vec
, origin_new
, destination_new
, bendy_type
, proximity_only
)) {
364 // detected a obstacle by vehicle's proximity sensor. Switch avoidance to BendyRuler till obstacle is out of the way
365 proximity_only
= false;
367 path_planner_used
= map_bendytype_to_pathplannerused(bendy_type
);
370 // cleared all obstacles, trigger Dijkstra's to calculate path based on current deviated position
372 if (proximity_only
== false) {
373 _oadijkstra
->recalculate_path();
376 // only use proximity avoidance now for BendyRuler
377 proximity_only
= true;
380 _oadijkstra
->set_fence_margin(_margin_max
);
381 const AP_OADijkstra::AP_OADijkstra_State dijkstra_state
= _oadijkstra
->update(avoidance_request2
.current_loc
,
382 avoidance_request2
.destination
,
383 avoidance_request2
.next_destination
,
386 next_destination_new
,
387 dest_to_next_dest_clear
);
388 switch (dijkstra_state
) {
389 case AP_OADijkstra::DIJKSTRA_STATE_NOT_REQUIRED
:
390 res
= OA_NOT_REQUIRED
;
392 case AP_OADijkstra::DIJKSTRA_STATE_ERROR
:
395 case AP_OADijkstra::DIJKSTRA_STATE_SUCCESS
:
399 path_planner_used
= OAPathPlannerUsed::Dijkstras
;
407 // give the main thread the avoidance result
408 WITH_SEMAPHORE(_rsem
);
410 // place the destination and next destination used into the result (used by the caller to verify the result matches their request)
411 avoidance_result
.destination
= avoidance_request2
.destination
;
412 avoidance_result
.next_destination
= avoidance_request2
.next_destination
;
413 avoidance_result
.dest_to_next_dest_clear
= dest_to_next_dest_clear
;
415 // fill the result structure with the intermediate path
416 avoidance_result
.origin_new
= (res
== OA_SUCCESS
) ? origin_new
: avoidance_result
.origin_new
;
417 avoidance_result
.destination_new
= (res
== OA_SUCCESS
) ? destination_new
: avoidance_result
.destination
;
418 avoidance_result
.next_destination_new
= (res
== OA_SUCCESS
) ? next_destination_new
: avoidance_result
.next_destination
;
420 // create new avoidance result.dest_to_next_dest_clear field. fill in with results from dijkstras or leave as unknown
421 avoidance_result
.result_time_ms
= AP_HAL::millis();
422 avoidance_result
.path_planner_used
= path_planner_used
;
423 avoidance_result
.ret_state
= res
;
428 // singleton instance
429 AP_OAPathPlanner
*AP_OAPathPlanner::_singleton
;
433 AP_OAPathPlanner
*ap_oapathplanner()
435 return AP_OAPathPlanner::get_singleton();
440 #endif // AP_OAPATHPLANNER_ENABLED