使用mpu6500/6050, PID,互补滤波实现一个简单的飞行自稳控制系统

首先,参考ai给出的客机飞机的比较平稳的最大仰府,偏转,和防滚角度,如下:

客机的最大平稳仰俯(Pitch)、偏转(Yaw)和防滚(Roll)角度,通常在飞机的设计和飞行规程中有所规定。这些角度的限制是为了确保飞机的安全性,并在复杂的气流条件下进行平稳飞行。

具体而言:

  • 仰俯(Pitch):

    指飞机机头向上或向下的角度变化。客机设计通常会限制仰俯角度,以避免飞机失控或结构损坏。一般而言,客机的最大仰俯角在10度到20度之间,具体取决于飞机类型和飞行阶段。

  • 偏转(Yaw):

    指飞机机头左右偏离飞行方向的角度变化。偏转角度的限制是为了防止飞机剧烈摇摆或失控。客机的最大偏转角度通常在10度到20度之间,具体取决于飞机类型和飞行阶段。

  • 防滚(Roll):

    指飞机机翼左右倾斜的角度变化。防滚角度的限制是为了防止飞机翻滚或失控。客机的最大防滚角度通常在30度到40度之间,具体取决于飞机类型和飞行阶段。

需要注意的是,这些角度是飞机设计和飞行规程的限制,而不是飞机的实际飞行。在飞机的正常飞行过程中,这些角度通常不会超过设计限制。

客机的最大平稳仰俯、偏转和防滚角度通常在10度到40度之间,具体取决于飞机类型和飞行阶段。这些限制是为了确保飞机的安全性,并在复杂的气流条件下进行平稳飞行。

还让ai以大型运输机查出了最大的安全角速度,如下:

根据大型运输机设计标准,对于客机平稳安全的最大角速度变化率, 设定了以下安全限制:

  • 俯仰最大安全角速度:10度/秒

  • 横滚最大安全角速度:25度/秒(参考大型运输机20-30度/秒的标准)

  • 偏航最大安全角速度:15度/秒

我们取: 最大仰俯角是+-20角,翻滚+-40度,偏航是+-20度

为了简单,

  1. 只是加速度acc和角度gyro这个六个mpu返回的值进行计算。 不使用dmp解算姿态。

  2. 使用积分的办法去粗略计算yaw, pitch和roll 使用gyro和acc直接计算。

  3. 把用户的三个输入(pwm 1000-2000), 转化成目标角度,即飞机的最大仰俯角是+-20角,翻滚+-40度,偏航是+-20度

  4. 使用互补滤波和PID ,计算出输出的pwm去控制舵面,最终令到俯角,翻滚和偏航达到用户输入出的值。

  5. 不考虑油门对速度的影响

  6. 进行单元测试,输出的pwm变成舵面角度基于物理学转化成对于加速度acc和角度gyro的影响,内容在update_sensor_by_control

  7. 估算当前舵面角度和速度能产生的最大角速度

  8. 比较这个最大角速度与安全角速度限制

  9. 如果舵面效果不足(例如在低速时),自动增加油门提高速度,从而增强舵面效果

  10. 如果舵面效果过强(例如在高速时),自动减小油门降低速度,避免过大的角速度变化

这种自适应控制方式能够在保证飞行安全和舒适性的同时,尽可能快速地达到目标姿态。

代码如下:

cpp 复制代码
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/**
 * @brief 互补滤波器结构体定义
 */
 typedef struct {
    float alpha;     /**< 互补滤波系数,范围0-1,越接近1越信任陀螺仪数据 */
    float angle;     /**< 当前角度 */
} complementary_filter_t;


/**
 * @brief 初始化互补滤波器
 * @param[in] *filter 指向互补滤波器结构体的指针
 * @param[in] alpha 互补滤波系数,范围0-1,越接近1越信任陀螺仪数据
 * @note 无
 */
 void complementary_filter_init(complementary_filter_t *filter, float alpha) 
 {
     filter->alpha = alpha;
     filter->angle = 0.0f;
 }
 
 /**
  * @brief 互补滤波计算
  * @param[in] *filter 指向互补滤波器结构体的指针
  * @param[in] acc_angle 从加速度计计算的角度
  * @param[in] gyro_rate 陀螺仪测量的角速度
  * @param[in] dt 时间间隔(秒)
  * @return 滤波后的角度值
  * @note 无
  */
 float complementary_filter_update(complementary_filter_t *filter, float acc_angle, float gyro_rate, float dt) 
 {
     // 互补滤波核心算法
     // 陀螺仪积分得到角度变化
     float gyro_angle = filter->angle + gyro_rate * dt;
     
     // 互补滤波:结合陀螺仪和加速度计的数据
     // alpha决定了对陀螺仪数据的信任程度
     filter->angle = filter->alpha * gyro_angle + (1.0f - filter->alpha) * acc_angle;
     
     return filter->angle;
 }
 

 

