3
This commit is contained in:
@ -1,153 +1,213 @@
|
||||
#include "skiing_tracker.h"
|
||||
#include <math.h> // 使用 sqrtf, fabsf, atan2f
|
||||
#include <string.h> // 使用 memset
|
||||
#include "../sensor/SC7U22.h" // 包含传感器驱动头文件以调用姿态解算函数
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// ======================= 用户可配置参数 =======================
|
||||
#define G_ACCELERATION 9.81f // 重力加速度 (m/s^2)
|
||||
#define DEG_TO_RAD (3.14159265f / 180.0f)
|
||||
|
||||
// IMU的采样率 (Hz)
|
||||
#define SAMPLE_RATE 100.0f
|
||||
#define DT (1.0f / SAMPLE_RATE)
|
||||
// --- 状态检测阈值 ---
|
||||
// 判断是否静止的加速度阈值 (m/s^2)。当加速度的模长减去重力后,小于此值,则认为可能静止。
|
||||
#define STATIC_ACC_THRESHOLD 1.0f
|
||||
// 连续多少帧满足静止条件才确认为静止状态
|
||||
#define STATIC_FRAMES_REQUIRED 50 // 累加,超过这个数加速度仍变化不大,说明近似静止
|
||||
|
||||
// 传感器灵敏度配置 (必须与硬件配置匹配)
|
||||
// 加速度计量程: ±8G -> 1G = 32768 / 8 = 4096 LSB
|
||||
#define ACCEL_SENSITIVITY 4096.0f // LSB/g
|
||||
#define GRAVITY_MSS 9.80665f // 标准重力加速度 (m/s^2)
|
||||
// 陀螺仪灵敏度 (2000dps)
|
||||
#define GYRO_SENSITIVITY 16.4f // LSB/(deg/s)
|
||||
|
||||
// 状态检测阈值
|
||||
#define MOTION_ACCEL_THRESHOLD (ACCEL_SENSITIVITY * 0.3f) // 加速度变化超过0.3g认为在运动
|
||||
#define MOTION_GYRO_THRESHOLD (GYRO_SENSITIVITY * 90.0f) // 角速度超过xx dps认为在运动
|
||||
#define STILL_SAMPLES_FOR_CALIBRATION 100 // 连续静止1秒 (100个点) 开始校准
|
||||
#define CALIBRATION_SAMPLE_COUNT 50 // 用于平均的校准样本数 (0.5秒)
|
||||
#define MOTION_SAMPLES_TO_START_SKIING 10 // 连续运动0.1秒开始滑行
|
||||
#define STILL_SAMPLES_TO_STOP_SKIING 20 // 连续静止0.2秒停止滑行
|
||||
|
||||
// 角度转弧度
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#define RAD_TO_DEG(rad) ((rad) * 180.0f / M_PI)
|
||||
|
||||
// ======================= 内部实现 =======================
|
||||
|
||||
void SkiingTracker_Init(SkiingTracker* tracker) {
|
||||
memset(tracker, 0, sizeof(SkiingTracker));
|
||||
tracker->state = STATE_UNCALIBRATED;
|
||||
/**
|
||||
* @brief 初始化滑雪追踪器
|
||||
*/
|
||||
void skiing_tracker_init(skiing_tracker_t *tracker)
|
||||
{
|
||||
if (!tracker) {
|
||||
return;
|
||||
}
|
||||
memset(tracker, 0, sizeof(skiing_tracker_t));
|
||||
tracker->state = SKIING_STATE_STATIC; // 初始状态为静止
|
||||
}
|
||||
|
||||
// 简单的低通滤波器
|
||||
static float low_pass_filter(float new_input, float prev_output, float alpha) {
|
||||
return alpha * new_input + (1.0f - alpha) * prev_output;
|
||||
/**
|
||||
* @brief 将设备坐标系下的加速度转换为世界坐标系
|
||||
* @param acc_device 设备坐标系下的加速度 [x, y, z]
|
||||
* @param angle 姿态角 [pitch, roll, yaw],单位: 度
|
||||
* @param acc_world 输出:世界坐标系下的加速度 [x, y, z]
|
||||
*/
|
||||
static void transform_acc_to_world_frame(const float *acc_device, const float *angle, float *acc_world)
|
||||
{
|
||||
float pitch = angle[0] * DEG_TO_RAD;
|
||||
float roll = angle[1] * DEG_TO_RAD;
|
||||
|
||||
float cp = cosf(pitch);
|
||||
float sp = sinf(pitch);
|
||||
float cr = cosf(roll);
|
||||
float sr = sinf(roll);
|
||||
|
||||
float ax = acc_device[0];
|
||||
float ay = acc_device[1];
|
||||
float az = acc_device[2];
|
||||
|
||||
// 旋转矩阵(简化版,仅用Pitch和Roll)
|
||||
// Z-Y-X 顺序旋转
|
||||
acc_world[0] = cr * ax + sr * sp * ay + sr * cp * az;
|
||||
acc_world[1] = cp * ay - sp * az;
|
||||
acc_world[2] = -sr * ax + cr * sp * ay + cr * cp * az;
|
||||
}
|
||||
|
||||
static void reset_calibration(SkiingTracker* tracker) {
|
||||
tracker->calib_samples_count = 0;
|
||||
tracker->calib_acc_sum[0] = 0;
|
||||
tracker->calib_acc_sum[1] = 0;
|
||||
tracker->calib_acc_sum[2] = 0;
|
||||
|
||||
/**
|
||||
* @brief 更新滑雪状态机
|
||||
*/
|
||||
static void update_state_machine(skiing_tracker_t *tracker, const float *acc_device)
|
||||
{
|
||||
// 计算当前加速度的模长
|
||||
float acc_magnitude = sqrtf(acc_device[0] * acc_device[0] + acc_device[1] * acc_device[1] + acc_device[2] * acc_device[2]);
|
||||
|
||||
// 状态判断逻辑
|
||||
switch (tracker->state) {
|
||||
case SKIING_STATE_STATIC:
|
||||
// 如果加速度变化较大,则切换到滑雪状态
|
||||
if (fabsf(acc_magnitude - G_ACCELERATION) > STATIC_ACC_THRESHOLD * 2.0f) { // 使用一个更大的阈值来启动
|
||||
tracker->state = SKIING_STATE_SKIING;
|
||||
tracker->static_frames_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SKIING_STATE_SKIING:
|
||||
// 检测是否进入静止状态 (零速更新 ZUPT)
|
||||
if (fabsf(acc_magnitude - G_ACCELERATION) < STATIC_ACC_THRESHOLD) {
|
||||
tracker->static_frames_count++;
|
||||
if (tracker->static_frames_count >= STATIC_FRAMES_REQUIRED) {
|
||||
tracker->state = SKIING_STATE_STATIC;
|
||||
// 进入静止状态,强制将速度清零以消除漂移
|
||||
memset(tracker->velocity, 0, sizeof(tracker->velocity));
|
||||
tracker->speed = 0.0f;
|
||||
}
|
||||
} else {
|
||||
// 如果在运动,则重置静止计数器
|
||||
tracker->static_frames_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
// 可以在此添加摔倒等其他状态的判断
|
||||
case SKIING_STATE_FALLEN:
|
||||
// TODO: 添加从摔倒状态恢复的逻辑
|
||||
break;
|
||||
|
||||
default:
|
||||
tracker->state = SKIING_STATE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 核心更新函数
|
||||
void SkiingTracker_Update(SkiingTracker* tracker, signed short* raw_accel, signed short* raw_gyro) {
|
||||
// 运动状态检测
|
||||
// 使用原始数据进行判断,避免校准误差影响
|
||||
float acc_mag = sqrtf((float)raw_accel[0] * raw_accel[0] + (float)raw_accel[1] * raw_accel[1] + (float)raw_accel[2] * raw_accel[2]);
|
||||
float gyro_mag = sqrtf((float)raw_gyro[0] * raw_gyro[0] + (float)raw_gyro[1] * raw_gyro[1] + (float)raw_gyro[2] * raw_gyro[2]);
|
||||
|
||||
int is_moving = (fabsf(acc_mag - ACCEL_SENSITIVITY) > MOTION_ACCEL_THRESHOLD) || (gyro_mag > MOTION_GYRO_THRESHOLD);
|
||||
|
||||
if (is_moving) {
|
||||
tracker->still_counter = 0;
|
||||
tracker->motion_counter++;
|
||||
printf("===motion count===\n");
|
||||
} else {
|
||||
tracker->motion_counter = 0;
|
||||
tracker->still_counter++;
|
||||
printf("===still count===\n");
|
||||
/**
|
||||
* @brief 主更新函数
|
||||
*/
|
||||
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc, float *angle, float dt)
|
||||
{
|
||||
if (!tracker || !acc || !angle || dt <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 状态机处理
|
||||
switch (tracker->state) {
|
||||
case STATE_UNCALIBRATED:
|
||||
if (tracker->still_counter > STILL_SAMPLES_FOR_CALIBRATION) {
|
||||
tracker->state = STATE_CALIBRATING;
|
||||
reset_calibration(tracker);
|
||||
}
|
||||
break;
|
||||
// 将原始g单位的加速度转换为 m/s^2
|
||||
float acc_device_ms2[3];
|
||||
acc_device_ms2[0] = acc[0] * G_ACCELERATION;
|
||||
acc_device_ms2[1] = acc[1] * G_ACCELERATION;
|
||||
acc_device_ms2[2] = acc[2] * G_ACCELERATION;
|
||||
|
||||
case STATE_CALIBRATING:
|
||||
if (is_moving) { // 如果在校准时移动了,则校准失败,返回未校准状态
|
||||
tracker->state = STATE_UNCALIBRATED;
|
||||
reset_calibration(tracker);
|
||||
break;
|
||||
}
|
||||
// 更新状态机
|
||||
update_state_machine(tracker, acc_device_ms2);
|
||||
|
||||
// 累加采样数据
|
||||
tracker->calib_acc_sum[0] += raw_accel[0];
|
||||
tracker->calib_acc_sum[1] += raw_accel[1];
|
||||
tracker->calib_acc_sum[2] += raw_accel[2];
|
||||
tracker->calib_samples_count++;
|
||||
// 只有在滑雪状态下才进行计算
|
||||
if (tracker->state == SKIING_STATE_SKIING) {
|
||||
// 坐标系转换: 首先,利用姿态角(Pitch, Roll)将传感器测得的总加速度(运动加速度+重力)
|
||||
// 从不断变化的“设备坐标系”转换到一个固定的“世界坐标系”。在这个世界坐标系里,Z轴永远垂直于地面指向上方。
|
||||
// 执行坐标系转换
|
||||
transform_acc_to_world_frame(acc_device_ms2, angle, tracker->acc_world);
|
||||
// 转换完成后,重力就变成了一个恒定的、方向沿Z轴向下的矢量。
|
||||
|
||||
if (tracker->calib_samples_count >= CALIBRATION_SAMPLE_COUNT) {
|
||||
// 校准完成,计算平均重力矢量
|
||||
tracker->static_gravity[0] = (short)(tracker->calib_acc_sum[0] / CALIBRATION_SAMPLE_COUNT);
|
||||
tracker->static_gravity[1] = (short)(tracker->calib_acc_sum[1] / CALIBRATION_SAMPLE_COUNT);
|
||||
tracker->static_gravity[2] = (short)(tracker->calib_acc_sum[2] / CALIBRATION_SAMPLE_COUNT);
|
||||
|
||||
// 计算坡度(可选,用于显示)
|
||||
float horiz_g = sqrtf((float)tracker->static_gravity[0] * tracker->static_gravity[0] + (float)tracker->static_gravity[1] * tracker->static_gravity[1]);
|
||||
float vert_g = fabsf((float)tracker->static_gravity[2]);
|
||||
tracker->slope_angle_deg = RAD_TO_DEG(atan2f(horiz_g, vert_g));
|
||||
// 在世界坐标系下,移除Z轴上的重力分量
|
||||
tracker->acc_world[2] -= G_ACCELERATION;
|
||||
|
||||
tracker->state = STATE_READY;
|
||||
}
|
||||
break;
|
||||
// 积分计算速度 (v = v0 + a*t)
|
||||
tracker->velocity[0] += tracker->acc_world[0] * dt;
|
||||
tracker->velocity[1] += tracker->acc_world[1] * dt;
|
||||
tracker->velocity[2] += tracker->acc_world[2] * dt; // 垂直方向速度也计算在内
|
||||
|
||||
case STATE_READY:
|
||||
if (tracker->motion_counter > MOTION_SAMPLES_TO_START_SKIING) {
|
||||
tracker->state = STATE_SKIING;
|
||||
}
|
||||
break;
|
||||
// 计算当前速率
|
||||
tracker->speed = sqrtf(tracker->velocity[0] * tracker->velocity[0] +
|
||||
tracker->velocity[1] * tracker->velocity[1] +
|
||||
tracker->velocity[2] * tracker->velocity[2]);
|
||||
|
||||
case STATE_SKIING:
|
||||
if (tracker->still_counter > STILL_SAMPLES_TO_STOP_SKIING) {
|
||||
tracker->state = STATE_STOPPED;
|
||||
tracker->velocity = 0.0f; // 零速更新
|
||||
tracker->forward_accel = 0.0f;
|
||||
break;
|
||||
}
|
||||
// 积分计算距离 (d = d0 + v*t)
|
||||
tracker->distance += tracker->speed * dt;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算线性加速度 (重力抵消)
|
||||
// 假设传感器的X轴指向滑雪板前进方向
|
||||
long linear_accel_x_lsb = (long)raw_accel[0] - tracker->static_gravity[0];
|
||||
|
||||
// 转换为 m/s^2
|
||||
float current_accel_mss = (float)linear_accel_x_lsb / ACCEL_SENSITIVITY * GRAVITY_MSS;
|
||||
|
||||
// 低通滤波以平滑加速度
|
||||
tracker->forward_accel = low_pass_filter(current_accel_mss, tracker->forward_accel, 0.3f);
|
||||
|
||||
// 积分计算速度和距离 (梯形积分)
|
||||
float prev_velocity = tracker->velocity;
|
||||
tracker->velocity += tracker->forward_accel * DT;
|
||||
// 传感器数据采集与处理任务
|
||||
void sensor_processing_task(signed short * acc_data_buf,signed short * gyr_data_buf) {
|
||||
// --- 1. 定义静态变量 ---
|
||||
static skiing_tracker_t my_skiing_tracker;
|
||||
static int initialized = 0;
|
||||
static int calibration_done = 0; // 新增:用于标记一次性静态校准是否完成
|
||||
|
||||
// 物理约束:速度不能为负(不能往坡上滑)
|
||||
if (tracker->velocity < 0) {
|
||||
tracker->velocity = 0;
|
||||
}
|
||||
tracker->distance += (prev_velocity + tracker->velocity) / 2.0f * DT;
|
||||
break;
|
||||
static signed short combined_raw_data[6];
|
||||
static float final_angle_data[3];
|
||||
static float calibrated_acc_g[3];
|
||||
|
||||
case STATE_STOPPED:
|
||||
// 在停止状态下,如果再次检测到运动,则重新进入滑行状态
|
||||
if (tracker->motion_counter > MOTION_SAMPLES_TO_START_SKIING) {
|
||||
tracker->state = STATE_SKIING;
|
||||
}
|
||||
// 如果长时间静止,返回未校准状态,以应对更换雪道的情况
|
||||
// if (tracker->still_counter > 3000) { // e.g., 30 seconds
|
||||
// tracker->state = STATE_UNCALIBRATED;
|
||||
// }
|
||||
break;
|
||||
// sensor_processing_task的调用频率, dt = 0.001s
|
||||
const float delta_time = 0.01f;
|
||||
|
||||
// --- 2. 初始化 ---
|
||||
if (!initialized) {
|
||||
skiing_tracker_init(&my_skiing_tracker);
|
||||
initialized = 1;
|
||||
printf("Skiing Tracker Initialized. Waiting for sensor calibration...\n");
|
||||
}
|
||||
|
||||
// --- 3. 数据处理 ---
|
||||
//合并加速度和陀螺仪数据
|
||||
memcpy(&combined_raw_data[0], acc_data_buf, 3 * sizeof(signed short));
|
||||
memcpy(&combined_raw_data[3], gyr_data_buf, 3 * sizeof(signed short));
|
||||
|
||||
unsigned char status;
|
||||
if (get_calibration_state() == 0) { //正在校准
|
||||
//领票校准
|
||||
status = SL_SC7U22_Angle_Output(1, combined_raw_data, final_angle_data, 0);
|
||||
if (status == 1) {
|
||||
calibration_done = 1;
|
||||
printf("Sensor calibration successful! Skiing mode is active.\n");
|
||||
}
|
||||
} else {
|
||||
status = SL_SC7U22_Angle_Output(0, combined_raw_data, final_angle_data, 0);
|
||||
}
|
||||
|
||||
// c. 检查姿态解算是否成功
|
||||
if (status == 1) {
|
||||
// 将校准后的加速度数据从 LSB (原始值) 转换为 g (重力单位)
|
||||
// ±8g量程下,8192 LSB 对应 1g
|
||||
calibrated_acc_g[0] = (float)combined_raw_data[0] / 8192.0f;
|
||||
calibrated_acc_g[1] = (float)combined_raw_data[1] / 8192.0f;
|
||||
calibrated_acc_g[2] = (float)combined_raw_data[2] / 8192.0f;
|
||||
|
||||
skiing_tracker_update(&my_skiing_tracker, calibrated_acc_g, final_angle_data, delta_time);
|
||||
|
||||
static count = 0;
|
||||
if(count < 10){
|
||||
count++;
|
||||
return;
|
||||
}else{
|
||||
count = 0;
|
||||
}
|
||||
printf("State: %d, Speed: %.2f m/s, Distance: %.2f m\n",
|
||||
my_skiing_tracker.state,
|
||||
my_skiing_tracker.speed,
|
||||
my_skiing_tracker.distance);
|
||||
|
||||
} else if (status == 0) {
|
||||
// 传感器正在进行静态校准
|
||||
// printf("Sensor is calibrating...\n");
|
||||
} else {
|
||||
// status == 2, 表示校准失败或发生错误
|
||||
// printf("Angle calculation error or calibration not finished.\n");
|
||||
}
|
||||
}
|
||||
@ -1,53 +1,41 @@
|
||||
#ifndef SKIING_TRACKER_H
|
||||
#define SKIING_TRACKER_H
|
||||
|
||||
// 定义滑雪者的运动状态
|
||||
// 定义滑雪者可能的状态
|
||||
typedef enum {
|
||||
STATE_UNCALIBRATED, // 未校准,等待在斜坡上静止
|
||||
STATE_CALIBRATING, // 正在校准重力矢量
|
||||
STATE_READY, // 校准完成,准备滑行
|
||||
STATE_SKIING, // 正在滑行
|
||||
STATE_STOPPED // 在斜坡上中途停止
|
||||
} MotionState;
|
||||
SKIING_STATE_STATIC, // 静止
|
||||
SKIING_STATE_SKIING, // 正在滑雪
|
||||
SKIING_STATE_FALLEN, // 已摔倒
|
||||
SKIING_STATE_UNKNOWN // 未知状态
|
||||
} skiing_state_t;
|
||||
|
||||
// 存储所有运动学数据
|
||||
// 追踪器数据结构体
|
||||
typedef struct {
|
||||
// 最终输出
|
||||
float velocity; // 沿斜坡方向的速度 (m/s)
|
||||
float distance; // 沿斜坡方向的滑行距离 (m)
|
||||
float slope_angle_deg; // 动态计算出的坡度 (度)
|
||||
float velocity[3]; // 当前速度 (x, y, z),单位: m/s
|
||||
float distance; // 总滑行距离,单位: m
|
||||
float speed; // 当前速率 (标量),单位: m/s
|
||||
skiing_state_t state; // 当前滑雪状态
|
||||
|
||||
// 内部状态变量
|
||||
MotionState state; // 当前运动状态
|
||||
|
||||
// 校准相关
|
||||
short static_gravity[3]; // 校准后得到的静态重力矢量 (LSB)
|
||||
long calib_acc_sum[3]; // 用于计算平均值的累加器
|
||||
int calib_samples_count; // 校准采样计数
|
||||
|
||||
// 运动检测
|
||||
int motion_counter;
|
||||
int still_counter; // 静止状态计数器
|
||||
|
||||
// 物理量
|
||||
float forward_accel; // 沿滑行方向的加速度 (m/s^2)
|
||||
|
||||
} SkiingTracker;
|
||||
// 私有成员,用于内部计算
|
||||
int static_frames_count; // 用于判断静止状态的帧计数器
|
||||
float acc_world[3]; // 在世界坐标系下的加速度
|
||||
} skiing_tracker_t;
|
||||
|
||||
/**
|
||||
* @brief 初始化滑雪追踪器
|
||||
* @param tracker 指向 SkiingTracker 实例的指针
|
||||
*
|
||||
* @param tracker 指向 skiing_tracker_t 结构体的指针
|
||||
*/
|
||||
void SkiingTracker_Init(SkiingTracker* tracker);
|
||||
void skiing_tracker_init(skiing_tracker_t *tracker);
|
||||
|
||||
/**
|
||||
* @brief 处理IMU数据,自动校准并计算速度和距离
|
||||
* @details 这是核心处理函数,应在每次获取新的IMU数据后调用。
|
||||
* @brief 处理传感器数据并更新滑雪状态
|
||||
*
|
||||
* @param tracker 指向 SkiingTracker 实例的指针
|
||||
* @param raw_accel 未经校准的原始加速度数据 [X, Y, Z],单位是 LSB
|
||||
* @param raw_gyro 未经校准的原始陀螺仪数据 [X, Y, Z],单位是 LSB
|
||||
* @param tracker 指向 skiing_tracker_t 结构体的指针
|
||||
* @param acc 校准后的加速度数据 [x, y, z],单位: g (1g = 9.8m/s^2)
|
||||
* @param angle 姿态角数据 [pitch, roll, yaw],单位: 度
|
||||
* @param dt 采样时间间隔,单位: 秒 (s)
|
||||
*/
|
||||
void SkiingTracker_Update(SkiingTracker* tracker, signed short* raw_accel, signed short* raw_gyro);
|
||||
void skiing_tracker_update(skiing_tracker_t *tracker, float *acc, float *angle, float dt);
|
||||
|
||||
#endif // SKIING_TRACKER_H
|
||||
Reference in New Issue
Block a user