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
stabilization_indi.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) Ewoud Smeur <ewoud_smeur@msn.com>
3  * MAVLab Delft University of Technology
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, write to
19  * the Free Software Foundation, 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 
37 
38 #include "state.h"
39 #include "generated/airframe.h"
40 #include "paparazzi.h"
42 
43 #if !defined(STABILIZATION_INDI_ACT_DYN_P) && !defined(STABILIZATION_INDI_ACT_DYN_Q) && !defined(STABILIZATION_INDI_ACT_DYN_R)
44 #error You have to define the first order time constant of the actuator dynamics!
45 #endif
46 
47 // these parameters are used in the filtering of the angular acceleration
48 // define them in the airframe file if different values are required
49 #ifndef STABILIZATION_INDI_FILT_OMEGA
50 #define STABILIZATION_INDI_FILT_OMEGA 50.0
51 #endif
52 
53 #ifndef STABILIZATION_INDI_FILT_ZETA
54 #define STABILIZATION_INDI_FILT_ZETA 0.55
55 #endif
56 
57 // the yaw sometimes requires more filtering
58 #ifndef STABILIZATION_INDI_FILT_OMEGA_R
59 #define STABILIZATION_INDI_FILT_OMEGA_R STABILIZATION_INDI_FILT_OMEGA
60 #endif
61 
62 #ifndef STABILIZATION_INDI_FILT_ZETA_R
63 #define STABILIZATION_INDI_FILT_ZETA_R STABILIZATION_INDI_FILT_ZETA
64 #endif
65 
66 #ifndef STABILIZATION_INDI_MAX_RATE
67 #define STABILIZATION_INDI_MAX_RATE 6.0
68 #endif
69 
70 #if STABILIZATION_INDI_USE_ADAPTIVE
71 #warning "Use caution with adaptive indi. See the wiki for more info"
72 #endif
73 
74 #ifndef STABILIZATION_INDI_MAX_R
75 #define STABILIZATION_INDI_MAX_R STABILIZATION_ATTITUDE_SP_MAX_R
76 #endif
77 
80 
81 static int32_t stabilization_att_indi_cmd[COMMANDS_NB];
82 static inline void stabilization_indi_calc_cmd(int32_t indi_commands[], struct Int32Quat *att_err, bool rate_control);
83 static void stabilization_indi_second_order_filter_init(struct IndiFilter *filter, float omega, float zeta, float omega_r);
84 static void stabilization_indi_second_order_filter(struct IndiFilter *filter, struct FloatRates *input);
85 static inline void lms_estimation(void);
86 
87 #define INDI_EST_SCALE 0.001 //The G values are scaled to avoid numerical problems during the estimation
88 struct IndiVariables indi = {
90  .attitude_max_yaw_rate = STABILIZATION_INDI_MAX_R,
91 
92  .g1 = {STABILIZATION_INDI_G1_P, STABILIZATION_INDI_G1_Q, STABILIZATION_INDI_G1_R},
93  .g2 = STABILIZATION_INDI_G2_R,
94  .reference_acceleration = {
95  STABILIZATION_INDI_REF_ERR_P,
96  STABILIZATION_INDI_REF_ERR_Q,
97  STABILIZATION_INDI_REF_ERR_R,
98  STABILIZATION_INDI_REF_RATE_P,
99  STABILIZATION_INDI_REF_RATE_Q,
100  STABILIZATION_INDI_REF_RATE_R},
101 
102  /* Estimation parameters for adaptive INDI */
103  .est = {
104  .g1 = {
105  STABILIZATION_INDI_G1_P / INDI_EST_SCALE,
106  STABILIZATION_INDI_G1_Q / INDI_EST_SCALE,
107  STABILIZATION_INDI_G1_R / INDI_EST_SCALE},
108  .g2 = STABILIZATION_INDI_G2_R / INDI_EST_SCALE,
109  .mu = STABILIZATION_INDI_ADAPTIVE_MU,
110  },
111 
112 #if STABILIZATION_INDI_USE_ADAPTIVE
113  .adaptive = TRUE,
114 #else
115  .adaptive = FALSE,
116 #endif
117 };
118 
119 #if PERIODIC_TELEMETRY
121 
122 static void send_att_indi(struct transport_tx *trans, struct link_device *dev)
123 {
124  //The estimated G values are scaled, so scale them back before sending
125  struct FloatRates g1_disp;
126  RATES_SMUL(g1_disp, indi.est.g1, INDI_EST_SCALE);
127  float g2_disp = indi.est.g2 * INDI_EST_SCALE;
128 
129  pprz_msg_send_STAB_ATTITUDE_INDI(trans, dev, AC_ID,
130  &indi.rate.dx.p,
131  &indi.rate.dx.q,
132  &indi.rate.dx.r,
133  &indi.angular_accel_ref.p,
134  &indi.angular_accel_ref.q,
135  &indi.angular_accel_ref.r,
136  &g1_disp.p,
137  &g1_disp.q,
138  &g1_disp.r,
139  &g2_disp);
140 }
141 #endif
142 
144 {
145  // Initialize filters
148  stabilization_indi_second_order_filter_init(&indi.est.rate, 10.0, 0.8, 10.0); //FIXME: no magic number
149  stabilization_indi_second_order_filter_init(&indi.est.u, 10.0, 0.8, 10.0); //FIXME: no magic number
150 
151 #if PERIODIC_TELEMETRY
152  register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_STAB_ATTITUDE_INDI, send_att_indi);
153 #endif
154 }
155 
157 {
158  /* reset psi setpoint to current psi angle */
160 
161  FLOAT_RATES_ZERO(indi.rate.x);
162  FLOAT_RATES_ZERO(indi.rate.dx);
163  FLOAT_RATES_ZERO(indi.rate.ddx);
165  FLOAT_RATES_ZERO(indi.du);
167  FLOAT_RATES_ZERO(indi.u_in);
168  FLOAT_RATES_ZERO(indi.u.x);
169  FLOAT_RATES_ZERO(indi.u.dx);
170  FLOAT_RATES_ZERO(indi.u.ddx);
171 }
172 
174 {
175  /* set failsafe to zero roll/pitch and current heading */
178  stab_att_sp_quat.qx = 0;
179  stab_att_sp_quat.qy = 0;
181 }
182 
184 {
185  // stab_att_sp_euler.psi still used in ref..
186  stab_att_sp_euler = *rpy;
187 
189 }
190 
192 {
193  // stab_att_sp_euler.psi still used in ref..
195 
196  // compute sp_euler phi/theta for debugging/telemetry
197  /* Rotate horizontal commands to body frame by psi */
199  int32_t s_psi, c_psi;
200  PPRZ_ITRIG_SIN(s_psi, psi);
201  PPRZ_ITRIG_COS(c_psi, psi);
202  stab_att_sp_euler.phi = (-s_psi * cmd->x + c_psi * cmd->y) >> INT32_TRIG_FRAC;
203  stab_att_sp_euler.theta = -(c_psi * cmd->x + s_psi * cmd->y) >> INT32_TRIG_FRAC;
204 
205  quat_from_earth_cmd_i(&stab_att_sp_quat, cmd, heading);
206 }
207 
208 static inline void stabilization_indi_calc_cmd(int32_t indi_commands[], struct Int32Quat *att_err, bool rate_control)
209 {
210  /* Propagate the second order filter on the gyroscopes */
211  struct FloatRates *body_rates = stateGetBodyRates_f();
212  stabilization_indi_second_order_filter(&indi.rate, body_rates);
213 
214  //The rates used for feedback are by default the measured rates. If needed they can be filtered (see below)
215  struct FloatRates rates_for_feedback;
216  RATES_COPY(rates_for_feedback, (*body_rates));
217 
218  //If there is a lot of noise on the gyroscope, it might be good to use the filtered value for feedback.
219  //Note that due to the delay, the PD controller can not be as aggressive.
220 #if STABILIZATION_INDI_FILTER_ROLL_RATE
221  rates_for_feedback.p = indi.rate.x.p;
222 #endif
223 #if STABILIZATION_INDI_FILTER_PITCH_RATE
224  rates_for_feedback.q = indi.rate.x.q;
225 #endif
226 #if STABILIZATION_INDI_FILTER_YAW_RATE
227  rates_for_feedback.r = indi.rate.x.r;
228 #endif
229 
231  - indi.reference_acceleration.rate_p * rates_for_feedback.p;
232 
234  - indi.reference_acceleration.rate_q * rates_for_feedback.q;
235 
236  //This separates the P and D controller and lets you impose a maximum yaw rate.
237  float rate_ref_r = indi.reference_acceleration.err_r * QUAT1_FLOAT_OF_BFP(att_err->qz)/indi.reference_acceleration.rate_r;
238  BoundAbs(rate_ref_r, indi.attitude_max_yaw_rate);
239  indi.angular_accel_ref.r = indi.reference_acceleration.rate_r * (rate_ref_r - rates_for_feedback.r);
240 
241  /* Check if we are running the rate controller and overwrite */
242  if(rate_control) {
243  indi.angular_accel_ref.p = indi.reference_acceleration.rate_p * ((float)radio_control.values[RADIO_ROLL] / MAX_PPRZ * indi.max_rate - body_rates->p);
244  indi.angular_accel_ref.q = indi.reference_acceleration.rate_q * ((float)radio_control.values[RADIO_PITCH] / MAX_PPRZ * indi.max_rate - body_rates->q);
245  indi.angular_accel_ref.r = indi.reference_acceleration.rate_r * ((float)radio_control.values[RADIO_YAW] / MAX_PPRZ * indi.max_rate - body_rates->r);
246  }
247 
248  //Increment in angular acceleration requires increment in control input
249  //G1 is the control effectiveness. In the yaw axis, we need something additional: G2.
250  //It takes care of the angular acceleration caused by the change in rotation rate of the propellers
251  //(they have significant inertia, see the paper mentioned in the header for more explanation)
252  indi.du.p = 1.0 / indi.g1.p * (indi.angular_accel_ref.p - indi.rate.dx.p);
253  indi.du.q = 1.0 / indi.g1.q * (indi.angular_accel_ref.q - indi.rate.dx.q);
254  indi.du.r = 1.0 / (indi.g1.r + indi.g2) * (indi.angular_accel_ref.r - indi.rate.dx.r + indi.g2 * indi.du.r);
255 
256  //add the increment to the total control input
257  indi.u_in.p = indi.u.x.p + indi.du.p;
258  indi.u_in.q = indi.u.x.q + indi.du.q;
259  indi.u_in.r = indi.u.x.r + indi.du.r;
260 
261  //bound the total control input
262  Bound(indi.u_in.p, -4500, 4500);
263  Bound(indi.u_in.q, -4500, 4500);
264  Bound(indi.u_in.r, -4500, 4500);
265 
266  //Propagate input filters
267  //first order actuator dynamics
268  indi.u_act_dyn.p = indi.u_act_dyn.p + STABILIZATION_INDI_ACT_DYN_P * (indi.u_in.p - indi.u_act_dyn.p);
269  indi.u_act_dyn.q = indi.u_act_dyn.q + STABILIZATION_INDI_ACT_DYN_Q * (indi.u_in.q - indi.u_act_dyn.q);
270  indi.u_act_dyn.r = indi.u_act_dyn.r + STABILIZATION_INDI_ACT_DYN_R * (indi.u_in.r - indi.u_act_dyn.r);
271 
272  //sensor filter
274 
275  //Don't increment if thrust is off
276  //TODO: this should be something more elegant, but without this the inputs will increment to the maximum before
277  //even getting in the air.
278  if (stabilization_cmd[COMMAND_THRUST] < 300) {
279  FLOAT_RATES_ZERO(indi.du);
281  FLOAT_RATES_ZERO(indi.u_in);
282  FLOAT_RATES_ZERO(indi.u.x);
283  FLOAT_RATES_ZERO(indi.u.dx);
284  FLOAT_RATES_ZERO(indi.u.ddx);
285  } else {
286  // only run the estimation if the commands are not zero.
287  lms_estimation();
288  }
289 
290  /* INDI feedback */
291  indi_commands[COMMAND_ROLL] = indi.u_in.p;
292  indi_commands[COMMAND_PITCH] = indi.u_in.q;
293  indi_commands[COMMAND_YAW] = indi.u_in.r;
294 }
295 
296 void stabilization_indi_run(bool enable_integrator __attribute__((unused)), bool rate_control)
297 {
298  /* attitude error */
299  struct Int32Quat att_err;
300  struct Int32Quat *att_quat = stateGetNedToBodyQuat_i();
301  int32_quat_inv_comp(&att_err, att_quat, &stab_att_sp_quat);
302  /* wrap it in the shortest direction */
303  int32_quat_wrap_shortest(&att_err);
304  int32_quat_normalize(&att_err);
305 
306  /* compute the INDI command */
308 
309  /* copy the INDI command */
310  stabilization_cmd[COMMAND_ROLL] = stabilization_att_indi_cmd[COMMAND_ROLL];
311  stabilization_cmd[COMMAND_PITCH] = stabilization_att_indi_cmd[COMMAND_PITCH];
312  stabilization_cmd[COMMAND_YAW] = stabilization_att_indi_cmd[COMMAND_YAW];
313 
314  /* bound the result */
315  BoundAbs(stabilization_cmd[COMMAND_ROLL], MAX_PPRZ);
316  BoundAbs(stabilization_cmd[COMMAND_PITCH], MAX_PPRZ);
317  BoundAbs(stabilization_cmd[COMMAND_YAW], MAX_PPRZ);
318 }
319 
320 // This function reads rc commands
321 void stabilization_indi_read_rc(bool in_flight, bool in_carefree, bool coordinated_turn)
322 {
323  struct FloatQuat q_sp;
324 #if USE_EARTH_BOUND_RC_SETPOINT
325  stabilization_attitude_read_rc_setpoint_quat_earth_bound_f(&q_sp, in_flight, in_carefree, coordinated_turn);
326 #else
327  stabilization_attitude_read_rc_setpoint_quat_f(&q_sp, in_flight, in_carefree, coordinated_turn);
328 #endif
330 }
331 
332 // Initialize a second order low pass filter
333 static void stabilization_indi_second_order_filter_init(struct IndiFilter *filter, float omega, float zeta, float omega_r)
334 {
335  filter->omega = omega;
336  filter->omega2 = omega * omega;
337  filter->zeta = zeta;
338  filter->omega_r = omega_r;
339  filter->omega2_r = omega_r * omega_r;
340 }
341 
342 // This is a simple second order low pass filter
343 static void stabilization_indi_second_order_filter(struct IndiFilter *filter, struct FloatRates *input)
344 {
345  float_rates_integrate_fi(&filter->x, &filter->dx, 1.0 / PERIODIC_FREQUENCY);
346  float_rates_integrate_fi(&filter->dx, &filter->ddx, 1.0 / PERIODIC_FREQUENCY);
347 
348  filter->ddx.p = -filter->dx.p * 2 * filter->zeta * filter->omega + (input->p - filter->x.p) * filter->omega2;
349  filter->ddx.q = -filter->dx.q * 2 * filter->zeta * filter->omega + (input->q - filter->x.q) * filter->omega2;
350  filter->ddx.r = -filter->dx.r * 2 * filter->zeta * filter->omega_r + (input->r - filter->x.r) * filter->omega2_r;
351 }
352 
353 // This is a Least Mean Squares adaptive filter
354 // It estiamtes the actuator effectiveness online by comparing the expected angular acceleration based on the inputs with the measured angular acceleration
355 static inline void lms_estimation(void)
356 {
357  static struct IndiEstimation *est = &indi.est;
358  // Only pass really low frequencies so you don't adapt to noise
360  struct FloatRates *body_rates = stateGetBodyRates_f();
361  stabilization_indi_second_order_filter(&est->rate, body_rates);
362 
363  // The inputs are scaled in order to avoid overflows
364  float du = est->u.dx.p * INDI_EST_SCALE;
365  est->g1.p = est->g1.p - (est->g1.p * du - est->rate.ddx.p) * du * est->mu;
366  du = est->u.dx.q * INDI_EST_SCALE;
367  est->g1.q = est->g1.q - (est->g1.q * du - est->rate.ddx.q) * du * est->mu;
368  du = est->u.dx.r * INDI_EST_SCALE;
369  float ddu = est->u.ddx.r * INDI_EST_SCALE / PERIODIC_FREQUENCY;
370  float error = (est->g1.r * du + est->g2 * ddu - est->rate.ddx.r);
371  est->g1.r = est->g1.r - error * du * est->mu / 3;
372  est->g2 = est->g2 - error * 1000 * ddu * est->mu / 3;
373 
374  //the g values should be larger than zero, otherwise there is positive feedback, the command will go to max and there is nothing to learn anymore...
375  if (est->g1.p < 0.01) { est->g1.p = 0.01; }
376  if (est->g1.q < 0.01) { est->g1.q = 0.01; }
377  if (est->g1.r < 0.01) { est->g1.r = 0.01; }
378  if (est->g2 < 0.01) { est->g2 = 0.01; }
379 
380  if (indi.adaptive) {
381  //Commit the estimated G values and apply the scaling
382  indi.g1.p = est->g1.p * INDI_EST_SCALE;
383  indi.g1.q = est->g1.q * INDI_EST_SCALE;
384  indi.g1.r = est->g1.r * INDI_EST_SCALE;
385  indi.g2 = est->g2 * INDI_EST_SCALE;
386  }
387 }
int32_t psi
in rad with INT32_ANGLE_FRAC
struct FloatRates g1
void stabilization_indi_init(void)
void stabilization_attitude_read_rc_setpoint_quat_f(struct FloatQuat *q_sp, bool in_flight, bool in_carefree, bool coordinated_turn)
Read attitude setpoint from RC as quaternion Interprets the stick positions as axes.
static void int32_quat_normalize(struct Int32Quat *q)
normalize a quaternion inplace
void stabilization_indi_set_failsafe_setpoint(void)
void int32_quat_inv_comp(struct Int32Quat *b2c, struct Int32Quat *a2b, struct Int32Quat *a2c)
Composition (multiplication) of two quaternions.
static void stabilization_indi_calc_cmd(int32_t indi_commands[], struct Int32Quat *att_err, bool rate_control)
static void stabilization_indi_second_order_filter(struct IndiFilter *filter, struct FloatRates *input)
#define RADIO_ROLL
Definition: spektrum_arch.h:43
Quaternion transformation functions.
#define STABILIZATION_INDI_FILT_OMEGA
#define RADIO_YAW
Definition: spektrum_arch.h:45
Periodic telemetry system header (includes downlink utility and generated code).
General attitude stabilization interface for rotorcrafts.
bool adaptive
Enable adataptive estimation.
#define STABILIZATION_INDI_FILT_ZETA
int32_t theta
in rad with INT32_ANGLE_FRAC
struct FloatRates du
struct FloatRates dx
int32_t stabilization_attitude_get_heading_i(void)
float r
in rad/s
#define STABILIZATION_INDI_MAX_RATE
Read an attitude setpoint from the RC.
static void lms_estimation(void)
#define FLOAT_RATES_ZERO(_r)
void stabilization_indi_set_rpy_setpoint_i(struct Int32Eulers *rpy)
float attitude_max_yaw_rate
Maximum yaw rate in atttiude control in rad/s.
struct FloatRates u_act_dyn
void stabilization_indi_enter(void)
float q
in rad/s
float p
in rad/s
pprz_t values[RADIO_CONTROL_NB_CHANNEL]
Definition: radio_control.h:58
struct FloatRates ddx
static struct Int32Quat * stateGetNedToBodyQuat_i(void)
Get vehicle body attitude quaternion (int).
Definition: state.h:1095
static const float omega_r[]
float max_rate
Maximum rate in rate control in rad/s.
struct FloatRates x
#define FALSE
Definition: std.h:5
#define STABILIZATION_INDI_MAX_R
Roation quaternion.
void stabilization_attitude_read_rc_setpoint_quat_earth_bound_f(struct FloatQuat *q_sp, bool in_flight, bool in_carefree, bool coordinated_turn)
struct IndiFilter rate
#define PPRZ_ITRIG_SIN(_s, _a)
#define TRUE
Definition: std.h:4
static float heading
Definition: ahrs_infrared.c:45
struct IndiFilter rate
struct IndiVariables indi
void quat_from_rpy_cmd_i(struct Int32Quat *quat, struct Int32Eulers *rpy)
void float_rates_integrate_fi(struct FloatRates *r, struct FloatRates *dr, float dt)
in place first order integration of angular rates
#define RADIO_PITCH
Definition: spektrum_arch.h:44
euler angles
void stabilization_indi_read_rc(bool in_flight, bool in_carefree, bool coordinated_turn)
struct IndiFilter u
struct ReferenceSystem reference_acceleration
#define QUAT_BFP_OF_REAL(_qi, _qf)
Definition: pprz_algebra.h:685
static int32_t stabilization_att_indi_cmd[COMMANDS_NB]
#define DefaultPeriodic
Set default periodic telemetry.
Definition: telemetry.h:66
void stabilization_indi_run(bool enable_integrator, bool rate_control)
struct RadioControl radio_control
Definition: radio_control.c:30
struct FloatRates g1
struct FloatRates angular_accel_ref
void quat_from_earth_cmd_i(struct Int32Quat *quat, struct Int32Vect2 *cmd, int32_t heading)
#define RATES_SMUL(_ro, _ri, _s)
Definition: pprz_algebra.h:378
static struct FloatRates * stateGetBodyRates_f(void)
Get vehicle body angular rate (float).
Definition: state.h:1182
signed long int32_t
Definition: types.h:19
#define INDI_EST_SCALE
#define QUAT1_FLOAT_OF_BFP(_qi)
static const struct usb_device_descriptor dev
Definition: usb_ser_hw.c:73
#define INT32_TRIG_FRAC
void stabilization_indi_set_earth_cmd_i(struct Int32Vect2 *cmd, int32_t heading)
int32_t phi
in rad with INT32_ANGLE_FRAC
API to get/set the generic vehicle states.
static void int32_quat_wrap_shortest(struct Int32Quat *q)
int32_t stabilization_cmd[COMMANDS_NB]
Stabilization commands.
Definition: stabilization.c:28
static void send_att_indi(struct transport_tx *trans, struct link_device *dev)
static void stabilization_indi_second_order_filter_init(struct IndiFilter *filter, float omega, float zeta, float omega_r)
#define RATES_COPY(_a, _b)
Definition: pprz_algebra.h:336
#define MAX_PPRZ
Definition: paparazzi.h:8
static struct Int32Eulers * stateGetNedToBodyEulers_i(void)
Get vehicle body attitude euler angles (int).
Definition: state.h:1107
#define STABILIZATION_INDI_FILT_OMEGA_R
struct IndiEstimation est
Estimation parameters for adaptive INDI.
struct FloatRates u_in
struct IndiFilter u
Rotation quaternion.
angular rates
int8_t register_periodic_telemetry(struct periodic_telemetry *_pt, uint8_t _id, telemetry_cb _cb)
Register a telemetry callback function.
Definition: telemetry.c:46
struct Int32Quat stab_att_sp_quat
with INT32_QUAT_FRAC
struct Int32Eulers stab_att_sp_euler
with INT32_ANGLE_FRAC
#define PPRZ_ITRIG_COS(_c, _a)