// 全局互补滤波器实例
static complementary_filter_t gs_pitch_filter;
static complementary_filter_t gs_roll_filter;
static complementary_filter_t gs_yaw_filter;
static float gs_prev_yaw = 0.0f;
static float gs_prev_time = 0.0f;

// PID控制器参数和状态变量
typedef struct {
    float kp;           // 比例系数
    float ki;           // 积分系数
    float kd;           // 微分系数
    float error_sum;    // 误差累积
    float prev_error;   // 上一次误差
    float output_limit; // 输出限制
} pid_controller_t;

// 全局PID控制器实例
static pid_controller_t gs_pitch_pid;
static pid_controller_t gs_roll_pid;
static pid_controller_t gs_yaw_pid;

/**
 * @brief 初始化互补滤波器
 * @note 无
 */
void init_filters(void)
{
    // 初始化俯仰角和横滚角的互补滤波器
    // alpha=0.98表示98%信任陀螺仪数据,2%信任加速度计数据
    complementary_filter_init(&gs_pitch_filter, 0.98f);
    complementary_filter_init(&gs_roll_filter, 0.98f);
    
    // 初始化偏航角的互补滤波器
    // 对于偏航角,我们更依赖陀螺仪数据,因此alpha设置得更高
    complementary_filter_init(&gs_yaw_filter, 0.99f);
    
    // 初始化时间和偏航角记录
    gs_prev_time = 0.0f;
    gs_prev_yaw = 0.0f;
}



/**
 * @brief 初始化PID控制器
 * @param[out] pid 指向PID控制器结构体的指针
 * @param[in] kp 比例系数
 * @param[in] ki 积分系数
 * @param[in] kd 微分系数
 * @param[in] output_limit 输出限制值
 * @note 无
 */
void pid_init(pid_controller_t *pid, float kp, float ki, float kd, float output_limit)
{
    pid->kp = kp;
    pid->ki = ki;
    pid->kd = kd;
    pid->error_sum = 0.0f;
    pid->prev_error = 0.0f;
    pid->output_limit = output_limit;
}

/**
 * @brief 计算PID控制器输出
 * @param[in,out] pid 指向PID控制器结构体的指针
 * @param[in] error 当前误差
 * @param[in] dt 时间间隔(秒)
 * @return PID控制器输出值
 * @note 无
 */
float pid_compute(pid_controller_t *pid, float error, float dt)
{
    // 计算积分项
    pid->error_sum += error * dt;
    
    // 计算微分项
    float error_diff = (error - pid->prev_error) / dt;
    
    // 计算PID输出
    float output = pid->kp * error + pid->ki * pid->error_sum + pid->kd * error_diff;
    
    // 限制输出范围
    if (output > pid->output_limit) {
        output = pid->output_limit;
    } else if (output < -pid->output_limit) {
        output = -pid->output_limit;
    }
    
    // 保存当前误差用于下次计算
    pid->prev_error = error;
    
    return output;
}

/**
 * @brief 初始化直线水平前进PID控制器
 * @note 无
 */
void init_simple_stable_pid(void)
{
    // 初始化俯仰角PID控制器
    pid_init(&gs_pitch_pid, 0.5f, 0.1f, 0.05f, 10.0f);
    
    // 初始化横滚角PID控制器
    pid_init(&gs_roll_pid, 0.5f, 0.1f, 0.05f, 10.0f);
    
    // 初始化偏航角PID控制器
    pid_init(&gs_yaw_pid, 0.8f, 0.2f, 0.1f, 10.0f);
    
    // 初始化互补滤波器
    init_filters();
}


/**
 * @brief 从加速度数据计算俯仰角和横滚角
 * @param[in] acc 加速度数据数组[3]
 * @param[out] pitch 计算得到的俯仰角(度)
 * @param[out] roll 计算得到的横滚角(度)
 * @note 无
 */
 void calculate_angles_from_acc(float acc[3], float *pitch, float *roll)
 {
     // 计算俯仰角(pitch)和横滚角(roll)
     // 注意:这里假设z轴垂直于地面,x轴向前,y轴向右
     
     // 计算俯仰角(绕y轴旋转)
     *pitch = atan2f(-acc[0], sqrtf(acc[1] * acc[1] + acc[2] * acc[2])) * 180.0f / M_PI;
     
     // 计算横滚角(绕x轴旋转)
     *roll = atan2f(acc[1], acc[2]) * 180.0f / M_PI;
 }
 

 
/**
 * @brief 使用PID控制算法计算直线水平前进的控制输出
 * @param[in] acc 加速度数据数组[3]
 * @param[in] gyro 陀螺仪数据数组[3]
 * @param[in] target_pitch 目标俯仰角
 * @param[in] target_roll 目标横滚角
 * @param[in] target_yaw_rate 目标偏航角变化率
 * @param[out] control_output 控制输出数组[3],分别对应俯仰、横滚和偏航的控制量
 * @param[in,out] throttle_pwm 油门PWM值,范围1000-2000,函数会根据需要调整此值
 * @return 是否已经达到直线水平前进状态
 * @note 此函数需要在初始化PID控制器后使用
 */
void simple_stable_pid_control(float acc[3], float gyro[3], float target_pitch, float target_roll, float target_yaw_rate, float control_output[3], int *throttle_pwm)
{
    float pitch_acc, roll_acc;
    float current_time;
    float dt;
    float pitch, roll;
    float yaw, yaw_gyro; // 使用陀螺仪积分计算偏航角
    
    // 获取当前时间(秒),实际应用中需要替换为真实的时间获取函数
    current_time = 0.01f + gs_prev_time; // 假设每次调用间隔10ms
    dt = current_time - gs_prev_time;
    
    // 使用陀螺仪z轴数据积分计算当前偏航角
    // gyro[2]是z轴角速度,单位为度/秒,积分得到角度变化
    yaw_gyro = gs_prev_yaw + gyro[2] * dt;
    
    // 从加速度计算出角度
    calculate_angles_from_acc(acc, &pitch_acc, &roll_acc);
    
    // 使用互补滤波更新角度
    pitch = complementary_filter_update(&gs_pitch_filter, pitch_acc, gyro[0], dt);
    roll = complementary_filter_update(&gs_roll_filter, roll_acc, gyro[1], dt);
    
    // 对偏航角也应用互补滤波
    // 注意:由于没有从加速度计直接获取偏航角的方法,这里使用陀螺仪积分值作为主要输入
    // 在实际应用中,可以考虑使用磁力计数据作为偏航角的参考
    yaw = complementary_filter_update(&gs_yaw_filter, yaw_gyro, gyro[2], dt);
    
    // 计算偏差 - 使用传入的目标值而不是固定的0
    float pitch_error = target_pitch - pitch; // 目标俯仰角
    float roll_error = target_roll - roll;    // 目标横滚角
    float yaw_rate = (yaw - gs_prev_yaw) / dt; // 当前偏航角变化率
    float yaw_error = target_yaw_rate - yaw_rate; // 目标偏航角变化率
    
    // 使用PID控制器计算控制输出
    control_output[0] = pid_compute(&gs_pitch_pid, pitch_error, dt); // 俯仰控制
    control_output[1] = pid_compute(&gs_roll_pid, roll_error, dt);   // 横滚控制
    control_output[2] = pid_compute(&gs_yaw_pid, yaw_error, dt);     // 偏航控制
    
    // 定义客机平稳安全的最大角速度变化率(度/秒)
    // 根据大型运输机设计标准,最大滚转速率通常在20-30度/秒之间
    const float MAX_SAFE_PITCH_RATE = 10.0f;  // 俯仰最大安全角速度(度/秒)
    const float MAX_SAFE_ROLL_RATE = 25.0f;   // 横滚最大安全角速度(度/秒)
    const float MAX_SAFE_YAW_RATE = 15.0f;    // 偏航最大安全角速度(度/秒)
    
    // 计算当前角速度(使用陀螺仪数据)
    float current_pitch_rate = fabsf(gyro[0]); // X轴陀螺仪数据对应俯仰角速度
    float current_roll_rate = fabsf(gyro[1]);  // Y轴陀螺仪数据对应横滚角速度
    float current_yaw_rate = fabsf(gyro[2]);   // Z轴陀螺仪数据对应偏航角速度
    
    // 计算控制输出对应的舵面角度
    float elevator_angle = control_output[0] * 4.5f; // 假设控制输出1对应4.5度舵面角度
    float aileron_angle = control_output[1] * 4.5f;
    float rudder_angle = control_output[2] * 4.5f;
    
    // 估计舵面能产生的最大角速度(简化模型)
    // 当前油门值(1000-2000)转换为百分比(0-100%)
    float throttle_percent = (*throttle_pwm - 1000) / 10.0f;
    // 假设速度与油门成正比,舵面效果与速度平方成正比(伯努利原理)
    float speed_factor = 1.0f + (throttle_percent * throttle_percent) * 0.0001f;
    
    // 估计当前舵面能产生的最大角速度
    float max_pitch_rate_possible = fabsf(elevator_angle) * 0.5f * speed_factor;
    float max_roll_rate_possible = fabsf(aileron_angle) * 0.4f * speed_factor;
    float max_yaw_rate_possible = fabsf(rudder_angle) * 0.3f * speed_factor;
    
    // 检查是否需要调整油门
    bool need_more_throttle = false;
    bool need_less_throttle = false;
    
    // 如果需要的角速度超过舵面能产生的最大角速度,需要增加油门
    if (max_pitch_rate_possible < MAX_SAFE_PITCH_RATE && fabsf(pitch_error) > 5.0f) {
        need_more_throttle = true;
    }
    if (max_roll_rate_possible < MAX_SAFE_ROLL_RATE && fabsf(roll_error) > 5.0f) {
        need_more_throttle = true;
    }
    if (max_yaw_rate_possible < MAX_SAFE_YAW_RATE && fabsf(yaw_error) > 5.0f) {
        need_more_throttle = true;
    }
    
    // 如果当前角速度已经超过安全角速度,但舵面角度很小,需要减小油门
    if (current_pitch_rate > MAX_SAFE_PITCH_RATE && fabsf(elevator_angle) < 10.0f) {
        need_less_throttle = true;
    }
    if (current_roll_rate > MAX_SAFE_ROLL_RATE && fabsf(aileron_angle) < 10.0f) {
        need_less_throttle = true;
    }
    if (current_yaw_rate > MAX_SAFE_YAW_RATE && fabsf(rudder_angle) < 10.0f) {
        need_less_throttle = true;
    }
    
    // 调整油门值
    if (need_more_throttle && !need_less_throttle) {
        // 增加油门,但不超过最大值2000
        *throttle_pwm += 20;
        if (*throttle_pwm > 2000) *throttle_pwm = 2000;
    } else if (need_less_throttle && !need_more_throttle) {
        // 减小油门,但不低于最小值1000
        *throttle_pwm -= 20;
        if (*throttle_pwm < 1000) *throttle_pwm = 1000;
    }
    
    // 更新时间和偏航角记录 
    gs_prev_time = current_time;
    gs_prev_yaw = yaw;
}

/**
 * @brief PWM值转换为角度
 * @param[in] pwm PWM值,范围1000-2000
 * @param[in] channel 通道号,0=俯仰(±20°),1=横滚(±40°),2=偏航(±20°)
 * @return 转换后的角度值
 * @note 根据不同通道返回不同范围的角度值
 */
float pwm_to_angle(int pwm, int channel)
{
    // 中点值1500对应0度
    float normalized = (pwm - 1500) / 500.0f; // 归一化到±1范围
    
    // 根据通道返回不同范围的角度
    switch(channel) {
        case 0: // 俯仰通道,±20度
            return normalized * 20.0f;
        case 1: // 横滚通道,±40度
            return normalized * 40.0f;
        case 2: // 偏航通道,±20度
            return normalized * 20.0f;
        default:
            return normalized * 20.0f; // 默认±20度
    }
}

/**
 * @brief 控制输出转换为PWM值
 * @param[in] control 控制输出值
 * @return 转换后的PWM值,范围1000-2000
 * @note 无
 */
int control_to_pwm(float control)
{
    // 将控制输出(-10到10)线性映射到PWM值(1000-2000)
    // 首先将控制输出限制在-10到10的范围内
    if (control > 10.0f) control = 10.0f;
    if (control < -10.0f) control = -10.0f;
    
    // 然后映射到1000-2000
    return 1500 + (int)(control * 50.0f);
}

