☰
无人机飞控串级PID算法:原理、代码实现与参数整定指南
2026/10/5 3:15:43 网站建设 项目流程

这次我们来看一个无人机控制领域的核心算法——串级PID。对于想深入无人机飞控开发、自己写控制代码的开发者来说,PID是绕不开的基础,而串级PID更是提升飞行器动态响应和稳定性的关键结构。它不是什么新概念,但能把原理讲透、代码写对、参数调好的人并不多。

这篇文章直接切入主题,不讲复杂的控制理论推导,重点解决三个问题:串级PID是什么结构、为什么比单级PID好、以及如何用代码实现一个可用的串级PID控制器。我们会从算法原理、代码结构、参数整定步骤到实际调试注意事项,完整走一遍。如果你关心如何在STM32、Arduino或者简单的仿真环境中实现无人机的姿态或位置控制,这篇内容可以直接参考。

1. 核心能力速览

在深入代码之前,我们先快速了解串级PID控制器的核心特性和应用场景。

能力项说明
控制结构两个PID控制器串联,内环(快环)和外环(慢环)分工协作。
主要优势提升系统动态响应速度,增强抗干扰能力,改善控制精度。常用于无人机姿态(角速度/角度)、位置(速度/位置)控制。
硬件门槛极低。算法本身不依赖特定硬件,从8位单片机到高性能处理器均可运行。关键在于传感器数据(如陀螺仪、加速度计)的获取频率和精度。
代码复杂度中等。结构比单级PID清晰,但参数翻倍,整定更需章法。
适用场景无人机、平衡车、云台、机器人等需要快速、稳定闭环控制的动态系统。
调试关键遵循“先内环后外环”、“先比例后积分微分”的参数整定原则。

2. 串级PID原理:为什么是“串级”?

单级PID控制器直接根据目标值与实际值的误差进行计算输出。但在像无人机这样的复杂对象中,直接控制最终目标(如角度)可能响应不够快,抗干扰性差。

串级PID引入了两个控制环:

  1. 外环(慢环):计算“目标值”与“实际值”的误差,输出作为内环的“目标值”。例如,角度环:输入目标角度和当前角度,输出目标角速度。
  2. 内环(快环):接收外环的输出作为自己的目标,与更快速的“实际值”比较计算误差,并生成最终的控制量输出。例如,角速度环:输入目标角速度(来自角度环)和当前角速度(来自陀螺仪),输出电机PWM信号。

这种结构好比公司管理:外环是经理,制定战略目标(要到某个角度);内环是执行员工,负责快速执行战术动作(以多快的速度旋转)。经理不直接指挥电机,而是通过指挥员工来实现目标,响应更快,管理更精细。

核心思想:外环负责“准”,消除静差,跟踪大趋势;内环负责“快”,抑制内扰,快速响应。将一个大而慢的控制问题,分解为两个小而快的控制问题。

3. 适用场景与使用边界

串级PID并非万能,理解其适用边界能避免误用。

最适合的场景:

  • 无人机姿态控制:最经典的案例。外环为角度环(Pitch/Roll/Yaw),内环为角速度环。内环快速响应陀螺仪数据,抑制机体震动;外环保证角度跟踪的准确性。
  • 无人机位置控制:外环为位置环(X/Y/Z),内环为速度环。内环控制飞行速度,外环控制最终到达的点。
  • 云台稳像:外环计算目标与实际角度的偏差,内环控制电机快速转动到指定角速度。
  • 平衡车:外环为车身倾角环,内环为电机速度环。

不适用或需谨慎的场景:

  • 被控对象模型简单、扰动小:如果用一个单级PID就能达到很好效果,增加串级只会增加调试复杂度。
  • 内外环耦合极强的系统:如果两个环的动力学特性无法明显区分快慢,串级结构优势不明显。
  • 对实时性要求极端苛刻:串级计算量约为单级的两倍,在计算资源极其有限的场合需评估。
  • “黑箱”系统:如果完全不了解被控对象的内环(快速)状态如何获取(比如没有陀螺仪,只有角度传感器),则无法构建内环。

