ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

无人机飞控PID算法实战:从原理到调参的工程细节

无人机飞控PID算法实战:从原理到调参的工程细节 1. 从一次炸机说起为什么PID是飞控的灵魂去年秋天我带着一台自组的450轴距四旋翼去郊外试飞。起飞很稳悬停也还行但一切到定高模式机身就开始像喝醉了酒一样上下点头幅度越来越大最后直接翻扣在草地上断了两根桨。回来查日志、翻代码折腾了整整两天最后发现问题出在高度环的PID参数上——微分项给得太大气压计本身的噪声被放大成了剧烈的抖动。这件事让我彻底明白一个道理无人机飞控里姿态解算、传感器融合这些固然重要但真正决定一架飞机好不好飞的是PID控制器调得怎么样。你可以把飞控板理解成大脑IMU是内耳电机是肌肉而PID就是那套把我想保持水平翻译成四个电机各出多少力的神经反射机制。这套机制没调好再贵的硬件也是白搭。这篇内容我想聊的是飞行控制中的PID算法围绕无人机飞控这个具体场景展开。不管你现在用的是APM、Pixhawk这类成熟飞控还是打算自己从零写一套控制代码PID都是绕不开的核心。我会从原理讲到代码实现从参数整定讲到实际调试中那些文档里不会写的坑尽量让刚入门的朋友也能看懂同时给已经在调机的老手一些可以对照的细节。需要说明的是PID本身是个很成熟的经典控制理论但把它用在多旋翼上有很多工程上的特殊处理——比如积分限幅、微分先行、串级控制、采样周期一致性等等。这些才是真正决定成败的地方也是我下面要重点展开的内容。2. PID在飞控里到底控制的是什么2.1 位置式PID的数学本质与物理直觉先把公式摆出来位置式PID的离散形式是这样的u(k) Kp * e(k) Ki * Σe(i) Kd * (e(k) - e(k-1))其中e(k)是当前时刻的误差也就是期望值减去实际值。u(k)是控制器输出在飞控里通常对应电机的修正量。很多人背公式背得很熟但一到调参就懵。我用一个生活化的类比来解释这三项各自在干什么。想象你在开车目标是让车保持在车道正中间比例项Kp你眼睛看到车偏左了就往右打一点方向盘。偏得越多打得越多。这就是比例控制反应直接但光靠它车会一直在中心线附近来回晃永远停不下来。积分项Ki你发现车总是稳定地偏左一点点怎么打方向都差那么一丢丢。这时候积分项开始累积这个长期偏差慢慢往右多打一点直到偏差被彻底消除。它管的是历史遗留问题。微分项Kd你看到车正在快速往左偏虽然现在偏得还不算多但趋势不对。微分项感知的是误差变化的速度提前往右打方向起到阻尼作用防止冲过头。放到无人机上姿态环的误差就是期望角度减去实际角度。比如你遥控器打杆要求横滚10度IMU解算出来当前是3度误差就是7度。PID根据这个7度算出应该给电机多大的差速修正。这里有个关键点很多人忽略飞控里的PID几乎都是串级结构。外环是角度环输入期望角度输出期望角速度内环是角速度环输入期望角速度输出电机修正量。为什么这么设计因为角度环响应慢、抗干扰差而角速度环响应快、能抑制突发扰动。串级结构相当于给系统加了一层快速内卫外环只需要操心大方向对不对内环负责动作干不干净。2.2 增量式PID为什么在飞控代码里更常见热词里提到了增量式PID算法这个确实值得单独说。增量式PID输出的不是控制量的绝对值而是控制量的增量Δu(k) Kp*(e(k)-e(k-1)) Ki*e(k) Kd*(e(k)-2e(k-1)e(k-2))它和位置式在数学上是等价的但工程上有几个实实在在的好处第一不需要累加历史误差。位置式的积分项要一直累加时间长了容易积分饱和还得额外做限幅。增量式天然只关心最近三次误差计算量小也不会累积出一个巨大的积分值。第二输出是增量天然抗积分饱和。当执行机构电机已经到极限了增量式不会像位置式那样继续把积分项堆到天上松手时不会出现积分反冲导致的剧烈回摆。第三切换无扰动。如果控制模式切换增量式从当前值继续叠加不会产生跳变。不过增量式也有代价它对积分项的物理意义没那么直观调参时Ki的作用和位置式略有不同需要重新建立手感。我个人的经验是姿态内环用增量式很舒服外环位置控制用位置式更直观。Pixhawk的AC_AttitudeControl里其实两种思路都有影子它用的是速率环角度环的串级速率环内部实现更接近增量式的思想。2.3 采样周期被最多人忽视的隐形参数我见过太多人调参调不出来最后发现是采样周期不一致导致的。PID的离散形式是建立在固定采样周期T上的Kp、Ki、Kd的物理量纲都和T有关。如果你在100Hz下整定好的参数直接搬到400Hz的循环里等效的积分和微分作用会完全变样。具体来说积分项实际是Ki * T * Σe微分项实际是Kd / T * (e(k)-e(k-1))。采样周期变了这两项的实际增益就变了。所以你在移植参数时要么保证采样周期一致要么按比例重新换算。飞控里通常主控制循环跑在1kHzPixhawk的陀螺仪采样和姿态控制而位置环可能只有50到100Hz。不同环路的参数绝对不能混用这是新手最容易踩的坑之一。3. 姿态环PID的代码实现细节3.1 一个能跑的最小实现框架下面这段是我自己写飞控时用的姿态角速度环PID核心逻辑用C语言写的思路清晰方便移植到STM32或者树莓派上typedef struct { float kp, ki, kd; float integral; float prev_error; float prev_measurement; float integral_limit; float output_limit; float d_term_filtered; float d_filter_alpha; // 微分低通滤波系数 } PID_Controller; float PID_Update(PID_Controller *pid, float setpoint, float measurement, float dt) { float error setpoint - measurement; // 比例项 float p_term pid-kp * error; // 积分项带限幅 pid-integral pid-ki * error * dt; if (pid-integral pid-integral_limit) pid-integral pid-integral_limit; else if (pid-integral -pid-integral_limit) pid-integral -pid-integral_limit; // 微分项对测量值微分避免设定值突变引起的微分冲击 float d_raw -pid-kd * (measurement - pid-prev_measurement) / dt; // 一阶低通滤波 pid-d_term_filtered pid-d_filter_alpha * pid-d_term_filtered (1 - pid-d_filter_alpha) * d_raw; pid-prev_measurement measurement; float output p_term pid-integral pid-d_term_filtered; // 输出限幅 if (output pid-output_limit) output pid-output_limit; if (output -pid-output_limit) output -pid-output_limit; return output; }这段代码里有几个细节值得展开说因为它们直接决定了实际飞行表现。3.2 微分项为什么用测量值微分而不是误差微分标准PID公式里微分项是Kd * (e(k) - e(k-1))也就是对误差微分。但误差等于设定值减测量值所以误差微分等于设定值微分减测量值微分。问题来了当遥控器快速打杆时设定值会阶跃变化设定值微分瞬间变成一个巨大的脉冲微分项就会输出一个剧烈的尖峰电机猛地一抖。这就是所谓的微分冲击Derivative Kick。解决办法就是只对测量值微分忽略设定值的变化。因为设定值突变是我们人为给的不需要微分项去预测它。代码里写成-kd * (measurement - prev_measurement) / dt注意前面的负号因为测量值增大意味着误差减小微分作用方向要反过来。这个细节在教科书里经常一笔带过但在实际飞控里是必须处理的否则每次快速打杆都会看到电机抽搐。3.3 微分低通滤波噪声和响应的权衡陀螺仪出来的角速度信号一定带噪声而微分项本质上是在放大高频成分。如果不滤波Kd稍微给大一点电机就会发出高频的滋滋声机身发烫严重时直接振荡。所以微分项后面必须跟一个低通滤波器。上面代码用的是一阶IIR滤波alpha越小滤波越强、相位滞后越大。这个系数怎么选我的经验是控制环路典型采样率建议alpha说明角速度内环1kHz0.7~0.9响应优先滤波轻角度外环250~500Hz0.5~0.7平衡噪声与滞后高度环50~100Hz0.3~0.5气压计噪声大重滤波位置环50Hz0.2~0.4最重滤波响应最慢滤波太强会导致相位滞后系统变得迟钝表现为打杆后飞机反应慢半拍松杆后回中拖泥带水。滤波太弱则噪声放大表现为高频抖动。这个平衡点只能靠试飞找没有万能值。3.4 积分限幅与抗饱和的工程处理积分项是把双刃剑。它能消除稳态误差但一旦执行机构饱和电机已经全速了积分项还在继续累加等误差反向时这个巨大的积分值需要很长时间才能泄放掉导致系统大幅超调甚至振荡。这就是积分饱和。处理办法有几种我用下来最实用的是积分分离限幅组合积分限幅给积分项设一个上下限比如输出限幅的30%~50%。上面代码里的integral_limit就是干这个的。积分分离当误差很大时比如刚起飞或者剧烈机动暂时关闭积分等误差进入小范围再启用。这样避免大误差期间积分疯狂累积。反计算抗饱和当输出饱和时把饱和量与输出的差值反馈回积分项主动减小积分。这个实现稍复杂但在高动态场景下效果最好。我自己的飞控里用的是积分限幅加简单的分离逻辑实测足够应付大多数飞行场景。如果你要做特技飞行或者大机动建议上反计算抗饱和。4. 参数整定从理论方法到实战手感4.1 先调内环再调外环的铁律串级PID的整定顺序绝对不能乱。必须先把角速度内环调好再调角度外环。原因很简单内环是外环的执行机构内环如果本身就在振荡外环根本没法正常工作。具体操作上我的流程是这样的只开内环P把角度外环全部置零或者设成纯比例且增益很小让飞机处于手动速率模式。慢慢增大内环Kp直到用手快速拨动飞机时能感到明显的抵抗但松手后不会自己振荡。加内环DKp到位后飞机会有回中过冲。逐步加Kd直到回中干脆利落、没有来回摆动。注意听电机声音出现高频啸叫说明D给多了或者滤波不够。加内环I内环I一般给得很小主要用来消除稳态误差。如果飞机在悬停时缓慢漂移可以稍微加一点I。但内环I给大了容易引起低频振荡。调外环P内环稳了之后加角度外环Kp。打杆要求10度飞机实际能到10度且不振荡就差不多了。调外环I和D外环I消除角度稳态误差外环D抑制角度超调。外环D同样需要滤波。这个顺序听起来简单但实际调的时候每一环都要反复试飞、看日志、改参数。我一般会准备一个蓝牙数传在飞场直接用手机改参数不用反复插拔USB效率高很多。4.2 Ziegler-Nichols法在飞控上的局限教科书里讲PID整定必提Ziegler-Nichols临界比例法先把I和D关掉增大P直到系统等幅振荡记录临界增益Ku和振荡周期Tu然后按表查参数。这个方法在飞控上能用但有几个现实问题。第一让一架四旋翼进入等幅振荡状态本身就很危险稍不注意就炸机。第二飞控系统有很强的非线性和延迟ZN法算出来的参数往往偏激进实际飞起来超调严重。第三多旋翼的四个轴之间有耦合单轴振荡会带动其他轴。我的建议是ZN法可以用来找一个初始范围但最终参数一定要靠试飞微调。别指望套公式就能得到完美参数。4.3 用日志说话黑匣子数据分析调参不能靠感觉一定要看日志。Pixhawk的日志里可以导出期望姿态、实际姿态、陀螺仪原始数据、电机输出等。我通常关注这几个指标期望角速度 vs 实际角速度两条曲线贴合度越高内环跟踪越好。如果实际值总是滞后于期望值说明Kp不够或者滤波太强。超调量阶跃响应下实际值超过期望值的百分比。一般控制在10%~20%比较舒服。振荡频率如果出现持续振荡看频率。高频振荡几十Hz以上通常是D太大或滤波不够低频振荡几Hz通常是P太大或I太大。电机输出饱和度如果电机输出经常顶到上限说明参数太激进或者动力不足。我习惯把日志导出来用Python画图对比比在飞控地面站里看直观得多。下面是一个简单的分析脚本框架import pandas as pd import matplotlib.pyplot as plt log pd.read_csv(flight_log.csv) t log[time] plt.figure(figsize(12, 6)) plt.subplot(2,1,1) plt.plot(t, log[roll_setpoint], label期望横滚角速度) plt.plot(t, log[roll_actual], label实际横滚角速度) plt.legend() plt.subplot(2,1,2) plt.plot(t, log[motor1], label电机1输出) plt.plot(t, log[motor2], label电机2输出) plt.legend() plt.show()通过对比曲线你能清楚看到是跟踪慢、超调大还是振荡然后有针对性地改参数而不是盲目乱试。5. 那些文档里不会写的调试坑5.1 振动是PID调不好的头号元凶我前面提到的那次炸机表面看是高度环参数问题但根子上是机架振动太大。振动会通过加速度计和陀螺仪进入控制回路被微分项放大导致电机输出抖动进而加剧振动形成恶性循环。判断振动问题的方法看陀螺仪原始数据的频谱。如果某个频率通常是电机转速对应的频率有明显的峰值那就是机械振动。解决办法是做好减震——用软胶垫安装飞控、做桨叶动平衡、检查机架刚性。这些机械层面的工作做不好PID怎么调都是治标不治本。我的经验是先解决振动再谈调参。一个振动干净的机架PID参数容差会大很多调起来轻松得多。5.2 电池电压下降导致的参数漂移这个坑很隐蔽。锂电池从满电4.2V放到3.5V动力系统响应会明显变软。如果你在满电时调好的参数飞到后半程可能会发现飞机变得肉响应变慢。这是因为同样的PID输出对应的实际力矩变小了。成熟飞控比如Pixhawk有电压补偿机制会根据电池电压动态调整输出增益。如果你自己写飞控建议也加上这个补偿或者至少在调参时用中等电量的电池别用满电调参然后抱怨后半程不好飞。5.3 温度对陀螺仪零偏的影响MEMS陀螺仪有温漂。冷机启动和飞了几分钟之后零偏会变化。如果积分项没有正确处理零偏飞机可能会缓慢漂移。解决办法是起飞前让飞控充分预热通电静置几分钟或者做温度补偿标定。这个细节在室内短时间飞可能感觉不到但长时间飞行或者温差大的环境下就很明显。5.4 参数不是越紧越好新手常犯的另一个错误是把参数调得太激进追求指哪打哪的凌厉手感。但过紧的参数意味着系统裕度小遇到阵风、负载变化、电池衰减时容易失稳。我现在的习惯是在保证跟踪性能的前提下留出20%~30%的裕度。飞机飞起来跟手但不神经质这才是好参数。6. 从单环到串级位置控制的PID设计6.1 高度环的特殊性高度控制是很多人的噩梦因为它涉及气压计、加速度计、超声波/激光定高多个传感器而且气压计本身噪声大、易受气流影响。高度环的PID设计和姿态环有本质区别。高度环通常是位置-速度串级外环输入期望高度输出期望爬升速度内环输入期望速度输出油门修正量。外环P为主I很小D基本不用因为气压计噪声太大D会放大噪声。内环速度环可以用加速度计提供较干净的速度估计P和I都用D适度。我调高度环的经验是先把悬停油门找准。悬停油门是高度环的工作点如果这个点找不准PID要花很大力气去补偿效果肯定差。找悬停油门的方法很简单在定高模式下慢慢加油门直到飞机基本不动记录那个油门值。6.2 位置环与返航逻辑水平位置控制通常用GPS或者光流。位置环也是串级位置误差经过P控制器变成期望速度速度误差经过PI控制器变成期望倾角倾角再交给姿态环执行。这里有个关键限制期望倾角必须限幅一般不超过30~35度否则飞机会倾斜过大导致升力不足掉高度。返航逻辑本质上就是给位置环一个动态的期望位置序列。PID负责让飞机沿着这条路径飞。这里D项要慎用因为GPS位置更新率低通常5~10Hz且有跳变D项会放大这些跳变。我一般位置环只用P速度环用PI。7. 进阶话题自适应与非线性PID7.1 为什么固定参数不够用一架无人机的动力学特性会随负载、电池电压、飞行速度变化。固定参数PID在一种工况下调好换一种工况可能就不理想。比如挂上云台后惯量增大同样的Kp会导致响应变慢高速前飞时气动阻尼变化参数也需要调整。工程上的折中方案是增益调度Gain Scheduling根据当前工况比如油门大小、飞行模式在不同参数组之间切换或插值。Pixhawk里就有类似的机制比如根据油门位置调整姿态环增益。7.2 自适应PID的简单实现思路真正的自适应控制比较复杂但有个简化版很实用根据误差大小动态调整Kp。误差大时用大Kp快速响应误差小时用小Kp避免超调。实现上就是给Kp乘一个随误差衰减的系数。float adaptive_kp pid-kp * (1.0f 0.5f * fabsf(error) / max_error);这种非线性PID在航拍机上效果不错能让飞机既响应快又不过冲。但要注意别让增益变化太剧烈否则会引起参数振荡。8. 我个人的调参心得与工具链最后分享一些我这些年攒下来的实操习惯都是踩坑踩出来的。工具方面我强烈建议配一个蓝牙数传模块配合手机地面站能在飞场实时改参数。每次改完立刻试飞效率比回办公室插USB高十倍。另外准备一个笔记本记录每次参数改动和对应的飞行表现形成自己的调参日志时间长了你会总结出针对自己机型的经验规律。试飞环境一定要选开阔、无人、无强风的地方。调参阶段飞机随时可能失控安全永远是第一位的。我一般会在飞机上装一个蜂鸣器万一飞丢了能靠声音找回来。心态方面调参是个耐心活别指望一次成功。我调一架新飞机通常要飞十几个起落才能达到满意状态。每次只改一个参数改完试飞记录结果再改下一个。同时改多个参数出了问题你根本不知道是哪个引起的。关于参数备份调好的参数一定要导出备份并且标注机型、负载、电池配置。换飞机或者换固件后参数不能直接照搬但可以作为起点参考。PID这个东西理论看着简单工程上全是细节。但正是这些细节区分了一架能飞的飞机和一架好飞的飞机。希望这些内容能帮你少走点弯路早点调出自己满意的手感。
返回列表