forked from multiwii/multiwii-firmware
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathIMU.cpp
227 lines (196 loc) · 8 KB
/
IMU.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include "Arduino.h"
#include "config.h"
#include "def.h"
#include "types.h"
#include "MahoWii.h"
#include "IMU.h"
#include "Sensors.h"
#include "Math.h"
void calculateAttitude();
void computeIMU () {
uint8_t axis;
static int16_t gyroADCprevious[3] = {0,0,0};
static int16_t gyroADCinter[3];
uint16_t timeInterleave = 0;
#if GYRO
Gyro_getADC();
#endif
for (axis = 0; axis < 3; axis++)
gyroADCinter[axis] = imu.gyroADC[axis];
timeInterleave=micros();
annexCode();
uint8_t t=0;
while((int16_t)(micros()-timeInterleave)<650) t=1; //empirical, interleaving delay between 2 consecutive reads
#ifdef LCD_TELEMETRY
if (!t) annex650_overrun_count++;
#endif
#if GYRO
Gyro_getADC();
#endif
#if ACC
ACC_getADC();
calculateAttitude();
#endif
for (axis = 0; axis < 3; axis++) {
gyroADCinter[axis] = imu.gyroADC[axis]+gyroADCinter[axis];
// empirical, we take a weighted value of the current and the previous values
imu.gyroData[axis] = (gyroADCinter[axis]+gyroADCprevious[axis])/3;
gyroADCprevious[axis] = gyroADCinter[axis]>>1;
if (!ACC) imu.accADC[axis]=0;
}
#if defined(GYRO_SMOOTHING)
static int16_t gyroSmooth[3] = {0,0,0};
for (axis = 0; axis < 3; axis++) {
imu.gyroData[axis] = (int16_t) ( ( (int32_t)((int32_t)gyroSmooth[axis] * (conf.Smoothing[axis]-1) )+imu.gyroData[axis]+1 ) / conf.Smoothing[axis]);
gyroSmooth[axis] = imu.gyroData[axis];
}
#elif defined(TRI)
static int16_t gyroYawSmooth = 0;
imu.gyroData[YAW] = (gyroYawSmooth*2+imu.gyroData[YAW])/3;
gyroYawSmooth = imu.gyroData[YAW];
#endif
}
// **************************************************
// Simplified IMU based on "Complementary Filter"
// Inspired by http://starlino.com/imu_guide.html
//
// adapted by ziss_dm : http://www.multiwii.com/forum/viewtopic.php?f=8&t=198
//
// The following ideas was used in this project:
// 1) Rotation matrix: http://en.wikipedia.org/wiki/Rotation_matrix
// 2) Small-angle approximation: http://en.wikipedia.org/wiki/Small-angle_approximation
// 3) C. Hastings approximation for atan2()
// 4) Optimization tricks: http://www.hackersdelight.org/
//
// Currently Magnetometer uses separate CF which is used only
// for heading approximation.
//
// **************************************************
//****** advanced users settings *******************
/* Set the Low Pass Filter factor for ACC
Increasing this value would reduce ACC noise (visible in GUI), but would increase ACC lag time. */
#ifndef ACC_LPF_FACTOR
#ifdef KILL_VIBRO
#define ACC_LPF_FACTOR 64
#else
#define ACC_LPF_FACTOR 32
#endif
#endif
/* Set the Gyro Weight for Gyro/Acc complementary filter
Increasing this value would reduce and delay Acc influence on the output of the filter*/
#ifndef GYR_CMPF_FACTOR
#define GYR_CMPF_FACTOR 10 // that means a CMP_FACTOR of 1024 (2^10)
/* #ifdef KILL_VIBRO
//#define GYR_CMPF_FACTOR 10 // that means a CMP_FACTOR of 1024 (2^10)
#define GYR_CMPF_FACTOR 1024
#else
//#define GYR_CMPF_FACTOR 9 // that means a CMP_FACTOR of 512 (2^9)
#define GYR_CMPF_FACTOR 800
#endif */
#endif
/* Set the Gyro Weight for Gyro/Magnetometer complementary filter
Increasing this value would reduce and delay Magnetometer influence on the output of the filter*/
//#define GYR_CMPFM_FACTOR 8 // that means a CMP_FACTOR of 256 (2^8)
#define GYR_CMPFM_FACTOR 9 // that means a CMP_FACTOR of 512 (2^9)
//#define GYR_CMPFM_FACTOR 10 // that means a CMP_FACTOR of 1024 (2^10)
typedef struct {
int32_t X,Y,Z;
} t_int32_t_vector_def;
typedef struct {
uint16_t XL; int16_t X;
uint16_t YL; int16_t Y;
uint16_t ZL; int16_t Z;
} t_int16_t_vector_def;
// note: we use implicit first 16 MSB bits 32 -> 16 cast. ie V32.X>>16 = V16.X
typedef union {
int32_t A32[3];
t_int32_t_vector_def V32;
int16_t A16[6];
t_int16_t_vector_def V16;
} t_int32_t_vector;
// Rotate Estimated vector(s) with small angle approximation, according to the gyro data
void rotateV32( t_int32_t_vector *v,int16_t* delta) {
int16_t X = v->V16.X;
int16_t Y = v->V16.Y;
int16_t Z = v->V16.Z;
v->V32.Z -= mul(delta[ROLL] , X) + mul(delta[PITCH] , Y);
v->V32.X += mul(delta[ROLL] , Z) - mul(delta[YAW] , Y);
v->V32.Y += mul(delta[PITCH] , Z) + mul(delta[YAW] , X);
}
void calculateAttitude(){
uint8_t axis;
int32_t accMag = 0;
float scale;
int16_t deltaGyroAngle16[3];
static t_int32_t_vector EstG = {0,0,(int32_t)ACC_1G<<16};
#if MAG
static t_int32_t_vector EstM;
#else
static t_int32_t_vector EstM = {0,(int32_t)1<<24,0};
#endif
static float LPFAcc[3] = {0, 0, ACC_1G};
float invG; // 1/|G|
static uint16_t previousT;
uint16_t currentT = micros();
// unit: radian per bit, scaled by 2^16 for further multiplication
// with a delta time of 3000 us, and GYRO scale of most gyros, scale = a little bit less than 1
scale = (currentT - previousT) * (GYRO_SCALE * 65536);
previousT = currentT;
// Initialization
for (axis = 0; axis < 3; axis++) {
// valid as long as LPF_FACTOR is less than 15
// imu.accSmooth[axis] = LPFAcc[axis]>>ACC_LPF_FACTOR;
// LPFAcc[axis] += imu.accADC[axis] - imu.accSmooth[axis];
LPFAcc[axis] = LPFAcc[axis] * (1.0f - (1.0f/ACC_LPF_FACTOR)) + imu.accADC[axis] * (1.0f/ACC_LPF_FACTOR);
imu.accSmooth[axis] = LPFAcc[axis];
// used to calculate later the magnitude of acc vector
accMag += mul(imu.accSmooth[axis], imu.accSmooth[axis]);
//accMag += mul(imu.accADC[axis] , imu.accADC[axis]);
// unit: radian scaled by 2^16
// imu.gyroADC[axis] is 14 bit long, the scale factor ensure deltaGyroAngle16[axis] is still 14 bit long
deltaGyroAngle16[axis] = imu.gyroADC[axis] * scale;
}
// we rotate the intermediate 32 bit vector with the radian vector (deltaGyroAngle16), scaled by 2^16
// however, only the first 16 MSB of the 32 bit vector is used to compute the result
// it is ok to use this approximation as the 16 LSB are used only for the complementary filter part
rotateV32(&EstG,deltaGyroAngle16);
rotateV32(&EstM,deltaGyroAngle16);
// Apply complimentary filter (Gyro drift correction)
// If accel magnitude >1.15G or <0.85G and ACC vector outside of the limit range => we neutralize the effect of accelerometers in the angle estimation.
// To do that, we just skip filter, as EstV already rotated by Gyro
for (axis = 0; axis < 3; axis++) {
if ( (int16_t)(0.85*ACC_1G*ACC_1G/256) < (int16_t)(accMag>>8) && (int16_t)(accMag>>8) < (int16_t)(1.15*ACC_1G*ACC_1G/256) )
EstG.A32[axis] += (int32_t)(imu.accSmooth[axis] - EstG.A16[2*axis+1])<<(16-GYR_CMPF_FACTOR);
//EstG.A32[axis] += (int32_t)(imu.accSmooth[axis] - EstG.A16[2*axis+1])*(65536/GYR_CMPF_FACTOR);
// accZ_tmp += mul(imu.accSmooth[axis] , EstG.A16[2*axis+1]);
#if MAG
EstM.A32[axis] += (int32_t)(imu.magADC[axis] - EstM.A16[2*axis+1])<<(16-GYR_CMPFM_FACTOR);
#endif
}
if (EstG.V16.Z > ACCZ_25deg)
f.SMALL_ANGLES_25 = 1;
else
f.SMALL_ANGLES_25 = 0;
// Attitude of the estimated vector
int32_t sqGX_sqGZ = mul(EstG.V16.X,EstG.V16.X) + mul(EstG.V16.Z,EstG.V16.Z);
invG = InvSqrt(sqGX_sqGZ + mul(EstG.V16.Y,EstG.V16.Y));
att.angle[ROLL] = _atan2(EstG.V16.X , EstG.V16.Z);
att.angle[PITCH] = _atan2(EstG.V16.Y , InvSqrt(sqGX_sqGZ)*sqGX_sqGZ);
//note on the second term: mathematically there is a risk of overflow (16*16*16=48 bits). assumed to be null with real values
att.angle_YAW = _atan2(
mul(EstM.V16.Z , EstG.V16.X) - mul(EstM.V16.X , EstG.V16.Z),
(EstM.V16.Y * sqGX_sqGZ - (mul(EstM.V16.X , EstG.V16.X) + mul(EstM.V16.Z , EstG.V16.Z)) * EstG.V16.Y)*invG );
#if MAG
att.angle_YAW += conf.mag_declination; // Set from GUI
#endif
if(att.angle_YAW > 1800) {
att.angle_YAW -= 3600;
} else if(att.angle_YAW < -1800) {
att.angle_YAW += 3600;
}
att.heading = att.angle_YAW / 10;
#if defined(THROTTLE_ANGLE_CORRECTION)
cosZ = mul(EstG.V16.Z , 100) / ACC_1G ; // cos(angleZ) * 100
throttleAngleCorrection = THROTTLE_ANGLE_CORRECTION * constrain(100 - cosZ, 0, 100) >>3; // 16 bit ok: 200*150 = 30000
#endif
}