Created
March 1, 2025 08:29
-
-
Save GOROman/0eedc280ea3e5e6d8fb75709d6a5db93 to your computer and use it in GitHub Desktop.
#がいためIMU テスト2 Powered by Cline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 // Cline Prompt | |
@cxd5602pwbimu_main.c を変更し、6軸IMUから加速度バイアス、角速度バイアス、クォータ二オンによる姿勢と速度と慣性空間における位置を算出する拡張カルマンフィルタのCのコードを生成してください。予測更新、観測更新、カルマンゲインの更新、誤差共分散の推定と省略せず完全なコードをお願いします。姿勢に関してはクォータニオンをオイラー角に変換して出力するのも含めてください。 | |
#endif | |
/**************************************************************************** | |
* examples/cxd5602pwbimu/cxd5602pwbimu_main.c | |
* | |
* Copyright 2025 Sony Semiconductor Solutions Corporation | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in | |
* the documentation and/or other materials provided with the | |
* distribution. | |
* 3. Neither the name of Sony Semiconductor Solutions Corporation nor | |
* the names of its contributors may be used to endorse or promote | |
* products derived from this software without specific prior written | |
* permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | |
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
* | |
****************************************************************************/ | |
/**************************************************************************** | |
* Included Files | |
****************************************************************************/ | |
#include <nuttx/config.h> | |
#include <sys/ioctl.h> | |
#include <time.h> | |
#include <inttypes.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <poll.h> | |
#include <errno.h> | |
#include <math.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <nuttx/sensors/cxd5602pwbimu.h> | |
/**************************************************************************** | |
* Pre-processor Definitions | |
****************************************************************************/ | |
#define CXD5602PWBIMU_DEVPATH "/dev/imu0" | |
#define itemsof(a) (sizeof(a)/sizeof(a[0])) | |
/* 拡張カルマンフィルタの設定 */ | |
#define EKF_STATE_DIM 16 /* 状態ベクトルの次元: 位置(3), 速度(3), クォータニオン(4), 加速度バイアス(3), 角速度バイアス(3) */ | |
#define EKF_MEAS_DIM 6 /* 測定ベクトルの次元: 加速度(3), 角速度(3) */ | |
#define GRAVITY 9.80665f /* 重力加速度 (m/s^2) */ | |
#define DEG_TO_RAD (M_PI / 180.0f) /* 度からラジアンへの変換 */ | |
#define RAD_TO_DEG (180.0f / M_PI) /* ラジアンから度への変換 */ | |
/**************************************************************************** | |
* Private values | |
****************************************************************************/ | |
/* 拡張カルマンフィルタの構造体 */ | |
typedef struct { | |
/* 状態ベクトル */ | |
float x[EKF_STATE_DIM]; /* [px, py, pz, vx, vy, vz, q0, q1, q2, q3, ax_bias, ay_bias, az_bias, gx_bias, gy_bias, gz_bias] */ | |
/* 誤差共分散行列 */ | |
float P[EKF_STATE_DIM][EKF_STATE_DIM]; | |
/* プロセスノイズ共分散行列 */ | |
float Q[EKF_STATE_DIM][EKF_STATE_DIM]; | |
/* 測定ノイズ共分散行列 */ | |
float R[EKF_MEAS_DIM][EKF_MEAS_DIM]; | |
/* 状態遷移行列のヤコビアン */ | |
float F[EKF_STATE_DIM][EKF_STATE_DIM]; | |
/* 測定行列のヤコビアン */ | |
float H[EKF_MEAS_DIM][EKF_STATE_DIM]; | |
/* カルマンゲイン */ | |
float K[EKF_STATE_DIM][EKF_MEAS_DIM]; | |
/* 前回の時間 */ | |
float prev_time; | |
/* 初期化フラグ */ | |
int initialized; | |
} ekf_t; | |
/**************************************************************************** | |
* Private Functions | |
****************************************************************************/ | |
/* 行列演算関数 */ | |
/* 行列の加算: C = A + B */ | |
static void matrix_add(float *C, float *A, float *B, int rows, int cols) { | |
for (int i = 0; i < rows; i++) { | |
for (int j = 0; j < cols; j++) { | |
C[i*cols + j] = A[i*cols + j] + B[i*cols + j]; | |
} | |
} | |
} | |
/* 行列の減算: C = A - B */ | |
static void matrix_sub(float *C, float *A, float *B, int rows, int cols) { | |
for (int i = 0; i < rows; i++) { | |
for (int j = 0; j < cols; j++) { | |
C[i*cols + j] = A[i*cols + j] - B[i*cols + j]; | |
} | |
} | |
} | |
/* 行列の乗算: C = A * B */ | |
static void matrix_mul(float *C, float *A, float *B, int rowsA, int colsA, int colsB) { | |
for (int i = 0; i < rowsA; i++) { | |
for (int j = 0; j < colsB; j++) { | |
C[i*colsB + j] = 0.0f; | |
for (int k = 0; k < colsA; k++) { | |
C[i*colsB + j] += A[i*colsA + k] * B[k*colsB + j]; | |
} | |
} | |
} | |
} | |
/* 行列の転置: B = A^T */ | |
static void matrix_transpose(float *B, float *A, int rows, int cols) { | |
for (int i = 0; i < rows; i++) { | |
for (int j = 0; j < cols; j++) { | |
B[j*rows + i] = A[i*cols + j]; | |
} | |
} | |
} | |
/* 単位行列の生成 */ | |
static void matrix_identity(float *A, int n) { | |
memset(A, 0, n * n * sizeof(float)); | |
for (int i = 0; i < n; i++) { | |
A[i*n + i] = 1.0f; | |
} | |
} | |
/* クォータニオン関連の関数 */ | |
/* クォータニオンの正規化 */ | |
static void quaternion_normalize(float q[4]) { | |
float norm = sqrtf(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); | |
if (norm > 1e-10f) { | |
q[0] /= norm; | |
q[1] /= norm; | |
q[2] /= norm; | |
q[3] /= norm; | |
} | |
} | |
/* クォータニオンからオイラー角への変換 (ZYX順序) */ | |
static void quaternion_to_euler(float q[4], float *roll, float *pitch, float *yaw) { | |
// ZYX順序のオイラー角に変換 | |
*roll = atan2f(2.0f * (q[0]*q[1] + q[2]*q[3]), 1.0f - 2.0f * (q[1]*q[1] + q[2]*q[2])); | |
*pitch = asinf(2.0f * (q[0]*q[2] - q[3]*q[1])); | |
*yaw = atan2f(2.0f * (q[0]*q[3] + q[1]*q[2]), 1.0f - 2.0f * (q[2]*q[2] + q[3]*q[3])); | |
// ラジアンから度に変換 | |
*roll *= RAD_TO_DEG; | |
*pitch *= RAD_TO_DEG; | |
*yaw *= RAD_TO_DEG; | |
} | |
/* クォータニオンの積 q = q1 * q2 */ | |
static void quaternion_multiply(float q[4], float q1[4], float q2[4]) { | |
q[0] = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3]; | |
q[1] = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2]; | |
q[2] = q1[0]*q2[2] - q1[1]*q2[3] + q1[2]*q2[0] + q1[3]*q2[1]; | |
q[3] = q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1] + q1[3]*q2[0]; | |
} | |
/* クォータニオンによる回転 v_rotated = q * v * q^(-1) */ | |
static void quaternion_rotate(float v_rotated[3], float q[4], float v[3]) { | |
float q_conj[4] = {q[0], -q[1], -q[2], -q[3]}; | |
float q_v[4] = {0, v[0], v[1], v[2]}; | |
float temp[4]; | |
float result[4]; | |
quaternion_multiply(temp, q, q_v); | |
quaternion_multiply(result, temp, q_conj); | |
v_rotated[0] = result[1]; | |
v_rotated[1] = result[2]; | |
v_rotated[2] = result[3]; | |
} | |
/* 拡張カルマンフィルタの関数 */ | |
/* EKFの初期化 */ | |
static void ekf_init(ekf_t *ekf, float accel[3], float gyro[3], float timestamp) { | |
// 状態ベクトルの初期化 | |
memset(ekf->x, 0, sizeof(ekf->x)); | |
// 初期姿勢の推定(重力方向から) | |
float accel_norm = sqrtf(accel[0]*accel[0] + accel[1]*accel[1] + accel[2]*accel[2]); | |
if (accel_norm > 1e-10f) { | |
// 重力方向を基準にした初期クォータニオンの計算 | |
float ax = accel[0] / accel_norm; | |
float ay = accel[1] / accel_norm; | |
float az = accel[2] / accel_norm; | |
// 重力ベクトルと鉛直方向(0,0,1)の間のクォータニオンを計算 | |
float v1[3] = {0, 0, 1}; // 鉛直方向 | |
float v2[3] = {ax, ay, az}; // 加速度方向(重力方向の逆) | |
// 外積を計算 | |
float cross[3] = { | |
v1[1]*v2[2] - v1[2]*v2[1], | |
v1[2]*v2[0] - v1[0]*v2[2], | |
v1[0]*v2[1] - v1[1]*v2[0] | |
}; | |
// 内積を計算 | |
float dot = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; | |
// クォータニオンを計算 | |
float cross_norm = sqrtf(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]); | |
if (cross_norm > 1e-10f) { | |
ekf->x[6] = sqrtf(0.5f * (1.0f + dot)); // q0 | |
float s = 0.5f / ekf->x[6]; | |
ekf->x[7] = s * cross[0]; // q1 | |
ekf->x[8] = s * cross[1]; // q2 | |
ekf->x[9] = s * cross[2]; // q3 | |
} else { | |
// 外積がゼロの場合(ベクトルが平行または反平行) | |
if (dot > 0) { | |
// ベクトルが平行の場合 | |
ekf->x[6] = 1.0f; | |
ekf->x[7] = 0.0f; | |
ekf->x[8] = 0.0f; | |
ekf->x[9] = 0.0f; | |
} else { | |
// ベクトルが反平行の場合 | |
ekf->x[6] = 0.0f; | |
ekf->x[7] = 1.0f; | |
ekf->x[8] = 0.0f; | |
ekf->x[9] = 0.0f; | |
} | |
} | |
} else { | |
// デフォルトの姿勢(単位クォータニオン) | |
ekf->x[6] = 1.0f; | |
ekf->x[7] = 0.0f; | |
ekf->x[8] = 0.0f; | |
ekf->x[9] = 0.0f; | |
} | |
// バイアスの初期値 | |
ekf->x[10] = 0.0f; // ax_bias | |
ekf->x[11] = 0.0f; // ay_bias | |
ekf->x[12] = 0.0f; // az_bias | |
ekf->x[13] = gyro[0]; // gx_bias | |
ekf->x[14] = gyro[1]; // gy_bias | |
ekf->x[15] = gyro[2]; // gz_bias | |
// 誤差共分散行列の初期化 | |
memset(ekf->P, 0, sizeof(ekf->P)); | |
for (int i = 0; i < EKF_STATE_DIM; i++) { | |
ekf->P[i][i] = 1.0f; // 対角要素を1に設定 | |
} | |
// プロセスノイズ共分散行列の初期化 | |
memset(ekf->Q, 0, sizeof(ekf->Q)); | |
// 位置のプロセスノイズ | |
ekf->Q[0][0] = 0.01f; | |
ekf->Q[1][1] = 0.01f; | |
ekf->Q[2][2] = 0.01f; | |
// 速度のプロセスノイズ | |
ekf->Q[3][3] = 0.1f; | |
ekf->Q[4][4] = 0.1f; | |
ekf->Q[5][5] = 0.1f; | |
// 姿勢のプロセスノイズ | |
ekf->Q[6][6] = 0.001f; | |
ekf->Q[7][7] = 0.001f; | |
ekf->Q[8][8] = 0.001f; | |
ekf->Q[9][9] = 0.001f; | |
// 加速度バイアスのプロセスノイズ | |
ekf->Q[10][10] = 0.0001f; | |
ekf->Q[11][11] = 0.0001f; | |
ekf->Q[12][12] = 0.0001f; | |
// 角速度バイアスのプロセスノイズ | |
ekf->Q[13][13] = 0.0001f; | |
ekf->Q[14][14] = 0.0001f; | |
ekf->Q[15][15] = 0.0001f; | |
// 測定ノイズ共分散行列の初期化 | |
memset(ekf->R, 0, sizeof(ekf->R)); | |
// 加速度の測定ノイズ | |
ekf->R[0][0] = 0.1f; | |
ekf->R[1][1] = 0.1f; | |
ekf->R[2][2] = 0.1f; | |
// 角速度の測定ノイズ | |
ekf->R[3][3] = 0.01f; | |
ekf->R[4][4] = 0.01f; | |
ekf->R[5][5] = 0.01f; | |
// 状態遷移行列と測定行列の初期化 | |
memset(ekf->F, 0, sizeof(ekf->F)); | |
memset(ekf->H, 0, sizeof(ekf->H)); | |
// カルマンゲインの初期化 | |
memset(ekf->K, 0, sizeof(ekf->K)); | |
// 時間の初期化 | |
ekf->prev_time = timestamp; | |
// 初期化完了 | |
ekf->initialized = 1; | |
} | |
/* 予測更新(時間更新)ステップ */ | |
static void ekf_predict(ekf_t *ekf, float gyro[3], float timestamp) { | |
// 時間差分の計算 | |
float dt = timestamp - ekf->prev_time; | |
if (dt <= 0.0f) { | |
return; // 時間が進んでいない場合は更新しない | |
} | |
// 状態ベクトルの取得 | |
float px = ekf->x[0]; | |
float py = ekf->x[1]; | |
float pz = ekf->x[2]; | |
float vx = ekf->x[3]; | |
float vy = ekf->x[4]; | |
float vz = ekf->x[5]; | |
float q0 = ekf->x[6]; | |
float q1 = ekf->x[7]; | |
float q2 = ekf->x[8]; | |
float q3 = ekf->x[9]; | |
float ax_bias = ekf->x[10]; | |
float ay_bias = ekf->x[11]; | |
float az_bias = ekf->x[12]; | |
float gx_bias = ekf->x[13]; | |
float gy_bias = ekf->x[14]; | |
float gz_bias = ekf->x[15]; | |
// バイアス補正した角速度 | |
float gx = gyro[0] - gx_bias; | |
float gy = gyro[1] - gy_bias; | |
float gz = gyro[2] - gz_bias; | |
// クォータニオンの更新(オイラー法) | |
float q_dot[4]; | |
q_dot[0] = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); | |
q_dot[1] = 0.5f * (q0 * gx + q2 * gz - q3 * gy); | |
q_dot[2] = 0.5f * (q0 * gy - q1 * gz + q3 * gx); | |
q_dot[3] = 0.5f * (q0 * gz + q1 * gy - q2 * gx); | |
float q_new[4]; | |
q_new[0] = q0 + q_dot[0] * dt; | |
q_new[1] = q1 + q_dot[1] * dt; | |
q_new[2] = q2 + q_dot[2] * dt; | |
q_new[3] = q3 + q_dot[3] * dt; | |
// クォータニオンの正規化 | |
quaternion_normalize(q_new); | |
// 重力ベクトルをボディフレームに変換 | |
float gravity_body[3]; | |
float gravity_world[3] = {0, 0, GRAVITY}; | |
float q_inv[4] = {q_new[0], -q_new[1], -q_new[2], -q_new[3]}; | |
quaternion_rotate(gravity_body, q_inv, gravity_world); | |
// 加速度バイアスを考慮した加速度 | |
float ax = -gravity_body[0] - ax_bias; | |
float ay = -gravity_body[1] - ay_bias; | |
float az = -gravity_body[2] - az_bias; | |
// 位置と速度の更新(オイラー法) | |
float px_new = px + vx * dt + 0.5f * ax * dt * dt; | |
float py_new = py + vy * dt + 0.5f * ay * dt * dt; | |
float pz_new = pz + vz * dt + 0.5f * az * dt * dt; | |
float vx_new = vx + ax * dt; | |
float vy_new = vy + ay * dt; | |
float vz_new = vz + az * dt; | |
// 状態ベクトルの更新 | |
ekf->x[0] = px_new; | |
ekf->x[1] = py_new; | |
ekf->x[2] = pz_new; | |
ekf->x[3] = vx_new; | |
ekf->x[4] = vy_new; | |
ekf->x[5] = vz_new; | |
ekf->x[6] = q_new[0]; | |
ekf->x[7] = q_new[1]; | |
ekf->x[8] = q_new[2]; | |
ekf->x[9] = q_new[3]; | |
// バイアスはランダムウォークモデルなので変化なし | |
// 状態遷移行列のヤコビアンの計算 | |
memset(ekf->F, 0, sizeof(ekf->F)); | |
// 位置の微分は速度 | |
ekf->F[0][3] = 1.0f; | |
ekf->F[1][4] = 1.0f; | |
ekf->F[2][5] = 1.0f; | |
// 速度の微分は加速度(クォータニオンと加速度バイアスに依存) | |
// これは複雑なので、数値的に近似する | |
// クォータニオンの微分は角速度に依存 | |
ekf->F[6][6] = 1.0f; | |
ekf->F[6][7] = -0.5f * gx * dt; | |
ekf->F[6][8] = -0.5f * gy * dt; | |
ekf->F[6][9] = -0.5f * gz * dt; | |
ekf->F[7][6] = 0.5f * gx * dt; | |
ekf->F[7][7] = 1.0f; | |
ekf->F[7][8] = 0.5f * gz * dt; | |
ekf->F[7][9] = -0.5f * gy * dt; | |
ekf->F[8][6] = 0.5f * gy * dt; | |
ekf->F[8][7] = -0.5f * gz * dt; | |
ekf->F[8][8] = 1.0f; | |
ekf->F[8][9] = 0.5f * gx * dt; | |
ekf->F[9][6] = 0.5f * gz * dt; | |
ekf->F[9][7] = 0.5f * gy * dt; | |
ekf->F[9][8] = -0.5f * gx * dt; | |
ekf->F[9][9] = 1.0f; | |
// クォータニオンの微分は角速度バイアスにも依存 | |
ekf->F[6][13] = 0.5f * q1 * dt; | |
ekf->F[6][14] = 0.5f * q2 * dt; | |
ekf->F[6][15] = 0.5f * q3 * dt; | |
ekf->F[7][13] = -0.5f * q0 * dt; | |
ekf->F[7][14] = 0.5f * q3 * dt; | |
ekf->F[7][15] = -0.5f * q2 * dt; | |
ekf->F[8][13] = -0.5f * q3 * dt; | |
ekf->F[8][14] = -0.5f * q0 * dt; | |
ekf->F[8][15] = 0.5f * q1 * dt; | |
ekf->F[9][13] = 0.5f * q2 * dt; | |
ekf->F[9][14] = -0.5f * q1 * dt; | |
ekf->F[9][15] = -0.5f * q0 * dt; | |
// バイアスの状態遷移(単位行列) | |
for (int i = 10; i < EKF_STATE_DIM; i++) { | |
ekf->F[i][i] = 1.0f; | |
} | |
// 残りの対角要素を1に設定 | |
for (int i = 0; i < 6; i++) { | |
ekf->F[i][i] = 1.0f; | |
} | |
// 誤差共分散行列の更新: P = F*P*F^T + Q | |
float F_P[EKF_STATE_DIM][EKF_STATE_DIM]; | |
float F_P_FT[EKF_STATE_DIM][EKF_STATE_DIM]; | |
float FT[EKF_STATE_DIM][EKF_STATE_DIM]; | |
// F*P の計算 | |
matrix_mul((float*)F_P, (float*)ekf->F, (float*)ekf->P, EKF_STATE_DIM, EKF_STATE_DIM, EKF_STATE_DIM); | |
// F の転置を計算 | |
matrix_transpose((float*)FT, (float*)ekf->F, EKF_STATE_DIM, EKF_STATE_DIM); | |
// (F*P) * F^T の計算 | |
matrix_mul((float*)F_P_FT, (float*)F_P, (float*)FT, EKF_STATE_DIM, EKF_STATE_DIM, EKF_STATE_DIM); | |
// P = F*P*F^T + Q | |
matrix_add((float*)ekf->P, (float*)F_P_FT, (float*)ekf->Q, EKF_STATE_DIM, EKF_STATE_DIM); | |
// 時間の更新 | |
ekf->prev_time = timestamp; | |
} | |
/* 観測更新(測定更新)ステップ */ | |
static void ekf_update(ekf_t *ekf, float accel[3], float gyro[3]) { | |
// 状態ベクトルの取得 | |
float q0 = ekf->x[6]; | |
float q1 = ekf->x[7]; | |
float q2 = ekf->x[8]; | |
float q3 = ekf->x[9]; | |
float ax_bias = ekf->x[10]; | |
float ay_bias = ekf->x[11]; | |
float az_bias = ekf->x[12]; | |
float gx_bias = ekf->x[13]; | |
float gy_bias = ekf->x[14]; | |
float gz_bias = ekf->x[15]; | |
// 重力ベクトルをボディフレームに変換 | |
float gravity_body[3]; | |
float gravity_world[3] = {0, 0, GRAVITY}; | |
float q_inv[4] = {q0, -q1, -q2, -q3}; | |
quaternion_rotate(gravity_body, q_inv, gravity_world); | |
// 予測された測定値 | |
float h[EKF_MEAS_DIM]; | |
h[0] = gravity_body[0] + ax_bias; // 予測される加速度x | |
h[1] = gravity_body[1] + ay_bias; // 予測される加速度y | |
h[2] = gravity_body[2] + az_bias; // 予測される加速度z | |
h[3] = gx_bias; // 予測される角速度x | |
h[4] = gy_bias; // 予測される角速度y | |
h[5] = gz_bias; // 予測される角速度z | |
// 実際の測定値 | |
float z[EKF_MEAS_DIM]; | |
z[0] = accel[0]; | |
z[1] = accel[1]; | |
z[2] = accel[2]; | |
z[3] = gyro[0]; | |
z[4] = gyro[1]; | |
z[5] = gyro[2]; | |
// 測定残差(イノベーション) | |
float y[EKF_MEAS_DIM]; | |
for (int i = 0; i < EKF_MEAS_DIM; i++) { | |
y[i] = z[i] - h[i]; | |
} | |
// 測定行列のヤコビアンの計算 | |
memset(ekf->H, 0, sizeof(ekf->H)); | |
// 加速度の測定は重力とバイアスに依存 | |
// 重力の測定は姿勢(クォータニオン)に依存 | |
// これは複雑なので、簡略化した近似を使用 | |
// 加速度バイアスに対する加速度の偏微分 | |
ekf->H[0][10] = 1.0f; | |
ekf->H[1][11] = 1.0f; | |
ekf->H[2][12] = 1.0f; | |
// 角速度バイアスに対する角速度の偏微分 | |
ekf->H[3][13] = 1.0f; | |
ekf->H[4][14] = 1.0f; | |
ekf->H[5][15] = 1.0f; | |
// クォータニオンに対する加速度の偏微分 | |
// これは複雑なので、数値的に近似する | |
float q_eps[4]; | |
float eps = 1e-4f; // 微小変化量 | |
// q0に対する偏微分 | |
q_eps[0] = q0 + eps; | |
q_eps[1] = q1; | |
q_eps[2] = q2; | |
q_eps[3] = q3; | |
quaternion_normalize(q_eps); | |
float gravity_body_eps[3]; | |
float q_eps_inv[4] = {q_eps[0], -q_eps[1], -q_eps[2], -q_eps[3]}; | |
quaternion_rotate(gravity_body_eps, q_eps_inv, gravity_world); | |
ekf->H[0][6] = (gravity_body_eps[0] - gravity_body[0]) / eps; | |
ekf->H[1][6] = (gravity_body_eps[1] - gravity_body[1]) / eps; | |
ekf->H[2][6] = (gravity_body_eps[2] - gravity_body[2]) / eps; | |
// q1に対する偏微分 | |
q_eps[0] = q0; | |
q_eps[1] = q1 + eps; | |
q_eps[2] = q2; | |
q_eps[3] = q3; | |
quaternion_normalize(q_eps); | |
q_eps_inv[0] = q_eps[0]; | |
q_eps_inv[1] = -q_eps[1]; | |
q_eps_inv[2] = -q_eps[2]; | |
q_eps_inv[3] = -q_eps[3]; | |
quaternion_rotate(gravity_body_eps, q_eps_inv, gravity_world); | |
ekf->H[0][7] = (gravity_body_eps[0] - gravity_body[0]) / eps; | |
ekf->H[1][7] = (gravity_body_eps[1] - gravity_body[1]) / eps; | |
ekf->H[2][7] = (gravity_body_eps[2] - gravity_body[2]) / eps; | |
// q2に対する偏微分 | |
q_eps[0] = q0; | |
q_eps[1] = q1; | |
q_eps[2] = q2 + eps; | |
q_eps[3] = q3; | |
quaternion_normalize(q_eps); | |
q_eps_inv[0] = q_eps[0]; | |
q_eps_inv[1] = -q_eps[1]; | |
q_eps_inv[2] = -q_eps[2]; | |
q_eps_inv[3] = -q_eps[3]; | |
quaternion_rotate(gravity_body_eps, q_eps_inv, gravity_world); | |
ekf->H[0][8] = (gravity_body_eps[0] - gravity_body[0]) / eps; | |
ekf->H[1][8] = (gravity_body_eps[1] - gravity_body[1]) / eps; | |
ekf->H[2][8] = (gravity_body_eps[2] - gravity_body[2]) / eps; | |
// q3に対する偏微分 | |
q_eps[0] = q0; | |
q_eps[1] = q1; | |
q_eps[2] = q2; | |
q_eps[3] = q3 + eps; | |
quaternion_normalize(q_eps); | |
q_eps_inv[0] = q_eps[0]; | |
q_eps_inv[1] = -q_eps[1]; | |
q_eps_inv[2] = -q_eps[2]; | |
q_eps_inv[3] = -q_eps[3]; | |
quaternion_rotate(gravity_body_eps, q_eps_inv, gravity_world); | |
ekf->H[0][9] = (gravity_body_eps[0] - gravity_body[0]) / eps; | |
ekf->H[1][9] = (gravity_body_eps[1] - gravity_body[1]) / eps; | |
ekf->H[2][9] = (gravity_body_eps[2] - gravity_body[2]) / eps; | |
// イノベーション共分散行列の計算: S = H*P*H^T + R | |
float H_P[EKF_MEAS_DIM][EKF_STATE_DIM]; | |
float H_P_HT[EKF_MEAS_DIM][EKF_MEAS_DIM]; | |
float HT[EKF_STATE_DIM][EKF_MEAS_DIM]; | |
float S[EKF_MEAS_DIM][EKF_MEAS_DIM]; | |
// H*P の計算 | |
matrix_mul((float*)H_P, (float*)ekf->H, (float*)ekf->P, EKF_MEAS_DIM, EKF_STATE_DIM, EKF_STATE_DIM); | |
// H の転置を計算 | |
matrix_transpose((float*)HT, (float*)ekf->H, EKF_MEAS_DIM, EKF_STATE_DIM); | |
// (H*P) * H^T の計算 | |
matrix_mul((float*)H_P_HT, (float*)H_P, (float*)HT, EKF_MEAS_DIM, EKF_STATE_DIM, EKF_MEAS_DIM); | |
// S = H*P*H^T + R | |
matrix_add((float*)S, (float*)H_P_HT, (float*)ekf->R, EKF_MEAS_DIM, EKF_MEAS_DIM); | |
// カルマンゲインの計算: K = P*H^T*S^(-1) | |
// S^(-1) の計算(簡略化のため、対角行列と仮定) | |
float S_inv[EKF_MEAS_DIM][EKF_MEAS_DIM]; | |
memset(S_inv, 0, sizeof(S_inv)); | |
for (int i = 0; i < EKF_MEAS_DIM; i++) { | |
if (fabs(S[i][i]) > 1e-10f) { | |
S_inv[i][i] = 1.0f / S[i][i]; | |
} else { | |
S_inv[i][i] = 0.0f; | |
} | |
} | |
// P*H^T の計算 | |
float P_HT[EKF_STATE_DIM][EKF_MEAS_DIM]; | |
matrix_mul((float*)P_HT, (float*)ekf->P, (float*)HT, EKF_STATE_DIM, EKF_STATE_DIM, EKF_MEAS_DIM); | |
// K = P*H^T*S^(-1) の計算 | |
matrix_mul((float*)ekf->K, (float*)P_HT, (float*)S_inv, EKF_STATE_DIM, EKF_MEAS_DIM, EKF_MEAS_DIM); | |
// 状態ベクトルの更新: x = x + K*y | |
float K_y[EKF_STATE_DIM]; | |
for (int i = 0; i < EKF_STATE_DIM; i++) { | |
K_y[i] = 0.0f; | |
for (int j = 0; j < EKF_MEAS_DIM; j++) { | |
K_y[i] += ekf->K[i][j] * y[j]; | |
} | |
ekf->x[i] += K_y[i]; | |
} | |
// クォータニオンの正規化 | |
float q[4] = {ekf->x[6], ekf->x[7], ekf->x[8], ekf->x[9]}; | |
quaternion_normalize(q); | |
ekf->x[6] = q[0]; | |
ekf->x[7] = q[1]; | |
ekf->x[8] = q[2]; | |
ekf->x[9] = q[3]; | |
// 誤差共分散行列の更新: P = (I - K*H)*P | |
float K_H[EKF_STATE_DIM][EKF_STATE_DIM]; | |
float I_KH[EKF_STATE_DIM][EKF_STATE_DIM]; | |
float I[EKF_STATE_DIM][EKF_STATE_DIM]; | |
float P_new[EKF_STATE_DIM][EKF_STATE_DIM]; | |
// K*H の計算 | |
matrix_mul((float*)K_H, (float*)ekf->K, (float*)ekf->H, EKF_STATE_DIM, EKF_MEAS_DIM, EKF_STATE_DIM); | |
// 単位行列の生成 | |
matrix_identity((float*)I, EKF_STATE_DIM); | |
// I - K*H の計算 | |
matrix_sub((float*)I_KH, (float*)I, (float*)K_H, EKF_STATE_DIM, EKF_STATE_DIM); | |
// (I - K*H)*P の計算 | |
matrix_mul((float*)P_new, (float*)I_KH, (float*)ekf->P, EKF_STATE_DIM, EKF_STATE_DIM, EKF_STATE_DIM); | |
// 誤差共分散行列の更新 | |
memcpy(ekf->P, P_new, sizeof(P_new)); | |
} | |
/**************************************************************************** | |
* Private Functions | |
****************************************************************************/ | |
static int start_sensing(int fd, int rate, int adrange, int gdrange, | |
int nfifos) | |
{ | |
cxd5602pwbimu_range_t range; | |
int ret; | |
/* | |
* Set sampling rate. Available values (Hz) are below. | |
* | |
* 15 (default), 30, 60, 120, 240, 480, 960, 1920 | |
*/ | |
ret = ioctl(fd, SNIOC_SSAMPRATE, rate); | |
if (ret) | |
{ | |
printf("ERROR: Set sampling rate failed. %d\n", errno); | |
return 1; | |
} | |
/* | |
* Set dynamic ranges for accelerometer and gyroscope. | |
* Available values are below. | |
* | |
* accel: 2 (default), 4, 8, 16 | |
* gyro: 125 (default), 250, 500, 1000, 2000, 4000 | |
*/ | |
range.accel = adrange; | |
range.gyro = gdrange; | |
ret = ioctl(fd, SNIOC_SDRANGE, (unsigned long)(uintptr_t)&range); | |
if (ret) | |
{ | |
printf("ERROR: Set dynamic range failed. %d\n", errno); | |
return 1; | |
} | |
/* | |
* Set hardware FIFO threshold. | |
* Increasing this value will reduce the frequency with which data is | |
* received. | |
*/ | |
ret = ioctl(fd, SNIOC_SFIFOTHRESH, nfifos); | |
if (ret) | |
{ | |
printf("ERROR: Set sampling rate failed. %d\n", errno); | |
return 1; | |
} | |
/* | |
* Start sensing, user can not change the all of configurations. | |
*/ | |
ret = ioctl(fd, SNIOC_ENABLE, 1); | |
if (ret) | |
{ | |
printf("ERROR: Enable failed. %d\n", errno); | |
return 1; | |
} | |
return 0; | |
} | |
/**************************************************************************** | |
* Public Functions | |
****************************************************************************/ | |
/**************************************************************************** | |
* sensor_main | |
****************************************************************************/ | |
int main(int argc, FAR char *argv[]) | |
{ | |
int fd; | |
int ret; | |
struct pollfd fds[1]; | |
struct timespec start, now, delta; | |
cxd5602pwbimu_data_t *outbuf = NULL; | |
cxd5602pwbimu_data_t *p = NULL; | |
cxd5602pwbimu_data_t *last; | |
ekf_t ekf; // 拡張カルマンフィルタのインスタンス | |
int ekf_initialized = 0; // EKFの初期化フラグ | |
/* Sensing parameters, see start sensing function. */ | |
const int samplerate = 1920; | |
const int adrange = 2; | |
const int gdrange = 125; | |
const int nfifos = 1; | |
fd = open(CXD5602PWBIMU_DEVPATH, O_RDONLY); | |
if (fd < 0) | |
{ | |
printf("ERROR: Device %s open failure. %d\n", CXD5602PWBIMU_DEVPATH, errno); | |
return 1; | |
} | |
outbuf = (cxd5602pwbimu_data_t *)malloc(sizeof(cxd5602pwbimu_data_t) * samplerate); | |
if (outbuf == NULL) | |
{ | |
printf("ERROR: Output buffer allocation failed.\n"); | |
return 1; | |
} | |
last = outbuf + samplerate; | |
fds[0].fd = fd; | |
fds[0].events = POLLIN; | |
ret = start_sensing(fd, samplerate, adrange, gdrange, nfifos); | |
if (ret) | |
{ | |
close(fd); | |
return ret; | |
} | |
memset(&now, 0, sizeof(now)); | |
for (p = outbuf; p < last; p++) | |
{ | |
ret = poll(fds, 1, 1000); | |
if (ret < 0) | |
{ | |
if (errno != EINTR) | |
{ | |
printf("ERROR: poll failed. %d\n", errno); | |
} | |
break; | |
} | |
if (ret == 0) | |
{ | |
printf("Timeout!\n"); | |
} | |
if (p == outbuf) | |
{ | |
/* To remove first sensing delay, start time measurement from | |
* the first captured data. | |
*/ | |
clock_gettime(CLOCK_MONOTONIC, &start); | |
} | |
if (fds[0].revents & POLLIN) | |
{ | |
ret = read(fd, p, sizeof(*p)); | |
if (ret != sizeof(*p)) | |
{ | |
printf("ERROR: read size mismatch! %d\n", ret); | |
} | |
} | |
clock_gettime(CLOCK_MONOTONIC, &now); | |
clock_timespec_subtract(&now, &start, &delta); | |
if (delta.tv_sec >= 1) | |
{ | |
break; | |
} | |
} | |
/* Save the latest written position */ | |
last = p; | |
close(fd); | |
/* Output buffered sensing data and run EKF */ | |
for (p = outbuf; p < last; p++) | |
{ | |
float timestamp = p->timestamp / 19200000.0f; | |
float accel[3] = {p->ax, p->ay, p->az}; | |
float gyro[3] = {p->gx, p->gy, p->gz}; | |
// EKFの初期化 | |
if (!ekf_initialized) { | |
ekf_init(&ekf, accel, gyro, timestamp); | |
ekf_initialized = 1; | |
} else { | |
// EKFの予測ステップ | |
ekf_predict(&ekf, gyro, timestamp); | |
// EKFの更新ステップ | |
ekf_update(&ekf, accel, gyro); | |
} | |
// クォータニオンからオイラー角への変換 | |
float roll, pitch, yaw; | |
float q[4] = {ekf.x[6], ekf.x[7], ekf.x[8], ekf.x[9]}; | |
quaternion_to_euler(q, &roll, &pitch, &yaw); | |
// 結果の出力 | |
printf("%.6f,%.6f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f\n", | |
timestamp, | |
p->temp, | |
p->gx, p->gy, p->gz, | |
p->ax, p->ay, p->az, | |
ekf.x[0], ekf.x[1], ekf.x[2], // 位置 | |
ekf.x[3], ekf.x[4], ekf.x[5], // 速度 | |
ekf.x[6], ekf.x[7], ekf.x[8], ekf.x[9], // クォータニオン | |
roll, pitch, yaw, // オイラー角 | |
ekf.x[10], ekf.x[11], ekf.x[12], // 加速度バイアス | |
ekf.x[13], ekf.x[14], ekf.x[15]); // 角速度バイアス | |
} | |
clock_timespec_subtract(&now, &start, &delta); | |
printf("Elapsed %ld.%09ld seconds\n", delta.tv_sec, delta.tv_nsec); | |
printf("%d samples captured\n", last - outbuf); | |
printf("Finished.\n"); | |
free(outbuf); | |
return 0; | |
} |
Author
GOROman
commented
Mar 1, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment