Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
nav_survey_hybrid.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Gautier Hattenberger <gautier.hattenberger@enac.fr>
3  * Based on OSAM poly survey
4  *
5  * This file is part of paparazzi.
6  *
7  * paparazzi is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * paparazzi is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with paparazzi; see the file COPYING. If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
21 
30 
34 #include "state.h"
35 #include "autopilot.h"
36 #include "generated/flight_plan.h"
37 
38 #ifdef DIGITAL_CAM
39 #include "modules/digital_cam/dc.h"
40 #endif
41 
42 // turn anticipation
43 #ifndef SURVEY_HYBRID_APPROACHING_TIME
44 #define SURVEY_HYBRID_APPROACHING_TIME 3.f
45 #endif
46 
47 // maximum number of polygon corners
48 #ifndef SURVEY_HYBRID_MAX_POLYGON_SIZE
49 #define SURVEY_HYBRID_MAX_POLYGON_SIZE 20
50 #endif
51 
52 // use half sweep at the end of polygon
53 #ifndef SURVEY_HYBRID_HALF_SWEEP_ENABLED
54 #define SURVEY_HYBRID_HALF_SWEEP_ENABLED true
55 #endif
56 
57 // maximum number of sweep lines (0 for unlimited)
58 #ifndef SURVEY_HYBRID_MAX_SWEEP
59 #define SURVEY_HYBRID_MAX_SWEEP 0
60 #endif
61 
62 // maximum number of sweep back (0 for unlimited)
63 #ifndef SURVEY_HYBRID_MAX_SWEEP_BACK
64 #define SURVEY_HYBRID_MAX_SWEEP_BACK 0
65 #endif
66 
67 // entry distance (default, half sweep distance)
68 #ifndef SURVEY_HYBRID_ENTRY_DISTANCE
69 #define SURVEY_HYBRID_ENTRY_DISTANCE (survey_private.sweep_distance / 2.f)
70 #endif
71 
72 struct Line {float m; float b; float x;};
74 
77  float orientation;
78  enum SurveyStatus status;
83  struct EnuCoor_f smallest_corner;
84  struct EnuCoor_f to_wp;
85  struct EnuCoor_f from_wp;
86  float max_y;
87  float sweep;
88  struct EnuCoor_f entry;
89  struct EnuCoor_f segment_from;
90  struct EnuCoor_f segment_to;
91  struct EnuCoor_f circle;
92  float radius;
93  bool valid;
94  bool circle_turns;
96 };
97 
100 
101 static void nav_survey_hybrid_setup(float orientation, float sweep, float radius, float height);
102 
103 static void TranslateAndRotateFromWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans);
104 static void RotateAndTranslateToWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans);
105 static void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2);
106 static float EvaluateLineForX(float y, struct Line L);
107 static float CrossProductZ(struct EnuCoor_f *p1_start, struct EnuCoor_f *p1_end, struct EnuCoor_f *p2_start, struct EnuCoor_f *p2_end);
108 
109 #define MaxFloat 1000000000
110 #define MinFloat -1000000000
111 
112 #ifndef LINE_START_FUNCTION
113 #define LINE_START_FUNCTION {}
114 #endif
115 #ifndef LINE_STOP_FUNCTION
116 #define LINE_STOP_FUNCTION {}
117 #endif
118 
119 #if USE_MISSION
121 
122 static bool nav_survey_hybrid_mission_local(uint8_t nb, float *params, enum MissionRunFlag flag)
123 {
124  if (flag == MissionInit) {
125  if (nb == 10 || nb == 12) {
126  float orientation = params[0];
127  float sweep = params[1];
128  float radius = params[2];
129  float height = params[3];
130  if (nb == 10) { survey_private.size = 3; }
131  else { survey_private.size = 4; }
132  for (int i = 0; i < survey_private.size; i++) {
133  survey_private.corners[i].x = params[4+2*i];
134  survey_private.corners[i].y = params[5+2*i+1];
135  survey_private.corners[i].z = height;
136  }
138  return nav_survey_hybrid_run();
139  }
140  }
141  else if (flag == MissionRun) {
142  return nav_survey_hybrid_run();
143  }
144  return false; // not a valid case
145 }
146 
147 static bool nav_survey_hybrid_mission_global(uint8_t nb, float *params, enum MissionRunFlag flag)
148 {
149  if (flag == MissionInit) {
150  if (nb == 10 || nb == 12) {
151  float orientation = params[0];
152  float sweep = params[1];
153  float radius = params[2];
154  float height = params[3];
155  if (nb == 10) { survey_private.size = 3; }
156  else { survey_private.size = 4; }
157  for (int i = 0; i < survey_private.size; i++) {
158  struct LlaCoor_f lla = {
159  .lat = RadOfDeg(params[4+2*i]),
160  .lon = RadOfDeg(params[4+2*i+1]),
161  .alt = state.ned_origin_f.lla.alt + height
162  };
163  struct EnuCoor_f corner;
164  enu_of_lla_point_f(&corner, &state.ned_origin_f, &lla);
165  survey_private.corners[i] = corner;
166  }
167  nav_survey_hybrid_setup(orientation, sweep, radius, height);
168  return nav_survey_hybrid_run();
169  }
170  }
171  else if (flag == MissionRun) {
172  return nav_survey_hybrid_run();
173  }
174  return false; // not a valid case
175 }
176 
177 #endif
178 
180 {
186 
187  memset(&survey_private, 0, sizeof(struct SurveyHybridPrivate));
188 
189 #if USE_MISSION
190  mission_register(nav_survey_hybrid_mission_local, "SRVHL");
191  mission_register(nav_survey_hybrid_mission_global, "SRVHG");
192 #endif
193 }
194 
197 static void nav_survey_hybrid_setup(float orientation, float sweep, float radius, float height)
198 {
200  int i = 0;
201  float ys = 0.f;
202  float LeftYInt;
203  float RightYInt;
204  float temp;
205  float XIntercept1 = 0.f;
206  float XIntercept2 = 0.f;
207 
208  // cap orientation angle to [-pi; +pi]
209  survey_private.orientation = RadOfDeg(orientation);
210  NormRadAngle(survey_private.orientation);
212 
213  // set auto radius mode if needed
214  if (radius < -0.1f) {
215  survey_private.radius = sweep / 2.f;
217  } else if (radius > 0.1f) {
218  survey_private.radius = radius;
220  } else {
221  survey_private.radius = 0.f;
223  }
224 
225  float entry_distance = SURVEY_HYBRID_ENTRY_DISTANCE;
230 
231  // Rotate Corners so sweeps are parellel with x axis
232  for (i = 0; i < survey_private.size; i++) {
233  struct EnuCoor_f zero = { 0 };
235  }
236 
237  // Find min x and min y
239  for (i = 1; i < survey_private.size; i++) {
242  }
245  }
246  }
247 
248  // Translate Corners all exist in quad #1
249  for (i = 0; i < survey_private.size; i++) {
251  }
252 
253  // Find max y
255  for (i = 1; i < survey_private.size; i++) {
258  }
259  }
260 
261  // Find polygon edges
262  for (i = 0; i < survey_private.size; i++) {
263  if (i == 0) {
264  if (survey_private.corners[survey_private.size - 1].x == survey_private.corners[i].x) { // Don't divide by zero!
266  } else {
268  }
269  }
270  else if (survey_private.corners[i].x == survey_private.corners[i - 1].x) {
272  } else {
274  }
275 
277  }
278 
279  // Find Min and Max y for each line
282 
283  if (LeftYInt > RightYInt) {
284  survey_private.edge_max_y[0] = LeftYInt;
285  survey_private.edge_min_y[0] = RightYInt;
286  } else {
287  survey_private.edge_max_y[0] = RightYInt;
288  survey_private.edge_min_y[0] = LeftYInt;
289  }
290 
291  for (i = 1; i < survey_private.size - 1; i++) {
292  FindInterceptOfTwoLines(&temp, &LeftYInt, survey_private.edges[i], survey_private.edges[i + 1]);
293  FindInterceptOfTwoLines(&temp, &RightYInt, survey_private.edges[i], survey_private.edges[i - 1]);
294 
295  if (LeftYInt > RightYInt) {
296  survey_private.edge_max_y[i] = LeftYInt;
297  survey_private.edge_min_y[i] = RightYInt;
298  } else {
299  survey_private.edge_max_y[i] = RightYInt;
300  survey_private.edge_min_y[i] = LeftYInt;
301  }
302  }
303 
306 
307  if (LeftYInt > RightYInt) {
309  survey_private.edge_min_y[survey_private.size - 1] = RightYInt;
310  } else {
311  survey_private.edge_max_y[survey_private.size - 1] = RightYInt;
313  }
314 
315  // Find amount to increment by every sweep
316  if (survey_private.corners[0].y >= survey_private.max_y / 2.f) {
317  entry_distance = -entry_distance;
319  } else {
321  }
322 
323  // Find y value of the first sweep
324  ys = survey_private.corners[0].y + entry_distance;
325 
326  // Find the edges which intercet the sweep line first
327  for (i = 0; i < survey_private.size; i++) {
328  if (survey_private.edge_min_y[i] <= ys && survey_private.edge_max_y[i] > ys) {
329  XIntercept2 = XIntercept1;
330  XIntercept1 = EvaluateLineForX(ys, survey_private.edges[i]);
331  }
332  }
333 
334  // Find point to come from and point to go to
335  if (fabsf(survey_private.corners[0].x - XIntercept2) <= fabsf(survey_private.corners[0].x - XIntercept1)) {
336  survey_private.to_wp.x = XIntercept1;
337  survey_private.to_wp.y = ys;
338  survey_private.from_wp.x = XIntercept2;
339  survey_private.from_wp.y = ys;
340  } else {
341  survey_private.to_wp.x = XIntercept2;
342  survey_private.to_wp.y = ys;
343  survey_private.from_wp.x = XIntercept1;
344  survey_private.from_wp.y = ys;
345  }
346 
347  // Find the entry point
349  survey_private.entry.y = survey_private.corners[0].y + entry_distance;
350  survey_private.entry.z = height;
351 
352  // Go into entry state
354 
357  survey_private.valid = true;
358 }
359 
360 void nav_survey_hybrid_setup_orientation(uint8_t start_wp, float orientation, uint8_t size, float sweep, float radius, float height)
361 {
362  survey_private.valid = false;
363  if (size < 3 || size > SURVEY_HYBRID_MAX_POLYGON_SIZE) {
364  return; // polygon is too small or too big
365  }
366  for (int i = 0; i < size; i++) {
367  struct EnuCoor_f *wp = waypoint_get_enu_f(start_wp + i);
368  if (wp == NULL) {
369  return; // not a valid waypoint
370  }
371  survey_private.corners[i] = *wp;
372  }
373  survey_private.size = size;
374 
375  nav_survey_hybrid_setup(orientation, sweep, radius, height);
376 }
377 
378 void nav_survey_hybrid_setup_towards(uint8_t start_wp, uint8_t second_wp, uint8_t size, float sweep, float radius, float height)
379 {
380  survey_private.valid = false;
381  struct EnuCoor_f *start = waypoint_get_enu_f(start_wp);
382  struct EnuCoor_f *second = waypoint_get_enu_f(second_wp);
383  if (start == NULL || second == NULL) {
384  return;
385  }
386 
387  float dx = second->x - start->x;
388  float dy = second->y - start->y;
389  float angle = DegOfRad(atan2f(dy, dx));
390  nav_survey_hybrid_setup_orientation(start_wp, angle, size, sweep, radius, height);
391 }
392 
393 //=========================================================================================================================
395 {
396  if (!survey_private.valid) {
397  return false; // don't start survey
398  }
399 
400  struct EnuCoor_f C;
401  float ys = 0.f;
402  static struct EnuCoor_f LastPoint;
403  int i = 0;
404  bool LastHalfSweep = false;
405  static bool HalfSweep = false;
406  float XIntercept1 = 0.f;
407  float XIntercept2 = 0.f;
408  float DInt1 = 0.f;
409  float DInt2 = 0.f;
410  struct EnuCoor_f zero = { 0 };
411  float qdr = 0.f;
412  float qdr_out = 0.f;
413 
414  switch (survey_private.status) {
415  case Entry:
416  C = survey_private.entry;
419 
421  // align segment at entry point with a circle
424  survey_private.circle.z = C.z;
426 
428  qdr_out = - survey_private.orientation;
429  if (CloseRadAngles(qdr, qdr_out) && NavCircleCount() > 0.5f) {
431  nav_init_stage();
433  }
434  } else {
435  // goto entry point
438 
440  && (fabsf(stateGetPositionEnu_f()->z - survey_private.entry.z)) < 5.f)) {
442  nav_init_stage();
444  }
445  }
446  break;
447  case Sweep:
448  LastHalfSweep = HalfSweep;
451 
452  // Rotate and Translate Line points into real world
457 
458  // follow the line
460 
462  LastPoint = survey_private.to_wp;
463 
464 #ifdef DIGITAL_CAM
465  float line_length = fabsf((fabsf(survey_private.segment_from.x) - fabsf(survey_private.segment_to.x)));
466  double inteiro;
467  double fract = modf(line_length / dc_distance_interval, &inteiro);
468  if (fract > .5) {
469  //if last shot is more than shot_distance/2 from the corner then take a picture in the corner before go to the next sweep
471  }
472 #endif
473 
474  if (LastPoint.y + survey_private.sweep >= survey_private.max_y || LastPoint.y + survey_private.sweep <= 0) {
475  // Your out of the Polygon so Sweep Back or Half Sweep
476  if ((LastPoint.y + (survey_private.sweep / 2.f)) <= survey_private.max_y ||
477  (LastPoint.y + (survey_private.sweep / 2.f)) >= 0.f ||
479  // Sweep back
481  }
482  if (LastHalfSweep) {
483  HalfSweep = false;
484  ys = LastPoint.y + survey_private.sweep;
485  } else {
486  HalfSweep = true;
487  ys = LastPoint.y + (survey_private.sweep / 2.f);
488  }
490  } else { // Normal sweep
491  // Find y value of the first sweep
492  HalfSweep = false;
493  ys = LastPoint.y + survey_private.sweep;
494  }
495 
496  // Find the edges which intercet the sweep line first
497  for (i = 0; i < survey_private.size; i++) {
498  if (survey_private.edge_min_y[i] < ys && survey_private.edge_max_y[i] >= ys) {
499  XIntercept2 = XIntercept1;
500  XIntercept1 = EvaluateLineForX(ys, survey_private.edges[i]);
501  }
502  }
503 
504  // Find point to come from and point to go to
505  DInt1 = XIntercept1 - LastPoint.x;
506  DInt2 = XIntercept2 - LastPoint.x;
507 
508  if (DInt1 *DInt2 >= 0) {
509  if (fabsf(DInt2) <= fabsf(DInt1)) {
510  survey_private.to_wp.x = XIntercept1;
511  survey_private.to_wp.y = ys;
512  survey_private.from_wp.x = XIntercept2;
513  survey_private.from_wp.y = ys;
514  } else {
515  survey_private.to_wp.x = XIntercept2;
516  survey_private.to_wp.y = ys;
517  survey_private.from_wp.x = XIntercept1;
518  survey_private.from_wp.y = ys;
519  }
520  } else {
521  if ((survey_private.to_wp.x - survey_private.from_wp.x) > 0 && DInt2 > 0) {
522  survey_private.to_wp.x = XIntercept1;
523  survey_private.to_wp.y = ys;
524  survey_private.from_wp.x = XIntercept2;
525  survey_private.from_wp.y = ys;
526  } else if ((survey_private.to_wp.x - survey_private.from_wp.x) < 0 && DInt2 < 0) {
527  survey_private.to_wp.x = XIntercept1;
528  survey_private.to_wp.y = ys;
529  survey_private.from_wp.x = XIntercept2;
530  survey_private.from_wp.y = ys;
531  } else {
532  survey_private.to_wp.x = XIntercept2;
533  survey_private.to_wp.y = ys;
534  survey_private.from_wp.x = XIntercept1;
535  survey_private.from_wp.y = ys;
536  }
537  }
538 
539  // Go into Turn state
541  nav_init_stage();
543 
547  // end survey if nb > nb_max and nb_max is not 0
548  // or if nb_back > nb_back_max and nb_back_max is not 0
549  return false;
550  }
551  }
552 
553  break;
554  case Turn:
555  survey_private.segment_from = LastPoint;
557 
558  // Rotate and Translate Line points into real world
563 
567  } else {
569  }
570  float dir = (survey_private.sweep < 0.f ? -1.f : 1.f) * fabsf(survey_private.radius);
574  // circle turns
576 
579  if (CloseRadAngles(qdr, qdr_out)) {
581  nav_init_stage();
583  }
584  } else {
585  // use straight lines to reach next point
587 
590  nav_init_stage();
592  }
593  }
594 
595  break;
596  case Init:
597  return false;
598  default:
599  return false;
600  }
601 
602  return true;
603 
604 }
605 
606 //============================================================================================================================================
607 /*
608  Translates point so (transX, transY) are (0,0) then rotates the point around z by Zrot
609 */
610 void TranslateAndRotateFromWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans)
611 {
612  float temp;
613 
614  p->x = p->x - trans->x;
615  p->y = p->y - trans->y;
616 
617  temp = p->x;
618  p->x = p->x * cosf(Zrot) + p->y * sinf(Zrot);
619  p->y = -temp * sinf(Zrot) + p->y * cosf(Zrot);
620 }
621 
623 void RotateAndTranslateToWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans)
624 {
625  float temp = p->x;
626 
627  p->x = p->x * cosf(Zrot) - p->y * sinf(Zrot);
628  p->y = temp * sinf(Zrot) + p->y * cosf(Zrot);
629 
630  p->x = p->x + trans->x;
631  p->y = p->y + trans->y;
632 }
633 
634 void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2)
635 {
636  *x = ((L2.b - L1.b) / (L1.m - L2.m));
637  *y = L1.m * (*x) + L1.b;
638 }
639 
640 
641 float EvaluateLineForX(float y, struct Line L)
642 {
643  return ((y - L.b) / L.m);
644 }
645 
646 float CrossProductZ(struct EnuCoor_f *p1_start, struct EnuCoor_f *p1_end, struct EnuCoor_f *p2_start, struct EnuCoor_f *p2_end)
647 {
648  float d1x = p1_end->x - p1_start->x;
649  float d1y = p1_end->y - p1_start->y;
650  float d2x = p2_end->x - p2_start->x;
651  float d2y = p2_end->y - p2_start->y;
652  return d1x * d2y - d1y * d2x;
653 }
654 
void dc_send_command(uint8_t cmd)
Send Command To Camera.
Core autopilot interface common to all firmwares.
float dc_distance_interval
AutoShoot photos on distance to last shot in meters.
Definition: dc.c:83
Standard Digital Camera Control Interface.
@ DC_SHOOT
Definition: dc.h:100
#define FLOAT_VECT2_ZERO(_v)
#define VECT2_COPY(_a, _b)
Definition: pprz_algebra.h:68
#define VECT3_COPY(_a, _b)
Definition: pprz_algebra.h:140
struct State state
Definition: state.c:36
static struct EnuCoor_f * stateGetPositionEnu_f(void)
Get position in local ENU coordinates (float).
Definition: state.h:719
struct LtpDef_f ned_origin_f
Definition of the local (flat earth) coordinate system.
Definition: state.h:220
static float p[2][2]
bool mission_register(mission_custom_cb cb, char *type)
Register a new navigation or action callback function.
mission planner library
MissionRunFlag
@ MissionInit
first exec
@ MissionRun
normal run
struct EnuCoor_f * waypoint_get_enu_f(uint8_t wp_id)
Get ENU coordinates (float)
Definition: waypoints.c:379
void nav_init_stage(void)
needs to be implemented by fixedwing and rotorcraft seperately
Definition: nav.c:92
#define NavCircleCount()
Definition: nav.h:155
#define NavVerticalAltitudeMode(_alt, _pre_climb)
Set the vertical mode to altitude control with the specified altitude setpoint and climb pre-command.
Definition: nav.h:191
#define CloseRadAngles(_c1, _c2)
#define SURVEY_HYBRID_MAX_SWEEP_BACK
uint8_t size
size of the polygon
#define SURVEY_HYBRID_APPROACHING_TIME
struct EnuCoor_f to_wp
tmp point in rotated frame
struct EnuCoor_f from_wp
tmp point in rotated frame
float orientation
requested orientation in radians
#define LINE_STOP_FUNCTION
static float CrossProductZ(struct EnuCoor_f *p1_start, struct EnuCoor_f *p1_end, struct EnuCoor_f *p2_start, struct EnuCoor_f *p2_end)
#define SURVEY_HYBRID_ENTRY_DISTANCE
#define SURVEY_HYBRID_MAX_SWEEP
float sweep
oriented sweep distance
static void nav_survey_hybrid_setup(float orientation, float sweep, float radius, float height)
finish preparation of survey based on private structure
static float EvaluateLineForX(float y, struct Line L)
static void RotateAndTranslateToWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans)
Rotates point round z by -Zrot then translates so (0,0) becomes (transX,transY)
struct EnuCoor_f entry
entry point
struct Line edges[SURVEY_HYBRID_MAX_POLYGON_SIZE]
polygon edges
void nav_survey_hybrid_init(void)
Init function.
static void TranslateAndRotateFromWorld(struct EnuCoor_f *p, float Zrot, struct EnuCoor_f *trans)
static struct SurveyHybridPrivate survey_private
struct EnuCoor_f circle
circle center
#define MaxFloat
float radius
turn radius
static void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2)
#define SURVEY_HYBRID_MAX_POLYGON_SIZE
#define LINE_START_FUNCTION
bool circle_turns
turns with circles (or lines between points otherwise)
bool valid
setup is valid
SurveyStatus
@ Turn
@ Init
@ Entry
@ Sweep
float edge_min_y[SURVEY_HYBRID_MAX_POLYGON_SIZE]
tmp point in rotated frame
bool nav_survey_hybrid_run(void)
Run polygon hybrid survey.
void nav_survey_hybrid_setup_orientation(uint8_t start_wp, float orientation, uint8_t size, float sweep, float radius, float height)
Setup polygon survey.
float sweep_distance
requested sweep distance
#define SURVEY_HYBRID_HALF_SWEEP_ENABLED
struct EnuCoor_f segment_to
end of current segment
void nav_survey_hybrid_setup_towards(uint8_t start_wp, uint8_t second_wp, uint8_t size, float sweep, float radius, float height)
Setup "dynamic" polygon survey with sweep orientation towards a waypoint.
float max_y
tmp value
float edge_max_y[SURVEY_HYBRID_MAX_POLYGON_SIZE]
tmp point in rotated frame
enum SurveyStatus status
current state
struct EnuCoor_f corners[SURVEY_HYBRID_MAX_POLYGON_SIZE]
corners location
struct SurveyHybrid survey_hybrid
struct EnuCoor_f segment_from
start of current segment
struct EnuCoor_f smallest_corner
tmp point in rotated frame
uint16_t sweep_back_nb_max
uint16_t sweep_back_nb
uint16_t sweep_nb_max
Paparazzi floating point algebra.
void enu_of_lla_point_f(struct EnuCoor_f *enu, struct LtpDef_f *def, struct LlaCoor_f *lla)
float alt
in meters (normally above WGS84 reference ellipsoid)
float y
in meters
float x
in meters
float lat
in radians
float z
in meters
struct LlaCoor_f lla
origin of local frame in LLA
vector in East North Up coordinates Units: meters
vector in Latitude, Longitude and Altitude
struct RotorcraftNavigation nav
Definition: navigation.c:51
Rotorcraft navigation functions.
navigation_approaching nav_approaching
Definition: navigation.h:152
navigation_circle nav_circle
Definition: navigation.h:153
navigation_goto nav_goto
Definition: navigation.h:150
navigation_route nav_route
Definition: navigation.h:151
static const float dir[]
API to get/set the generic vehicle states.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98