四旋翼这玩意儿看着像个玩具真做起来才知道控制有多让人头疼。我刚开始做姿态控制的时候用的是最常规的PD控制器——就是比例加微分那一套Kp管“拉回去的力度”Kd管“拉回去时的刹车”。听起来简单但真正把四旋翼悬停稳定做出来我发现调参调了一周姿态在振荡、稳定、受风扰动后再振荡之间来回横跳。后来一位做控制的同学提了一句PD本身并没有错问题在于增益不匹配既然手动调不准能不能让算法自己去调于是我开始尝试用DDPG算法去自动搜索合适的PD增益跑了差不多一个月最后确实把原来自稳和响应速度之间的“妥协”打破了。这篇文章就是那段时间的完整记录。我会把为什么DDPG能用于优化PD控制、它在Matlab里怎么和四旋翼动力学模型结合起来、训练过程中遇到哪些坑、最终效果如何全部摊开讲。适合正在做四旋翼控制方向研究、又不愿意纯靠经验调参数的读者也适合刚入门强化学习、想看看一个连续控制实例长什么样的人。文中的代码都是可以直接在Matlab里跑的级别但我更希望大家关注背后的设计思路不然换个场景你照样不会用。1. PD控制器与四旋翼之间的“不匹配”问题的根源1.1 四旋翼真实的动力学难点四旋翼是一个欠驱动系统四个旋翼转速对应六自由度的运动输出这意味着横滚、俯仰、偏航和高度四条通道是强耦合在一起的。很多入门教程会先在悬停点附近做小角度线性化得到一个简化模型然后告诉你PD控制器加在这个线性模型上就够用了。理论上是这样工程上却处处碰壁。实际飞行中电机和电调存在响应延迟气流和旋翼之间的气动阻尼是非线性的机身稍有重心偏移就会引入额外的常值干扰电池电压下降还会导致推力特性变化。这些因素叠加在一起让真实被控对象和课本上的线性模型产生了明显偏差。固定增益的PD控制器只有在设计所依据的那个工作点附近才“最优”一旦姿态做大角度机动、载荷变化、或者碰上一阵不稳定的风原来自认为精心调出来的参数马上就露馅了。我记得最初测试时Kp调大一点悬停响应确实变快但机架开始高频抖动Kd加大之后抖动被压下去了可转向动作又变得迟钝。这就是固定增益PD在四旋翼上的核心矛盾它只能在一个窄窄的折中区间里工作没办法随着飞行状态动态调整自己的“性格”。1.2 PD参数之间的经典权衡PD控制器本质上是在做两件事比例项把系统拉向目标微分项预判误差趋势并提前刹车。要系统响应快Kp要大要系统不振荡Kd要跟上。问题在于Kp和Kd并不是独立作用它们通过系统阻尼比和自然频率耦合在一起。从控制理论的角度看一个二阶系统在增大Kp时自然频率上升响应变快但阻尼比会被压低超调和振荡随之而来提高Kd可以补阻尼但微分项对噪声极其敏感Kd稍微给大一点角速度传感器的毛刺就会被放大成控制指令的抖动。这个权衡是固定参数控制器的宿命你只能在工作点附近做到“够用”做不到“适应”。手动调参之所以痛苦是因为调节过程本质上是在高维参数空间里搜索一个满足多重约束的点人脑处理两三个变量可以同时考虑响应速度、超调、稳态误差、抗扰性、控制能耗还有作动器安全边界就已经超出了经验判断的能力范围。这也是我后来坚持用强化学习去自动搜索增益的核心动机。1.3 为什么选DDPG而不是别的算法既然要自动调参第一个想法可能是用PID整定方法里的Ziegler-Nichols或者单纯用优化算法去搜索。但这些方法都要求先有一个相对准确的对象模型而且对非线性、时变系统的适应性很有限。强化学习的优势在于它不需要显式建模系统的非线性只需要通过与环境交互试错来学习策略。选DDPG是因为这个任务的动作空间是连续的我要输出的正是Kp和Kd的调节系数而不是一个离散的动作。DQN那一类基于值函数的算法处理离散动作可以处理连续参数就不灵了。DDPG采用Actor-Critic结构Actor网络直接输出连续动作值天然适配这类参数生成任务。当然DDPG本身存在Q值过估计的问题实践中有不少团队会直接改用TD3我在后面的训练环节也会提到这个改进方向。但作为理解思路和复现结果的起点DDPG更直接涉及的网络数量和超参也更少适合先把整个流程跑通。2. DDPG的Action是从哪里来的Actor-Critic机制拆解2.1 用生活化的方式理解Actor-CriticDDPG的学习过程可以类比成“学生表演动作老师点评分数”。Actor就像一个学生它根据当前状态给出一个动作这个动作也就是一组PD增益的调节量。Critic就像一个严苛的老师它对“在某个状态下采取某个动作之后能得到多少累计奖励”打出一个分数Q(s,a)。学生每做一次动作老师会根据实际情况打分学生再根据分数高低调整自己的行为策略让分数更高的动作更容易被输出。和普通监督学习不同这里没有标准答案只有延迟到来的奖励信号。所以Critic的打分也必须是学出来的它通过不断接收环境的反馈来更新自己的评分标准。Actor和Critic交替更新、互相促进这就是Actor-Critic架构的核心思想。DDPG在这个框架上做了三个关键设计确定性策略、目标网络、经验回放这三者合起来才让连续控制任务能够稳定学习。2.2 确定性策略、目标网络和经验回放在这里各自扮演什么角色确定性策略意味着给定状态sActor直接输出一个确定的动作值a而不是一组动作的概率分布。这样做的优点是采样效率高连续动作空间的输出更平滑不会像随机策略那样在参数周围频繁抖动。缺点是需要额外引入探索机制否则策略会过早收敛到局部最优。我用的探索手段是OU噪声在训练早期给Actor输出的动作叠加一个时间相关的随机扰动让控制器能试探更多增益组合。OU噪声和普通高斯噪声的区别在于它有时间惯性动作的变化不会突然跳变这个特性对物理系统更友好控制量不会出现剧烈的突变。目标网络的作用是让学习过程变得稳定。Actor和Critic都存在一份“目标版本”它们的参数更新不是从主网络直接复制而是做软更新每隔一段时间把主网络参数向目标网络参数“滑”过去一小步而不是一次性替换。这样做能避免目标值频繁变动导致的学习震荡这很像用移动平均去平滑一组波动很大的数据代价是目标网络反应慢一点但换来了训练的稳定性。经验回放则是把过去交互的样本存进一个缓冲区训练时随机抽一批样本进行更新。四旋翼飞行轨迹中的数据是高度时间相关的如果不打乱顺序直接用网络很容易对刚刚经历的那一小段状态“过度记忆”导致学到的策略只对局部情况有效。回放机制把相关性打破让每次更新的样本尽可能多样化这是DDPG能在无人驾驶、机械臂等连续控制任务里稳定应用的重要基础。2.3 从强化学习视角重新定义“PD参数整定”把PD参数整定放进强化学习的框架里需要重新定义三个要素状态、动作、奖励。在这个项目里我把状态设计为六维向量包括当前高度误差、误差变化率、误差积分、目标高度变化率、当前姿态角和角速度。动作设计为两个连续值分别对应Kp和Kd的缩放系数。这样设计有一个好处支持的增益范围是相对的避免网络盲目输出绝对值很大的增益导致系统直接发散。奖励函数是强化学习中最能体现设计水平的部分。我用的奖励形式是奖励 -w1 * 高度误差平方 - w2 * 误差变化率平方 - w3 * 控制量平方 - w4 * 控制变化量平方每一项都有实际意义第一项惩罚位置偏差第二项惩罚速度抖动第三项惩罚控制能耗第四项惩罚控制指令的突变。四者加权求和之后取负值让控制器在“飞到位”和“飞得稳”之间自动寻找平衡点。权重w1到w4的选择本质上就是在表达你对控制器品质的偏好这就是调参从“调Kp、Kd”变成了“调奖励函数权重”的过程理解这一点很重要。3. 在Matlab里落地环境、奖励和训练循环的搭建细节3.1 先搭四旋翼动力学环境用Matlab实现DDPG优化PD控制第一步不是写强化学习代码而是先把四旋翼动力学环境搭起来。环境是强化学习的“游戏场地”它需要接收动作增益缩放系数输出下一时刻的状态和奖励。我用的是高度通道作示例动力学方程可以简化为一个二阶系统加电机响应延迟核心状态变量是高度和垂直速度。Matlab里面对这类环境有两种常见做法一种是直接用Simulink搭四旋翼模型在Simulink环境里配reinforcement learning相关接口另一种是用m脚本写一个step函数和一个reset函数然后用环境封装接口去构建。我选择了m脚本方式原因很简单迭代速度快、代码审计容易、不需要反复编译模型适合在算法探索阶段频繁修改环境参数。关键的step函数逻辑大概是这样的先利用当前状态和参考值计算出高度误差然后把DDPG输出的缩放系数作用在初始PD增益上得到实际使用的Kp和Kd再计算PD控制量最后通过动力学方程更新状态。其中还要把更新前的状态和更新后的状态都记录下来方便计算奖励和判断是否结束回合。3.2 用Matlab代码把状态空间、动作空间和网络结构写出来环境定义好之后就可以定义状态空间和动作空间了。在强化学习工具箱里状态和动作都是用指定的空间对象来表示的。状态维度是六维动作维度是二维动作的上下限我分别设为0.3和1.8代表PD增益最小可以缩到原始值的30%最大可以放到180%。这个范围是基于四旋翼动力学的稳定性分析确定的范围再大一点训练初期很容易出现系统发散范围再小一点策略的表达空间又不够。Actor和Critic网络的结构我采用的是比较经典的双隐藏层结构。Actor网络输入六维状态经过两个128神经元的全连接层加ReLU激活最后通过tanh激活函数输出两维动作。使用tanh激活是因为它能自然把输出压缩到[-1,1]区间再通过线性变换映射到动作空间的上限和下限这样可以避免网络输出超出安全范围。Critic网络的结构稍有不同它需要同时接收状态和动作两个输入所以在网络中间做了一个拼接层之后再经过全连接层输出一个标量Q值。为了让代码可读性更高我把网络构建、环境定义、训练配置分开写在不同的脚本里。网络参数初始化也值得注意很多人在这一步偷懒直接用默认初始化但实际训练中我发现Actor最后一层如果初始化范围太大训练初期输出动作就可能让四旋翼剧烈振荡直接毁掉整个训练曲线。我会把最后一层权重初始化为原始范围的0.01倍目的就是让策略在训练早期尽量平稳地“先探索再激进”。3.3 奖励函数的权重怎么定更合理奖励函数的权重是训练效果的分水岭。我在第一次尝试时用了比较随意的权重w1取1w2取1w3取0.1结果训练出来的策略非常激进控制器为了把误差降到最小疯狂输出大幅度的控制指令姿态角速度和电机转速都频繁触顶。后来我意识到强化学习优化的是完整奖励总和不是单项误差所以每一项的量级需要先做归一化。实践中我采用了一个简单的做法先计算每个误差项在无控制或基础PD控制下的典型取值范围然后把权重设为其倒数量级让所有项在相同量级上参与决策。经过几轮调整我最终采用的奖励函数各项惩罚量级基本接近训练出来的策略既保持了高度跟踪精度又没有出现控制量抖动的问题。还要补充一个细节如果某个回合因为超过允许的最大步骤数而结束我给这个回合追加一个相对较小的负奖励作为“超时惩罚”。如果状态误差超过预设的物理边界就直接视为失败给一个较大的负奖励并提前结束该回合。这种设计能显著加速学习因为智能体能更频繁地接触到“失败”信号对于解决奖励稀疏问题很有效。3.4 训练循环与在线监控训练部分我用的是强化学习工具箱里的训练函数和训练选项。设置最大回合数800每回合最多500步同时设置了一个停止条件当最近若干回合的平均奖励超过一个预设阈值时就自动停止训练。训练过程中会在界面上实时画出奖励曲线和平均奖励曲线这是判断训练是否正常进展的第一手资料。我特别想强调一个实操细节训练用的环境里的初始条件不能千篇一律否则学出来的策略会过拟合到单一场景。我会在每次reset函数里随机化目标高度、初始高度和初始速度让智能体在训练过程中见到足够多样的初值分布。这样训练出来的增益调节策略才能在不同工况下都能给出合理的PD参数而不是只对某一条固定轨迹有效。训练过程中还需要关注一个指标——实验回合平均奖励曲线的斜率变化。如果曲线持续上升并趋于平缓通常意味着策略在收敛如果曲线剧烈波动、长时间没有上升趋势我就会暂停训练检查是奖励权重失衡、探索噪声过大还是网络结构出了问题。这里有一个很实用的经验不要盲目加大学习率来加速收敛在DDPG里学习率过高经常导致Q值发散训练曲线直接崩盘。4. 调训练参数和调PD参数其实是同一门手艺实测中的坑与对策4.1 收敛缓慢与奖励波动的根因排查我和一起做这个项目的同学在训练中期遇到过一个很典型的现象训练了三百多个回合奖励曲线始终在一个很窄的区间内来回震荡没有明显上升趋势。最初怀疑是网络容量不够把隐藏层加到256个神经元结果训练更慢了。后来排查到根因发现是探索噪声的强度设置有问题。OU噪声的强度如果太大叠加在动作上的扰动幅度甚至盖过了动作本身的合理范围导致智能体“明明已经学到了一个较好的策略却因为每回合都被强噪声干扰而无法稳定复现”。把探索噪声的幅度调小、并让它在训练中期逐步衰减之后训练曲线很快就摆脱了平台期。这个排查过程给我的教训是DDPG不是只用来看最终结果的算法它内部任何一环的失调都会直接反映到训练过程曲线上学会读曲线比学会调参数更重要。4.2 奖励函数设计不当导致的隐性“刷分”行为还有一个让我印象深刻的案例是我试图修正超调问题时引入“超调惩罚项”结果智能体很快学会了一个投机取巧的策略它把增益整体调低让系统变成反应极慢的“懒控制器”。这样确实没有超调了但误差积分持续累积回合内的大部分时间飞行器都没有真正跟上目标。这种现象在强化学习中很常见智能体不会按照人的意图去优化它只会优化你给出的奖励信号本身。如果你只惩罚超调而不惩罚跟踪误差它就会找到一个“不做任何事”的局部最优解。我后来调整了奖励设计把跟踪误差的惩罚权重重新提高并且加入了对控制输入变化率的惩罚才让策略回归到“既要动作快又要过程稳”的正常轨道上。这个经历让我更加确定一点奖励函数不是写完就完事的它需要根据训练出的策略行为不断调整就像PD调参时需要根据阶跃响应曲线不断调整Kp和Kd一样。强化学习项目里有一半的时间其实是在调奖励、看曲线、再调奖励。4.3 DDPG特有的Q值过估计问题与对策DDPG有一个知名短板Critic网络对Q值的估计存在系统性的高估偏差。这个偏差在训练后期会逐渐放大出现的情况是奖励曲线看似还在上升但把最终学到的策略拿到真实动力学环境里测试表现却差得离谱。这是因为Actor会优先选择那些被高估的动作导致学到的策略偏向“看起来好”而不是“真的好”。我在项目中做出的调整有三个方向第一个方向是改用TD3算法它的核心改进就是引入两个Critic网络取较小值抑制过高估计第二个方向是降低Critic网络的学习率让Q值的更新更保守第三个方向是增加目标网络的软更新系数调节让目标值变化更平缓。事实上我在第四轮训练中直接切换到TD3之后训练效率和最终策略质量都有明显提升。这个经验也印证了很多文献里的结论虽然标题写的是DDPG但如果要做实际项目TD3几乎是一个低成本的升级选择。4.4 训练完成后的验证策略别只在训练环境里自嗨训练收敛之后很多人会把训练好的智能体直接搬到真实飞行器上这一步极其危险。我的做法是在仿真环境里做三轮验证第一轮是“训练分布内验证”把初始条件设定为训练时见过的范围确认策略在熟悉环境里的表现第二轮是“分布外验证”把目标高度、初始偏差、甚至动力学模型里的阻尼系数都做了扰动确认策略的泛化能力第三轮是“极端工况验证”比如让初始高度偏差超出训练范围看看控制器的行为是否仍然处于安全边界以内。这三轮验证里第二轮的失败率最高因为训练时随机化的初值范围还是有点窄模型在分布外表现出明显的增益调节异常。补齐这个问题的办法也很朴素扩大训练时初始条件的随机范围重新训练。训练流程跑完之后我对“仿真里收敛不等于真实环境可用”这句话有了更深的理解验证方案本身就是项目里最值钱的一部分。这一段的另一个实操建议是每训练一版策略就把最终的增益曲线导出来画成一张“误差-增益映射图”人工检查一下这个映射是否符合控制常识。如果出现“误差很大的时候增益反而很小”之类的反直觉映射说明训练过程有问题而不是策略发现了什么新原理。把强化学习结果和控制专家的直觉放在一起交叉验证能过滤掉大部分训练故障。5. 后续还能怎么扩展从单通道到多通道从离线到在线这个项目完成之后我一度觉得DDPG调增益的思路已经到头了但后来在另一个仿真任务里我发现同样的框架可以很容易扩展到姿态角和偏航角的多通道控制只需要把动作空间的维度从二维扩展成六维让每个通道拥有独立的Kp、Kd缩放系数Actor网络的输出对应增加到六个节点即可。状态空间也只需要把更多通道的误差信息拼进去其他组件几乎不用改动。更进一步还可以把DDPG从一个“离线整定工具”改成“在线自适应控制器”让智能体每隔一段时间读取最近一段时间的误差统计量在线输出新的增益调整量从而让控制器在飞行过程中不断适应环境变化。这种在线自适应方案的难点在于训练时需要模拟足够多的环境漂移场景否则智能体在真实场景中很难知道该往哪个方向调。就我个人的体验来说强化学习和经典控制结合的时候最有效的思路不是用强化学习替代掉控制器而是让强化学习来负责“参数协调”让经典控制负责“底线稳定”。PD控制器提供了一个可解释、有稳定性保障的基础闭环DDPG则负责在基础闭环之上做增益的动态修正。这样既保留了经典控制的可靠性又获得了强化学习的适应性在工程上是一个相对稳妥的落地姿势。最后再分享一个我踩过几次坑之后的小技巧在训练环境里加入一点点噪声和高频扰动后再开始训练比直接在理想模型上训练出来的策略鲁棒得多。四旋翼模型里电机响应延迟、传感器噪声、推力脉动这些看起来不起眼的细节最终都会成为训练策略在真实场景中是否能站住脚的分水岭。做控制研究的人最怕的就是仿真里完美无瑕、实飞时原形毕露把环境扰动前置到训练阶段是成本最低的避坑方式。
阅读完成 · 觉得有帮助?