BaseFlight IMU - FabLabSeoul/WingProject GitHub Wiki
IMU (Inertia Measurement Unit)
μ΄ κΈμ Multiwii Baseflight μ€νμμ€λ₯Ό λΆμν κΈμ΄λ€.
Multiwii Baseflightλ₯Ό Stm32 Value line discovery 보λμ λ§κ² μ½λλ₯Ό μ¬μμ±νμλ€.
Stm32 Value line discovery 보λλ 24MHz μλκ° μ΅λμ΄κΈ° λλ¬Έμ, 72MHz μλμ Naze32보λ νκ²½μμ νλ‘κ·Έλλ°λ Baseflight μμ€κ° λ¬Έμ κ° λ°μν μ¬μ§κ° μλ€. νΉν μμ€ν
ν΄λμ μ΄μ©νλ micros()
ν¨μμ millis()
ν¨μκ° λ¬Έμ μΈλ°, νμ¬ μμ μμ micros()
ν¨μκ° μ μλνμ§ μλλ€λ κ±Έ νμΈ νλ€. μ‘°λ§κ° λ¬Έμ λ₯Ό ν΄κ²°νκ³ , κΈμ μΈ μκ°μ΄λ€.
Baseflight μμ imu.c λΌμ΄λΈλ¬λ¦¬λ Sensor λΌμ΄λΈλ¬λ¦¬λ₯Ό μ΄μ©ν΄, λ©ν°μ½₯ν°μ Roll,Pitch,Yawκ°μ κ³μ°νλ€. imu λΌμ΄λΈλ¬λ¦¬μ μ£Όμν¨μλ λ€μκ³Ό κ°λ€.
- imuInit()
- IMU μ΄κΈ°ν
- computeIMU()
- κ°μλ,μμ΄λ‘,μ§μκΈ° μΌμ κ°μ μ½μ΄μ€κ³ ,
getEstimatedAttitude()
ν¨μλ₯Ό νΈμΆνλ€.
- κ°μλ,μμ΄λ‘,μ§μκΈ° μΌμ κ°μ μ½μ΄μ€κ³ ,
- getEstimatedAttitude()
- κ°μλ, μμ΄λ‘μ€μ½ν, μ§μκΈ° μΌμ κ°μ μ΄μ©ν΄, λ©ν°μ½₯ν°μ Roll,Pitch,Yawκ°μ κ³μ°νλ€.
imuInit() ν¨μ
void imuInit(void)
{
smallAngle = lrintf(acc_1G * cosf(RAD * cfg.small_angle));
accVelScale = 9.80665f / acc_1G / 10000.0f;
throttleAngleScale = (1800.0f / M_PI) * (900.0f / cfg.throttle_correction_angle);
fc_acc = 0.5f / (M_PI * cfg.accz_lpf_cutoff); // calculate RC time constant used in the accZ lpf
Mag_init();
}
lrintf()
ν¨μλ float κ°μ μμμ μ΄ν κ°μ μλΌμ intνμΌλ‘ λ³ν νλ€.
computeIMU() ν¨μ
Baseflight imu::computeIMU()
ν¨μλ λ€μκ³Ό κ°λ€.
void computeIMU(void)
{
Gyro_getADC();
ACC_getADC();
getEstimatedAttitude();
gyroData[YAW] = gyroADC[YAW];
gyroData[ROLL] = gyroADC[ROLL];
gyroData[PITCH] = gyroADC[PITCH];
}
κ°μλ, μμ΄λ‘μ€μ½ν μΌμ κ°μ μ½μ΄μ¨ ν, getEstimatedAttitude()
ν¨μλ₯Ό νΈμΆνλ€.
getEstimatedAttitude() ν¨μ
getEstimatedAttitude()
ν¨μμμλ accADC[]
, gyroADC[]
, magADC[]
λ³μλ₯Ό ν λλ‘ λ©ν°μ½₯ν°μ Roll,Pitch,Yaw κ°μ κ³μ°νλ€.
μ 체 μ½λλ λ€μκ³Ό κ°λ€. νλνλμ© λΆμν΄λ³΄μ.
static void getEstimatedAttitude(void)
{
int32_t axis;
int32_t accMag = 0;
static t_fp_vector EstM;
static t_fp_vector EstN = { .A = { 1.0f, 0.0f, 0.0f } };
static float accLPF[3];
static uint32_t previousT;
uint32_t currentT = micros();
uint32_t deltaT;
float scale, deltaGyroAngle[3];
deltaT = currentT - previousT;
scale = deltaT * gyro.scale;
// TO: dont understand
// μκ°μ΄ μ΄κΈ°ν λλ λ²κ·Έ λλ¬Έμ μΆκ°ν¨.
// μ΄μ λ μμ§ λͺ¨λ₯΄κ² μ.
// νμ¬μκ°κ³Ό μ μκ° μ°¨μ΄κ° λ무 ν¬λ©΄ μ°μ°νμ§ μλλ€.
if (currentT - previousT > 10000) // 10ms over
{
previousT = currentT;
return;
}
previousT = currentT;
// Initialization
for (axis = 0; axis < 3; axis++)
{
deltaGyroAngle[axis] = gyroADC[axis] * scale;
if (cfg.acc_lpf_factor > 0)
{
accLPF[axis] = accLPF[axis] * (1.0f - (1.0f / cfg.acc_lpf_factor)) + accADC[axis] * (1.0f / cfg.acc_lpf_factor);
accSmooth[axis] = accLPF[axis];
}
else
{
accSmooth[axis] = accADC[axis];
}
accMag += (int32_t)accSmooth[axis] * accSmooth[axis];
}
accMag = accMag * 100 / ((int32_t)acc_1G * acc_1G);
rotateV(&EstG.V, deltaGyroAngle);
// 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
if (72 < (uint16_t)accMag && (uint16_t)accMag < 133) {
for (axis = 0; axis < 3; axis++)
EstG.A[axis] = (EstG.A[axis] * (float)mcfg.gyro_cmpf_factor + accSmooth[axis]) * INV_GYR_CMPF_FACTOR;
}
f.SMALL_ANGLE = (EstG.A[Z] > smallAngle);
// Attitude of the estimated vector
anglerad[ROLL] = atan2f(EstG.V.Y, EstG.V.Z);
anglerad[PITCH] = atan2f(-EstG.V.X, sqrtf(EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z));
angle[ROLL] = lrintf(anglerad[ROLL] * (1800.0f / M_PI));
angle[PITCH] = lrintf(anglerad[PITCH] * (1800.0f / M_PI));
if (0)
{
rotateV(&EstM.V, deltaGyroAngle);
for (axis = 0; axis < 3; axis++)
EstM.A[axis] = (EstM.A[axis] * (float)mcfg.gyro_cmpfm_factor + magADC[axis]) * INV_GYR_CMPFM_FACTOR;
heading = calculateHeading(&EstM);
}
else
{
rotateV(&EstN.V, deltaGyroAngle);
normalizeV(&EstN.V, &EstN.V);
heading = calculateHeading(&EstN);
}
acc_calc(deltaT); // rotate acc vector into earth frame
if (cfg.throttle_correction_value)
{
float cosZ = EstG.V.Z / sqrtf(EstG.V.X * EstG.V.X + EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z);
if (cosZ <= 0.015f)
{ // we are inverted, vertical or with a small angle < 0.86 deg
throttleAngleCorrection = 0;
}
else
{
int deg = lrintf(acosf(cosZ) * throttleAngleScale);
if (deg > 900)
deg = 900;
throttleAngleCorrection = lrintf(cfg.throttle_correction_value * sinf(deg / (900.0f * M_PI / 2.0f)));
}
}
}
###1. deltaT κ³μ°
Stme32 Value line discovery 보λμ μλ λλ¬Έμ, micros()
ν¨μκ° μ€κ°μ μ΄κΈ°ν λλ λ¬Έμ κ° μλ€. κ·Έλμ μ μκ°κ³Ό λ무 λ§μ΄ μ°¨μ΄λλ©΄ 무μνκ² νλ μ½λκ° μΆκ° λμλ€. μ΄λκΉμ§λ μμλ°©νΈ μ½λμ΄κΈ° λλ¬Έμ, μμ ν΄μΌ ν μ½λλ€.
uint32_t currentT = micros();
uint32_t deltaT;
deltaT = currentT - previousT;
if (currentT - previousT > 10000) // 10ms over
{
previousT = currentT;
return;
}
###2. κ°μλ, μμ΄λ‘μ€μ½ν μΌμ κ°μ κ°μ Έμ¨λ€.
gyroADC[axis] * scale;
μ½λλ₯Ό λ€μ μ°λ©΄ gyroADC[axis] * (4.0f / 16.4f) * (M_PI / 180.0f) * 0.000001f * deltaT;
μ κ°λ€. κ°μλ κ°μ΄ μμ΄λ‘μ€μ½ν μΌμ κ°μ micro seconds λ¨μμ λΌλμ μλ κ°μΌλ‘ λ³νν΄μ£Όλ μ½λλ€. μ΄μ μμ΄λ‘μ€μ½ν μΌμ κ°μ deltaGyroAngle[axis]
μ μ μ₯λλ€.
cfg.acc_lpf_factor
μ μλ³μλ 4λ‘ μ΄κΈ°ν λμ΄ μλ μνμ΄κΈ° λλ¬Έμ, if (cfg.acc_lpf_factor > 0)
쑰건μ μ±λ¦½νλ€. λΈλ μμ μλ μ½λλ Low Pass Filterλ₯Ό λ»νλ©°, κ°μλ κ°μ νκ· μ ꡬνλ, 1/cfg.acc_lpf_factor
ν¬κΈ° λ§νΌμ μ±λΆμ κ°μ₯ μ΅κ·Ό κ°μ λΉμ€μ λκ³ , λλ¨Έμ§ 1 - (1/cfg.acc_lpf_factor)
κ°λ§νΌ κ·Έ μ νκ· κ°μ λΉμ€μ λλ€λ λ»μ΄λ€.
Low Pass Filtering λ κ°μ accLPF[]
μ€νν± λ³μμ accSmooth[]
λ³μμ μ μ₯λλ€. λ‘μ° ν¨μ€ νν°λ₯Ό μ°λ μ°μ§μλ , μ΅μ’
κ°μλ μΌμ κ°μ accSmooth[]
λ³μμ μ μ₯λλ€.
accMag
λ³μλ (κ°μλ 벑ν°μ ν¬κΈ° X 100) μ μ μ₯νλ€. floatκ°μ int16_tλ‘ λ³νν λ, μμμ λ λ²μ§Έ μ리κΉμ§ νννκΈ° μν΄μ X100 μ΄ λμλ€.
μ 리νλ©΄
deltaGyroAngle[]
= μ΄λΉ (x,y,z) κ°μλ -> λ§μ΄ν¬λ‘ μ΄λΉ (x,y,z) λΌλμ μλλ₯Ό μ μ₯νλ€.accSmooth[]
= λ‘μ° ν¨μ€ νν°κ° μ μ©λ x,y,z κ°μλλ₯Ό μ μ₯νλ€.accMag
= κ°μλ λ²‘ν° ν¬κΈ° X 100 κ°μ μ μ₯νλ€.
int32_t accMag = 0;
static float accLPF[3];
float scale, deltaGyroAngle[3];
deltaT = currentT - previousT;
scale = deltaT * gyro.scale;
// Initialization
for (axis = 0; axis < 3; axis++)
{
deltaGyroAngle[axis] = gyroADC[axis] * scale;
if (cfg.acc_lpf_factor > 0)
{
accLPF[axis] = accLPF[axis] * (1.0f - (1.0f / cfg.acc_lpf_factor)) + accADC[axis] * (1.0f / cfg.acc_lpf_factor);
accSmooth[axis] = accLPF[axis];
}
else
{
accSmooth[axis] = accADC[axis];
}
accMag += (int32_t)accSmooth[axis] * accSmooth[axis];
}
accMag = accMag * 100 / ((int32_t)acc_1G * acc_1G);
###3. x,y,zμΆ λ§μ΄ν¬λ‘μ΄λΉ λΌλμ μλ κ°μΌλ‘ 벑ν°λ₯Ό νμ μν¨λ€.
rotateV()
ν¨μλ μΈμλ‘ λμ΄μ¨ 벑ν°λ₯Ό x,y,zμΆ κ°μλ κ°μ μ μ©ν΄ νμ μν¨λ€.
rotateV()
ν¨μλ μ νν x,y,zμΆ μ€μΌλ¬ νμ κ°μΌλ‘ νμ νλ ¬μ λ§λ ν, 벑ν°λ₯Ό νμ μν€λ μ½λλ€.
κ²°κ΅ EstG
벑ν°λ λ©ν°μ½₯ν°κ° λ°λΌλ³΄λ λ°©ν₯μ λνλΈλ€.
t_fp_vector EstG; // μ μλ³μ
rotateV(&EstG.V, deltaGyroAngle);
// Rotate Estimated vector(s) with small angle approximation, according to the gyro data
void rotateV(struct fp_vector *v, float *delta)
{
struct fp_vector v_tmp = *v;
// This does a "proper" matrix rotation using gyro deltas without small-angle approximation
float mat[3][3];
float cosx, sinx, cosy, siny, cosz, sinz;
float coszcosx, sinzcosx, coszsinx, sinzsinx;
cosx = cosf(delta[ROLL]);
sinx = sinf(delta[ROLL]);
cosy = cosf(delta[PITCH]);
siny = sinf(delta[PITCH]);
cosz = cosf(delta[YAW]);
sinz = sinf(delta[YAW]);
coszcosx = cosz * cosx;
sinzcosx = sinz * cosx;
coszsinx = sinx * cosz;
sinzsinx = sinx * sinz;
mat[0][0] = cosz * cosy;
mat[0][1] = -cosy * sinz;
mat[0][2] = siny;
mat[1][0] = sinzcosx + (coszsinx * siny);
mat[1][1] = coszcosx - (sinzsinx * siny);
mat[1][2] = -sinx * cosy;
mat[2][0] = (sinzsinx) - (coszcosx * siny);
mat[2][1] = (coszsinx) + (sinzcosx * siny);
mat[2][2] = cosy * cosx;
v->X = v_tmp.X * mat[0][0] + v_tmp.Y * mat[1][0] + v_tmp.Z * mat[2][0];
v->Y = v_tmp.X * mat[0][1] + v_tmp.Y * mat[1][1] + v_tmp.Z * mat[2][1];
v->Z = v_tmp.X * mat[0][2] + v_tmp.Y * mat[1][2] + v_tmp.Z * mat[2][2];
}
###4. μ보νν° μ μ©
accMag
μ κ°μλ벑ν°ν¬κΈ° X 100 μ΄ λ μνμ΄λ―λ‘, 100μ΄ 1Gκ° λλ€. μ½λ μμμ 72 < accMag
< 133 μ΄λ―λ‘, κ°μλλ²‘ν° ν¬κΈ°κ° 0.72G λ³΄λ€ ν¬λ©΄μ, 1.33Gλ³΄λ€ μλ€λ©΄, μ보νν°λ₯Ό μ μ©νλ€. μ¦, μ λΉν μμ§μΌ λλ§, μ보νν°λ₯Ό μ μ©νκ³ , κ·Έλ μ§ μμ κ²½μ°μλ μμ΄λ‘μ€μ½ν μΌμκ°μ κ·Έλλ‘ λ°λ₯΄κ² λ€λ λ»μ΄λ€.
μ보νν° κ° mcfg.gyro_cmpf_factor
λ 600μ΄ μ€μ λμ΄μλ€. μ½λκ° μ½κ° 볡μ‘νκ² λμ΄μμ§λ§, μ보νν° κ³΅μμ κ·Έλλ‘ λ°λ₯Έλ€.
EstG.A[axis] = EstG.A[axis] * (600/601) + accSmooth[axis]/601
λ‘ νν ν μ μλ€.
λ κ°λ¨ν νμλ©΄,
EstG.A[axis] = EstG.A[axis] * 0.99833f + accSmooth[axis]*0.00166f
λ‘ ννλλ€.
mcfg.gyro_cmpf_factor
κ°μ micro second λ¨μ μΈμ§, milli second λ¨μ μΈμ§, ν¨μκ° νΈμΆλλ μ£ΌκΈ°μ λ°λΌ κ²°μ λλ€.
// 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
if (72 < (uint16_t)accMag && (uint16_t)accMag < 133) {
for (axis = 0; axis < 3; axis++)
EstG.A[axis] = (EstG.A[axis] * (float)mcfg.gyro_cmpf_factor + accSmooth[axis]) * INV_GYR_CMPF_FACTOR;
}
###5. λ©ν°μ½₯ν°μ μμΈ Roll, Pitch, Yaw κ°μ κ³μ°νλ€.
ZμΆ νμ κ°μ΄ smallAngleκ°μ λμ΄κ°λ©΄ f.SMALL_ANGLE
νλκ·Έκ° νμ±ν λλ€.
μ΅μ’
κ³μ°λ EstG
벑ν°λ‘ λ©ν°μ½₯ν°μ Roll, Pitch νμ κ°μ κ³μ°νλ€. μ΄ μμμ x,y,z 벑ν°λ₯Ό μ΄μ©ν΄ Roll, Pitch κ°μ ꡬνλ 곡μ κ·Έλλ‘ λ°λ₯Έλ€.
anglerad[]
λ³μλ λΌλμμΌλ‘ νμ κ°μ μ μ₯νκ³ , angle[]
μ κ°λ κ°μΌλ‘ μ μ₯νλ€.
angle[]
μ ꡬν λ, (1800.0f / M_PI)
κ³±νλ μ΄μ λ, μ€μ κ°μ μ μ κ°μΌλ‘ λ³ν λ λ, μμμ μλ¦¬κ° μμ΄μ§κΈ° λλ¬Έμ, μ μ΄λ μμμ 첫 λ²μ§Έ μ리κΉμ§ κ³μ°νκΈ° μν΄ X10 μ νλ€. κ·Έλμ μ€μ λ‘ angle[]
λ³μλ 45λ μΌ λλ 450κ°μ κ°μ§κ²λλ€.
f.SMALL_ANGLE = (EstG.A[Z] > smallAngle);
// Attitude of the estimated vector
anglerad[ROLL] = atan2f(EstG.V.Y, EstG.V.Z);
anglerad[PITCH] = atan2f(-EstG.V.X, sqrtf(EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z));
angle[ROLL] = lrintf(anglerad[ROLL] * (1800.0f / M_PI));
angle[PITCH] = lrintf(anglerad[PITCH] * (1800.0f / M_PI));
###6. λ©ν°μ½₯ν°κ° λ°λΌλ³΄λ λ°©ν₯μ κ³μ°νλ€. μ§μκΈ° μΌμκ° μμ λμ κ·Έλ μ§ μμ λ, λ°λ‘ λΆκΈ° λμλ€.
μ§μκΈ° μΌμκ° μμ κ²½μ°, μ보νν°λ₯Ό μ΄μ©ν΄ EstM
벑ν°λ₯Ό ꡬν ν, calculateHeading()
ν¨μλ‘ μ΅μ’
Yaw νμ κ°μ κ³μ°νλ€. μ§μκΈ° μΌμ μ보νν° κ³μ mcfg.gyro_cmpfm_factor`λ 250μΌλ‘ μ€μ λλ€.
μ§μκΈ° μΌμκ° μμ κ²½μ°, νμ¬ x,y,zμΆ κ°μλ κ°μ λνλ΄λ deltaGyroAngle[]
λ³μλ‘ EstN
벑ν°λ₯Ό ꡬνκ³ , calculateHeading()
ν¨μλ‘ μ΅μ’
Yaw νμ κ°μ κ³μ°νλ€.
// if (sensors(SENSOR_MAG))
if (0)
{
rotateV(&EstM.V, deltaGyroAngle);
for (axis = 0; axis < 3; axis++)
EstM.A[axis] = (EstM.A[axis] * (float)mcfg.gyro_cmpfm_factor + magADC[axis]) * INV_GYR_CMPFM_FACTOR;
heading = calculateHeading(&EstM);
}
else
{
rotateV(&EstN.V, deltaGyroAngle);
normalizeV(&EstN.V, &EstN.V);
heading = calculateHeading(&EstN);
}
// baseflight calculation by Luggi09 originates from arducopter
static int16_t calculateHeading(t_fp_vector *vec)
{
int16_t head;
float cosineRoll = cosf(anglerad[ROLL]);
float sineRoll = sinf(anglerad[ROLL]);
float cosinePitch = cosf(anglerad[PITCH]);
float sinePitch = sinf(anglerad[PITCH]);
float Xh = vec->A[X] * cosinePitch + vec->A[Y] * sineRoll * sinePitch + vec->A[Z] * sinePitch * cosineRoll;
float Yh = vec->A[Y] * cosineRoll - vec->A[Z] * sineRoll;
float hd = (atan2f(Yh, Xh) * 1800.0f / M_PI + magneticDeclination) / 10.0f;
head = lrintf(hd);
if (head < 0)
head += 360;
return head;
}