Paparazzi UAS  v5.10_stable-5-g83a0da5-dirty
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
optical_flow_landing.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015
3  *
4  * This file is part of Paparazzi.
5  *
6  * Paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * Paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Paparazzi; see the file COPYING. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
36 // variables for in message:
37 float divergence;
41 float cov_div;
42 float pstate;
43 float pused;
44 float dt;
47 int landing;
50 
51 // minimum value of the P-gain for divergence control
52 // adaptive control will not be able to go lower
53 #define MINIMUM_GAIN 0.1
54 
55 // used for automated landing:
59 
60 // used for calculating velocity from height measurements:
61 #include <time.h>
63 
64 // sending the divergence message to the ground station:
65 static void send_divergence(struct transport_tx *trans, struct link_device *dev)
66 {
67  pprz_msg_send_DIVERGENCE(trans, dev, AC_ID,
70 }
71 
73 #include "generated/airframe.h"
74 #include "paparazzi.h"
75 #include "subsystems/abi.h"
77 
78 /* Default sonar/agl to use */
79 #ifndef OPTICAL_FLOW_LANDING_AGL_ID
80 #define OPTICAL_FLOW_LANDING_AGL_ID ABI_BROADCAST
81 #endif
82 PRINT_CONFIG_VAR(OPTICAL_FLOW_LANDING_AGL_ID)
83 
84 /* Use optical flow estimates */
85 #ifndef OPTICAL_FLOW_LANDING_OPTICAL_FLOW_ID
86 #define OPTICAL_FLOW_LANDING_OPTICAL_FLOW_ID ABI_BROADCAST
87 #endif
89 
90 // Other default values:
91 #ifndef OPTICAL_FLOW_LANDING_PGAIN
92 #define OPTICAL_FLOW_LANDING_PGAIN 1.0
93 #endif
94 
95 #ifndef OPTICAL_FLOW_LANDING_IGAIN
96 #define OPTICAL_FLOW_LANDING_IGAIN 0.0
97 #endif
98 
99 #ifndef OPTICAL_FLOW_LANDING_DGAIN
100 #define OPTICAL_FLOW_LANDING_DGAIN 0.0
101 #endif
102 
103 #ifndef OPTICAL_FLOW_LANDING_VISION_METHOD
104 #define OPTICAL_FLOW_LANDING_VISION_METHOD 1
105 #endif
106 
107 #ifndef OPTICAL_FLOW_LANDING_CONTROL_METHOD
108 #define OPTICAL_FLOW_LANDING_CONTROL_METHOD 0
109 #endif
110 
111 #ifndef OPTICAL_FLOW_LANDING_COV_METHOD
112 #define OPTICAL_FLOW_LANDING_COV_METHOD 0
113 #endif
114 
117 
119 static void vertical_ctrl_agl_cb(uint8_t sender_id __attribute__((unused)), float distance);
120 // Callback function of the optical flow estimate:
121 static void vertical_ctrl_optical_flow_cb(uint8_t sender_id __attribute__((unused)), uint32_t stamp, int16_t flow_x, int16_t flow_y, int16_t flow_der_x, int16_t flow_der_y, uint8_t quality, float size_divergence, float dist);
122 
124 
125 void vertical_ctrl_module_init(void);
126 void vertical_ctrl_module_run(bool in_flight);
127 
132 {
133  unsigned int i;
134 
135  of_landing_ctrl.agl = 0.0f;
136  of_landing_ctrl.agl_lp = 0.0f;
137  of_landing_ctrl.vel = 0.0f;
139  of_landing_ctrl.cov_set_point = -0.025f;
140  of_landing_ctrl.cov_limit = 0.0010f; //1.0f; // for cov(uz,div)
141  of_landing_ctrl.lp_factor = 0.95f;
145  of_landing_ctrl.sum_err = 0.0f;
146  of_landing_ctrl.nominal_thrust = 0.710f; //0.666f; // 0.640 with small battery
154 
155  struct timespec spec;
156  clock_gettime(CLOCK_REALTIME, &spec);
157  previous_time = spec.tv_nsec / 1.0E6;
158  //previous_time = time(NULL);
159 
160  // clear histories:
161  ind_hist = 0;
162  for (i = 0; i < COV_WINDOW_SIZE; i++) {
163  thrust_history[i] = 0;
164  divergence_history[i] = 0;
165  }
166 
167  // reset errors, thrust, divergence, etc.:
168  previous_err = 0.0f;
169  previous_cov_err = 0.0f;
170  normalized_thrust = 0.0f;
171  divergence = 0.0f;
172  divergence_vision = 0.0f;
173  divergence_vision_dt = 0.0f;
174  cov_div = 0.0f;
175  dt = 0.0f;
177  pused = pstate;
178  vision_message_nr = 1;
180  of_landing_ctrl.agl_lp = 0.0f;
181  landing = 0;
182 
183  // Subscribe to the altitude above ground level ABI messages
184  AbiBindMsgAGL(OPTICAL_FLOW_LANDING_AGL_ID, &agl_ev, vertical_ctrl_agl_cb);
185  // Subscribe to the optical flow estimator:
186  AbiBindMsgOPTICAL_FLOW(OPTICAL_FLOW_LANDING_OPTICAL_FLOW_ID, &optical_flow_ev, vertical_ctrl_optical_flow_cb);
187 
189 }
190 
195 {
196 
197  int i;
199  stabilization_cmd[COMMAND_THRUST] = 0;
200  ind_hist = 0;
203  normalized_thrust = 0.0f;
204  dt = 0.0f;
205  previous_err = 0.0f;
206  previous_cov_err = 0.0f;
208  struct timespec spec;
209  clock_gettime(CLOCK_REALTIME, &spec);
210  previous_time = spec.tv_nsec / 1.0E6;
211  vision_message_nr = 1;
213  for (i = 0; i < COV_WINDOW_SIZE; i++) {
214  thrust_history[i] = 0;
215  divergence_history[i] = 0;
216  }
217  landing = 0;
218 }
219 
224 void vertical_ctrl_module_run(bool in_flight)
225 {
226  int i;
227  float lp_height; // low-pass height
228  float div_factor; // factor that maps divergence in pixels as received from vision to /frame
229 
230  // ensure dt >= 0
231  if (dt < 0) { dt = 0.0f; }
232 
233  // get delta time, dt, to scale the divergence measurements correctly when using "simulated" vision:
234  struct timespec spec;
235  clock_gettime(CLOCK_REALTIME, &spec);
236  long new_time = spec.tv_nsec / 1.0E6;
237  long delta_t = new_time - previous_time;
238  dt += ((float)delta_t) / 1000.0f;
239  if (dt > 10.0f) {
240  dt = 0.0f;
241  return;
242  }
243  previous_time = new_time;
244 
245  if (!in_flight) {
246 
247  // When not flying and in mode module:
248  // Reset integrators
249  reset_all_vars();
250 
251  } else {
252 
253  /***********
254  * VISION
255  ***********/
256 
257  if (of_landing_ctrl.VISION_METHOD == 0) {
258 
259  // SIMULATED DIVERGENCE:
260 
261  // USE OPTITRACK HEIGHT
262  of_landing_ctrl.agl = (float) gps.lla_pos.alt / 1000.0f;
263  // else we get an immediate jump in divergence when switching on.
264  if (of_landing_ctrl.agl_lp < 1E-5 || ind_hist == 0) {
266  }
267  if (fabs(of_landing_ctrl.agl - of_landing_ctrl.agl_lp) > 1.0f) {
268  // ignore outliers:
270  }
271  // calculate the new low-pass height and the velocity
273 
274  // only calculate velocity and divergence if dt is large enough:
275  if (dt > 0.0001f) {
276  of_landing_ctrl.vel = (lp_height - of_landing_ctrl.agl_lp) / dt;
277  of_landing_ctrl.agl_lp = lp_height;
278 
279  // calculate the fake divergence:
280  if (of_landing_ctrl.agl_lp > 0.0001f) {
283  if (fabs(divergence_vision_dt) > 1E-5) {
284  div_factor = divergence / divergence_vision_dt;
285  }
286  } else {
287  divergence = 1000.0f;
288  // perform no control with this value (keeping thrust the same)
289  return;
290  }
291  // reset dt:
292  dt = 0.0f;
293  }
294  } else {
295  // USE REAL VISION OUTPUTS:
296 
297  if (vision_message_nr != previous_message_nr && dt > 1E-5 && ind_hist > 1) {
298  // TODO: this div_factor depends on the subpixel-factor (automatically adapt?)
299  div_factor = -1.28f; // magic number comprising field of view etc.
300  float new_divergence = (divergence_vision * div_factor) / dt;
301 
302  if (fabs(new_divergence - divergence) > 0.20) {
303  if (new_divergence < divergence) { new_divergence = divergence - 0.10f; }
304  else { new_divergence = divergence + 0.10f; }
305  }
306  // low-pass filter the divergence:
307  divergence = divergence * of_landing_ctrl.lp_factor + (new_divergence * (1.0f - of_landing_ctrl.lp_factor));
309  dt = 0.0f;
310  } else {
311  // after re-entering the module, the divergence should be equal to the set point:
312  if (ind_hist <= 1) {
314  for (i = 0; i < COV_WINDOW_SIZE; i++) {
315  thrust_history[i] = 0;
316  divergence_history[i] = 0;
317  }
318  ind_hist++;
319  dt = 0.0f;
320  int32_t nominal_throttle = of_landing_ctrl.nominal_thrust * MAX_PPRZ;
321  stabilization_cmd[COMMAND_THRUST] = nominal_throttle;
322 
323  }
324  // else: do nothing, let dt increment
325  return;
326  }
327  }
328 
329  /***********
330  * CONTROL
331  ***********/
332 
333  int32_t nominal_throttle = of_landing_ctrl.nominal_thrust * MAX_PPRZ; \
334 
335  // landing indicates whether the drone is already performing a final landing procedure (flare):
336  if (!landing) {
337 
338  if (of_landing_ctrl.CONTROL_METHOD == 0) {
339  // fixed gain control, cov_limit for landing:
340 
341  // use the divergence for control:
343  int32_t thrust = nominal_throttle + of_landing_ctrl.pgain * err * MAX_PPRZ + of_landing_ctrl.igain * of_landing_ctrl.sum_err * MAX_PPRZ;
344  // make sure the p gain is logged:
346  pused = pstate;
347  // bound thrust:
348  Bound(thrust, 0.8 * nominal_throttle, 0.75 * MAX_PPRZ);
349 
350  // histories and cov detection:
351  normalized_thrust = (float)(thrust / (MAX_PPRZ / 100));
355  while (ind_past < 0) { ind_past += COV_WINDOW_SIZE; }
356  float past_divergence = divergence_history[ind_past];
357  past_divergence_history[ind_hist % COV_WINDOW_SIZE] = past_divergence;
358  ind_hist++;
359  // determine the covariance for landing detection:
360  if (of_landing_ctrl.COV_METHOD == 0) {
362  } else {
364  }
365 
367  // land by setting 90% nominal thrust:
368  landing = 1;
369  thrust = 0.90 * nominal_throttle;
370  }
371  stabilization_cmd[COMMAND_THRUST] = thrust;
372  of_landing_ctrl.sum_err += err;
373  } else {
374  // ADAPTIVE GAIN CONTROL:
375 
376  // adapt the gains according to the error in covariance:
377  float error_cov = of_landing_ctrl.cov_set_point - cov_div;
378 
379  // limit the error_cov, which could else become very large:
380  if (error_cov > fabs(of_landing_ctrl.cov_set_point)) { error_cov = fabs(of_landing_ctrl.cov_set_point); }
381  pstate -= (of_landing_ctrl.igain_adaptive * pstate) * error_cov;
382  if (pstate < MINIMUM_GAIN) { pstate = MINIMUM_GAIN; }
383 
384  // regulate the divergence:
386  pused = pstate - (of_landing_ctrl.pgain_adaptive * pstate) * error_cov;
387 
388  // make sure pused does not become too small, nor grows too fast:
389  if (pused < MINIMUM_GAIN) { pused = MINIMUM_GAIN; }
390  if (of_landing_ctrl.COV_METHOD == 1 && error_cov > 0.001) {
391  pused = 0.5 * pused;
392  }
393 
394  // set thrust:
395  int32_t thrust = nominal_throttle + pused * err * MAX_PPRZ + of_landing_ctrl.igain * of_landing_ctrl.sum_err * MAX_PPRZ;
396 
397  // histories and cov detection:
398  normalized_thrust = (float)(thrust / (MAX_PPRZ / 100));
402  while (ind_past < 0) { ind_past += COV_WINDOW_SIZE; }
403  float past_divergence = divergence_history[ind_past];
404  past_divergence_history[ind_hist % COV_WINDOW_SIZE] = 100.0f * past_divergence;
405  ind_hist++;
406 
407  // only take covariance into account if there are enough samples in the histories:
408  if (ind_hist >= COV_WINDOW_SIZE) {
409  if (of_landing_ctrl.COV_METHOD == 0) {
411  } else {
413  }
414  } else {
416  }
417 
418  // TODO: could put a landing condition here based on pstate (if too low)
419 
420  // bound thrust:
421  Bound(thrust, 0.8 * nominal_throttle, 0.75 * MAX_PPRZ); // was 0.6 0.9
422  stabilization_cmd[COMMAND_THRUST] = thrust;
423  of_landing_ctrl.sum_err += err;
424  }
425  } else {
426  // land with 90% nominal thrust:
427  int32_t thrust = 0.90 * nominal_throttle;
428  Bound(thrust, 0.6 * nominal_throttle, 0.9 * MAX_PPRZ);
429  stabilization_cmd[COMMAND_THRUST] = thrust;
430  }
431  }
432 }
433 
440 float get_mean_array(float *a, int n_elements)
441 {
442  // determine the mean for the vector:
443  float mean = 0;
444  for (unsigned int i = 0; i < n_elements; i++) {
445  mean += a[i];
446  }
447  mean /= n_elements;
448 
449  return mean;
450 }
451 
459 float get_cov(float *a, float *b, int n_elements)
460 {
461  // Determine means for each vector:
462  float mean_a = get_mean_array(a, n_elements);
463  float mean_b = get_mean_array(b, n_elements);
464 
465  // Determine the covariance:
466  float cov = 0;
467  for (unsigned int i = 0; i < n_elements; i++) {
468  cov += (a[i] - mean_a) * (b[i] - mean_b);
469  }
470 
471  cov /= n_elements;
472 
473  return cov;
474 }
475 
476 
477 
478 // Reading from "sensors":
479 static void vertical_ctrl_agl_cb(uint8_t sender_id, float distance)
480 {
481  of_landing_ctrl.agl = distance;
482 }
483 static void vertical_ctrl_optical_flow_cb(uint8_t sender_id, uint32_t stamp, int16_t flow_x, int16_t flow_y, int16_t flow_der_x, int16_t flow_der_y, uint8_t quality, float size_divergence, float dist)
484 {
485  divergence_vision = size_divergence;
487  if (vision_message_nr > 10) { vision_message_nr = 0; }
488 }
489 
490 
492 // Call our controller
494 {
496 }
497 
502 {
503  int i;
504  // reset integrator
505  of_landing_ctrl.sum_err = 0.0f;
506  landing = 0;
507  ind_hist = 0;
508  previous_err = 0.0f;
509  previous_cov_err = 0.0f;
510  of_landing_ctrl.agl_lp = 0.0f;
512  normalized_thrust = 0.0f;
514  dt = 0.0f;
515  struct timespec spec;
516  clock_gettime(CLOCK_REALTIME, &spec);
517  previous_time = spec.tv_nsec / 1.0E6;
518  vision_message_nr = 1;
520  for (i = 0; i < COV_WINDOW_SIZE; i++) {
521  thrust_history[i] = 0;
522  divergence_history[i] = 0;
523  }
524 }
525 
526 void guidance_v_module_run(bool in_flight)
527 {
528  vertical_ctrl_module_run(in_flight);
529 }
Event structure to store callbacks in a linked list.
Definition: abi_common.h:65
void guidance_v_module_enter(void)
Entering the module (user switched to module)
unsigned long ind_hist
This module implements optical flow landings in which the divergence is kept constant.
float igain
I-gain for constant divergence control.
float thrust_history[COV_WINDOW_SIZE]
float get_mean_array(float *a, int n_elements)
Get the mean value of an array.
float pstate
float divergence_vision
void guidance_v_module_run(bool in_flight)
struct OpticalFlowLanding of_landing_ctrl
Periodic telemetry system header (includes downlink utility and generated code).
float previous_cov_err
float normalized_thrust
float previous_err
void reset_all_vars()
Reset all variables:
float pused
float dgain
D-gain for constant divergence control.
long previous_time
Main include for ABI (AirBorneInterface).
#define OPTICAL_FLOW_LANDING_OPTICAL_FLOW_ID
Autopilot modes.
float lp_factor
low-pass factor in [0,1], with 0 purely using the current measurement
float dt
float agl
agl = height from sonar (only used when using "fake" divergence)
float igain_adaptive
I-gain for adaptive gain control.
int32_t alt
in millimeters above WGS84 reference ellipsoid
static abi_event agl_ev
The altitude ABI event.
#define OPTICAL_FLOW_LANDING_IGAIN
#define OPTICAL_FLOW_LANDING_VISION_METHOD
float pgain_adaptive
P-gain for adaptive gain control.
#define OPTICAL_FLOW_LANDING_DGAIN
float vel
vertical velocity as determined with sonar (only used when using "fake" divergence) ...
void vertical_ctrl_module_init(void)
Initialize the optical flow landing module.
#define OPTICAL_FLOW_LANDING_AGL_ID
#define OPTICAL_FLOW_LANDING_CONTROL_METHOD
float divergence_history[COV_WINDOW_SIZE]
float divergence_setpoint
setpoint for constant divergence approach
unsigned long uint32_t
Definition: types.h:18
static void vertical_ctrl_agl_cb(uint8_t sender_id, float distance)
Callback function of the ground altitude.
signed short int16_t
Definition: types.h:17
#define DefaultPeriodic
Set default periodic telemetry.
Definition: telemetry.h:66
int delay_steps
number of delay steps for div past
int COV_METHOD
method to calculate the covariance: between thrust and div (0) or div and div past (1) ...
#define MINIMUM_GAIN
signed long int32_t
Definition: types.h:19
void vertical_ctrl_module_run(bool in_flight)
Run the optical flow landing module.
static const struct usb_device_descriptor dev
Definition: usb_ser_hw.c:73
static abi_event optical_flow_ev
int CONTROL_METHOD
type of divergence control: 0 = fixed gain, 1 = adaptive gain
float past_divergence_history[COV_WINDOW_SIZE]
void guidance_v_module_init(void)
float divergence
int VISION_METHOD
whether to use vision (1) or Optitrack / sonar (0)
float agl_lp
low-pass version of agl
unsigned char uint8_t
Definition: types.h:14
static void send_divergence(struct transport_tx *trans, struct link_device *dev)
General stabilization interface for rotorcrafts.
static void vertical_ctrl_optical_flow_cb(uint8_t sender_id, uint32_t stamp, int16_t flow_x, int16_t flow_y, int16_t flow_der_x, int16_t flow_der_y, uint8_t quality, float size_divergence, float dist)
int32_t stabilization_cmd[COMMANDS_NB]
Stabilization commands.
Definition: stabilization.c:28
Common flight_plan functions shared between fixedwing and rotorcraft.
float cov_limit
for fixed gain control, what is the cov limit triggering the landing
float cov_div
float divergence_vision_dt
float cov_set_point
for adaptive gain control, setpoint of the covariance (oscillations)
#define E
float get_cov(float *a, float *b, int n_elements)
Get the covariance of two arrays.
#define OPTICAL_FLOW_LANDING_PGAIN
float pgain
P-gain for constant divergence control (from divergence error to thrust)
struct LlaCoor_i lla_pos
position in LLA (lat,lon: deg*1e7; alt: mm over ellipsoid)
Definition: gps.h:86
int previous_message_nr
#define MAX_PPRZ
Definition: paparazzi.h:8
#define COV_WINDOW_SIZE
#define OPTICAL_FLOW_LANDING_COV_METHOD
float sum_err
integration of the error for I-gain
float dgain_adaptive
D-gain for adaptive gain control.
int vision_message_nr
struct GpsState gps
global GPS state
Definition: gps.c:75
int8_t register_periodic_telemetry(struct periodic_telemetry *_pt, uint8_t _id, telemetry_cb _cb)
Register a telemetry callback function.
Definition: telemetry.c:46
float nominal_thrust
nominal thrust around which the PID-control operates
if(PrimarySpektrumState.SpektrumTimer)
int landing