/**
 * @brief PWM值转换为舵面角度
 * @param[in] pwm PWM值,范围1000-2000
 * @return 转换后的舵面角度值,范围-45到+45度
 * @note 无
 */
float pwm_to_control_surface_angle(int pwm)
{
    // 将PWM值(1000-2000)线性映射到舵面角度值(-45到+45度)
    return -45.0f + (pwm - 1000) * 90.0f / 1000.0f;
}

/**
 * @brief 根据舵面角度更新传感器数据
 * @param[in,out] acc 加速度数据数组[3]
 * @param[in,out] gyro 陀螺仪数据数组[3]
 * @param[in] output_pwm 输出PWM值数组[3],分别对应俯仰、横滚和偏航通道
 * @param[in] dt 时间间隔(秒)
 * @note 根据物理公式模拟舵面角度对加速度和陀螺仪数据的影响
 */
void update_sensor_by_control(float acc[3], float gyro[3], int output_pwm[3], int throttle_pwm, float dt)
{
    // 将PWM值转换为舵面角度(-45到+45度)
    float elevator_angle = pwm_to_control_surface_angle(output_pwm[0]); // 升降舵角度(俯仰)
    float aileron_angle = pwm_to_control_surface_angle(output_pwm[1]);  // 副翼角度(横滚)
    float rudder_angle = pwm_to_control_surface_angle(output_pwm[2]);   // 方向舵角度(偏航)
    
    // 计算飞行速度(基于油门值)
    // 油门范围1000-2000,转换为0-100%的油门量
    float throttle_percent = (throttle_pwm - 1000) / 10.0f;
    // 假设最大速度为30m/s,根据油门百分比计算当前速度
    float airspeed = throttle_percent * 0.3f; // 0-30 m/s
    
    // 模拟舵面角度对陀螺仪数据的影响
    // 舵面角度越大,产生的角速度越大
    // 这里使用简化的物理模型,实际上应该考虑更复杂的空气动力学模型
    
    // 计算舵面角度变化率(简化为当前角度除以时间步长)
    // 这个变化率用于模拟舵面移动时产生的瞬时反作用力
    float elevator_rate = elevator_angle / dt;
    float aileron_rate = aileron_angle / dt;
    float rudder_rate = rudder_angle / dt;
    
    // 考虑速度对舵面效果的影响(伯努利原理)
    // 速度越大,舵面产生的力矩越大
    float speed_factor = 1.0f + (airspeed * airspeed) * 0.01f; // 二次方关系,模拟动压与速度平方成正比
    
    // 升降舵影响俯仰角速度(绕Y轴)
    // 正角度产生向上的力矩,负角度产生向下的力矩
    gyro[0] += elevator_angle * 0.5f * speed_factor; // 比例因子可以根据实际情况调整
    
    // 副翼影响横滚角速度(绕X轴)
    // 正角度产生向右的力矩,负角度产生向左的力矩
    gyro[1] += aileron_angle * 0.4f * speed_factor;
    
    // 方向舵影响偏航角速度(绕Z轴)
    // 正角度产生向右的力矩,负角度产生向左的力矩
    gyro[2] += rudder_angle * 0.3f * speed_factor;
    
    // 添加舵面移动产生的反作用力对陀螺仪的影响
    // 舵面快速移动时会产生反向的力矩
    gyro[0] -= elevator_rate * 0.05f * speed_factor; // 升降舵移动产生的反作用力矩
    gyro[1] -= aileron_rate * 0.04f * speed_factor;  // 副翼移动产生的反作用力矩
    gyro[2] -= rudder_rate * 0.03f * speed_factor;   // 方向舵移动产生的反作用力矩
    
    // 模拟舵面角度对加速度数据的影响
    // 舵面角度会改变飞行器的姿态,从而影响加速度计测量的重力分量
    
    // 升降舵影响前后加速度(X轴)
    // 正角度(向上)会产生向后的加速度,负角度(向下)会产生向前的加速度
    acc[0] += elevator_angle * 0.01f * speed_factor;
    
    // 副翼影响左右加速度(Y轴)
    // 正角度(向右)会产生向右的加速度,负角度(向左)会产生向左的加速度
    acc[1] += aileron_angle * 0.008f * speed_factor;
    
    // 方向舵主要影响偏航,对加速度的直接影响较小,这里简化处理
    // 但在实际飞行中,方向舵会通过改变飞行器的侧滑角间接影响加速度
    acc[1] += rudder_angle * 0.003f * speed_factor;
    
    // 添加舵面移动产生的反作用力对加速度计的影响
    // 舵面快速移动时会产生反向的加速度
    acc[0] -= elevator_rate * 0.002f * speed_factor; // 升降舵移动产生的反向加速度
    acc[1] -= aileron_rate * 0.0015f * speed_factor; // 副翼移动产生的反向加速度
    acc[1] -= rudder_rate * 0.0005f * speed_factor;  // 方向舵移动产生的反向加速度
    
    // 模拟伯努利原理对传感器的影响
    // 速度越大,气流对传感器的影响越大
    // 这里简化为速度对加速度和陀螺仪的直接影响
    
    // 速度产生的振动影响(随机噪声与速度成正比)
    float vibration_factor = airspeed * 0.02f;
    acc[0] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor;
    acc[1] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor;
    acc[2] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor;
    
    gyro[0] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor * 5.0f;
    gyro[1] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor * 5.0f;
    gyro[2] += (rand() / (float)RAND_MAX - 0.5f) * vibration_factor * 5.0f;
    
    // 速度产生的稳定性影响(高速时更稳定,低速时更不稳定)
    // 这是因为高速时舵面效果更好,控制更精确
    float stability_factor = 0.0f;
    if (airspeed > 5.0f) { // 假设5m/s以上才有明显的稳定效果
        stability_factor = (airspeed - 5.0f) * 0.01f;
        if (stability_factor > 0.2f) stability_factor = 0.2f; // 限制最大稳定效果
        
        // 高速时减小随机扰动
        acc[0] *= (1.0f - stability_factor);
        acc[1] *= (1.0f - stability_factor);
        gyro[0] *= (1.0f - stability_factor);
        gyro[1] *= (1.0f - stability_factor);
        gyro[2] *= (1.0f - stability_factor);
    }
    
    // 模拟重力对加速度的影响(根据当前姿态)
    // 这里使用简化模型,假设舵面角度直接影响姿态角
    // 在实际情况下,应该通过积分角速度来更新姿态角
    
    // 假设当前俯仰角和横滚角与舵面角度成正比(简化模型)
    float pitch_angle = elevator_angle * 0.2f; // 比例因子可以根据实际情况调整
    float roll_angle = aileron_angle * 0.2f;
    
    // 将角度转换为弧度
    float pitch_rad = pitch_angle * M_PI / 180.0f;
    float roll_rad = roll_angle * M_PI / 180.0f;
    
    // 根据姿态角计算重力在三个轴上的分量
    // 这是一个简化的模型,实际上应该使用完整的旋转矩阵
    float gx = sinf(pitch_rad);
    float gy = -sinf(roll_rad) * cosf(pitch_rad);
    float gz = cosf(roll_rad) * cosf(pitch_rad);
    
    // 将重力分量添加到加速度中(重力加速度标准值为1g)
    acc[0] = 0.8f * acc[0] + 0.2f * gx;
    acc[1] = 0.8f * acc[1] + 0.2f * gy;
    acc[2] = 0.8f * acc[2] + 0.2f * gz;
    
    // 添加一些随机噪声,模拟真实传感器的噪声
    acc[0] += (rand() / (float)RAND_MAX - 0.5f) * 0.02f;
    acc[1] += (rand() / (float)RAND_MAX - 0.5f) * 0.02f;
    acc[2] += (rand() / (float)RAND_MAX - 0.5f) * 0.02f;
    
    gyro[0] += (rand() / (float)RAND_MAX - 0.5f) * 0.5f;
    gyro[1] += (rand() / (float)RAND_MAX - 0.5f) * 0.5f;
    gyro[2] += (rand() / (float)RAND_MAX - 0.5f) * 0.5f;
}

