MPUとHMCでうごくかもver
Dependencies: ConfigFile SDFileSystem mbed
Fork of LAURUS_program by
main.cpp
- Committer:
- YusukeWakuta
- Date:
- 2015-12-16
- Revision:
- 37:97152b80d28c
- Parent:
- 36:94dc027e05cd
- Child:
- 38:ada39f1c6c76
File content as of revision 37:97152b80d28c:
#include "mbed.h" #include "MPU6050.h" #include "HMC5883L.h" #include "LPS25H.h" #include "GMS6_CR6.h" #include "Vector.h" #include "Matrix.h" #include "Vector_Matrix_operator.h" #include "myConstants.h" #include "SDFileSystem.h" #include "BufferedSerial.h" #include "ConfigFile.h" const float dt = 0.01f; // 割り込み周期(s) enum Direction {LEFT, RIGHT, NEUTRAL}; /****************************** private macro ******************************/ /****************************** private typedef ******************************/ /****************************** private variables ******************************/ DigitalOut myled(LED1); // デバッグ用LEDのためのデジタル出力 I2C i2c(p9,p10); // I2Cポート MPU6050 mpu(&i2c); // 加速度・角速度センサ HMC5883L hmc(&i2c); // 地磁気センサ LPS25H lps(&i2c); // 気圧センサ Serial pc(USBTX,USBRX); // PC通信用シリアルポート FILE * fp; // ログファイルのポインタ ConfigFile cfg; // ConfigFile Ticker ticker; // 割り込みタイマー Timer timer; // 時間計測用タイマー Direction dir = NEUTRAL; // 旋回方向 volatile int lps_cnt = 0; // 気圧センサ読み取りカウント volatile bool INT_flag = true; // 割り込み可否フラグ /* こちらの変数群はメインループでは参照しない */ Vector raw_acc(3); // 加速度(m/s^2) 生 Vector raw_gyro(3); // 角速度(deg/s) 生 Vector raw_geomag(3); // 地磁気(?) 生 float raw_press; // 気圧(hPa) 生 float raw_temp; // 温度(℃) 生 /* メインループ内ではこちらを参照する */ Vector acc(3); // 加速度(m/s^2) Vector gyro(3); // 角速度(deg/s) Vector geomag(3); // 地磁気(?) float p0; // 気圧の初期値 float press; // 気圧(hPa) float temp; // 温度(℃) float height; // 高さ(m) Vector raw_g(3); // 重力ベクトル 生 Vector g(3); // 重力ベクトル Vector target_p(2); // 目標情報(経度、緯度)(rad) Vector p(2); // 現在の位置情報(補完含む)(経度, 緯度)(rad) Vector new_p(2); // 最新の位置情報(経度, 緯度)(rad) Vector pre_p(2); // 過去の位置情報(経度, 緯度)(rad) int UTC_t = 0; // UTC時刻 int pre_UTC_t = 0; // 前のUTC時刻 int ss = 0; // 時刻の秒数の小数部分 Vector b_f(3); // 機体座標に固定された、機体前方向きのベクトル(x軸) Vector b_u(3); // 機体座標に固定された、機体上方向きのベクトル(z軸) Vector b_l(3); // 機体座標に固定された、機体左方向きのベクトル(y軸) Vector r_f(3); // 世界座標に固定された、北向きのベクトル(X軸) Vector r_u(3); // 世界座標に固定された、上向きのベクトル(Z軸) Vector r_l(3); // 世界座標に固定された、西向きのベクトル(Y軸) float pull_L = 10.0f; // 左サーボの引っ張り量(目標値)(mm:0~PullMax) float pull_R = 10.0f; // 右サーボの引っ張り量(目標値)(mm:0~PullMax) float yaw = 0.0f; // 本体のヨー角(deg)z軸周り float pitch = 0.0f; // 本体のピッチ角(deg)y軸周り float roll = 0.0f; // 本体のロール角(deg)x軸周り float vrt_acc = 0.0f; // 鉛直方向の加速度成分(落下検知に使用) volatile int step = 0; // シーケンス制御のステップ char data[512] = {}; // 送信データ用配 int loopTime = 0; // 1ループに掛かる時間(デバッグ用 float sv = 0.0f; // サーボ電源電圧 float lv = 0.0f; // ロジック電源電圧 #ifdef RULE3_1 float beta = 0.0f; // β変数 (制御則3_1で使用 #endif /** config.txt ** * #から始めるのはコメント行 * #イコールの前後に空白を入れない * target_x=111.222 * target_y=33.444 */ float target_x, target_y; /* ---------- Kalman Filter ---------- */ // 地磁気ベクトル用 // ジャイロのz軸周りのバイアスも推定 Vector pri_x1(7); Matrix pri_P1(7, 7); Vector post_x1(7); Matrix post_P1(7, 7); Matrix F1(7, 7), H1(3, 7); Matrix R1(7, 7), Q1(3, 3); Matrix I1(7, 7); Matrix K1(7, 3); Matrix S1(3, 3), S_inv1(3, 3); // 重力ベクトル用 // ジャイロのx軸、y軸周りのバイアスも同時に推定 Vector pri_x2(5); Matrix pri_P2(5, 5); Vector post_x2(5); Matrix post_P2(5, 5); Matrix F2(5, 5), H2(3, 5); Matrix R2(5, 5), Q2(3, 3); Matrix I2(5, 5); Matrix K2(5, 3); Matrix S2(3, 3), S_inv2(3, 3); /* ---------- ------------- ---------- */ /****************************** private functions ******************************/ void SensorsInit(); // 各種センサーの初期化 void KalmanInit(); // カルマンフィルタ初期化 void DataProcessing(); // データ処理関数 void KalmanUpdate(); // カルマンフィルタ更新 void DataUpdate(); // データ取得&更新関数 /** 小さい値を返す関数 * @param a: 1つ目の入力値 * @param b: 2つ目の入力値 * * @return a,bのうち、小さい方の値 */ inline float min(float a, float b) { return ((a > b) ? b : a); } /****************************** main function ******************************/ int main() { pc.printf("HELLO"); i2c.frequency(400000); // I2Cの通信速度を400kHzに設定 //カルマンフィルタ初期化 KalmanInit(); ticker.attach(&DataUpdate, dt); // 割り込み有効化(Freq = 0.01fなので、10msおきの割り込み) /* ------------------------------ ↓↓↓ ここからメインループ ↓↓↓ ------------------------------ */ while(1) { timer.stop(); timer.reset(); timer.start(); myled = 1; // LED is ON INT_flag = false; // 割り込みによる変数書き換えを阻止 /******************************* データ処理 *******************************/ DataProcessing(); INT_flag = true; } /* ------------------------------ ↑↑↑ ここまでメインループ ↑↑↑ ------------------------------ */ } /** データ処理関数 * */ void DataProcessing() { static float R_11; // 回転行列(1,1)成分 static float R_12; // 回転行列(1,2)成分 static float r_cos; // ロール角のcos値 static float r_sin; // ロール角のsin値 static float p_cos; // ピッチ角のcos値 static float p_sin; // ピッチ角のsin値 // 参照座標系の基底を求める r_u = g; r_f = geomag.GetPerpCompTo(g).Normalize(); r_l = Cross(r_u, r_f); // 回転行列を経由してオイラー角を求める // オイラー角はヨー・ピッチ・ロールの順に回転したものとする // 各オイラー角を求めるのに回転行列の全成分は要らないので、一部だけ計算する。 R_11 = r_f * b_f; // 回転行列の(1,1)成分 R_12 = r_f * b_l; // 回転行列の(1,2)成分 yaw = atan2(-R_12, R_11) * RAD_TO_DEG + MAG_DECLINATION; r_cos = g.GetPerpCompTo(b_f).Normalize() * b_u; r_sin = Cross(g.GetPerpCompTo(b_f).Normalize(), b_u) * b_f; if(r_sin > 0.0f) { roll = acos(r_cos) * RAD_TO_DEG; } else { roll = -acos(r_cos) * RAD_TO_DEG; } p_cos = g.GetPerpCompTo(b_l).Normalize() * b_u; p_sin = Cross(g.GetPerpCompTo(b_l).Normalize(), b_u) * b_l; if(p_sin > 0.0f) { pitch = acos(p_cos) * RAD_TO_DEG; } else { pitch = -acos(p_cos) * RAD_TO_DEG; } if(yaw > 180.0f) yaw -= 360.0f; // ヨー角を[-π, π]に補正 else if(yaw < -180.0f) yaw += 360.0f; // ヨー角を[-π, π]に補正 } /** 各種センサーの初期化を行う関数 * */ void SensorsInit() { if(!mpu.init()) error("mpu6050 Initialize Error !!"); // mpu6050初期化 if(!hmc.init()) error("hmc5883l Initialize Error !!"); // hmc5883l初期化 //重力ベクトルの初期化 raw_g.SetComp(1, 0.0f); raw_g.SetComp(2, 0.0f); raw_g.SetComp(3, 1.0f); // 機体に固定されたベクトルの初期化 b_f.SetComp(1, 0.0f); b_f.SetComp(2, 0.0f); b_f.SetComp(3, -1.0f); b_u.SetComp(1, 1.0f); b_u.SetComp(2, 0.0f); b_u.SetComp(3, 0.0f); b_l = Cross(b_u, b_f); // 目標座標をベクトルに代入 target_p.SetComp(1, target_x * DEG_TO_RAD); target_p.SetComp(2, target_y * DEG_TO_RAD); } void KalmanInit() { // 重力 { // 誤差共分散行列の値を決める(対角成分のみ) float alpha_R2 = 0.002f; float alpha_Q2 = 0.5f; R2 *= alpha_R2; Q2 *= alpha_Q2; // 観測方程式のヤコビアンの値を設定(時間変化無し) float h2[15] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }; H2.SetComps(h2); } // 地磁気 { // 誤差共分散行列の値を決める(対角成分のみ) float alpha_R1 = 0.002f; float alpha_Q1 = 0.5f; R1 *= alpha_R1; Q1 *= alpha_Q1; // 観測方程式のヤコビアンの値を設定(時間変化無し) float h1[21] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f }; H1.SetComps(h1); } } /** カルマンフィルタの更新を行う関数 * */ void KalmanUpdate() { // 重力 { // ヤコビアンの更新 float f2[25] = { 1.0f, (raw_gyro.GetComp(3) - post_x1.GetComp(7))*dt, -(raw_gyro.GetComp(2) - post_x2.GetComp(5))*dt, 0.0f, post_x2.GetComp(3)*dt, -(raw_gyro.GetComp(3) - post_x1.GetComp(7))*dt, 1.0f, (raw_gyro.GetComp(1) - post_x2.GetComp(4))*dt, -post_x2.GetComp(3)*dt, 0.0f, (raw_gyro.GetComp(2) - post_x2.GetComp(5))*dt, -(raw_gyro.GetComp(1) - post_x2.GetComp(4))*dt, 1.0f, post_x2.GetComp(2)*dt, -post_x2.GetComp(1)*dt, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; F2.SetComps(f2); // 事前推定値の更新 //pri_x2 = F2 * post_x2; float pri_x2_vals[5] = { post_x2.GetComp(1) + post_x2.GetComp(2) * (raw_gyro.GetComp(3) - post_x1.GetComp(7)) * dt - post_x2.GetComp(3) * (raw_gyro.GetComp(2) - post_x2.GetComp(5)) * dt, post_x2.GetComp(2) + post_x2.GetComp(3) * (raw_gyro.GetComp(1) - post_x2.GetComp(4)) * dt - post_x2.GetComp(1) * (raw_gyro.GetComp(3) - post_x1.GetComp(7)) * dt, post_x2.GetComp(3) + post_x2.GetComp(1) * (raw_gyro.GetComp(2) - post_x2.GetComp(5)) * dt - post_x2.GetComp(2) * (raw_gyro.GetComp(1) - post_x2.GetComp(4)) * dt, post_x2.GetComp(4), post_x2.GetComp(5) }; pri_x2.SetComps(pri_x2_vals); // 事前誤差分散行列の更新 pri_P2 = F2 * post_P2 * F2.Transpose() + R2; // カルマンゲインの計算 S2 = Q2 + H2 * pri_P2 * H2.Transpose(); static float det; if((det = S2.Inverse(S_inv2)) >= 0.0f) { pc.printf("E:%.3f\r\n", det); return; // 万が一、逆行列が見つからなかった場合は前回の推定値を保持して終了 } K2 = pri_P2 * H2.Transpose() * S_inv2; // 事後推定値の更新 post_x2 = pri_x2 + K2 * (raw_acc - H2 * pri_x2); // 事後誤差分散行列の更新 post_P2 = (I2 - K2 * H2) * pri_P2; } // 地磁気 { // ヤコビアンの更新 float f1[49] = { 1.0f, (raw_gyro.GetComp(3) - post_x1.GetComp(7))*dt, -(raw_gyro.GetComp(2) - post_x2.GetComp(5))*dt, 0.0f, 0.0f, 0.0f, -post_x1.GetComp(2) * dt, -(raw_gyro.GetComp(3) - post_x1.GetComp(7))*dt, 1.0f, (raw_gyro.GetComp(1) - post_x2.GetComp(4))*dt, 0.0f, 0.0f, 0.0f, post_x1.GetComp(1) * dt, (raw_gyro.GetComp(2) - post_x2.GetComp(5))*dt, -(raw_gyro.GetComp(1) - post_x2.GetComp(4))*dt, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; F1.SetComps(f1); // 事前推定値の更新 //pri_x1 = F1 * post_x1; float pri_x1_vals[7] = { post_x1.GetComp(1) + post_x1.GetComp(2) * (raw_gyro.GetComp(3) - post_x1.GetComp(7)) * dt - post_x1.GetComp(3) * (raw_gyro.GetComp(2) - post_x2.GetComp(5)) * dt, post_x1.GetComp(2) + post_x1.GetComp(3) * (raw_gyro.GetComp(1) - post_x2.GetComp(4)) * dt - post_x1.GetComp(1) * (raw_gyro.GetComp(3) - post_x1.GetComp(7)) * dt, post_x1.GetComp(3) + post_x1.GetComp(1) * (raw_gyro.GetComp(2) - post_x2.GetComp(5)) * dt - post_x1.GetComp(2) * (raw_gyro.GetComp(1) - post_x2.GetComp(4)) * dt, post_x1.GetComp(4), post_x1.GetComp(5), post_x1.GetComp(6), post_x1.GetComp(7) }; pri_x1.SetComps(pri_x1_vals); // 事前誤差分散行列の更新 pri_P1 = F1 * post_P1 * F1.Transpose() + R1; // カルマンゲインの計算 S1 = Q1 + H1 * pri_P1 * H1.Transpose(); static float det; if((det = S1.Inverse(S_inv1)) >= 0.0f) { pc.printf("E:%.3f\r\n", det); return; // 万が一、逆行列が見つからなかった場合は前回の推定値を保持して終了 } K1 = pri_P1 * H1.Transpose() * S_inv1; // 事後推定値の更新 post_x1 = pri_x1 + K1 * (raw_geomag - H1 * pri_x1); // 事後誤差分散行列の更新 post_P1 = (I1 - K1 * H1) * pri_P1; } } /* ------------------------------ 割り込み関数 ------------------------------ */ /** データ取得&更新関数 * */ void DataUpdate() { // センサーの値を更新 mpu.read(); hmc.read(); for(int i=0; i<3; i++) { raw_acc.SetComp(i+1, (float)mpu.data.value.acc[i] * ACC_LSB_TO_G); raw_gyro.SetComp(i+1, (float)mpu.data.value.gyro[i] * GYRO_LSB_TO_DEG * DEG_TO_RAD); raw_geomag.SetComp(i+1, (float)hmc.data.value[i] * MAG_LSB_TO_GAUSS); } Vector delta_g = Cross(raw_gyro, raw_g); // Δg = ω × g raw_g = 0.9f * (raw_g - delta_g * dt) + 0.1f * raw_acc.Normalize(); // 相補フィルタ raw_g = raw_g.Normalize(); KalmanUpdate(); if(INT_flag) { g = raw_g; for(int i=0; i<3; i++) { geomag.SetComp(i+1, post_x1.GetComp(i+1)); } acc = raw_acc; gyro = raw_gyro; press = raw_press; temp = raw_temp; vrt_acc = raw_acc * raw_g; } }