Larus glider flight sensor system 3.9.2024
Software-In-The-Loop test and validation system
Loading...
Searching...
No Matches
AHRS.cpp
Go to the documentation of this file.
1/***********************************************************************/
25#include <AHRS.h>
27#include "GNSS.h"
29#include "embedded_memory.h"
32
33#if USE_HARDWARE_EEPROM == 0
34#include "EEPROM_emulation.h"
35#endif
36
40void
42 const float3vector &mag)
43{
45
47 if( compass_calibration.available())
48 induction = compass_calibration.calibrate( mag);
49 else
50 induction = mag;
51
52 down = acceleration;
53
54 down.negate ();
55 down.normalize ();
56
57 north = induction; // deviation neglected here
59
60 // setup world coordinate system
62 east.normalize ();
65
66 // create rotation matrix from unity direction vectors
67 float fcoordinates[] =
68 { north[0], north[1], north[2],
69 east[0], east[1], east[2],
70 down[0], down[1], down[2] };
71
72 float3matrix coordinates (fcoordinates);
73 attitude.from_rotation_matrix (coordinates);
74 attitude.get_rotation_matrix (body2nav);
75 euler = attitude;
76}
77
82AHRS_type::update_circling_state ()
83{
84#if DISABLE_CIRCLING_STATE
85 return STRAIGHT_FLIGHT;
86#else
87 float turn_rate_abs = abs (turn_rate_averager.get_output());
88
89 if (circling_counter < CIRCLE_LIMIT)
91 ++circling_counter;
92
93 if (circling_counter > 0)
95 --circling_counter;
96
97 if (circling_counter == 0)
98 circling_state = STRAIGHT_FLIGHT;
99 else if (circling_counter == CIRCLE_LIMIT)
100 circling_state = CIRCLING;
101 else
102 circling_state = TRANSITION;
103
104 return circling_state;
105#endif
106}
107
108void AHRS_type::feed_magnetic_induction_observer(const float3vector &mag_sensor)
109{
110 float error_margin = nav_correction.abs();
112 return;
113
114 bool turning_right = turn_rate_averager.get_output() > 0.0f;
115
116 bool calibration_data_complete = calib_3D.learn( mag_sensor, mag_sensor-expected_body_induction, attitude, turning_right, error_margin);
118 {
119 calib_3D.calculate();
120 }
121
122 for (unsigned i = 0; i < 3; ++i)
123 if( turning_right)
124 mag_calibration_data_collector_right_turn[i].add_value ( MAG_SCALE * expected_body_induction[i], MAG_SCALE * mag_sensor[i]);
125 else
126 mag_calibration_data_collector_left_turn[i].add_value ( MAG_SCALE * expected_body_induction[i], MAG_SCALE * mag_sensor[i]);
127}
128
130:
131 Ts(sampling_time),
132 Ts_div_2 (sampling_time / 2.0f),
133 attitude(),
134 gyro_integrator({0}),
135 circling_counter(0),
136 circling_state( STRAIGHT_FLIGHT),
137 nav_correction(),
138 gyro_correction(),
139 acceleration_nav_frame(),
140 induction_nav_frame(),
141 expected_nav_induction(),
142 body2nav(),
143 euler(),
144 slip_angle_averager( ANGLE_F_BY_FS),
145 pitch_angle_averager( ANGLE_F_BY_FS),
146 turn_rate_averager( ANGLE_F_BY_FS),
147 G_load_averager( G_LOAD_F_BY_FS),
148 compass_calibration(),
149 calib_3D(),
150 antenna_DOWN_correction( configuration( ANT_SLAVE_DOWN) / configuration( ANT_BASELENGTH)),
151 antenna_RIGHT_correction( configuration( ANT_SLAVE_RIGHT) / configuration( ANT_BASELENGTH)),
152 heading_difference_AHRS_DGNSS(0.0f),
153 cross_acc_correction(0.0f),
154 magnetic_disturbance(0.0f),
155 magnetic_control_gain(1.0f),
156 automatic_magnetic_calibration( configuration(MAG_AUTO_CALIB)),
157 automatic_earth_field_parameters( false),
158 magnetic_calibration_updated( false)
159{
160 update_magnetic_loop_gain(); // adapt to magnetic inclination
161
162 bool fail = compass_calibration.read_from_EEPROM();
163 if( fail)
164 compass_calibration.set_default();
165}
166
167void
169 const float3vector &acc,
170 const float3vector &mag,
171 const float3vector &GNSS_acceleration,
172 float GNSS_heading,
174{
175#if DISABLE_SAT_COMPASS
176 update_compass(gyro, acc, mag, GNSS_acceleration);
177#else
179 update_diff_GNSS (gyro, acc, mag, GNSS_acceleration, GNSS_heading);
180 else
181 update_compass(gyro, acc, mag, GNSS_acceleration);
182#endif
183}
184
190void
191AHRS_type::update_attitude ( const float3vector &acc,
192 const float3vector &gyro,
193 const float3vector &mag)
194{
195 attitude.rotate (gyro[ROLL] * Ts_div_2,
196 gyro[PITCH] * Ts_div_2,
197 gyro[YAW] * Ts_div_2);
198
199 attitude.normalize ();
200
201 attitude.get_rotation_matrix (body2nav);
202
203 acceleration_nav_frame = body2nav * acc;
204 induction_nav_frame = body2nav * mag;
205 euler = attitude;
206
208 nav_rotation = body2nav * gyro;
209 turn_rate_averager.respond( nav_rotation[DOWN]);
210
211 slip_angle_averager.respond( ATAN2( -acc[RIGHT], -acc[DOWN]));
212 pitch_angle_averager.respond( ATAN2( +acc[FRONT], -acc[DOWN]));
213 G_load_averager.respond( acc.abs());
214 magnetic_disturbance = (induction_nav_frame - expected_nav_induction).abs();
215}
216
220void
221AHRS_type::update_diff_GNSS (const float3vector &gyro,
222 const float3vector &acc,
224 const float3vector &GNSS_acceleration,
225 float GNSS_heading)
226{
227 circle_state_t old_circle_state = circling_state;
228 update_circling_state ();
229
230 expected_body_induction = body2nav.reverse_map( expected_nav_induction);
231
232 if( calib_3D.available())
233 body_induction = mag_sensor - calib_3D.calibrate( mag_sensor, attitude);
234 else if( compass_calibration.available())
235 body_induction = compass_calibration.calibrate(mag_sensor);
236 else
237 body_induction = mag_sensor; // fall back to uncalibrated sensor signal
238
239 body_induction_error = body_induction - expected_body_induction;
240 float3vector nav_acceleration = body2nav * acc;
241
242 float heading_gnss_work = GNSS_heading // correct for antenna alignment
243 + antenna_DOWN_correction * SIN (euler.r)
244 - antenna_RIGHT_correction * COS (euler.r);
245
246 heading_gnss_work = heading_gnss_work - euler.y; // = heading difference D-GNSS - AHRS
247
248 if (heading_gnss_work > M_PI_F) // map into { -PI PI}
249 heading_gnss_work -= 2.0f * M_PI_F;
251 heading_gnss_work += 2.0f * M_PI_F;
252
253 heading_difference_AHRS_DGNSS = heading_gnss_work;
254
255 nav_correction[NORTH] = - nav_acceleration[EAST] + GNSS_acceleration[EAST];
256 nav_correction[EAST] = + nav_acceleration[NORTH] - GNSS_acceleration[NORTH];
257
258 cross_acc_correction = // vector cross product GNSS-acc und INS-acc -> heading error
259 + nav_acceleration[NORTH] * GNSS_acceleration[EAST]
260 - nav_acceleration[EAST] * GNSS_acceleration[NORTH];
261
262 if( circling_state == CIRCLING) // heading correction using acceleration cross product GNSS * INS
263 {
264
265#if USE_ACCELERATION_CROSS_GAIN_ALONE_WHEN_CIRCLING
266 nav_correction[DOWN] = cross_acc_correction * CROSS_GAIN; // no MAG or D-GNSS use here !
267#else
268 float3vector nav_induction = body2nav * body_induction;
269 float mag_correction =
270 + nav_induction[NORTH] * expected_nav_induction[EAST]
271 - nav_induction[EAST] * expected_nav_induction[NORTH];
272 nav_correction[DOWN] = cross_acc_correction * CROSS_GAIN + mag_correction * magnetic_control_gain ; // use X-ACC and MAG: better !
273#endif
274 }
275 else
276 nav_correction[DOWN] = heading_gnss_work * H_GAIN;
277
278 gyro_correction = body2nav.reverse_map(nav_correction);
279 gyro_correction *= P_GAIN;
280
281 if (circling_state == STRAIGHT_FLIGHT)
282 gyro_integrator += gyro_correction; // update integrator
283
284 gyro_correction = gyro_correction + gyro_integrator * I_GAIN;
285 gyro_correction_power = SQR( gyro_correction[0]) + SQR( gyro_correction[1]) +SQR( gyro_correction[2]);
286
287 update_attitude (acc, gyro + gyro_correction, body_induction);
288
289 // only here we get fresh magnetic entropy
290 // and: wait for low control loop error
291 if ( circling_state == CIRCLING)
292 feed_magnetic_induction_observer (mag_sensor);
293
294 // when circling is finished eventually update the magnetic calibration
295 if (automatic_magnetic_calibration && (old_circle_state == CIRCLING) && (circling_state == TRANSITION))
296 handle_magnetic_calibration ('s');
297}
298
302void
305 const float3vector &GNSS_acceleration)
306{
307 expected_body_induction = body2nav.reverse_map( expected_nav_induction);
308
309 if( calib_3D.available())
310 body_induction = mag_sensor - calib_3D.calibrate( mag_sensor, attitude);
311 else if( compass_calibration.available())
312 body_induction = compass_calibration.calibrate(mag_sensor);
313 else
314 body_induction = mag_sensor; // fall back to uncalibrated sensor signal
315
316 body_induction_error = body_induction - expected_body_induction;
317
318 float3vector nav_acceleration = body2nav * acc;
319 float3vector nav_induction = body2nav * body_induction;
320
321 // calculate horizontal leveling error
322 nav_correction[NORTH] = -nav_acceleration[EAST] + GNSS_acceleration[EAST];
323 nav_correction[EAST] = +nav_acceleration[NORTH] - GNSS_acceleration[NORTH];
324
325 // *******************************************************************************************************
326 // calculate heading error depending on the present circling state
327 // on state changes handle MAG auto calibration
328
329 circle_state_t old_circle_state = circling_state;
330 update_circling_state ();
331
332 float mag_correction =
333 + nav_induction[NORTH] * expected_nav_induction[EAST]
334 - nav_induction[EAST] * expected_nav_induction[NORTH];
335
336 cross_acc_correction = // vector cross product GNSS-acc und INS-acc -> heading error
337 + nav_acceleration[NORTH] * GNSS_acceleration[EAST]
338 - nav_acceleration[EAST] * GNSS_acceleration[NORTH];
339
340 switch (circling_state)
341 {
342 case STRAIGHT_FLIGHT:
343 case TRANSITION:
344 default:
345 {
346 nav_correction[DOWN] = magnetic_control_gain * mag_correction;
347 gyro_correction = body2nav.reverse_map (nav_correction);
348 gyro_correction *= P_GAIN;
349 gyro_integrator += gyro_correction; // update integrator
350 }
351 break;
352 // *******************************************************************************************************
353 case CIRCLING:
354 {
355#if USE_ACCELERATION_CROSS_GAIN_ALONE_WHEN_CIRCLING
356 nav_correction[DOWN] = cross_acc_correction * CROSS_GAIN; // no MAG or D-GNSS use here ! (old version)
357#else
358 nav_correction[DOWN] = cross_acc_correction * CROSS_GAIN
359 + mag_correction * M_H_GAIN; // use cross-acceleration and induction: better !
360#endif
361 gyro_correction = body2nav.reverse_map (nav_correction);
362 gyro_correction *= P_GAIN;
363 }
364 break;
365 }
366
367 gyro_correction = gyro_correction + gyro_integrator * I_GAIN;
368 gyro_correction_power = SQR( gyro_correction[0]) + SQR( gyro_correction[1]) +SQR( gyro_correction[2]);
369
370 // feed quaternion update with corrected sensor readings
371 update_attitude (acc, gyro + gyro_correction, body_induction);
372
373 // only here we get fresh magnetic entropy
374 // and: wait for low control loop error
375 if ( (circling_state == CIRCLING) && ( nav_correction.abs() < NAV_CORRECTION_LIMIT))
376 feed_magnetic_induction_observer (mag_sensor);
377
378 // when circling is finished eventually update the magnetic calibration
379 if (automatic_magnetic_calibration && (old_circle_state == CIRCLING) && (circling_state == TRANSITION))
380 handle_magnetic_calibration('m');
381}
382
383
389 const float3vector &GNSS_acceleration)
390{
391 if( calib_3D.available())
392 body_induction = mag_sensor - calib_3D.calibrate( mag_sensor, attitude);
393 else if( compass_calibration.available())
394 body_induction = compass_calibration.calibrate(mag_sensor);
395 else
396 body_induction = mag_sensor; // fall back to uncalibrated sensor signal
397
398 expected_body_induction = body2nav.reverse_map( expected_nav_induction);
399 body_induction_error = body_induction - expected_body_induction;
400
401 float3vector nav_acceleration = body2nav * acc;
402
403 // calculate horizontal leveling error
404 nav_correction[NORTH] = -nav_acceleration[EAST] + GNSS_acceleration[EAST];
405 nav_correction[EAST] = +nav_acceleration[NORTH] - GNSS_acceleration[NORTH];
406
407 update_circling_state ();
408
409 cross_acc_correction = // vector cross product GNSS-acc und INS-acc -> heading error
410 +nav_acceleration[NORTH] * GNSS_acceleration[EAST]
411 - nav_acceleration[EAST] * GNSS_acceleration[NORTH];
412
413 if( circling_state == STRAIGHT_FLIGHT)
414 // empirically tuned OM flight 2022 7 24
415 nav_correction[DOWN] = cross_acc_correction * 40.0f * CROSS_GAIN; // no MAG or D-GNSS use here !
416 else
417 nav_correction[DOWN] = cross_acc_correction * CROSS_GAIN; // no MAG or D-GNSS use here !
418
419 gyro_correction = body2nav.reverse_map (nav_correction);
420 gyro_correction *= P_GAIN;
421
422 gyro_integrator += gyro_correction; // update integrator
423 gyro_correction = gyro_correction + gyro_integrator * I_GAIN; // use integrator
424
425 // feed quaternion update with corrected sensor readings
426 update_attitude(acc, gyro + gyro_correction, body_induction);
427}
428
430{
431 if( !magnetic_calibration_updated)
432 return;
433
434 lock_EEPROM( false);
435
436 compass_calibration.write_into_EEPROM();
437 magnetic_calibration_updated = false; // done ...
438
439 lock_EEPROM( true);
440}
441
442void AHRS_type::handle_magnetic_calibration ( char type)
443{
444 bool calibration_changed = compass_calibration.set_calibration_if_changed ( mag_calibration_data_collector_right_turn, mag_calibration_data_collector_left_turn, MAG_SCALE);
445
446 float induction_error = 0.0f;
447
449
451 {
453 for( unsigned i=0; i<3; ++i)
454 magnetic_induction_report.calibration[i] = (compass_calibration.get_calibration())[i];
455
457 magnetic_calibration_updated = true;
458 }
459}
attitude and heading reference system (interface)
@ ROLL
Definition AHRS.h:40
@ YAW
Definition AHRS.h:40
@ PITCH
Definition AHRS.h:40
circle_state_t
Definition AHRS.h:44
@ TRANSITION
Definition AHRS.h:44
@ STRAIGHT_FLIGHT
Definition AHRS.h:44
@ CIRCLING
Definition AHRS.h:44
@ DOWN
Definition AHRS.h:42
@ EAST
Definition AHRS.h:42
@ NORTH
Definition AHRS.h:42
float3vector nav_induction
@ FRONT
Definition AHRS.h:41
@ RIGHT
Definition AHRS.h:41
Replacement on the PC for the nonvolatile memory of the micro-controller.
uBlox GNSS + D-GNSS interface
Tuning parameters for navigation, wind an variometer.
#define H_GAIN
Attitude controller: horizontal gain.
#define I_GAIN
Attitude controller: integral gain.
#define G_LOAD_F_BY_FS
#define NAV_CORRECTION_LIMIT
limit for "low AHRS correcting variable"
#define P_GAIN
Attitude controller: proportional gain.
#define HIGH_TURN_RATE
turn rate high limit
#define CROSS_GAIN
Attitude controller: cross-product gain.
#define CIRCLE_LIMIT
10 * 1/100 s delay into / out of circling state
#define LOW_TURN_RATE
turn rate low limit
#define M_H_GAIN
Attitude controller: horizontal gain magnetic.
#define ANGLE_F_BY_FS
#define MAG_SCALE
scale factor for high-precision integer statistics
AHRS_type(float sampling_time)
Definition AHRS.cpp:129
void write_calibration_into_EEPROM(void)
Definition AHRS.cpp:429
void attitude_setup(const float3vector &acceleration, const float3vector &induction)
initial attitude setup from observables
Definition AHRS.cpp:41
void update_ACC_only(const float3vector &gyro, const float3vector &acc, const float3vector &mag, const float3vector &GNSS_acceleration)
rotate quaternion taking angular rate readings
Definition AHRS.cpp:387
void update_compass(const float3vector &gyro, const float3vector &acc, const float3vector &mag, const float3vector &GNSS_acceleration)
rotate quaternion taking angular rate readings
Definition AHRS.cpp:303
void update(const float3vector &gyro, const float3vector &acc, const float3vector &mag, const float3vector &GNSS_acceleration, float GNSS_heading, bool GNSS_heading_valid)
Definition AHRS.cpp:168
bool set_calibration_if_changed(linear_least_square_fit< sample_type, evaluation_type > mag_calibrator_right[3], linear_least_square_fit< sample_type, evaluation_type > mag_calibrator_left[3], float scale_factor)
const single_axis_calibration_t * get_calibration(void) const
float3vector calibrate(const float3vector &in)
void write_into_EEPROM(void) const
bool learn(const float3vector &observed_induction, const float3vector &expected_induction, const quaternion< float > &q, bool turning_right, float error_margin)
float3vector calibrate(const float3vector &induction, const quaternion< float > &q)
datatype y
Definition euler.h:17
datatype r
Definition euler.h:17
void add_value(const sample_type x, const sample_type y)
vector< datatype, size > reverse_map(const vector< datatype, size > &right) const
multiplication (matrix times vector) -> vector
Definition matrix.h:126
datatype get_output(void) const
Definition pt2.h:88
datatype respond(const datatype &input)
Definition pt2.h:79
void get_rotation_matrix(matrix< datatype, 3 > &m) const
quaternion -> rotation matrix transformation
Definition quaternion.h:156
void rotate(datatype p, datatype q, datatype r)
quaternion update using rotation vector
Definition quaternion.h:122
void normalize(void)
normalize quaternion absolute value to ONE
Definition quaternion.h:52
void from_rotation_matrix(matrix< datatype, 3 > &rotm)
Definition quaternion.h:194
void normalize(void)
vector normalization
Definition vector.h:129
datatype abs(void) const
vector abs operator returns absolute value
Definition vector.h:77
vector & negate(void)
Definition vector.h:92
vector vector_multiply(const vector &right) const
< vector cross product -> vector
Definition vector.h:66
induction sensor calibration and magnetic disturbance compensation
#define SIN(x)
#define COS(x)
#define M_PI_F
#define ATAN2(y, x)
settings to allow compiling embedded software on a PC target
void report_magnetic_calibration_has_changed(magnetic_induction_report_t *p_magnetic_induction_report, char type)
float configuration(EEPROM_PARAMETER_ID id)
bool lock_EEPROM(bool lockit)
#define SQR(x)
helper struct containing magnetic calibration data
collection of system tuning parameters