/**
 * @brief 模拟读取加速度和陀螺仪数据
 * @param[out] acc 加速度数据数组[3]
 * @param[out] gyro 陀螺仪数据数组[3]
 * @note 在实际应用中,这个函数应该从传感器读取真实数据
 */
void read_sensor_data(float acc[3], float gyro[3])
{
    // 模拟数据 - 在实际应用中应该从传感器读取
    // 这里假设飞行器处于近似水平状态,有轻微扰动
    
    // 加速度数据 (x, y, z) 单位: g
    // 理想情况下,水平静止时 acc = {0, 0, 1}
    static float time_counter = 0.0f;
    time_counter += 0.02f; // 每次增加20ms
    
    // 添加一些随机扰动和周期性变化模拟真实环境
    acc[0] = 0.05f * sinf(time_counter * 0.5f); // 轻微前后晃动
    acc[1] = 0.03f * cosf(time_counter * 0.7f); // 轻微左右晃动
    acc[2] = 0.98f + 0.02f * sinf(time_counter * 0.3f); // 接近1g,有轻微上下波动
    
    // 陀螺仪数据 (x, y, z) 单位: 度/秒
    // 理想情况下,静止时 gyro = {0, 0, 0}
    gyro[0] = 2.0f * cosf(time_counter * 0.6f); // 轻微俯仰角速度
    gyro[1] = 1.5f * sinf(time_counter * 0.8f); // 轻微横滚角速度
    gyro[2] = 1.0f * sinf(time_counter * 0.4f); // 轻微偏航角速度
}

