使用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;
        }
    }
}
相关推荐
点灯小铭8 小时前
基于单片机的多路热电偶温度监测与报警器
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
tianyue10012 小时前
STM32G431 ADC 多个channel 采集
stm32·单片机·嵌入式硬件
清风66666613 小时前
基于单片机的水泵效率温差法测量与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
z203483152015 小时前
定时器练习报告
单片机·嵌入式硬件
zk0015 小时前
内容分类目录
单片机·嵌入式硬件
安生生申15 小时前
STM32 ESP8266连接ONENET
c语言·stm32·单片机·嵌入式硬件·esp8266
广药门徒15 小时前
电子器件烧毁的底层逻辑与避坑指南
单片机·嵌入式硬件
点灯小铭20 小时前
基于单片机的社区医院小型高压蒸汽灭菌自动控制器设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
youcans_20 小时前
【动手学STM32G4】(3)STM32G431之定时器
stm32·单片机·嵌入式硬件·定时器
悠哉悠哉愿意20 小时前
【嵌入式学习笔记】AD/DA
笔记·单片机·嵌入式硬件·学习