安全边界:在调试真实无人机时,务必在安全环境下进行,先进行软件仿真或硬件台架测试。参数不当可能导致系统剧烈震荡甚至失控。永远不要在未经验证的代码和参数下进行首次飞行。

4. 环境准备与前置条件

实现串级PID不需要特殊的软件环境,关键在于对硬件平台和工具链的熟悉。

  1. 硬件平台(三选一或自选):

    • STM32系列(如F103、F405、F407):主流选择,资源丰富,社区支持好。需要准备调试器(ST-Link/J-Link)和IDE(Keil、STM32CubeIDE、PlatformIO)。
    • Arduino(如Mega 2560、Due):入门友好,适合快速验证概念。但处理能力和定时器精度可能成为高性能应用的瓶颈。
    • 仿真环境(如MATLAB/Simulink, Python):强烈推荐先在仿真中验证算法逻辑和参数范围,安全且高效。
  2. 传感器数据:

    • 内环反馈:必须是高速、直接的测量值。例如角速度环需要陀螺仪(Gyroscope)数据,速度环需要编码器或光流等提供的速度信息。
    • 外环反馈:相对低速、间接或积分得到的值。例如角度环可以由加速度计与陀螺仪融合(如互补滤波、卡尔曼滤波)得到,位置可以由速度积分或GPS得到。
    • 关键点:内环数据的更新频率必须显著高于外环,通常内环控制频率在500Hz-1kHz,外环在100-250Hz。
  3. 软件准备:

    • 嵌入式开发环境:配置好对应的编译、下载、调试工具。
    • 数据可视化工具:调试PID离不开数据观察。可以是串口绘图(如SerialPlot、Vofa+)、OLED屏幕显示,或者仿真软件的Scope模块。
    • 代码版本管理:建议使用Git,方便回溯参数修改。

5. 串级PID控制器代码实现

下面我们用一个C语言示例,展示一个通用的串级PID控制器结构。这个结构可以移植到大多数嵌入式平台。

首先,定义PID结构体。我们将分别定义内环和外环的结构体。

// pid_controller.h #ifndef PID_CONTROLLER_H #define PID_CONTROLLER_H typedef struct { float target; // 目标值 float measure; // 测量值 float error; // 当前误差 float error_last; // 上一次误差 float error_integral; // 误差积分项 float error_derivative; // 误差微分项 float kp, ki, kd; // PID参数 float output; // 控制器输出 float output_max; // 输出限幅上限 float output_min; // 输出限幅下限 float integral_max; // 积分限幅上限,防止积分饱和 // 可选:微分项滤波器系数,用于抑制高频噪声 float derivative_filter; float derivative_last; } PID_Controller; // 初始化PID控制器 void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float out_max, float out_min, float i_max); // 执行PID计算(位置式) float PID_Calculate(PID_Controller *pid, float target, float measure); #endif // PID_CONTROLLER_H

接下来是串级PID的核心结构。我们以“外环为角度,内环为角速度”为例。

// cascade_pid.h #ifndef CASCADE_PID_H #define CASCADE_PID_H #include "pid_controller.h" typedef struct { PID_Controller outer_loop; // 外环PID,例如角度环 PID_Controller inner_loop; // 内环PID,例如角速度环 // 串级计算的核心:外环的输出作为内环的目标 // 流程:外环计算 -> 结果作为内环目标 -> 内环计算 -> 最终输出 float final_output; } Cascade_PID_Controller; // 初始化串级PID void Cascade_PID_Init(Cascade_PID_Controller *cascade_pid, float outer_kp, float outer_ki, float outer_kd, float outer_out_max, float outer_out_min, float outer_i_max, float inner_kp, float inner_ki, float inner_kd, float inner_out_max, float inner_out_min, float inner_i_max); // 执行串级PID计算 // 参数:外环目标,外环测量值,内环测量值 float Cascade_PID_Calculate(Cascade_PID_Controller *cascade_pid, float outer_target, float outer_measure, float inner_measure); #endif // CASCADE_PID_H

下面是具体的实现文件:

// cascade_pid.c #include "cascade_pid.h" void Cascade_PID_Init(Cascade_PID_Controller *cascade_pid, float outer_kp, float outer_ki, float outer_kd, float outer_out_max, float outer_out_min, float outer_i_max, float inner_kp, float inner_ki, float inner_kd, float inner_out_max, float inner_out_min, float inner_i_max) { // 初始化外环PID PID_Init(&(cascade_pid->outer_loop), outer_kp, outer_ki, outer_kd, outer_out_max, outer_out_min, outer_i_max); // 初始化内环PID PID_Init(&(cascade_pid->inner_loop), inner_kp, inner_ki, inner_kd, inner_out_max, inner_out_min, inner_i_max); cascade_pid->final_output = 0.0f; } float Cascade_PID_Calculate(Cascade_PID_Controller *cascade_pid, float outer_target, float outer_measure, float inner_measure) { // 1. 外环计算:根据角度误差,计算出目标角速度 float inner_target = PID_Calculate(&(cascade_pid->outer_loop), outer_target, outer_measure); // 2. 内环计算:根据(目标角速度 vs 实际角速度)误差,计算出最终控制量(如电机PWM) cascade_pid->final_output = PID_Calculate(&(cascade_pid->inner_loop), inner_target, inner_measure); return cascade_pid->final_output; }

单级PID的计算实现(位置式,带积分抗饱和和输出限幅):

// pid_controller.c #include "pid_controller.h" #include <math.h> // 用于fabsf void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float out_max, float out_min, float i_max) { pid->target = 0.0f; pid->measure = 0.0f; pid->error = 0.0f; pid->error_last = 0.0f; pid->error_integral = 0.0f; pid->error_derivative = 0.0f; pid->kp = kp; pid->ki = ki; pid->kd = kd; pid->output = 0.0f; pid->output_max = out_max; pid->output_min = out_min; pid->integral_max = i_max; pid->derivative_filter = 0.0f; // 0表示不使用滤波器 pid->derivative_last = 0.0f; } float PID_Calculate(PID_Controller *pid, float target, float measure) { pid->target = target; pid->measure = measure; pid->error_last = pid->error; pid->error = pid->target - pid->measure; // 计算误差 // 积分项:累加误差,并做抗饱和处理 pid->error_integral += pid->error; if (pid->integral_max > 0) { // 限制积分项幅值 if (pid->error_integral > pid->integral_max) pid->error_integral = pid->integral_max; if (pid->error_integral < -pid->integral_max) pid->error_integral = -pid->integral_max; } // 微分项:计算误差变化率,可选加入低通滤波 pid->error_derivative = pid->error - pid->error_last; // 简易微分,实际需除以dt if (pid->derivative_filter > 0) { // 一阶低通滤波,抑制高频噪声 pid->derivative_last = pid->derivative_filter * pid->derivative_last + (1 - pid->derivative_filter) * pid->error_derivative; pid->error_derivative = pid->derivative_last; } // PID输出 = P + I + D pid->output = (pid->kp * pid->error) + (pid->ki * pid->error_integral) + (pid->kd * pid->error_derivative); // 输出限幅 if (pid->output > pid->output_max) pid->output = pid->output_max; if (pid->output < pid->output_min) pid->output = pid->output_min; return pid->output; }

代码关键点说明:

  1. Cascade_PID_Calculate是核心:它清晰地展示了数据流向——外环输出成为内环目标。
  2. 注意时间间隔:上述代码省略了微分项的时间间隔dt。在实际嵌入式系统中,必须在固定的定时中断中调用PID计算函数,并根据真实的时间间隔(如0.002秒对应500Hz)来修正微分和积分项。通常ki和kd参数是已经考虑了dt的。
  3. 积分抗饱和:integral_max非常重要,能防止系统在启动或遇到大误差时积分项过大,导致控制量饱和和超调。
  4. 微分滤波:微分项对噪声非常敏感,derivative_filter提供了一个简单的低通滤波选项。

6. 功能测试与参数整定步骤

