ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
est_pipeline.c
Go to the documentation of this file.
1
4#include "est_pipeline.h"
5#include "control_hooks.h"
6#include "so3.h"
7#include <string.h>
8#include <math.h>
9
10static void trace_put(est_t *e, uint8_t op, const float *v, int nf, int trusted)
11{
12 if (!e->trace_enable || !e->running) return;
13 est_trace_t *t = &e->trace;
14 const uint16_t need = (uint16_t)(1u + 4u*(unsigned)nf + (op == EST_OP_ACC ? 1u : 0u));
15 if (t->len + need > EST_TRACE_MAX) { t->overflow = 1; return; }
16 t->buf[t->len++] = op;
17 memcpy(&t->buf[t->len], v, 4u*(unsigned)nf);
18 t->len = (uint16_t)(t->len + 4u*(unsigned)nf);
19 if (op == EST_OP_ACC) t->buf[t->len++] = (uint8_t)(trusted ? 1 : 0);
20 t->nops++;
21}
22
23void est_init(est_t *e, const est_config_t *cfg, est_cb_t on_state, est_cb_t on_init, void *user)
24{
25 memset(e, 0, sizeof *e);
26 e->cfg = *cfg;
27 e->on_state = on_state;
28 e->on_init = on_init;
29 e->user = user;
30 calib_reset(&e->cal);
31 accwin_reset(&e->win);
32}
33
34static void on_step(est_t *e, float h)
35{
36 float gm[3];
37 const float inv = 1.0f / (float)e->k;
38 for (int i = 0; i < 3; ++i) {
39 gm[i] = e->gsum[i] * inv;
40 e->gwin_sum[i] += gm[i];
41 }
42 e->gwin_n++;
43
44 if (e->running) {
45 const float pv[4] = { e->dth[0], e->dth[1], e->dth[2], h };
46 trace_put(e, EST_OP_PROP, pv, 4, 0);
47 iekf_propagate_dtheta(&e->f, e->dth, h);
48 accwin_rotate(&e->win, e->f.E_last);
49 e->t += h;
50
52 for (int i = 0; i < 3; ++i) in.gyro[i] = e->omega[i] = gm[i] - e->f.b[i];
53 in.dt = h; in.step = e->step;
54 ctrl_rate_step(&in);
55 }
56 e->step++;
57 memset(e->dth, 0, sizeof e->dth);
58 memset(e->gsum, 0, sizeof e->gsum);
59 e->k = 0;
60}
61
62static void on_acc_window(est_t *e, float h_win)
63{
64 if (accwin_mean(&e->win, e->acc_mean) == 0) e->have_acc_mean = 1;
65 accwin_reset(&e->win);
66 if (e->gwin_n) {
67 for (int i = 0; i < 3; ++i) e->gyro_mean[i] = e->gwin_sum[i] / (float)e->gwin_n;
68 }
69 memset(e->gwin_sum, 0, sizeof e->gwin_sum);
70 e->gwin_n = 0;
71
72 if (!e->running || !e->have_acc_mean) return;
73
74 if (e->cfg.still_time_s > 0.0f) {
75 /* RAW gyro, not corrected with the estimated bias: after a large error the
76 * bias estimate is itself perturbed and would blind the detector.
77 * The real LSM6DSV16X bias is well below the threshold. */
78 const float *w = e->gyro_mean;
79 const float dev = fabsf(vec3_norm(e->acc_mean) - e->f.nom_acc) / e->f.nom_acc;
80 if (dev < e->cfg.still_acc_tol && vec3_norm(w) < e->cfg.still_gyro_thr)
81 e->still_t += h_win;
82 else
83 e->still_t = 0.0f;
84 if (e->still_t >= e->cfg.still_time_s) {
85 e->f.acc_trusted = 1; /* applies to the next update */
86 e->flags |= EST_F_STILL;
87 }
88 }
89
90 trace_put(e, EST_OP_ACC, e->acc_mean, 3, e->f.acc_trusted);
91 if (iekf_update_acc(&e->f, e->acc_mean)) e->flags |= EST_F_ACC_GATED;
92
94 iekf_get_quat(&e->f, in.q);
95 in.dt = h_win; in.step = e->step;
97
98 e->flags |= EST_F_RUNNING;
99 if (e->on_state) e->on_state(e, e->user);
100 e->flags = 0;
101 memset(&e->trace, 0, sizeof e->trace);
102 e->win_count++;
103}
104
105static void on_mag(est_t *e, const float m_sensor[3])
106{
107 const float *M = e->cfg.mag_map;
108 for (int i = 0; i < 3; ++i) {
109 const float k = (e->cfg.mag_scale[i] > 0.0f) ? e->cfg.mag_scale[i] : 1.0f;
110 e->mag[i] = k * (M[3*i]*m_sensor[0] + M[3*i + 1]*m_sensor[1] + M[3*i + 2]*m_sensor[2]
111 - e->cfg.mag_hard_iron[i]);
112 }
113
114 if (e->running) {
115 /* A world-fixed vector seen from the body evolves as v' = -omega x v:
116 * a measurement tau seconds old is brought to "now" with y - tau*(omega x y). */
117 float y[3], wxy[3];
118 vec3_cross(e->omega, e->mag, wxy);
119 for (int i = 0; i < 3; ++i) y[i] = e->mag[i] - e->cfg.mag_latency_s * wxy[i];
120 const float nm = vec3_norm(e->mag);
121 const float wxu = (nm > 1e-6f) ? vec3_norm(wxy) / nm : 0.0f; /* |omega x u| */
122 const float sd = wxu * e->cfg.mag_latency_std_s;
123 e->flags |= EST_F_MAG_NEW;
124 const float mv[4] = { y[0], y[1], y[2], sd*sd };
125 trace_put(e, EST_OP_MAG, mv, 4, 0);
126 if (iekf_update_mag_ex(&e->f, y, sd*sd)) e->flags |= EST_F_MAG_GATED;
127 return;
128 }
129
130 /* ---- static calibration: one sample per new mag reading ---- */
131 if (!e->have_acc_mean) return;
132 if (calib_add(&e->cal, e->acc_mean, e->gyro_mean, e->mag, e->cfg.calib_gyro_still) != 0) {
133 calib_reset(&e->cal); /* it moved: start over */
134 return;
135 }
136 if (calib_count(&e->cal) < e->cfg.calib_samples) return;
137
138 if (calib_finish(&e->cal, e->cfg.sigma_vec_min, &e->calres) != 0) {
139 calib_reset(&e->cal);
140 return;
141 }
142 iekf_config_t c = e->cfg.ekf;
143 c.sigma_acc = e->calres.sigma_acc;
144 c.sigma_mag = e->calres.sigma_mag;
145 e->cfg.ekf = c;
146 iekf_init(&e->f, &c, &e->calres.seed);
147 accwin_reset(&e->win);
148 e->running = 1;
149 e->t = 0.0f;
150 e->win_count = 0;
151 memset(&e->trace, 0, sizeof e->trace);
152 if (e->on_init) e->on_init(e, e->user);
153}
154
155void est_process(est_t *e, const imu_event_t *ev, size_t n, float dt)
156{
157 for (size_t j = 0; j < n; ++j) {
158 switch (ev[j].type) {
159 case IMU_EV_GYRO:
160 for (int i = 0; i < 3; ++i) {
161 e->dth[i] += ev[j].v[i] * dt;
162 e->gsum[i] += ev[j].v[i];
163 }
164 if (++e->k >= e->cfg.gyro_per_step) {
165 const float h = (float)e->k * dt;
166 on_step(e, h);
167 if ((e->step % e->cfg.steps_per_acc) == 0)
168 on_acc_window(e, h * (float)e->cfg.steps_per_acc);
169 }
170 break;
171 case IMU_EV_ACC:
172 accwin_add(&e->win, ev[j].v);
173 break;
174 case IMU_EV_MAG:
175 on_mag(e, ev[j].v);
176 break;
177 default:
178 break;
179 }
180 }
181}
void accwin_rotate(accwin_t *w, const float E[9])
after every propagation
Definition accwin.c:22
void accwin_add(accwin_t *w, const float acc[3])
sample in the current body
Definition accwin.c:14
int accwin_mean(const accwin_t *w, float out[3])
in the current body, 0 = OK
Definition accwin.c:29
void accwin_reset(accwin_t *w)
Definition accwin.c:8
static void on_init(const est_t *e, void *user)
Definition app_main.c:201
static void on_state(const est_t *e, void *user)
Definition app_main.c:158
void calib_reset(calib_t *c)
Definition calib.c:10
int calib_finish(const calib_t *c, float sigma_min, calib_result_t *out)
Definition calib.c:52
int calib_add(calib_t *c, const float acc[3], const float gyro[3], const float mag[3], float gyro_still_thr)
Returns 0 if the sample is compatible with "still", -1 if moving.
Definition calib.c:14
uint32_t calib_count(const calib_t *c)
Definition calib.c:12
uint32_t n
Definition calib_mgr.c:35
void ctrl_attitude_step(const ctrl_att_in_t *in)
240 Hz
void ctrl_rate_step(const ctrl_rate_in_t *in)
960 Hz
Control hook points, already called by the estimation task at the right rates.
static void on_mag(est_t *e, const float m_sensor[3])
static void on_step(est_t *e, float h)
void est_init(est_t *e, const est_config_t *cfg, est_cb_t on_state, est_cb_t on_init, void *user)
void est_process(est_t *e, const imu_event_t *ev, size_t n, float dt)
dt_sample: IMU sample period measured by the MCU (s)
static void trace_put(est_t *e, uint8_t op, const float *v, int nf, int trusted)
static void on_acc_window(est_t *e, float h_win)
Multi-rate estimation pipeline, WITHOUT any HAL or RTOS dependency.
#define EST_F_RUNNING
void(* est_cb_t)(const est_t *e, void *user)
#define EST_F_STILL
stillness detector active
#define EST_F_MAG_GATED
#define EST_F_ACC_GATED
#define EST_OP_MAG
'M'
#define EST_OP_PROP
'P'
#define EST_F_MAG_NEW
#define EST_OP_ACC
'A'
#define EST_TRACE_MAX
int iekf_update_mag_ex(iekf_t *f, const float mag[3], float extra_var)
As above, with an additional per-component variance (e.g.
Definition iekf.c:371
void iekf_propagate_dtheta(iekf_t *f, const float dtheta[3], float h)
Propagation with a measured angle increment (bias NOT yet removed): phi = dtheta - b*dt.
Definition iekf.c:99
void iekf_init(iekf_t *f, const iekf_config_t *cfg, const iekf_seed_t *s)
Definition iekf.c:74
void iekf_get_quat(const iekf_t *f, float q[4])
Definition iekf.c:408
int iekf_update_acc(iekf_t *f, const float acc[3])
Return 1 if the measurement was "gated" (de-weighted), 0 otherwise.
Definition iekf.c:315
@ IMU_EV_ACC
@ IMU_EV_GYRO
@ IMU_EV_MAG
void vec3_cross(const float a[3], const float b[3], float c[3])
Definition so3.c:49
float vec3_norm(const float v[3])
Definition so3.c:47
Single-precision SO(3) Lie-group utilities (port of expSO3.m / skewSym.m + firmware helpers).
float q[4]
IEKF attitude, body->world NWU, [w x y z].
float gyro[3]
rad/s: mean of the step samples, estimated bias removed
uint8_t nops
uint16_t len
uint8_t buf[EST_TRACE_MAX]
uint8_t overflow
float sigma_mag
Definition iekf.h:36
float sigma_acc
per-component std of the normalised vector
Definition iekf.h:35