Files
tc264smart/code/attitude_solution.c
32343 843447835d Initial commit: TC264 smart car project with IPS200 menu system
- IPS200 display menu with real-time status and parameter settings
- Camera image processing and line tracking
- IMU attitude solution and PID control
- WiFi SPI oscilloscope data transmission
2026-05-10 21:38:49 +08:00

271 lines
9.3 KiB
C

/*********************************************************************************************************************
* TC264 智能车 - 姿态解算模块 (attitude_solution.c)
*
* 基于 Mahony 互补滤波算法:
* 陀螺仪积分得到姿态角,加速度计修正陀螺零漂。
* 使用四元数表示姿态,避免万向节死锁。
* 移植自 RT1064 开源库中 ICM_AHRSupdate 实现。
*
* 调用周期: 5ms (在 isr.c 的 CCU60_CH0 中断中调用, 陀螺仪校准完成后)
*
* IMU660RB 转换因子 (2000DPS / 8G):
* 陀螺仪: raw / 14.3 = °/s, * π/180 = rad/s
* 加速度: raw / 4098 = G (m/s^2单位)
*********************************************************************************************************************/
#include "zf_common_typedef.h"
#include "zf_driver_delay.h"
#include "zf_device_imu660rb.h"
#include "attitude_solution.h"
/* ---------------------------------------------------------------------------
* 常数与可调参数 (匹配 RT1064 参考实现)
* --------------------------------------------------------------------------- */
#define DELTA_T 0.005f /* 姿态更新周期 (秒), 匹配 5ms ISR */
#define DEG_TO_RAD 0.01745329252f /* π / 180 */
#define RAD_TO_DEG 57.2957795131f /* 180 / π */
#define KP 0.17f /* 加速度计收敛比例增益 (同 RT1064) */
#define KI 0.004f /* 陀螺仪零漂积分增益 (同 RT1064) */
#define ACC_ALPHA 0.3f /* 加速度低通滤波系数 (同 RT1064) */
/* ---------------------------------------------------------------------------
* 全局姿态角 (外部引用)
* --------------------------------------------------------------------------- */
euler_param_t eulerAngle;
/* ---------------------------------------------------------------------------
* 静态变量 — 四元数 & 误差积分 & 加速度滤波状态
* --------------------------------------------------------------------------- */
static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;
static float i_ex = 0.0f, i_ey = 0.0f, i_ez = 0.0f;
static float acc_fx = 0.0f, acc_fy = 0.0f, acc_fz = 0.0f;
/* ---------------------------------------------------------------------------
* 快速反平方根 (Quake III 算法, 两次牛顿迭代)
* --------------------------------------------------------------------------- */
static float fast_rsqrt(float x)
{
float halfx = 0.5f * x;
float y = x;
long i = *(long *)&y;
i = 0x5f3759df - (i >> 1);
y = *(float *)&i;
y = y * (1.5f - halfx * y * y);
y = y * (1.5f - halfx * y * y);
return y;
}
/* ---------------------------------------------------------------------------
* 姿态解算初始化
* 多次读取加速度计取平均, 用加速度计计算初始 pitch/roll, 设置正确的初始四元数
* --------------------------------------------------------------------------- */
void attitude_init(void)
{
float ax_sum, ay_sum, az_sum;
float ax, ay, az;
float norm, mag;
float roll_init, pitch_init;
float cr, sr, cp, sp;
int i;
/* 传感器上电后需要时间稳定, 丢弃前若干次读数 */
for(i = 0; i < 10; i++)
{
imu660rb_get_acc();
system_delay_ms(5);
}
/* 多次采样取平均, 提高初始姿态估计的稳健性 */
ax_sum = 0.0f; ay_sum = 0.0f; az_sum = 0.0f;
for(i = 0; i < 20; i++)
{
imu660rb_get_acc();
ax_sum += imu660rb_acc_transition(imu660rb_acc_x);
ay_sum += imu660rb_acc_transition(imu660rb_acc_y);
az_sum += imu660rb_acc_transition(imu660rb_acc_z);
system_delay_ms(5);
}
ax = ax_sum / 20.0f;
ay = ay_sum / 20.0f;
az = az_sum / 20.0f;
/* 校验加速度幅值是否在合理范围 (0.6G ~ 1.6G), 若异常则回退到默认值 */
mag = ax * ax + ay * ay + az * az;
if(mag < 0.36f || mag > 2.56f)
{
ax = 0.0f;
ay = 0.0f;
az = 1.0f;
}
else
{
norm = fast_rsqrt(mag);
ax *= norm;
ay *= norm;
az *= norm;
}
/* 从重力矢量估计初始 roll 和 pitch */
roll_init = atan2f(ay, az);
pitch_init = atan2f(-ax, sqrtf(ay * ay + az * az));
/* 欧拉角 -> 四元数 */
cr = cosf(roll_init * 0.5f);
sr = sinf(roll_init * 0.5f);
cp = cosf(pitch_init * 0.5f);
sp = sinf(pitch_init * 0.5f);
q0 = cr * cp;
q1 = sr * cp;
q2 = cr * sp;
q3 = -sr * sp;
/* 四元数归一化 */
norm = fast_rsqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= norm;
q1 *= norm;
q2 *= norm;
q3 *= norm;
/* 复位积分项 */
i_ex = 0.0f; i_ey = 0.0f; i_ez = 0.0f;
/* 加速度滤波状态初始化为首次有效读数 (避免从零开始的衰减过程) */
acc_fx = ax;
acc_fy = ay;
acc_fz = az;
/* 根据初始四元数计算初始欧拉角 */
{
float q0q0 = q0 * q0;
float q0q1 = q0 * q1;
float q0q2 = q0 * q2;
float q0q3 = q0 * q3;
float q1q1 = q1 * q1;
float q1q2 = q1 * q2;
float q1q3 = q1 * q3;
float q2q2 = q2 * q2;
float q2q3 = q2 * q3;
float q3q3 = q3 * q3;
eulerAngle.pitch = asinf(-2.0f * q1q3 + 2.0f * q0q2) * RAD_TO_DEG;
eulerAngle.roll = atan2f(2.0f * q2q3 + 2.0f * q0q1,
-2.0f * q1q1 - 2.0f * q2q2 + 1.0f) * RAD_TO_DEG;
eulerAngle.yaw = 0.0f; /* 无磁力计,yaw 从 0 开始 */
}
}
/* ---------------------------------------------------------------------------
* 姿态更新 — 每 5ms 调用一次 (陀螺仪校准完成后)
* 读取已去除零偏的陀螺仪数据 + 原始加速度计数据,
* 运行 Mahony 互补滤波, 输出欧拉角
* --------------------------------------------------------------------------- */
void attitude_update(void)
{
float gx, gy, gz;
float ax, ay, az;
float norm;
float vx, vy, vz;
float ex, ey, ez;
float halfT = 0.5f * DELTA_T;
/* --- 1. 获取传感器数据并转换为物理量 --- */
/* 陀螺仪: ISR 已减零偏 (LSB) -> °/s -> rad/s, 量程因子由驱动层统一管理 */
gx = imu660rb_gyro_transition(imu660rb_gyro_x) * DEG_TO_RAD;
gy = imu660rb_gyro_transition(imu660rb_gyro_y) * DEG_TO_RAD;
gz = imu660rb_gyro_transition(imu660rb_gyro_z) * DEG_TO_RAD;
/* 加速度计: LSB -> G, 一阶低通滤波抑制振动噪声 (同 RT1064 alpha=0.3) */
ax = imu660rb_acc_transition(imu660rb_acc_x);
ay = imu660rb_acc_transition(imu660rb_acc_y);
az = imu660rb_acc_transition(imu660rb_acc_z);
acc_fx = ACC_ALPHA * ax + (1.0f - ACC_ALPHA) * acc_fx;
acc_fy = ACC_ALPHA * ay + (1.0f - ACC_ALPHA) * acc_fy;
acc_fz = ACC_ALPHA * az + (1.0f - ACC_ALPHA) * acc_fz;
ax = acc_fx;
ay = acc_fy;
az = acc_fz;
/* --- 2. 加速度归一化 (单位重力向量) --- */
norm = fast_rsqrt(ax * ax + ay * ay + az * az);
ax *= norm;
ay *= norm;
az *= norm;
/* --- 3. 估算重力分量 (从当前四元数) --- */
vx = 2.0f * (q1 * q3 - q0 * q2);
vy = 2.0f * (q0 * q1 + q2 * q3);
vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
/* --- 3.5 重力方向检测: 若估计重力与实测重力反平行 (点积<0),
* 则四元数处于翻转平衡点, 绕X轴旋转180°拉回正确方向 --- */
if(ax * vx + ay * vy + az * vz < 0.0f)
{
float t0 = -q1, t1 = q0, t2 = q3, t3 = -q2;
q0 = t0; q1 = t1; q2 = t2; q3 = t3;
vx = 2.0f * (q1 * q3 - q0 * q2);
vy = 2.0f * (q0 * q1 + q2 * q3);
vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
}
/* --- 4. 叉积求重力误差 --- */
ex = ay * vz - az * vy;
ey = az * vx - ax * vz;
ez = ax * vy - ay * vx;
/* --- 5. PI 修正陀螺仪 --- */
i_ex += halfT * ex;
i_ey += halfT * ey;
i_ez += halfT * ez;
gx += KP * ex + KI * i_ex;
gy += KP * ey + KI * i_ey;
gz += KP * ez + KI * i_ez;
/* --- 6. 一阶龙格库塔更新四元数 --- */
{
float dq0 = -q1 * gx - q2 * gy - q3 * gz;
float dq1 = q0 * gx + q2 * gz - q3 * gy;
float dq2 = q0 * gy - q1 * gz + q3 * gx;
float dq3 = q0 * gz + q1 * gy - q2 * gx;
q0 += dq0 * halfT;
q1 += dq1 * halfT;
q2 += dq2 * halfT;
q3 += dq3 * halfT;
}
/* --- 7. 四元数归一化 --- */
norm = fast_rsqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= norm;
q1 *= norm;
q2 *= norm;
q3 *= norm;
/* --- 8. 四元数 -> 欧拉角 (°) --- */
{
float q0q0 = q0 * q0;
float q0q1 = q0 * q1;
float q0q2 = q0 * q2;
float q0q3 = q0 * q3;
float q1q1 = q1 * q1;
float q1q2 = q1 * q2;
float q1q3 = q1 * q3;
float q2q2 = q2 * q2;
float q2q3 = q2 * q3;
float q3q3 = q3 * q3;
eulerAngle.pitch = asinf(-2.0f * q1q3 + 2.0f * q0q2) * RAD_TO_DEG;
eulerAngle.roll = atan2f(2.0f * q2q3 + 2.0f * q0q1,
-2.0f * q1q1 - 2.0f * q2q2 + 1.0f) * RAD_TO_DEG;
eulerAngle.yaw = atan2f(2.0f * q1q2 + 2.0f * q0q3,
-2.0f * q2q2 - 2.0f * q3q3 + 1.0f) * RAD_TO_DEG;
}
/* --- 9. Yaw 归一化到 [0, 360) --- */
if (eulerAngle.yaw >= 360.0f)
eulerAngle.yaw -= 360.0f;
else if (eulerAngle.yaw < 0.0f)
eulerAngle.yaw += 360.0f;
}