有了代码框架,下一步就是让它动起来并调好。参数整定是PID控制的“艺术”,对于串级PID,必须遵循严格的步骤。

6.1 测试环境搭建

  1. 硬件在环:将控制器代码烧录到飞控板,连接电机、电调和传感器。将飞控固定在测试台架上(非常重要!避免炸机)。通过遥控器或地面站发送角度指令。
  2. 软件仿真:在MATLAB/Simulink或Python中建立无人机姿态动力学模型,将上面的C代码改写成对应的仿真模块进行测试。这是最安全、成本最低的方法。

6.2 参数整定黄金法则:“先内后外,先P后I再D”

总原则:将外环断开,先调好内环;内环稳定后,再接入外环进行调试。

步骤一:整定内环(角速度环)

  1. 初始化:将外环PID参数全部设为0,使其输出为0。确保你能获取到可靠的角速度测量值(如陀螺仪数据)。
  2. 调 P(比例):
    • 设定一个较小的目标角速度(例如 10 deg/s)。
    • inner_kp从0开始慢慢增大。
    • 观察现象:系统开始响应。继续增大kp,直到系统出现持续、小幅度的等幅振荡。此时系统处于临界稳定状态。
    • 记录:记下此时的kp值为Ku(临界增益),并测量振荡周期Tu。
  3. 调 D(微分):
    • 内环需要快速响应,微分项D对于抑制超调、增加阻尼非常有效。
    • 根据经验公式,初步设定kd = Ku * Tu / 8。
    • 加入kd后,振荡应该被抑制。微调kd,使系统能快速跟随目标角速度变化且无明显超调。
  4. 调 I(积分):
    • 内环主要追求快速无静差跟踪,积分项I通常可以设为0,或者一个很小的值,用于消除稳态误差。
    • 如果需要,加入一个很小的ki,观察是否能消除静差。注意内环积分太强会导致系统反应迟钝。

理想的内环效果:给一个阶跃的目标角速度信号,实际角速度应能快速、平稳地跟上,超调小,调节时间短。

步骤二:整定外环(角度环)

  1. 连接:将调好的内环参数固定。现在,外环的输出将作为内环的目标。
  2. 调 P(比例):
    • 设定一个目标角度(例如 10 度)。
    • outer_kp从0开始增大。
    • 观察现象:无人机会开始朝目标角度转动。继续增大kp,直到角度响应出现轻微振荡或达到满意的响应速度。外环的振荡频率远低于内环。
  3. 调 D(微分):
    • 外环的微分项有时可以不要,或者很小。如果需要,可以加入一点kd来增加阻尼,抑制角度超调。
  4. 调 I(积分):
    • 外环是加积分项的主要位置,用于消除角度静差(例如,在有风或重心不对称时仍能保持水平)。
    • 加入一个较小的outer_ki,观察角度能否稳定在目标值而无静差。积分项太强会引起系统缓慢振荡。

理想的串级效果:给定目标角度,无人机应平稳、快速地转动到该角度并保持稳定。内环像“肌肉”一样快速执行,外环像“大脑”一样精确指挥。

7. 资源占用与性能观察

串级PID本身计算量很小,在STM32F4等MCU上耗时通常小于10微秒,资源占用不是问题。真正的性能瓶颈和观察重点在于:

  1. 传感器数据频率与延迟:

    • 内环频率:角速度环建议运行在500Hz以上(周期2ms)。确保陀螺仪的读取和滤波能跟上这个速度。
    • 外环频率:角度环100-250Hz(周期4-10ms)通常足够。角度数据来自传感器融合(如互补滤波),需注意融合算法带来的延迟。
    • 关键观察:用逻辑分析仪或翻转GPIO引脚的方式,测量从传感器数据就绪到PID计算完成的时间,必须小于控制周期。
  2. 控制输出延迟:

    • PID计算出的PWM值,需要及时更新到定时器。确保PWM更新机制(如DMA、定时器中断)的延迟可控且稳定。
  3. 实时性保障:

    • 将PID计算放在高优先级的定时器中断中。
    • 避免在中断中进行浮点除法、三角函数等耗时操作。可以考虑使用arm_math库(CMSIS-DSP)的浮点运算加速,或者使用定点数运算来进一步提升速度。
  4. 内存占用:

    • 几个float型变量和结构体,内存占用可忽略不计。