/**
 * @brief 模拟读取PWM输入通道
 * @param[out] pwm_channels PWM值数组[4],分别对应俯仰、横滚、偏航和油门通道
 * @note 在实际应用中,这个函数应该从接收机读取真实PWM值
 */
void read_pwm_channels(int pwm_channels[4])
{
    // 模拟数据 - 在实际应用中应该从接收机读取
    // 这里假设接收到的PWM值在中间位置附近有轻微变化
    static float time_counter = 0.0f;
    time_counter += 0.02f; // 每次增加20ms
    
    // 添加一些随机扰动和周期性变化模拟真实环境
    // 中间值1500附近波动
    pwm_channels[0] = 1500 + (int)(50.0f * sinf(time_counter * 0.3f)); // 俯仰通道
    pwm_channels[1] = 1500 + (int)(40.0f * cosf(time_counter * 0.5f)); // 横滚通道
    pwm_channels[2] = 1500 + (int)(30.0f * sinf(time_counter * 0.7f)); // 偏航通道
    pwm_channels[3] = 1500 + (int)(100.0f * sinf(time_counter * 0.2f)); // 油门通道
}

/**
 * @brief 判断PID控制是否达到稳定状态
 * @param[in] control_output 控制输出数组[3]
 * @return 如果所有控制输出都小于阈值,返回1,否则返回0
 * @note 无
 */
int is_pid_stable(float control_output[3])
{
    // 定义稳定阈值
    const float threshold = 0.5f;
    
    // 检查所有控制输出是否都小于阈值
    if (fabsf(control_output[0]) < threshold &&
        fabsf(control_output[1]) < threshold &&
        fabsf(control_output[2]) < threshold) {
        return 1; // 稳定
    }
    
    return 0; // 不稳定
}

/**
 * @brief 稳定飞行单元测试函数
 * @note 此函数包含一个无限循环,模拟飞行控制系统的运行
 */
