Line data Source code
1 : #include "Rover.h"
2 :
3 : #define AUTO_GUIDED_SEND_TARGET_MS 1000
4 :
5 40 : bool ModeAuto::_enter()
6 : {
7 : // fail to enter auto if no mission commands
8 40 : if (!mission.present()) {
9 0 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "No Mission. Can't set AUTO.");
10 0 : return false;
11 : }
12 :
13 : // initialise waypoint navigation library
14 40 : g2.wp_nav.init();
15 :
16 : // other initialisation
17 40 : auto_triggered = false;
18 :
19 : // clear guided limits
20 40 : rover.mode_guided.limit_clear();
21 :
22 : // initialise submode to stop or loiter
23 40 : if (rover.is_boat()) {
24 7 : if (!start_loiter()) {
25 0 : start_stop();
26 : }
27 : } else {
28 33 : start_stop();
29 : }
30 :
31 : // set flag to start mission
32 40 : waiting_to_start = true;
33 :
34 40 : return true;
35 : }
36 :
37 34 : void ModeAuto::_exit()
38 : {
39 : // stop running the mission
40 34 : if (mission.state() == AP_Mission::MISSION_RUNNING) {
41 14 : mission.stop();
42 : }
43 34 : }
44 :
45 138859 : void ModeAuto::update()
46 : {
47 : // check if mission exists (due to being cleared while disarmed in AUTO,
48 : // if no mission, then stop...needs mode change out of AUTO, mission load,
49 : // and change back to AUTO to run a mission at this point
50 138859 : if (!rover.arming.is_armed_and_safety_off() && !mission.present()) {
51 477 : start_stop();
52 : }
53 : // start or update mission
54 138859 : if (waiting_to_start) {
55 : // don't start the mission until we have an origin
56 2735 : Location loc;
57 2735 : if (ahrs.get_origin(loc)) {
58 : // start/resume the mission (based on MIS_RESTART parameter)
59 40 : mission.start_or_resume();
60 40 : waiting_to_start = false;
61 :
62 : // initialise mission change check
63 40 : IGNORE_RETURN(mis_change_detector.check_for_mission_change());
64 : }
65 : } else {
66 : // check for mission changes
67 136124 : if (mis_change_detector.check_for_mission_change()) {
68 : // if mission is running restart the current command if it is a waypoint command
69 11 : if ((mission.state() == AP_Mission::MISSION_RUNNING) && (_submode == SubMode::WP)) {
70 0 : if (mission.restart_current_nav_cmd()) {
71 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Auto mission changed, restarted command");
72 : } else {
73 : // failed to restart mission for some reason
74 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Auto mission changed but failed to restart command");
75 : }
76 : }
77 : }
78 :
79 136124 : mission.update();
80 : }
81 :
82 138859 : switch (_submode) {
83 91192 : case SubMode::WP:
84 : {
85 : // boats loiter once the waypoint is reached
86 91192 : bool keep_navigating = true;
87 91192 : if (rover.is_boat() && g2.wp_nav.reached_destination() && !g2.wp_nav.is_fast_waypoint()) {
88 0 : keep_navigating = !start_loiter();
89 : }
90 :
91 : // update navigation controller
92 91192 : if (keep_navigating) {
93 91192 : navigate_to_waypoint();
94 : }
95 91192 : break;
96 : }
97 :
98 0 : case SubMode::HeadingAndSpeed:
99 : {
100 0 : if (!_reached_heading) {
101 : // run steering and throttle controllers
102 0 : calc_steering_to_heading(_desired_yaw_cd);
103 0 : calc_throttle(calc_speed_nudge(_desired_speed, is_negative(_desired_speed)), true);
104 : // check if we have reached within 5 degrees of target
105 0 : _reached_heading = (fabsf(_desired_yaw_cd - ahrs.yaw_sensor) < 500);
106 : } else {
107 : // we have reached the destination so stay here
108 0 : if (rover.is_boat()) {
109 0 : if (!start_loiter()) {
110 0 : stop_vehicle();
111 : }
112 : } else {
113 0 : stop_vehicle();
114 : }
115 : }
116 0 : break;
117 : }
118 :
119 22909 : case SubMode::RTL:
120 22909 : rover.mode_rtl.update();
121 22909 : break;
122 :
123 1611 : case SubMode::Loiter:
124 1611 : rover.mode_loiter.update();
125 1611 : break;
126 :
127 500 : case SubMode::Guided:
128 : {
129 : // send location target to offboard navigation system
130 500 : send_guided_position_target();
131 500 : rover.mode_guided.update();
132 500 : break;
133 : }
134 :
135 20669 : case SubMode::Stop:
136 20669 : stop_vehicle();
137 20669 : break;
138 :
139 0 : case SubMode::NavScriptTime:
140 0 : rover.mode_guided.update();
141 0 : break;
142 :
143 1978 : case SubMode::Circle:
144 1978 : g2.mode_circle.update();
145 1978 : break;
146 : }
147 138859 : }
148 :
149 91192 : void ModeAuto::calc_throttle(float target_speed, bool avoidance_enabled)
150 : {
151 : // If not autostarting set the throttle to minimum
152 91192 : if (!check_trigger()) {
153 0 : stop_vehicle();
154 0 : return;
155 : }
156 91192 : Mode::calc_throttle(target_speed, avoidance_enabled);
157 : }
158 :
159 : // return heading (in degrees) to target destination (aka waypoint)
160 58310 : float ModeAuto::wp_bearing() const
161 : {
162 58310 : switch (_submode) {
163 38304 : case SubMode::WP:
164 38304 : return g2.wp_nav.wp_bearing_cd() * 0.01f;
165 8667 : case SubMode::HeadingAndSpeed:
166 : case SubMode::Stop:
167 8667 : return 0.0f;
168 9633 : case SubMode::RTL:
169 9633 : return rover.mode_rtl.wp_bearing();
170 668 : case SubMode::Loiter:
171 668 : return rover.mode_loiter.wp_bearing();
172 210 : case SubMode::Guided:
173 : case SubMode::NavScriptTime:
174 210 : return rover.mode_guided.wp_bearing();
175 828 : case SubMode::Circle:
176 828 : return g2.mode_circle.wp_bearing();
177 : }
178 :
179 : // this line should never be reached
180 0 : return 0.0f;
181 : }
182 :
183 : // return short-term target heading in degrees (i.e. target heading back to line between waypoints)
184 55525 : float ModeAuto::nav_bearing() const
185 : {
186 55525 : switch (_submode) {
187 36476 : case SubMode::WP:
188 36476 : return g2.wp_nav.nav_bearing_cd() * 0.01f;
189 8248 : case SubMode::HeadingAndSpeed:
190 : case SubMode::Stop:
191 8248 : return 0.0f;
192 9175 : case SubMode::RTL:
193 9175 : return rover.mode_rtl.nav_bearing();
194 637 : case SubMode::Loiter:
195 637 : return rover.mode_loiter.nav_bearing();
196 200 : case SubMode::Guided:
197 : case SubMode::NavScriptTime:
198 200 : return rover.mode_guided.nav_bearing();
199 789 : case SubMode::Circle:
200 789 : return g2.mode_circle.nav_bearing();
201 : }
202 :
203 : // this line should never be reached
204 0 : return 0.0f;
205 : }
206 :
207 : // return cross track error (i.e. vehicle's distance from the line between waypoints)
208 58310 : float ModeAuto::crosstrack_error_m() const
209 : {
210 58310 : switch (_submode) {
211 38304 : case SubMode::WP:
212 38304 : return g2.wp_nav.crosstrack_error_m();
213 8667 : case SubMode::HeadingAndSpeed:
214 : case SubMode::Stop:
215 8667 : return 0.0f;
216 9633 : case SubMode::RTL:
217 9633 : return rover.mode_rtl.crosstrack_error_m();
218 668 : case SubMode::Loiter:
219 668 : return rover.mode_loiter.crosstrack_error_m();
220 210 : case SubMode::Guided:
221 : case SubMode::NavScriptTime:
222 210 : return rover.mode_guided.crosstrack_error_m();
223 828 : case SubMode::Circle:
224 828 : return g2.mode_circle.crosstrack_error_m();
225 : }
226 :
227 : // this line should never be reached
228 0 : return 0.0f;
229 : }
230 :
231 : // return desired lateral acceleration
232 27773 : float ModeAuto::get_desired_lat_accel() const
233 : {
234 27773 : switch (_submode) {
235 18242 : case SubMode::WP:
236 18242 : return g2.wp_nav.get_lat_accel();
237 4132 : case SubMode::HeadingAndSpeed:
238 : case SubMode::Stop:
239 4132 : return 0.0f;
240 4581 : case SubMode::RTL:
241 4581 : return rover.mode_rtl.get_desired_lat_accel();
242 322 : case SubMode::Loiter:
243 322 : return rover.mode_loiter.get_desired_lat_accel();
244 100 : case SubMode::Guided:
245 : case SubMode::NavScriptTime:
246 100 : return rover.mode_guided.get_desired_lat_accel();
247 396 : case SubMode::Circle:
248 396 : return g2.mode_circle.get_desired_lat_accel();
249 : }
250 :
251 : // this line should never be reached
252 0 : return 0.0f;
253 : }
254 :
255 : // return straight-line distance (in meters) to destination
256 58310 : float ModeAuto::get_distance_to_destination() const
257 : {
258 58310 : switch (_submode) {
259 38304 : case SubMode::WP:
260 38304 : return _distance_to_destination;
261 8667 : case SubMode::HeadingAndSpeed:
262 : case SubMode::Stop:
263 : // no valid distance so return zero
264 8667 : return 0.0f;
265 9633 : case SubMode::RTL:
266 9633 : return rover.mode_rtl.get_distance_to_destination();
267 668 : case SubMode::Loiter:
268 668 : return rover.mode_loiter.get_distance_to_destination();
269 210 : case SubMode::Guided:
270 : case SubMode::NavScriptTime:
271 210 : return rover.mode_guided.get_distance_to_destination();
272 828 : case SubMode::Circle:
273 828 : return g2.mode_circle.get_distance_to_destination();
274 : }
275 :
276 : // this line should never be reached
277 0 : return 0.0f;
278 : }
279 :
280 : // get desired location
281 27753 : bool ModeAuto::get_desired_location(Location& destination) const
282 : {
283 27753 : switch (_submode) {
284 18235 : case SubMode::WP:
285 18235 : if (g2.wp_nav.is_destination_valid()) {
286 18235 : destination = g2.wp_nav.get_oa_destination();
287 18235 : return true;
288 : }
289 0 : return false;
290 4116 : case SubMode::HeadingAndSpeed:
291 : case SubMode::Stop:
292 : // no desired location for this submode
293 4116 : return false;
294 4594 : case SubMode::RTL:
295 4594 : return rover.mode_rtl.get_desired_location(destination);
296 315 : case SubMode::Loiter:
297 315 : return rover.mode_loiter.get_desired_location(destination);
298 100 : case SubMode::Guided:
299 : case SubMode::NavScriptTime:
300 100 : return rover.mode_guided.get_desired_location(destination);
301 393 : case SubMode::Circle:
302 393 : return g2.mode_circle.get_desired_location(destination);
303 : }
304 :
305 : // we should never reach here but just in case
306 0 : return false;
307 : }
308 :
309 : // set desired location to drive to
310 125 : bool ModeAuto::set_desired_location(const Location &destination, Location next_destination)
311 : {
312 : // call parent
313 125 : if (!Mode::set_desired_location(destination, next_destination)) {
314 0 : return false;
315 : }
316 :
317 125 : _submode = SubMode::WP;
318 :
319 125 : return true;
320 : }
321 :
322 : // return true if vehicle has reached or even passed destination
323 114074 : bool ModeAuto::reached_destination() const
324 : {
325 114074 : switch (_submode) {
326 91176 : case SubMode::WP:
327 91176 : return g2.wp_nav.reached_destination();
328 : break;
329 2 : case SubMode::HeadingAndSpeed:
330 : case SubMode::Stop:
331 : // always return true because this is the safer option to allow missions to continue
332 2 : return true;
333 : break;
334 22896 : case SubMode::RTL:
335 22896 : return rover.mode_rtl.reached_destination();
336 : break;
337 0 : case SubMode::Loiter:
338 0 : return rover.mode_loiter.reached_destination();
339 : break;
340 0 : case SubMode::Guided:
341 : case SubMode::NavScriptTime:
342 0 : return rover.mode_guided.reached_destination();
343 0 : case SubMode::Circle:
344 0 : return g2.mode_circle.reached_destination();
345 : }
346 :
347 : // we should never reach here but just in case, return true to allow missions to continue
348 0 : return true;
349 : }
350 :
351 : // set desired speed in m/s
352 9 : bool ModeAuto::set_desired_speed(float speed_ms)
353 : {
354 9 : switch (_submode) {
355 9 : case SubMode::WP:
356 : case SubMode::Stop:
357 9 : return g2.wp_nav.set_speed_max(speed_ms);
358 0 : case SubMode::HeadingAndSpeed:
359 0 : _desired_speed = speed_ms;
360 0 : return true;
361 0 : case SubMode::RTL:
362 0 : return rover.mode_rtl.set_desired_speed(speed_ms);
363 0 : case SubMode::Loiter:
364 0 : return rover.mode_loiter.set_desired_speed(speed_ms);
365 0 : case SubMode::Guided:
366 : case SubMode::NavScriptTime:
367 0 : return rover.mode_guided.set_desired_speed(speed_ms);
368 0 : case SubMode::Circle:
369 0 : return g2.mode_circle.set_desired_speed(speed_ms);
370 : }
371 0 : return false;
372 : }
373 :
374 : // start RTL (within auto)
375 18 : void ModeAuto::start_RTL()
376 : {
377 18 : if (rover.mode_rtl.enter()) {
378 18 : _submode = SubMode::RTL;
379 : }
380 18 : }
381 :
382 : // lua scripts use this to retrieve the contents of the active command
383 0 : bool ModeAuto::nav_script_time(uint16_t &id, uint8_t &cmd, float &arg1, float &arg2, int16_t &arg3, int16_t &arg4)
384 : {
385 : #if AP_SCRIPTING_ENABLED
386 0 : if (_submode == SubMode::NavScriptTime) {
387 0 : id = nav_scripting.id;
388 0 : cmd = nav_scripting.command;
389 0 : arg1 = nav_scripting.arg1;
390 0 : arg2 = nav_scripting.arg2;
391 0 : arg3 = nav_scripting.arg3;
392 0 : arg4 = nav_scripting.arg4;
393 0 : return true;
394 : }
395 : #endif
396 0 : return false;
397 : }
398 :
399 : // lua scripts use this to indicate when they have complete the command
400 0 : void ModeAuto::nav_script_time_done(uint16_t id)
401 : {
402 : #if AP_SCRIPTING_ENABLED
403 0 : if ((_submode == SubMode::NavScriptTime) && (id == nav_scripting.id)) {
404 0 : nav_scripting.done = true;
405 : }
406 : #endif
407 0 : }
408 :
409 : // check for triggering of start of auto mode
410 91192 : bool ModeAuto::check_trigger(void)
411 : {
412 : // check for user pressing the auto trigger to off
413 91192 : if (auto_triggered && g.auto_trigger_pin != -1 && rover.check_digital_pin(g.auto_trigger_pin) == 1) {
414 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AUTO triggered off");
415 0 : auto_triggered = false;
416 0 : return false;
417 : }
418 :
419 : // if already triggered, then return true, so you don't
420 : // need to hold the switch down
421 91192 : if (auto_triggered) {
422 91154 : return true;
423 : }
424 :
425 : // return true if auto trigger and kickstart are disabled
426 38 : if (g.auto_trigger_pin == -1 && is_zero(g.auto_kickstart)) {
427 : // no trigger configured - let's go!
428 38 : auto_triggered = true;
429 38 : return true;
430 : }
431 :
432 : // check if trigger pin has been pushed
433 0 : if (g.auto_trigger_pin != -1 && rover.check_digital_pin(g.auto_trigger_pin) == 0) {
434 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Triggered AUTO with pin");
435 0 : auto_triggered = true;
436 0 : return true;
437 : }
438 :
439 : // check if mission is started by giving vehicle a kick with acceleration > AUTO_KICKSTART
440 0 : if (!is_zero(g.auto_kickstart)) {
441 0 : const float xaccel = rover.ins.get_accel().x;
442 0 : if (xaccel >= g.auto_kickstart) {
443 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Triggered AUTO xaccel=%.1f", static_cast<double>(xaccel));
444 0 : auto_triggered = true;
445 0 : return true;
446 : }
447 : }
448 :
449 0 : return false;
450 : }
451 :
452 10 : bool ModeAuto::start_loiter()
453 : {
454 10 : if (rover.mode_loiter.enter()) {
455 10 : _submode = SubMode::Loiter;
456 10 : return true;
457 : }
458 0 : return false;
459 : }
460 :
461 : // hand over control to external navigation controller in AUTO mode
462 1 : void ModeAuto::start_guided(const Location& loc)
463 : {
464 1 : if (rover.mode_guided.enter()) {
465 1 : _submode = SubMode::Guided;
466 :
467 : // initialise guided start time and position as reference for limit checking
468 1 : rover.mode_guided.limit_init_time_and_location();
469 :
470 : // sanity check target location
471 1 : if ((loc.lat != 0) || (loc.lng != 0)) {
472 1 : guided_target.loc = loc;
473 1 : guided_target.loc.sanitize(rover.current_loc);
474 1 : guided_target.valid = true;
475 : } else {
476 0 : guided_target.valid = false;
477 : }
478 : }
479 1 : }
480 :
481 : // start stopping vehicle as quickly as possible
482 519 : void ModeAuto::start_stop()
483 : {
484 519 : _submode = SubMode::Stop;
485 519 : }
486 :
487 : // send latest position target to offboard navigation system
488 500 : void ModeAuto::send_guided_position_target()
489 : {
490 500 : if (!guided_target.valid) {
491 0 : return;
492 : }
493 :
494 : // send at maximum of 1hz
495 500 : const uint32_t now_ms = AP_HAL::millis();
496 500 : if ((guided_target.last_sent_ms == 0) || (now_ms - guided_target.last_sent_ms > AUTO_GUIDED_SEND_TARGET_MS)) {
497 10 : guided_target.last_sent_ms = now_ms;
498 :
499 : // get system id and component id of offboard navigation system
500 : uint8_t sysid;
501 : uint8_t compid;
502 : mavlink_channel_t chan;
503 10 : if (GCS_MAVLINK::find_by_mavtype(MAV_TYPE_ONBOARD_CONTROLLER, sysid, compid, chan)) {
504 0 : gcs().chan(chan-MAVLINK_COMM_0)->send_set_position_target_global_int(sysid, compid, guided_target.loc);
505 : }
506 : }
507 :
508 : }
509 :
510 : /********************************************************************************/
511 : // Command Event Handlers
512 : /********************************************************************************/
513 156 : bool ModeAuto::start_command(const AP_Mission::Mission_Command& cmd)
514 : {
515 156 : switch (cmd.id) {
516 123 : case MAV_CMD_NAV_WAYPOINT: // Navigate to Waypoint
517 123 : return do_nav_wp(cmd, false);
518 :
519 18 : case MAV_CMD_NAV_RETURN_TO_LAUNCH:
520 18 : do_RTL();
521 18 : break;
522 :
523 2 : case MAV_CMD_NAV_LOITER_UNLIM: // Loiter indefinitely
524 : case MAV_CMD_NAV_LOITER_TIME: // Loiter for specified time
525 2 : return do_nav_wp(cmd, true);
526 :
527 2 : case MAV_CMD_NAV_LOITER_TURNS:
528 2 : return do_circle(cmd);
529 :
530 1 : case MAV_CMD_NAV_GUIDED_ENABLE: // accept navigation commands from external nav computer
531 1 : do_nav_guided_enable(cmd);
532 1 : break;
533 :
534 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
535 0 : do_nav_set_yaw_speed(cmd);
536 0 : break;
537 :
538 0 : case MAV_CMD_NAV_DELAY: // 93 Delay the next navigation command
539 0 : do_nav_delay(cmd);
540 0 : break;
541 :
542 : #if AP_SCRIPTING_ENABLED
543 0 : case MAV_CMD_NAV_SCRIPT_TIME:
544 0 : do_nav_script_time(cmd);
545 0 : break;
546 : #endif
547 :
548 : // Conditional commands
549 0 : case MAV_CMD_CONDITION_DELAY:
550 0 : do_wait_delay(cmd);
551 0 : break;
552 :
553 0 : case MAV_CMD_CONDITION_DISTANCE:
554 0 : do_within_distance(cmd);
555 0 : break;
556 :
557 : // Do commands
558 9 : case MAV_CMD_DO_CHANGE_SPEED:
559 9 : do_change_speed(cmd);
560 9 : break;
561 :
562 0 : case MAV_CMD_DO_SET_HOME:
563 0 : do_set_home(cmd);
564 0 : break;
565 :
566 : #if HAL_MOUNT_ENABLED
567 : // Sets the region of interest (ROI) for a sensor set or the
568 : // vehicle itself. This can then be used by the vehicles control
569 : // system to control the vehicle attitude and the attitude of various
570 : // devices such as cameras.
571 : // |Region of interest mode. (see MAV_ROI enum)| Waypoint index/ target ID. (see MAV_ROI enum)| ROI index (allows a vehicle to manage multiple cameras etc.)| Empty| x the location of the fixed ROI (see MAV_FRAME)| y| z|
572 : // ROI_NONE can be handled by the regular ROI handler because lat, lon, alt are always zero
573 0 : case MAV_CMD_DO_SET_ROI_LOCATION:
574 : case MAV_CMD_DO_SET_ROI_NONE:
575 : case MAV_CMD_DO_SET_ROI:
576 0 : if (!cmd.content.location.initialised()) {
577 : // switch off the camera tracking if enabled
578 0 : if (rover.camera_mount.get_mode() == MAV_MOUNT_MODE_GPS_POINT) {
579 0 : rover.camera_mount.set_mode_to_default();
580 : }
581 : } else {
582 : // send the command to the camera mount
583 0 : rover.camera_mount.set_roi_target(cmd.content.location);
584 : }
585 0 : break;
586 : #endif
587 :
588 0 : case MAV_CMD_DO_SET_REVERSE:
589 0 : do_set_reverse(cmd);
590 0 : break;
591 :
592 1 : case MAV_CMD_DO_GUIDED_LIMITS:
593 1 : do_guided_limits(cmd);
594 1 : break;
595 :
596 0 : default:
597 : // return false for unhandled commands
598 0 : return false;
599 : }
600 :
601 : // if we got this far we must have been successful
602 29 : return true;
603 : }
604 :
605 : // exit_mission - callback function called from ap-mission when the mission has completed
606 23 : void ModeAuto::exit_mission()
607 : {
608 : // play a tone
609 23 : AP_Notify::events.mission_complete = 1;
610 : // send message
611 23 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Mission Complete");
612 :
613 23 : switch ((DoneBehaviour)g2.mis_done_behave) {
614 9 : case DoneBehaviour::HOLD:
615 : // the default "start_stop" behaviour is used
616 9 : break;
617 3 : case DoneBehaviour::LOITER:
618 3 : if (start_loiter()) {
619 3 : return;
620 : }
621 0 : break;
622 2 : case DoneBehaviour::ACRO:
623 2 : if (rover.set_mode(rover.mode_acro, ModeReason::MISSION_END)) {
624 2 : return;
625 : }
626 0 : break;
627 9 : case DoneBehaviour::MANUAL:
628 9 : if (rover.set_mode(rover.mode_manual, ModeReason::MISSION_END)) {
629 9 : return;
630 : }
631 0 : break;
632 : }
633 :
634 9 : start_stop();
635 : }
636 :
637 : // verify_command_callback - callback function called from ap-mission at 10hz or higher when a command is being run
638 : // we double check that the flight mode is AUTO to avoid the possibility of ap-mission triggering actions while we're not in AUTO mode
639 116561 : bool ModeAuto::verify_command_callback(const AP_Mission::Mission_Command& cmd)
640 : {
641 116561 : const bool cmd_complete = verify_command(cmd);
642 :
643 : // send message to GCS
644 116561 : if (cmd_complete) {
645 139 : gcs().send_mission_item_reached_message(cmd.index);
646 : }
647 :
648 116561 : return cmd_complete;
649 : }
650 :
651 : /*******************************************************************************
652 : Verify command Handlers
653 :
654 : Each type of mission element has a "verify" operation. The verify
655 : operation returns true when the mission element has completed and we
656 : should move onto the next mission element.
657 : Return true if we do not recognize the command so that we move on to the next command
658 : *******************************************************************************/
659 :
660 116561 : bool ModeAuto::verify_command(const AP_Mission::Mission_Command& cmd)
661 : {
662 116561 : switch (cmd.id) {
663 90268 : case MAV_CMD_NAV_WAYPOINT:
664 90268 : return verify_nav_wp(cmd);
665 :
666 22896 : case MAV_CMD_NAV_RETURN_TO_LAUNCH:
667 22896 : return verify_RTL();
668 :
669 910 : case MAV_CMD_NAV_LOITER_UNLIM:
670 910 : return verify_loiter_unlimited(cmd);
671 :
672 1977 : case MAV_CMD_NAV_LOITER_TURNS:
673 1977 : return verify_circle(cmd);
674 :
675 0 : case MAV_CMD_NAV_LOITER_TIME:
676 0 : return verify_loiter_time(cmd);
677 :
678 500 : case MAV_CMD_NAV_GUIDED_ENABLE:
679 500 : return verify_nav_guided_enable(cmd);
680 :
681 0 : case MAV_CMD_NAV_DELAY:
682 0 : return verify_nav_delay(cmd);
683 :
684 : #if AP_SCRIPTING_ENABLED
685 0 : case MAV_CMD_NAV_SCRIPT_TIME:
686 0 : return verify_nav_script_time();
687 : #endif
688 :
689 0 : case MAV_CMD_CONDITION_DELAY:
690 0 : return verify_wait_delay();
691 :
692 0 : case MAV_CMD_CONDITION_DISTANCE:
693 0 : return verify_within_distance();
694 :
695 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
696 0 : return verify_nav_set_yaw_speed();
697 :
698 : // do commands (always return true)
699 10 : case MAV_CMD_DO_CHANGE_SPEED:
700 : case MAV_CMD_DO_SET_HOME:
701 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
702 : case MAV_CMD_DO_SET_ROI_LOCATION:
703 : case MAV_CMD_DO_SET_ROI_NONE:
704 : case MAV_CMD_DO_SET_ROI:
705 : case MAV_CMD_DO_SET_REVERSE:
706 : case MAV_CMD_DO_FENCE_ENABLE:
707 : case MAV_CMD_DO_GUIDED_LIMITS:
708 10 : return true;
709 :
710 0 : default:
711 : // error message
712 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Skipping invalid cmd #%i", cmd.id);
713 : // return true if we do not recognize the command so that we move on to the next command
714 0 : return true;
715 : }
716 : }
717 :
718 : /********************************************************************************/
719 : // Nav (Must) commands
720 : /********************************************************************************/
721 :
722 18 : void ModeAuto::do_RTL(void)
723 : {
724 : // start rtl in auto mode
725 18 : start_RTL();
726 18 : }
727 :
728 125 : bool ModeAuto::do_nav_wp(const AP_Mission::Mission_Command& cmd, bool always_stop_at_destination)
729 : {
730 : // retrieve and sanitize target location
731 125 : Location cmdloc = cmd.content.location;
732 125 : cmdloc.sanitize(rover.current_loc);
733 :
734 : // delayed stored in p1 in seconds
735 125 : loiter_duration = ((int16_t) cmd.p1 < 0) ? 0 : cmd.p1;
736 125 : loiter_start_time = 0;
737 125 : if (loiter_duration > 0) {
738 0 : always_stop_at_destination = true;
739 : }
740 :
741 : // do not add next wp if there are no more navigation commands
742 125 : AP_Mission::Mission_Command next_cmd;
743 125 : if (always_stop_at_destination || !mission.get_next_nav_cmd(cmd.index+1, next_cmd)) {
744 : // single destination
745 18 : if (!set_desired_location(cmdloc)) {
746 0 : return false;
747 : }
748 : } else {
749 : // retrieve and sanitize next destination location
750 107 : Location next_cmdloc = next_cmd.content.location;
751 107 : next_cmdloc.sanitize(cmdloc);
752 107 : if (!set_desired_location(cmdloc, next_cmdloc)) {
753 0 : return false;
754 : }
755 : }
756 :
757 : // just starting so we haven't previously reached the waypoint
758 125 : previously_reached_wp = false;
759 :
760 125 : return true;
761 : }
762 :
763 : // do_nav_delay - Delay the next navigation command
764 0 : void ModeAuto::do_nav_delay(const AP_Mission::Mission_Command& cmd)
765 : {
766 0 : nav_delay_time_start_ms = millis();
767 :
768 : // boats loiter, cars and balancebots stop
769 0 : if (rover.is_boat()) {
770 0 : if (!start_loiter()) {
771 0 : start_stop();
772 : }
773 : } else {
774 0 : start_stop();
775 : }
776 :
777 0 : if (cmd.content.nav_delay.seconds > 0) {
778 : // relative delay
779 0 : nav_delay_time_max_ms = cmd.content.nav_delay.seconds * 1000; // convert seconds to milliseconds
780 : } else {
781 : // absolute delay to utc time
782 : #if AP_RTC_ENABLED
783 0 : nav_delay_time_max_ms = AP::rtc().get_time_utc(cmd.content.nav_delay.hour_utc, cmd.content.nav_delay.min_utc, cmd.content.nav_delay.sec_utc, 0);
784 : #else
785 : nav_delay_time_max_ms = 0;
786 : #endif
787 : }
788 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Delaying %u sec", (unsigned)(nav_delay_time_max_ms/1000));
789 0 : }
790 :
791 : // start guided within auto to allow external navigation system to control vehicle
792 1 : void ModeAuto::do_nav_guided_enable(const AP_Mission::Mission_Command& cmd)
793 : {
794 1 : if (cmd.p1 > 0) {
795 1 : start_guided(cmd.content.location);
796 : }
797 1 : }
798 :
799 : // do_set_yaw_speed - turn to a specified heading and achieve a given speed
800 0 : void ModeAuto::do_nav_set_yaw_speed(const AP_Mission::Mission_Command& cmd)
801 : {
802 : float desired_heading_cd;
803 :
804 : // get final angle, 1 = Relative, 0 = Absolute
805 0 : if (cmd.content.set_yaw_speed.relative_angle > 0) {
806 : // relative angle
807 0 : desired_heading_cd = wrap_180_cd(ahrs.yaw_sensor + cmd.content.set_yaw_speed.angle_deg * 100.0f);
808 : } else {
809 : // absolute angle
810 0 : desired_heading_cd = cmd.content.set_yaw_speed.angle_deg * 100.0f;
811 : }
812 :
813 : // set targets
814 0 : const float speed_max = g2.wp_nav.get_default_speed();
815 0 : _desired_speed = constrain_float(cmd.content.set_yaw_speed.speed, -speed_max, speed_max);
816 0 : _desired_yaw_cd = desired_heading_cd;
817 0 : _reached_heading = false;
818 0 : _submode = SubMode::HeadingAndSpeed;
819 0 : }
820 :
821 : /********************************************************************************/
822 : // Verify Nav (Must) commands
823 : /********************************************************************************/
824 91178 : bool ModeAuto::verify_nav_wp(const AP_Mission::Mission_Command& cmd)
825 : {
826 : // exit immediately if we haven't reached the destination
827 91178 : if (!reached_destination()) {
828 90155 : return false;
829 : }
830 :
831 : // Check if this is the first time we have noticed reaching the waypoint
832 1023 : if (!previously_reached_wp) {
833 115 : previously_reached_wp = true;
834 :
835 : // check if we are loitering at this waypoint - the message sent to the GCS is different
836 115 : if (loiter_duration > 0) {
837 : // send message including loiter time
838 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Reached waypoint #%u. Loiter for %u seconds",
839 : (unsigned int)cmd.index,
840 : (unsigned int)loiter_duration);
841 : // record the current time i.e. start timer
842 0 : loiter_start_time = millis();
843 : } else {
844 : // send simpler message to GCS
845 115 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Reached waypoint #%u", (unsigned int)cmd.index);
846 : }
847 : }
848 :
849 : // Check if we have loitered long enough
850 1023 : if (loiter_duration == 0) {
851 1023 : return true;
852 : } else {
853 0 : return (((millis() - loiter_start_time) / 1000) >= loiter_duration);
854 : }
855 : }
856 :
857 : // verify_nav_delay - check if we have waited long enough
858 0 : bool ModeAuto::verify_nav_delay(const AP_Mission::Mission_Command& cmd)
859 : {
860 0 : if (millis() - nav_delay_time_start_ms > nav_delay_time_max_ms) {
861 0 : nav_delay_time_max_ms = 0;
862 0 : return true;
863 : }
864 :
865 0 : return false;
866 : }
867 :
868 22896 : bool ModeAuto::verify_RTL() const
869 : {
870 22896 : return reached_destination();
871 : }
872 :
873 910 : bool ModeAuto::verify_loiter_unlimited(const AP_Mission::Mission_Command& cmd)
874 : {
875 910 : verify_nav_wp(cmd);
876 910 : return false;
877 : }
878 :
879 : // verify_loiter_time - check if we have loitered long enough
880 0 : bool ModeAuto::verify_loiter_time(const AP_Mission::Mission_Command& cmd)
881 : {
882 0 : const bool result = verify_nav_wp(cmd);
883 0 : if (result) {
884 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Finished active loiter");
885 : }
886 0 : return result;
887 : }
888 :
889 : // check if guided has completed
890 500 : bool ModeAuto::verify_nav_guided_enable(const AP_Mission::Mission_Command& cmd)
891 : {
892 : // if we failed to enter guided or this command disables guided
893 : // return true so we move to next command
894 500 : if (_submode != SubMode::Guided || cmd.p1 == 0) {
895 0 : return true;
896 : }
897 :
898 : // if a location target was set, return true once vehicle is close
899 500 : if (guided_target.valid) {
900 500 : if (rover.current_loc.get_distance(guided_target.loc) <= g2.wp_nav.get_radius()) {
901 0 : return true;
902 : }
903 : }
904 :
905 : // guided command complete once a limit is breached
906 500 : return rover.mode_guided.limit_breached();
907 : }
908 :
909 : // verify_yaw - return true if we have reached the desired heading
910 0 : bool ModeAuto::verify_nav_set_yaw_speed()
911 : {
912 0 : if (_submode == SubMode::HeadingAndSpeed) {
913 0 : return _reached_heading;
914 : }
915 : // we should never reach here but just in case, return true to allow missions to continue
916 0 : return true;
917 : }
918 :
919 2 : bool ModeAuto::do_circle(const AP_Mission::Mission_Command& cmd)
920 : {
921 : // retrieve and sanitize target location
922 2 : Location circle_center = cmd.content.location;
923 2 : circle_center.sanitize(rover.current_loc);
924 :
925 : // calculate radius
926 2 : uint16_t circle_radius_m = HIGHBYTE(cmd.p1); // circle radius held in high byte of p1
927 2 : if (cmd.id == MAV_CMD_NAV_LOITER_TURNS &&
928 2 : cmd.type_specific_bits & (1U << 0)) {
929 : // special storage handling allows for larger radii
930 1 : circle_radius_m *= 10;
931 : }
932 :
933 : // initialise circle mode
934 2 : if (g2.mode_circle.set_center(circle_center, circle_radius_m, cmd.content.location.loiter_ccw)) {
935 2 : _submode = SubMode::Circle;
936 2 : return true;
937 : }
938 0 : return false;
939 : }
940 :
941 1977 : bool ModeAuto::verify_circle(const AP_Mission::Mission_Command& cmd)
942 : {
943 1977 : const float turns = cmd.get_loiter_turns();
944 : // check if we have completed circling
945 1977 : return ((g2.mode_circle.get_angle_total_rad() / M_2PI) >= turns);
946 : }
947 :
948 : /********************************************************************************/
949 : // Condition (May) commands
950 : /********************************************************************************/
951 :
952 0 : void ModeAuto::do_wait_delay(const AP_Mission::Mission_Command& cmd)
953 : {
954 0 : condition_start = millis();
955 0 : condition_value = static_cast<int32_t>(cmd.content.delay.seconds * 1000); // convert seconds to milliseconds
956 0 : }
957 :
958 0 : void ModeAuto::do_within_distance(const AP_Mission::Mission_Command& cmd)
959 : {
960 0 : condition_value = cmd.content.distance.meters;
961 0 : }
962 :
963 : /********************************************************************************/
964 : // Verify Condition (May) commands
965 : /********************************************************************************/
966 :
967 0 : bool ModeAuto::verify_wait_delay()
968 : {
969 0 : if (static_cast<uint32_t>(millis() - condition_start) > static_cast<uint32_t>(condition_value)) {
970 0 : condition_value = 0;
971 0 : return true;
972 : }
973 0 : return false;
974 : }
975 :
976 0 : bool ModeAuto::verify_within_distance()
977 : {
978 0 : if (get_distance_to_destination() < condition_value) {
979 0 : condition_value = 0;
980 0 : return true;
981 : }
982 0 : return false;
983 : }
984 :
985 :
986 : /********************************************************************************/
987 : // Do (Now) commands
988 : /********************************************************************************/
989 :
990 9 : void ModeAuto::do_change_speed(const AP_Mission::Mission_Command& cmd)
991 : {
992 : // set speed for active mode
993 9 : if (set_desired_speed(cmd.content.speed.target_ms)) {
994 9 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "speed: %.1f m/s", static_cast<double>(cmd.content.speed.target_ms));
995 : }
996 9 : }
997 :
998 0 : void ModeAuto::do_set_home(const AP_Mission::Mission_Command& cmd)
999 : {
1000 0 : if (cmd.p1 == 1 && rover.have_position) {
1001 0 : if (!rover.set_home_to_current_location(false)) {
1002 : // ignored...
1003 : }
1004 : } else {
1005 0 : if (!rover.set_home(cmd.content.location, false)) {
1006 : // ignored...
1007 : }
1008 : }
1009 0 : }
1010 :
1011 0 : void ModeAuto::do_set_reverse(const AP_Mission::Mission_Command& cmd)
1012 : {
1013 0 : set_reversed(cmd.p1 == 1);
1014 0 : }
1015 :
1016 : // set timeout and position limits for guided within auto
1017 1 : void ModeAuto::do_guided_limits(const AP_Mission::Mission_Command& cmd)
1018 : {
1019 1 : rover.mode_guided.limit_set(
1020 1 : cmd.p1 * 1000, // convert seconds to ms
1021 1 : cmd.content.guided_limits.horiz_max);
1022 1 : }
1023 :
1024 : #if AP_SCRIPTING_ENABLED
1025 : // start accepting position, velocity and acceleration targets from lua scripts
1026 0 : void ModeAuto::do_nav_script_time(const AP_Mission::Mission_Command& cmd)
1027 : {
1028 : // call regular guided flight mode initialisation
1029 0 : if (rover.mode_guided.enter()) {
1030 0 : _submode = SubMode::NavScriptTime;
1031 0 : nav_scripting.done = false;
1032 0 : nav_scripting.id++;
1033 0 : nav_scripting.start_ms = millis();
1034 0 : nav_scripting.command = cmd.content.nav_script_time.command;
1035 0 : nav_scripting.timeout_s = cmd.content.nav_script_time.timeout_s;
1036 0 : nav_scripting.arg1 = cmd.content.nav_script_time.arg1.get();
1037 0 : nav_scripting.arg2 = cmd.content.nav_script_time.arg2.get();
1038 0 : nav_scripting.arg3 = cmd.content.nav_script_time.arg3;
1039 0 : nav_scripting.arg4 = cmd.content.nav_script_time.arg4;
1040 : } else {
1041 : // for safety we set nav_scripting to done to protect against the mission getting stuck
1042 0 : nav_scripting.done = true;
1043 : }
1044 0 : }
1045 :
1046 : // check if verify_nav_script_time command has completed
1047 0 : bool ModeAuto::verify_nav_script_time()
1048 : {
1049 : // if done or timeout then return true
1050 0 : if (nav_scripting.done ||
1051 0 : ((nav_scripting.timeout_s > 0) &&
1052 0 : (AP_HAL::millis() - nav_scripting.start_ms) > (nav_scripting.timeout_s * 1000))) {
1053 0 : return true;
1054 : }
1055 0 : return false;
1056 : }
1057 : #endif
|