dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vtol_follower_fsm.c
Go to the documentation of this file.
1 
32 /*
33  * This program is free software; you can redistribute it and/or modify
34  * it under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 3 of the License, or
36  * (at your option) any later version.
37  *
38  * This program is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
40  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
41  * for more details.
42  *
43  * You should have received a copy of the GNU General Public License along
44  * with this program; if not, see <http://www.gnu.org/licenses/>
45  */
46 
47 #include "openpilot.h"
48 
49 #include <pios_thread.h>
50 
51 #include "coordinate_conversions.h"
52 #include "physical_constants.h"
53 
54 #include "vtol_follower_priv.h"
55 
56 #include "pathdesired.h"
57 #include "positionactual.h"
58 #include "vtolpathfollowersettings.h"
59 #include "vtolpathfollowerstatus.h"
60 
61 // Various navigation constants
62 const static float RTH_ALT_ERROR = 0.8f;
63 const static float RTH_CLIMB_SPEED = 1.0f;
64 
72 };
73 
92 };
93 
96  void (*entry_fn)();
97  int32_t (*static_fn)();
98  uint32_t timeout;
100 };
101 
102 #define MILLI 1000
103 
104 // State transition methods, typically enabling for certain actions
105 static void go_enable_hold_here(void);
106 static void go_enable_fly_path(void);
107 static void go_enable_rise_here(void);
108 static void go_enable_pause_home_10s(void);
109 static void go_enable_fly_home(void);
110 static void go_enable_land_home(void);
111 
112 // Methods that actually achieve the desired nav mode
113 static int32_t do_hold(void);
114 static int32_t do_path(void);
115 static int32_t do_requested_path(void);
116 static int32_t do_land(void);
117 static int32_t do_loiter(void);
118 static int32_t do_ph_climb(void);
119 
120 // Utility functions
121 static void vtol_fsm_timer_set(int32_t ms);
122 static bool vtol_fsm_timer_expired();
123 static void hold_position(float north, float east, float down);
124 
130  [FSM_STATE_INIT] = {
131  .next_state = {
133  },
134  },
135  [FSM_STATE_HOLDING] = {
136  .entry_fn = go_enable_hold_here,
137  .static_fn = do_loiter,
138  .next_state = {
141  },
142  },
143 };
144 
150  [FSM_STATE_INIT] = {
151  .next_state = {
153  },
154  },
156  .entry_fn = go_enable_fly_path,
157  .static_fn = do_requested_path,
158  .next_state = {
161  },
162  },
163 };
164 
175  [FSM_STATE_INIT] = {
176  .next_state = {
178  },
179  },
181  .entry_fn = go_enable_rise_here,
182  .static_fn = do_ph_climb,
183  .timeout = 8 * MILLI, // Spend at least 8s in this state
184  .next_state = {
188  },
189  },
191  .entry_fn = go_enable_fly_home,
192  .static_fn = do_path,
193  .next_state = {
195  },
196  },
198  .entry_fn = go_enable_pause_home_10s,
199  .static_fn = do_hold,
200  .timeout = 10 * MILLI,
201  .next_state = {
205  },
206  },
207  [FSM_STATE_LANDING] = {
208  .entry_fn = go_enable_land_home,
209  .static_fn = do_land,
210  .next_state = {
212  },
213  },
214 
215 };
216 
218 static uint32_t vtol_fsm_timer_expiration = 0;
219 
224 static void vtol_fsm_timer_set(int32_t ms)
225 {
226  if (ms) {
227  uint32_t now = PIOS_Thread_Systime();
228  vtol_fsm_timer_expiration = ms+now;
229 
230  // If we wrap and get very unlucky, make sure we still
231  // have a timer 1ms later.
232  if (vtol_fsm_timer_expiration == 0) {
234  }
235  } else {
237  }
238 }
239 
244 static bool vtol_fsm_timer_expired() {
245  /* If there's a timer... */
246  if (vtol_fsm_timer_expiration > 0) {
247  /* See if it expires. */
248 
249  uint32_t now = PIOS_Thread_Systime();
250  uint32_t interval = vtol_fsm_timer_expiration - now;
251 
252  /* If it has expired, this will wrap around and be a big
253  * number. Use a windowed scheme to detect this:
254  * Assume we will run at least every 0x10000000 us
255  * (3 days) and timeouts can't exceed 0xf0000000 us (46 days)
256  */
257  if (interval > 0xf0000000) {
258  return true;
259  }
260  }
261 
262  return false;
263 }
264 
265 
273 const static struct vtol_fsm_transition *current_goal;
277 
285 {
286  if (state != FSM_STATE_UNCHANGED) {
287  curr_state = state;
288 
289  /* Call the entry function (if any) for the next state. */
290  if (current_goal[curr_state].entry_fn) {
291  current_goal[curr_state].entry_fn();
292  }
293 
294  /* 0 disables any pending timeout, otherwise it's set to the
295  * value for this state */
296  vtol_fsm_timer_set(current_goal[curr_state].timeout);
297 
298  return 1;
299  }
300 
301  return 0;
302 }
303 
309 static bool vtol_fsm_process_event(enum vtol_fsm_event event)
310 {
311  enum vtol_fsm_state next = current_goal[curr_state].next_state[event];
312 
313  /* Special if condition to not have to explicitly define auto
314  * (don't do fault transitions on auto) */
315  if ((event != FSM_EVENT_AUTO) || (next != FSM_STATE_FAULT)) {
316  return vtol_fsm_enter_state(next);
317  }
318 
319  return false;
320 }
321 
326 {
328 }
329 
334 static void vtol_fsm_fsm_init(const struct vtol_fsm_transition *goal)
335 {
336  current_goal = goal;
337 
339 
340  /* Process any AUTO transitions in the FSM */
342 }
343 
354 static void vtol_fsm_inject_event(enum vtol_fsm_event event)
355 {
356  vtol_fsm_process_event(event);
357 
358  /* Process any AUTO transitions in the FSM */
360 }
361 
368 static int32_t vtol_fsm_static()
369 {
370  VtolPathFollowerStatusFSM_StateSet((uint8_t *) &curr_state);
371 
372  // If the current state has a static function, call it
373  if (current_goal[curr_state].static_fn) {
374  current_goal[curr_state].static_fn();
375  }
376 
377  if (vtol_fsm_timer_expired()) {
379  }
380 
381  return 0;
382 }
383 
385 
393 static float vtol_hold_position_ned[3];
395 
404 static int32_t do_hold()
405 {
407  if (vtol_follower_control_attitude(vtol_dT, NULL) == 0) {
408  return 0;
409  }
410  }
411 
412  return -1;
413 }
414 
416 static PathDesiredData vtol_fsm_path_desired = {
417  .Waypoint = 8000 // Unlikely to clash with pathplanner
418 };
419 
428 static int32_t do_path()
429 {
430  struct path_status progress;
431  if (vtol_follower_control_path(&vtol_fsm_path_desired, &progress) == 0) {
432  if (vtol_follower_control_attitude(vtol_dT, NULL) == 0) {
433 
434  if (progress.fractional_progress >= 1.0f) {
436  }
437 
438  return 0;
439  }
440  }
441 
442  return -1;
443 }
444 
451 static int32_t do_requested_path()
452 {
453  // Fetch the path desired from the path planner
454  PathDesiredGet(&vtol_fsm_path_desired);
455 
456  switch(vtol_fsm_path_desired.Mode) {
457  case PATHDESIRED_MODE_LAND:
458  for (uint8_t i = 0; i < 3; i++)
460  return do_land();
461  case PATHDESIRED_MODE_HOLDPOSITION:
462  case PATHDESIRED_MODE_ENDPOINT:
463  for (uint8_t i = 0; i < 3; i++)
465  return do_hold();
466  default:
467  return do_path();
468  }
469 }
470 
479 static int32_t do_land()
480 {
481  bool landed;
483  if (vtol_follower_control_attitude(vtol_dT, NULL) == 0) {
484  return 0;
485  }
486  }
487 
488  return 0;
489 }
490 
494 static int32_t do_loiter()
495 {
496  float att_adj[2] = { 0, 0 };
497  float hold_pos[3] = {
500  vtol_hold_position_ned[2]
501  };
502 
503  float alt_adj = 0;
504 
505  if (vtol_follower_control_loiter(vtol_dT, hold_pos, att_adj, &alt_adj)) {
506  // If hold position changed, use it!
507  // We follow this conditional just to avoid unnecessarily
508  // spamming updates to the PositionDesired object.
509 
510  hold_position(hold_pos[0], hold_pos[1], hold_pos[2]);
511  }
512 
514  alt_adj) == 0) {
515  if (vtol_follower_control_attitude(vtol_dT, att_adj) == 0) {
516  return 0;
517  }
518  }
519 
520  return -1;
521 }
522 
531 static int32_t do_slow_altitude_change(float descent_rate)
532 {
534  descent_rate) == 0) {
535  if (vtol_follower_control_attitude(vtol_dT, NULL) == 0) {
536  return 0;
537  }
538  }
539 
540  return -1;
541 }
542 
548 static int32_t do_ph_climb()
549 {
550  float cur_down;
551  PositionActualDownGet(&cur_down);
552 
553  int32_t ret_val;
554 
555  const float err = fabsf(cur_down - vtol_hold_position_ned[2]);
556 
557  if (err < RTH_ALT_ERROR) {
558  // If we're close to desired altitude, use poshold logic
559  // until timer expires.
560  ret_val = do_hold();
561 
562  if (vtol_fsm_timer_expired()) {
564  }
565  } else {
566  // If we are low, control for altitude change speed.
567  // If we are high, use the normal control loop.
568  if (cur_down > vtol_hold_position_ned[2]) {
570  } else {
571  ret_val = do_hold();
572  }
573  }
574 
575  return ret_val;
576 }
577 
579 
594 static void hold_position(float north, float east, float down)
595 {
596  vtol_hold_position_ned[0] = north;
597  vtol_hold_position_ned[1] = east;
598  vtol_hold_position_ned[2] = down;
599 
600  /* Store the data in the UAVO */
601  vtol_fsm_path_desired.Start[0] = north;
602  vtol_fsm_path_desired.Start[1] = east;
603  vtol_fsm_path_desired.Start[2] = down;
604  vtol_fsm_path_desired.End[0] = north;
605  vtol_fsm_path_desired.End[1] = east;
606  vtol_fsm_path_desired.End[2] = down;
607  vtol_fsm_path_desired.StartingVelocity = 0;
608  vtol_fsm_path_desired.EndingVelocity = 0;
609  vtol_fsm_path_desired.Mode = PATHDESIRED_MODE_ENDPOINT;
610  vtol_fsm_path_desired.ModeParameters = 0;
611 
612  vtol_fsm_path_desired.Waypoint++;
613 
614  PathDesiredSet(&vtol_fsm_path_desired);
615 }
616 
620 static void go_enable_hold_here()
621 {
622  PositionActualData positionActual;
623  PositionActualGet(&positionActual);
624 
625  hold_position(positionActual.North, positionActual.East, positionActual.Down);
626 }
627 
628 static void go_enable_fly_path()
629 {
630 }
631 
635 static void go_enable_rise_here()
636 {
637  PositionActualData positionActual;
638  PositionActualGet(&positionActual);
639 
640  float down = positionActual.Down;
641 
642  float min_rise, rth_alt;
643 
644  VtolPathFollowerSettingsReturnToHomeMinRiseGet(&min_rise);
645  VtolPathFollowerSettingsReturnToHomeAltitudeGet(&rth_alt);
646 
647  // Set the target altitude for MIN_RISE above our current alt
648  down -= min_rise;
649 
650  // But also make sure we return at a minimum of 15 m above home
651  if (down > -rth_alt)
652  down = -rth_alt;
653 
654  hold_position(positionActual.North, positionActual.East, down);
655 }
656 
661 {
662  float down = vtol_hold_position_ned[2];
663 
664  float rth_alt;
665  VtolPathFollowerSettingsReturnToHomeAltitudeGet(&rth_alt);
666 
667  if (down > -rth_alt)
668  down = -rth_alt;
669 
670  hold_position(0, 0, down);
671 }
672 
676 static void go_enable_fly_home()
677 {
678  PositionActualData positionActual;
679  PositionActualGet(&positionActual);
680 
681  // Set start position at current position
682  vtol_fsm_path_desired.Start[0] = positionActual.North;
683  vtol_fsm_path_desired.Start[1] = positionActual.East;
684  vtol_fsm_path_desired.Start[2] = positionActual.Down;
685 
686  // Set end position above home
687  vtol_fsm_path_desired.End[0] = 0;
688  vtol_fsm_path_desired.End[1] = 0;
689  vtol_fsm_path_desired.End[2] = positionActual.Down;
690 
691  float rth_alt;
692  VtolPathFollowerSettingsReturnToHomeAltitudeGet(&rth_alt);
693 
694  if (positionActual.Down > -rth_alt) {
695  vtol_fsm_path_desired.Start[2] = -rth_alt;
696  vtol_fsm_path_desired.End[2] = -rth_alt;
697  }
698 
699  /* Paranoia: set to 3.0f just in case calls below fail. TODO: change
700  * UAVO semantics to be guaranteed to either succeed or crash */
701  float rth_vel = 3.0f;
702  VtolPathFollowerSettingsReturnToHomeVelGet(&rth_vel);
703 
704  vtol_fsm_path_desired.StartingVelocity = rth_vel;
705  vtol_fsm_path_desired.EndingVelocity = rth_vel;
706 
707  vtol_fsm_path_desired.Mode = PATHDESIRED_MODE_VECTOR;
708  vtol_fsm_path_desired.ModeParameters = 0;
709 
710  /* It's necessary that this increment so that we don't end up
711  * latching completion status. Wraparound, etc, is OK.
712  * This is for compatibility with the waypoint handshaking in the
713  * path planner.
714  */
715  vtol_fsm_path_desired.Waypoint++;
716 
717  PathDesiredSet(&vtol_fsm_path_desired);
718 }
719 
723 static void go_enable_land_home()
724 {
725  vtol_hold_position_ned[0] = 0;
726  vtol_hold_position_ned[1] = 0;
727  vtol_hold_position_ned[2] = 0; // Has no effect
728 }
729 
731 
732 // Public API methods
734 {
735  switch(new_goal) {
736  case GOAL_LAND_HOME:
737  vtol_fsm_fsm_init(fsm_land_home);
738  return 0;
739  case GOAL_HOLD_POSITION:
740  vtol_fsm_fsm_init(fsm_hold_position);
741  return 0;
742  case GOAL_FLY_PATH:
743  vtol_fsm_fsm_init(fsm_follow_path);
744  return 0;
745  default:
746  return -1;
747  }
748 }
749 
751 {
752  vtol_fsm_static();
753  return 0;
754 }
755 
static const float RTH_ALT_ERROR
The altitude to come within for RTH */.
static void go_enable_hold_here(void)
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
int32_t vtol_follower_fsm_update()
static const struct vtol_fsm_transition fsm_land_home[FSM_STATE_NUM_STATES]
int32_t vtol_follower_fsm_activate_goal(enum vtol_goals new_goal)
float vtol_dT
int32_t vtol_follower_control_attitude(float dT, const float *att_adj)
static void go_enable_pause_home_10s(void)
static enum vtol_fsm_state curr_state
The current state within the goal fsm.
enum vtol_fsm_state next_state[FSM_EVENT_NUM_EVENTS]
int32_t vtol_follower_control_endpoint(const float *hold_pos_ned)
static bool vtol_fsm_process_event(enum vtol_fsm_event event)
float fractional_progress
Definition: paths.h:37
Structure for the FSM.
static const struct vtol_fsm_transition fsm_follow_path[FSM_STATE_NUM_STATES]
static void go_enable_rise_here(void)
static int32_t vtol_fsm_static()
static void vtol_fsm_inject_event(enum vtol_fsm_event event)
static int32_t do_loiter(void)
int32_t vtol_follower_control_land(const float *hold_pos_ned, bool *landed)
static int32_t do_land(void)
static void hold_position(float north, float east, float down)
static uint32_t vtol_fsm_timer_expiration
Specifies a time since startup in ms that the timeout fires.
vtol_fsm_event
Events that can be be injected into the FSM and trigger state changes.
static const struct vtol_fsm_transition fsm_hold_position[FSM_STATE_NUM_STATES]
vtol_fsm_state
static void go_enable_fly_path(void)
static const struct vtol_fsm_transition * current_goal
The currently selected goal FSM.
static PathDesiredData vtol_fsm_path_desired
The configured path desired. Uses the PathDesired structure.
static int32_t do_slow_altitude_change(float descent_rate)
uint8_t i
Definition: msp_messages.h:97
Includes for the internal methods.
static float vtol_hold_position_ned[3]
The setpoint for position hold relative to home in m.
vtol_goals
bool vtol_follower_control_loiter(float dT, float *hold_pos, float *att_adj, float *alt_adj)
static void vtol_fsm_process_auto()
static void vtol_fsm_fsm_init(const struct vtol_fsm_transition *goal)
static int32_t do_ph_climb(void)
static int vtol_fsm_enter_state(enum vtol_fsm_state state)
static void vtol_fsm_timer_set(int32_t ms)
Header for Coordinate conversions library in coordinate_conversions.c.
static int32_t do_path(void)
struct UAVOData * next
static void go_enable_land_home(void)
static int32_t do_hold(void)
static int32_t do_requested_path(void)
int32_t vtol_follower_control_path(const PathDesiredData *pathDesired, struct path_status *progress)
static bool vtol_fsm_timer_expired()
Includes PiOS and core architecture components.
static void go_enable_fly_home(void)
static const float RTH_CLIMB_SPEED
Climb at 1m/s.
#define MILLI
enum arena_state state
int32_t vtol_follower_control_altrate(const float *hold_pos_ned, float alt_adj)