void stable_flight_unit_test(void)
{
    // 初始化PID控制器
    init_simple_stable_pid();
    
    // 定义变量
    float acc[3];          // 加速度数据
    float gyro[3];         // 陀螺仪数据
    int pwm_channels[4];   // PWM输入通道,包括油门
    float target_angles[3]; // 目标角度
    float control_output[3]; // 控制输出
    int output_pwm[3];     // 输出PWM值
    int stable_count = 0;   // 稳定计数器
    int iteration = 0;      // 迭代计数器
    float dt = 0.02f;      // 时间步长,20ms
    
    // 初始化输出PWM值为中间值(1500)
    output_pwm[0] = 1500;
    output_pwm[1] = 1500;
    output_pwm[2] = 1500;
    
    // 初始化随机数生成器
    srand(time(NULL));
    
    printf("开始稳定飞行单元测试...\n");
    
    // 无限循环,每次迭代模拟0.02秒
    while (1) {
        // 1. 读取当前的加速度和陀螺仪数据
        read_sensor_data(acc, gyro);
        
        // 1.1 根据上一次的控制输出和当前油门值更新传感器数据,模拟舵面和速度对传感器的物理影响
        update_sensor_by_control(acc, gyro, output_pwm, pwm_channels[3], dt);
        
        // 2. 读取三个通道的PWM值(1000-2000)
        read_pwm_channels(pwm_channels);
        
        // 将PWM值线性转换为目标角度
        target_angles[0] = pwm_to_angle(pwm_channels[0], 0); // 俯仰目标角度 (±20°)
        target_angles[1] = pwm_to_angle(pwm_channels[1], 1); // 横滚目标角度 (±40°)
        target_angles[2] = pwm_to_angle(pwm_channels[2], 2); // 偏航目标角度变化率 (±20°)
        
        // 3. 调用PID控制函数,并传递油门值进行自动调整
        simple_stable_pid_control(acc, gyro, 
                                  target_angles[0], 
                                  target_angles[1], 
                                  target_angles[2], 
                                  control_output,
                                  &pwm_channels[3]);
        
        // 4. 将控制输出转换为PWM值(1000-2000)并打印结果
        output_pwm[0] = control_to_pwm(control_output[0]);
        output_pwm[1] = control_to_pwm(control_output[1]);
        output_pwm[2] = control_to_pwm(control_output[2]);
        
        // 打印当前迭代的信息
        printf("迭代 %d:\n", iteration);
        printf("  输入 PWM: [%d, %d, %d, %d]\n", pwm_channels[0], pwm_channels[1], pwm_channels[2], pwm_channels[3]);
        printf("  目标角度: [%.2f, %.2f, %.2f]\n", target_angles[0], target_angles[1], target_angles[2]);
        printf("  控制输出: [%.2f, %.2f, %.2f]\n", control_output[0], control_output[1], control_output[2]);
        
        // 记录调整前的油门值,用于检测是否被自动调整
        static int prev_throttle = 0;
        if (iteration == 0) {
            prev_throttle = pwm_channels[3];
        }
        
        // 显示油门信息,包括是否被自动调整
        printf("  油门: %d (%.1f%%)", pwm_channels[3], (pwm_channels[3] - 1000) / 10.0f);
        if (pwm_channels[3] > prev_throttle) {
            printf(" [自动增加 +%d]\n", pwm_channels[3] - prev_throttle);
        } else if (pwm_channels[3] < prev_throttle) {
            printf(" [自动减少 -%d]\n", prev_throttle - pwm_channels[3]);
        } else {
            printf(" [未调整]\n");
        }
        prev_throttle = pwm_channels[3];
        
        printf("  输出 PWM: [%d, %d, %d]\n", output_pwm[0], output_pwm[1], output_pwm[2]);
        
        // 5. 检查PID是否接近稳定
        if (is_pid_stable(control_output)) {
            stable_count++;
            printf("  状态: 稳定 (%d/5)\n", stable_count);
            
            // 如果连续5次稳定,则退出循环
            if (stable_count >= 5) {
                printf("\n稳定飞行测试成功! PID控制器已达到稳定状态.\n");
                break;
            }
        } else {
            stable_count = 0;
            printf("  状态: 不稳定\n");
        }
        
        printf("\n");
        
        // 增加迭代计数器
        iteration++;
        
        // 模拟延时0.02秒
        // 在实际应用中,应该使用真实的延时函数
        // delay_ms(20);
        
        // 为了防止无限循环,设置最大迭代次数
        if (iteration >= 100) {
            printf("\n达到最大迭代次数,测试结束。PID控制器未能达到稳定状态.\n");
            break;
        }
    }
}
相关推荐
腾飞的信仰6 小时前
单片机,主循环和中断资源访问冲突的案例
单片机·嵌入式硬件
猿来不是梦8 小时前
RT_Thread内核源码分析(五)——内存管理@小堆内存管理算法
stm32·单片机·算法·系统架构·rt_thread操作系统
tyl211019 小时前
凌科芯安国产安全MCU简介
单片机·嵌入式硬件·安全
郦77719 小时前
国产入门级32位单片机PY32F002A开发板
单片机·嵌入式硬件
passer__jw7671 天前
【51单片机】6. 定时器、按键切换流水灯&时钟Demo
单片机·51单片机
Wendy_robot1 天前
【零基础勇闯嵌入式岗】从单片机低功耗中获得的启发
c++·单片机·嵌入式硬件
学习噢学个屁1 天前
基于STM32汽车温度空调控制系统
c语言·stm32·单片机·嵌入式硬件·汽车
郦7771 天前
价格性价比高系列的高性能单片机MS32C001-C
单片机·嵌入式硬件
iCxhust1 天前
汇编字符串比较函数
c语言·开发语言·汇编·单片机·嵌入式硬件