基于DDPG强化学习的四旋翼PD控制器参数自整定与Matlab仿真实践 四旋翼的PD控制器调参说多了都是泪。比例项给大了抖微分项给大了闹人工凑参数往往要磨掉几个下午。最近我试了一个思路把DDPGDeep Deterministic Policy Gradient强化学习算法和四旋翼的PD控制结合起来让智能体自己去搜索那组最优的PD增益。整套链路从建模、控制器设计到Matlab仿真实现跑通之后的效果确实让人眼前一亮顺手把整个实操过程完整梳理了出来。这条路子的核心在于不改变PD控制器的结构而是利用DDPG在连续动作空间中的强搜索能力根据四旋翼当前的飞行误差状态动态调整PD参数。说是纸上谈兵也好说是工程实践也罢至少在我手上的仿真环境里它比人工调参和固定参数PD都稳得多。下面直接进入正题从理论推演到代码细节再到我踩过的坑一次说清楚。1. 内容整体设计与思路拆解1.1 为什么偏偏选DDPG来做PD参数优化先说结论因为四旋翼的姿态控制本身是连续状态、连续动作的典型问题而PD参数也是连续变量这正好落在DDPG擅长的区间里。做参数自整定大家可能首先想到模糊控制、自适应控制、遗传算法或粒子群优化。这些方法各有千秋但实际操作下来都有各自的问题。模糊控制依赖专家经验来构建隶属度函数和规则库规则一旦不完整泛化能力就非常堪忧遗传算法和粒子群属于离线优化算出来的参数在某个工况下是最优的但四旋翼在空中飞姿态和位置一直在变同一个参数在悬停时合适在大机动时就未必合适。DDPG的优势是它可以在线决策——每过一个控制周期智能体观察当前姿态误差和误差变化率直接输出一组PD增量实现增益的动态调整。这种“根据实时状态微调控制强度”的思路比离线寻参灵活得多。另外还有一个很现实的原因Matlab的强化学习工具箱已经把DDPG最复杂的神经网络训练环节封装好了而且支持与Simulink模型直接交互。这意味着我们不需要从零搭深度网络和回放缓冲区可以把大部分精力放在四旋翼建模和奖励函数设计上。1.2 系统的整体构成智能体、环境和PD控制器之间的关系整个闭环系统可以拆成三层看待。最底层是四旋翼的动力学模型它接收期望姿态角通常从遥控器或上层位置控制器给出输出实际姿态角。中间层是PD控制器输入是姿态误差输出是期望力矩。最上层是DDPG智能体它观测当前误差状态输出PD增益的修正值。用公式来表达就是PD控制律u(t) Kp * e(t) Kd * de(t)/dtDDPG输出ΔKp π(s)ΔKd π(s)其中s [e(t), de(t)/dt]及其历史信息最终作用到控制器上的增益Kp_new Kp_base ΔKpKd_new Kd_base ΔKd注意这里我用了“增量修正”的设计而不是让DDPG直接输出完整增益。这样做的原因有两个一是保证训练初期增益不会因为探索噪声而突然变得过大或过小导致四旋翼模型直接发散二是缩小动作空间范围智能体只需要学习在小范围内的微调策略收敛难度显著降低。这一点在后面的实操中会反复体现强烈建议不要省略。1.3 解决问题路径的整体规划这个项目从启动到出结果大致分四个阶段。第一阶段是四旋翼数学模型构建。我没有直接用Simulink里现成的四旋翼模块而是在Matlab里用状态空间方程自己搭了一个六自由度模型这样之后如果要做控制律改动自由度会更高。第二阶段是PD控制器设计和基线性能测试。先手动整定一组能稳定飞行的PD参数作为基准顺便确认模型的响应特性和采样频率要求。第三阶段是DDPG智能体的设计包括状态空间、动作空间、神经网络结构和奖励函数。这是整个项目中最花时间的部分后面会详细展开。第四阶段是联合仿真、训练调优和结果验证。整个流程走下来我从开始动手到得到一个令人满意的结果前后花了两周左右的业余时间。如果是熟悉强化学习的读者并且模型代码现成一周内应该能跑完第一版结果。2. 四旋翼建模与PD控制基础2.1 四旋翼动力学模型的建立思路模型是控制算法验证的基础。这里我采用的是经典的四旋翼简化模型忽略桨叶挥舞动力学和空气阻力的高阶项只保留重力、推力和陀螺力矩这些核心因素。位置方程基于牛顿第二定律姿态方程基于欧拉角表示下的刚体转动方程。俯仰角、翻滚角、偏航角的二阶导与电机转速差直接相关四个电机的转速组合分别产生总推力、横滚力矩、俯仰力矩和偏航力矩。Matlab里实现时我一般写成函数形式function sdot quadrotor_dynamics(s, tau, params) % s [x y z vx vy vz phi theta psi p q r] % tau [throttle_pwm; roll_pwm; pitch_pwm; yaw_pwm] phi s(7); theta s(8); psi s(9); % 旋转矩阵世界坐标系到机体坐标系 R [... 1, sin(phi)*tan(theta), cos(phi)*tan(theta);... 0, cos(phi), -sin(phi);... 0, sin(phi)/cos(theta), cos(phi)/cos(theta)]; % 角加速度 ang_acc params.I_inv * (tau_body - cross(s(10:12), params.I * s(10:12))); sdot zeros(12,1); sdot(1:3) s(4:6); sdot(4:6) -[0;0;params.g] R * tau_body(1:3); sdot(7:9) R * s(10:12); sdot(10:12) ang_acc; end简化之处在于没有建模螺旋桨的饱和特性也就是电机推力上限和响应延迟没有精细化处理。在仿真验证阶段这样做的风险是可接受的但后续如果要上真机典型做法是把电机模型从一阶惯性环节替换为更精确的响应模型并且加入输入饱和约束。2.2 PD控制器的结构和参数设计四旋翼的姿态PD控制一般分内外环内环控制角速度输出期望力矩外环控制姿态角输出期望角速度。不过在这个项目中我重点做的是姿态角的直接PD控制也就是外环直接输出力矩命令以便让DDPG干预的效果更直观。基础PD参数我是通过频域分析和时域仿真结合凑出来的。先根据四旋翼的转动惯量估算自然频率经验公式大致是Kp ≈ I * ωn²Kd ≈ 2 * ζ * I * ωn。如果转动惯量约0.01 kg·m²自然频率取8 rad/s阻尼比取0.9那Kp就在0.64附近Kd在0.144附近。这套初始值再叠加时域调试效果会比较理想。基准确认后记录其对阶跃响应的表现。我用的期望姿态角阶跃为10度在这一动作下上升时间大约0.3秒超调小于5%稳态误差小于0.1度。这个水平已经比较接近实际飞控的响应表现了可以用于后续DDPG训练的交互环境。2.3 为什么需要“自适应”的PD参数固定PD在单一工况下可以表现得很好但四旋翼飞行过程中模型参数会发生变化例如电池电压下降导致电机推力响应变慢载荷变化导致转动惯量增大飞行速度增大导致气动力矩变得不可忽略。当这些变化发生时固定参数的鲁棒性就会显露出问题。我曾经做过一个对比在模型参数不变时固定PD表现很好当转动惯量增大30%后同样的参数导致俯仰通道出现明显的振荡超调量从5%飙到接近20%。这个测试直观地说明了为什么需要让控制参数“跟着状态走”。DDPG在这个过程中扮演的角色本质上是一个实时参数调度器由误差驱动的调度器。和增益调度控制相比DDPG不需要人为划分工况区间而是依靠策略网络自然学习出参数调整规则。3. DDPG算法原理与神经网络结构设计3.1 DDPG的工作机制简述DDPG属于Actor-Critic家族核心是同时学习一个策略网络Actor和一个价值网络Critic。策略网络负责根据当前状态输出动作价值网络负责评估在某个状态下采取某个动作的期望回报。过程中会使用经验回放Experience Replay来打破样本相关性并使用目标网络Target Network来稳定训练。DDPG是确定性策略梯度算法训练通过最小化Critic的时序差分误差来实现Actor网络的梯度则由策略梯度定理给出。在连续控制问题中DDPG是稳定且相对通用的选择这也是我优先考虑它的原因。3.2 状态空间、动作空间和奖励函数的设计这部分是决定项目成败的关键需要重点展开。状态空间我选了以下六个维度横滚角误差、俯仰角误差、偏航角误差、横滚角误差变化率、俯仰角误差变化率、偏航角误差变化率。为什么不需要位置和速度信息因为这个项目专注姿态控制位置控制可以依靠外环位置控制器提供期望姿态角来实现。如果希望DDPG同时优化位置控制和姿态控制状态空间就得扩充训练难度会跳一个数量级。务必要分清边界。动作空间是四维输出分别为横滚、俯仰、偏航三个通道的Kp修正值和Kd修正值。输出范围限制在[-1, 1]再映射到实际增益范围例如[-0.2, 0.2]。这样设计的好处是数值稳定神经网络输出饱和的问题会少很多。奖励函数是我迭代了很多次才定下来的方案。第一版直接使用误差平方的负数作为奖励训练结果非常不稳定。改用叠加形式后效果明显变好reward - (8*e_roll^2 8*e_pitch^2 2*e_yaw^2 0.5*de_roll^2 0.5*de_pitch^2 0.2*u^2 0.1*penalty_over)其中penalty_over是超调惩罚项当姿态误差超过激励幅度的20%时额外累加。这样设计目的分明大角度误差纠正优先平滑性次之能量消耗再次之。3.3 Actor与Critic网络的层数、神经元数和激活函数选择神经网络的规模直接影响训练效率和稳定性。我的网络结构如下Actor网络是三层全连接输入层6维 → 隐层256维ReLU→ 隐层256维ReLU→ 输出层4维tanh。输出层使用tanh是强制动作范围落在[-1, 1]的标准做法。Critic网络稍微特殊一点把状态和动作分别输入后在第二层拼接这在强化学习领域被证明比直接拼接全部输入更稳定。具体结构是状态输入6维经过第一层128维与4维动作拼接后进入第二层256维最后输出一个Q值。关于神经元数量有人觉得256太过了我试过64和128训练速度和最终效果都不如256。深度强化学习这里反而呈现出模型容量越大、训练越快的现象。但前提是奖励函数设计合理否则模型容量大只会加速过拟合到错误策略上。3.4 超参数选择与训练策略优化超参数表里最值得关注的是学习率、软更新系数和噪声策略。我最终采用的配置如下学习率Actor是1e-4Critic是1e-3。两个网络学习率不同在DDPG中是标准做法因为Critic的学习任务更难需要更快更新步伐Actor如果学习率过大容易在训练中后期出现策略剧烈抖动。软更新系数tau是0.005回放缓冲区大小50万条批量大小128折扣因子0.99。探索噪声采用Ornstein-Uhlenbeck过程初始sigma为0.15衰减系数0.995每回合衰减300回合后基本退化为确定性策略。OU噪声和简单高斯噪声的区别在于它有时间相关性产生更平滑的探索轨迹这对四旋翼动力学很重要——高频率随机噪声会让飞行轨迹急剧震荡训练反而学不到有效内容。4. Matlab环境下DDPG与四旋翼模型的联合仿真实现4.1 Matlab强化学习工具箱的版本兼容与模块选择我用的是Matlab R2023a版本强化学习工具箱提供了rlDDPGAgent这个对象。如果版本低一些例如R2020a之前工具箱里没有DDPG函数只能自己手写整个训练框架工作量大很多。所以建议至少是R2021a之后版本。创建DDPG智能体时标准流程是定义网络结构、指定状态和动作的维度、指定Agent选项。在我的案例中状态维度是6动作维度是4采样时间0.01秒。创建Agent对象的代码如下agent rlDDPGAgent(actor, critic, obsInfo, actInfo, agentOptions);4.2 编写交互环境接口在Matlab中训练强化学习智能体需要把四旋翼动力学模型封装成环境。有两种做法一是直接写一个step函数接收动作输出下一个状态和奖励二是借助Simulink模型进行仿真。我用的是第一种因为它在命令行模式下运行效率更高适合快速迭代。核心的step逻辑function [nextObs, reward, done, info] step(env, action) % action DDPG输出的4维PD增益修正值 Kp_new env.Kp_base action(1:3) * env.Kp_range; Kd_new env.Kd_base action(3:4) * env.Kd_range; % 执行一个仿真步长0.01s % 在四旋翼模型上施加PD控制力矩得到新状态 % 计算误差、新误差变化率、奖励 end这里出现了一个工程细节问题由于在数组层面操作PD控制器的微分项不能直接通过连续模型的导数得到需要对误差做离散差分。建议在step之前对误差做低通滤波否则微分项的噪声会被放大导致奖励函数不稳定。4.3 完整训练流程和核心代码训练主循环我写成一个可配置的函数这样既能方便调整奖励权重也能保存最佳模型。核心框架如下% 创建环境 env QuadrotorDDPGEnv(params); % 创建DDPG智能体 agent createDDPGAgent(obsInfo, actInfo); % 训练选项 trainOpts rlTrainingOptions(... MaxEpisodes, 500, ... MaxStepsPerEpisode, 400, ... ScoreAveragingWindowLength, 10, ... StopTrainingCriteria, AverageReward, ... StopTrainingValue, -50); % 开始训练 trainResult train(agent, env, trainOpts);这里的400步对应一个回合4秒足够覆盖一次完整的阶跃响应过程。500回合的总时长在GPU可用时大约是2-3小时纯CPU可能需要翻倍。4.4 训练结果评估与策略导出训练结束后需要全面评估训练得到的策略。评估方式不能只看奖励曲线还要观察实际控制效果。我用两种方式一是运行测试仿真给不同幅度的姿态阶跃指令看超调、稳态误差和上升时间二是对比固定PD的参数基线看改进了多少。策略导出只需要调用getAction函数就能获得当前状态下的增益修正。导出到外部系统时需要把网络权重矩阵导出为模型参数然后用纯C或C实现推理。评估测试的代码逻辑如下obs env.reset(); for i 1:steps action selectAction(agent, obs); % 加入少量噪声或不加 [obs, reward, done, info] env.step(action); record(i, :) [obs, action, reward]; end我大约在第200回合后能看到奖励曲线明显收敛第350回合后基本稳定。训练后期如果奖励还有大幅震荡多半是网络结构或奖励权重的问题。5. 训练过程中踩过的坑与排查技巧实录5.1 训练发散最常见的元凶是奖励函数第一版奖励函数只用误差平方的负数训练过程中智能体学到的是把增益无限调大因为这样能在极短时间内把误差压小但代价是姿态震荡严重、控制量超限。从奖励曲线看训练前50回合惩罚值确实在快速下降但观察仿真轨迹就会发现四旋翼在疯狂抖震。解决办法是加控制量惩罚项和超调惩罚项。控制量惩罚项约束增益不要超过合理范围超调惩罚项则避免仅追求“到达快”而牺牲“稳定好”。加了这两项后训练200回合和之前的500回合相比最终策略的控制效果更优。5.2 OU噪声参数不是越大越好我一度把初始sigma设成0.3想加快探索覆盖度。结果就是智能体动作输出波动剧烈四旋翼模型频繁发散奖励曲线不断出现尖峰。后来把sigma降到0.15探索更平稳模型稳定训练效率反而更高。这说明探索噪声需要匹配动力学系统的响应时间尺度——动作扰动导致的系统响应需要时间显现如果噪声变化太快系统还没有反馈到下一状态就变了方向学习信号基本没有意义。5.3 批量大小和回放缓冲区对训练速度的影响批量大小我试过32、64、128三种。批量32训练速度快但方差大奖励曲线锯齿感很强批量128训练更stable但每个episode的耗时明显上升。综合效果来看批量64和128差别不大如果追求更稳的收敛曲线建议用128。缓冲区大小如果设太小例如10万条容易出现旧策略的样本反复被采样训练后期出现灾难性遗忘现象。500万条或者更大可以缓解这个问题但内存占用也高。折中的选择是50万条左右在常规配置的机器上运行没有压力。5.4 常被忽略的仿真采样时间问题采样时间的选择直接影响控制效果和训练难度。我一开始用0.05秒采样模型计算量小但PD控制的微分项误差较大训练出来增益偏保守。改成0.01秒后控制更精细训练收敛速度也有所提升。再小到0.005秒时增益已经有收敛趋势但训练时长几乎翻倍。权衡之下0.01秒就是四旋翼PD控制仿真的甜点区间。6. 实测效果对比与优化心得6.1 固定PD与DDPG优化PD的对比结果在验证阶段我用三组控制器做横向对比固定PD参数、遗传算法离线寻优PD、DDPG在线自适应PD。设定同一测试任务——从悬停状态快速俯仰10度然后回到悬停重复10次统计超调量、调节时间和累计误差。固定PD多次实验的平均结果为超调4.8%调节时间0.35秒。遗传算法寻优的PD超调略微改善到3.5%但由于参数固定姿态较大时依然出现0.4秒左右的调节时间。DDPG在线自适应的结果最优超调1.8%调节时间0.2秒而且随着训练回合增加控制效果越来越平滑。这验证了“在控制器结构不变的前提下通过实时调整增益可以显著改善动态响应”的思路。6.2 泛化能力检测稍改动力学参数后结果如何为了检验DDPG策略的泛化能力我把转动惯量增加了20%同时模拟电池电压下降导致的最大推力减少10%。固定PD的情况超调飙升到15%以上几乎无法稳定。DDPG策略在刚开始约10步内依然出现轻微超调但随后策略开始根据误差状态调整增益约0.3秒后姿态恢复稳定最终超调控制在5%以内。这说明DDPG确实学到了“根据误差状态动态调整增益以保持稳定”的策略而不是简单背下了训练场景的动作序列。6.3 可扩展的方向从姿态控制到位置控制基于当前实现后续最直接的扩展是加入位置环形成完整的级联控制。DDPG可以根据位置误差、速度误差和姿态信息同时调整内环和外环的PD增益形成更完整的自适应方案。需要注意的是状态空间将从6维扩张到10维以上动作空间可能翻倍训练时间会指数级上升。如果资源紧张建议采用分阶段训练先训练内环姿态控制器锁定后再训练外环位置控制器。另一个值得尝试的方向是引入多智能体架构把横滚、俯仰、偏航三道PD控制器视为多个智能体协同决策。但考虑到各通道之间存在动力学耦合单智能体控制所有通道在当前阶段仍然是更稳妥的选择。回到最初的想法本身。用强化学习去做控制参数整定很多人觉得是花架子但实测下来的收益非常明确。难点不在算法本身而在工程细节的处理——建模是否准确奖励函数是否覆盖了所有需要约束的目标训练环境是否稳定。把这些问题逐一解决之后DDPG优化PD这件事才能真正跑起来。至少在四旋翼仿真这个维度上我认为这是一条值得继续投入的技术方向。