性能测试方法:在代码中关键位置插入GPIO翻转语句,用示波器测量时间间隔,是评估实时性能最直接的方法。

8. 常见问题与排查方法

调试串级PID时,你会遇到各种现象。下表列出了典型问题及解决思路。

问题现象可能原因排查方式解决方案
系统剧烈发散振荡1. 内环P值过大。
2. 内外环极性(正负反馈)错误。
1. 断开外环,单独测试内环是否稳定。
2. 检查误差计算符号 (target - measure)。
1. 大幅降低内环P值,从零开始重调。
2. 确保控制器输出与系统响应方向正确。
内环稳定,加上外环后振荡外环P值过大。将外环P值减半观察。遵循“先内后外”原则,降低外环P值,可能还需要微调外环D。
响应非常慢,像“软绵绵”1. 内环P值过小。
2. 积分项过强(尤其是外环I)。
1. 观察内环对阶跃信号的响应。
2. 暂时将外环I设为0。
1. 适当增加内环P值。
2. 减小积分系数,或加入积分限幅。
存在稳态误差(静差)缺少积分作用或积分作用太弱。观察稳定后目标值与测量值的差值。适当增加外环的I值。注意,应先尝试加大P值,若仍无法消除再加I。
电机发出高频啸叫1. 控制频率进入人耳可闻范围(如8kHz以下)。
2. PWM频率与电机共振。
1. 测量控制循环实际频率。
2. 尝试改变PWM频率。
1. 确保控制频率足够高(>10kHz不现实,但输出滤波后可改善)。
2. 在PID输出后加入低通滤波器。
角度控制时缓慢漂移1. 陀螺仪零漂未校准。
2. 加速度计数据受线性加速度污染。
1. 静止时读取陀螺仪输出是否为零。
2. 运动时观察角度融合结果是否合理。
1. 上电时进行陀螺仪零偏校准。
2. 优化传感器融合算法(如使用更可靠的DCM或四元数卡尔曼滤波)。
代码运行一次后卡死1. 浮点运算溢出(如积分饱和后未限幅)。
2. 中断嵌套或优先级问题。
1. 检查所有变量范围,特别是积分项。
2. 检查中断服务函数是否过长或重入。
1. 加强变量限幅和异常值处理。
2. 简化中断服务程序,确保关键中断不被阻塞。

9. 最佳实践与使用建议

  1. 仿真先行:在MATLAB/Simulink或Python中搭建模型和控制器进行仿真。这能帮你快速理解参数影响,避免硬件损坏。
  2. 参数持久化:将调好的PID参数保存在飞控的Flash或EEPROM中,避免每次上电重新设置。
  3. 动态调整:可以考虑根据飞行模式(如自稳、定高、定点)切换不同的PID参数集。
  4. 加入死区:对于存在机械死区或静摩擦的系统,在PID输出端加入一个小的死区,可以避免电机持续微振。
  5. 输出滤波:在PID最终输出到执行器(如电机)之前,加一个一阶低通滤波器,可以平滑指令,减少机械磨损和噪声。
  6. 安全第一:
    • 始终在台架或安全网内进行初步测试。
    • 为所有控制量(角度、角速度、输出)设置软件限幅。
    • 编写紧急停止机制,例如遥控器开关触发所有电机停转。
  7. 数据记录与回放:实现一个简单的飞行数据黑匣子功能,记录传感器数据、控制指令和PID输出。这对于分析异常飞行状态至关重要。

串级PID是无人机飞控的基石之一。理解其原理,掌握从代码实现到参数整定的完整流程,你就掌握了让无人机“听话”的核心技能。从调通第一个稳定的角速度环开始,到最终实现流畅的姿态控制,这个过程充满挑战,但解决问题的成就感也是巨大的。建议将本文的代码框架作为起点,结合具体的硬件平台和传感器,动手实践,积累自己的调试经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询