DDPG这种“连续动作+经验回放”的组合,天然匹配SMC参数在线调整这种连续决策问题,而且Simulink下整套链路都能搭出来。
先说结论:如果手上正好有滑模控制的Simulink模型,又一直苦恼切换增益、趋近律系数靠手感调,那你可以把DDPG当成一个“参数调度器”——它不直接输出控制量,而是根据当前误差、误差变化率、滑模面实时输出一组SMC参数,让固定结构的滑模控制器在不同工况下保持较好性能。这种方式最大的优势是训练一次之后,部署阶段只跑Actor网络,计算量比在线优化要小得多,非常适合电机控制、飞行器姿态控制、机械臂轨迹跟踪这类强非线性、强扰动场景。
这篇文的面向对象,是已经懂基础滑模控制、会搭Simulink模型,但对强化学习只停留在听说过阶段的人。我会把DDPG和SMC结合时的环境建模、奖励塑造、训练细节、踩过的坑全部拆开讲,保证你看完能直接在模型上跑起来。
1. 为什么DDPG让SMC调参从“试凑”变成“策略”
1.1 SMC的经典痛点和传统整定思路
滑模控制的研究者都知道,设计一个大差不差的滑模面并不难,难的是参数整定。以最常见的二阶系统为例,滑模面通常设计为:
s = c*e + de(其中 e 为跟踪误差,de 为误差变化率,c 为滑模面斜率)
控制律一般写成等效控制加切换控制:
u = u_eq + k*sgn(s) + lambda*s
这里的c、k、lambda三个参数决定控制性能。k太大,抖振严重,执行器发热、磨损快;k太小,抗扰动能力不足,系统状态可能滑不到滑模面上。传统的调节手段,要么是手动试凑,要么是模糊规则查表,要么是自适应律在线估计。手动试凑的问题很明显,不同工况下最优参数差异很大,一个在空载情况下调好的参数,加载之后可能就出现明显振荡。
我见过不少做工程项目的朋友,为了把一台样机的速度环调稳,光在k和lambda两个参数上就花了一周。你用固定参数应对固定工况没问题,但一旦负载变化、模型摄动、外部扰动上来,这套固定参数就露怯了。这也是滑模控制在工业现场名声两极分化的原因,理论上它鲁棒性很强,实际用起来却经常被抖振劝退。
1.2 DDPG在这里到底干了什么
DDPG(Deep Deterministic Policy Gradient)是深度强化学习里专门解决连续动作空间问题的算法。它的特点是用一个Actor网络直接输出确定性动作,用一个Critic网络评估这个动作的好坏,再通过经验回放让训练过程稳定。
把DDPG接到SMC调参场景里,直觉很清晰:把系统当前状态(误差、误差变化率、滑模面、跟踪目标等)作为观测,把SMC的关键参数作为动作,把“控制性能好不好”定义为奖励,然后让Agent通过试错学习“什么状态下该给什么参数”。
这里有一个容易被忽视的设计决策,DDPG到底直接输出整个控制量,还是只输出SMC的几个增益参数?我的经验是后者。纯强化学习直接输出控制量,训练难度非常高,奖励稀疏,初期探索很可能直接把系统搞发散;而让SMC这个结构稳固的控制器打底,DDPG只负责在参数空间里调整,搜索空间小得多,训练收敛快,而且即便Agent学得不好,SMC的底线鲁棒性也在那里兜底。这个“结构控制+强化调参”的混合架构,是工程落地最现实的选择。
另外,实时性也要考虑。SMC的参数调整不需要每个控制周期都变,通常每隔几个采样周期更新一次即可。DDPG的Actor网络前向推理一次的开销,在普通嵌入式芯片上也就几十微秒级别,完全扛得住。这也是为什么它能从仿真走向实际控制器。
2. DDPG算法细节与强化学习环境设计
2.1 算法核心组件与连续控制适配
DDPG本质上是DQN在连续动作空间的扩展。DQN只能从有限离散动作里选,而SMC参数是连续实数,显然不能靠DQN枚举。DDPG的做法是让Actor网络输出一个确定性动作向量:
a_t = μ(s_t | θμ)
Critic网络则估计这个动作的状态-动作价值:
Q(s_t, a_t | θQ)
训练过程里,Actor的梯度方向是由Critic提供的∇aQ引导的——这就是为什么Critic训练得准不准,直接影响Actor能不能学到好的策略。
为了稳定训练,DDPG引入了三个关键机制。第一个是经验回放,把每次交互的(s, a, r, s')存进缓冲区,训练时随机采样小批量,打破样本之间的时间相关性。第二个是目标网络,Actor和Critic各维护一份滞后更新的目标网络,让TD误差的优化目标不那么晃。第三个是探索噪声,训练时在Actor输出上加随机噪声(常用Ornstein-Uhlenbeck过程或高斯噪声),让Agent有足够机会尝试不同的动作组合。
针对SMC调参这个场景,有一点要特别注意:动作空间里的每个维度,最好是经过归一化后输出的。比如把切换增益k映射到[0,1]区间,Actor网络输出0到1之间的值,再在环境里反归一化映射到实际物理范围。这么做的原因在于,神经网络对相近量纲的输入输出更友好,训练更容易收敛。如果让网络直接输出几百几千的增益值,激活函数很容易饱和或者梯度爆炸,训练初期就废了。
2.2 状态空间与动作空间设计
状态空间设计直接决定Agent能否区分不同工况。一个良好的状态集合应该包含:跟踪误差e、误差变化率de、滑模面s、时间积分误差或历史误差积累。我常用的观测向量是:
[e, de, s, integral_e]
这里的关键是每个量最好做归一化或标准化处理。比如误差范围是±0.5,那就除以0.5映射到±1;滑模面范围更大,就按它的量级单独缩放。虽然DDPG内部有BatchNorm的可能,但我在实践中发现,输入归一化之后训练稳定性和收敛速度都肉眼可见地变好。
动作空间对应SMC的可调参数。我最多调3个参数:滑模面系数c、切换增益k、指数趋近律系数lambda。如果你用的是超螺旋滑模或积分滑模,还可以把对应参数加进动作空间,但原则上不要超过4个维度,否则探索难度指数上升。
动作限幅是必须的。每个动作维度都要设置物理上下界:k太小不抗扰动,太大抖振剧烈;lambda太大可能引发系统振荡。限幅方式有两种:一种在Actor输出层用Sigmoid或Tanh缩放到[-1,1],再线性映射到实际范围;另一种是环境里做Clip。我更推荐前者,因为梯度信息能顺着网络传回来,训练效率更高。
2.3 奖励函数设计
奖励函数是这个项目里最看重“手艺”的地方。它不像监督学习有标准答案,完全靠你对控制目标的拆解。我的常用模板是:
r = -w1*e^2 - w2*de^2 - w3*(Δa)^2 - w4*u^2 + bonus
e^2惩罚稳态误差,是奖励函数里最重要的部分;de^2惩罚误差振荡,抑制超调;(Δa)^2惩罚参数突变,防止DDPG把SMC参数跳来跳去,导致控制量剧烈抖动;u^2惩罚过大控制能耗;bonus是稀疏奖励,比如当|e| < 0.01持续200步时,给一个较大的正向奖励。
权重系数需要按量级协调。如果e的量级在0.1附近,e^2就是0.01量级,那w1设1000也不过分;如果动作变化率Δa每次在0.01附近,w3设100左右。一开始没把握的时候,可以先单独调试每一项的量级,再组合起来。
这里要特别强调(Δa)^2这一项。很多人第一次做DDPG+调参,奖励函数里只有误差项,结果学出来的策略是每隔几个周期把增益从一个极端跳到另一个极端。从误差曲线上看好像也收敛了,但实际控制量抖得没法用。加上参数变化率惩罚之后,Agent学出来的策略就平滑多了,SMC本身的抖振抑制优势也能发挥出来。
3. Simulink仿真搭建与训练实操
3.1 被控对象与滑模控制基准回路的搭建
我以一个典型的二阶非线性系统为例,系统动力学为:
x'' = -a*x' - b*x + c*x^3 + u + d(t)
其中d(t)是外部扰动,可能是阶跃也可能是正弦波。这个系统有一点点非线性项c*x^3,又带扰动,非常适合验证SMC加DDPG调参的效果。
Simulink里的搭建思路是:被控对象用积分模块搭出状态变量,控制律用MATLAB Function块实现。滑模面s = c*e + de,控制律用指数趋近律:
u = u_eq + lambda*sgn(s) + k*s
其中等效控制部分由被控对象的标称模型推导出来。如果模型参数存在摄动,等效控制会有偏差,这部分偏差就靠切换项k*sgn(s)和趋近项k*s来补偿。
这里有一个关键仿真参数:采样时间。SMC本身就是变结构控制,对控制周期很敏感。仿真步长我习惯设成固定步长1ms,控制周期跟仿真步长一致,或者5个步长控制一次。强化学习环境里的采样时间要和控制周期对应,DDPG的决策频率一般比控制频率低,比如每20ms更新一次参数,这样更接近实际工程节奏,训练负担也小很多。
3.2 RL Agent环境的接入配置
用MATLAB的Reinforcement Learning Toolbox接入DDPG时,通常有两条路线。一条是纯MATLAB代码方式,用rlDDPGAgent配合rlFunctionEnv写环境函数;另一条是在Simulink里用RL Agent模块直接搭环境。做仿真验证,我推荐后者,因为能复用现有被控对象模型,比把动力学方程迁移到MATLAB函数里更省事、更不容易出错。
Simulink里接入的要点是三个接口:Observation、Action、Reward。
Observation接口把状态向量从被控对象侧引出来,经过缩放后送入RL Agent模块。Action接口接受Agent输出的动作(0到1之间的小数),在模块内部或外部用一个增益和偏置块映射成实际SMC参数,然后送入控制器。Reward接口需要你自定义一个Reward计算子系统,输入是误差、控制量等,输出是标量奖励值。
这里有个容易犯的低级错误:Reward计算子系统和RL Agent模块之间可能出现代数环。因为它俩都是直通模块,同一采样时刻互相依赖。解决方法是在Reward回路里加一个单位延迟Unit Delay,或者把观测向量经延迟后再送入Reward模块。统一采样时间、合理加延迟,这个问题完全可以规避。
环境里还要设置一个持续时间。每个Episode不是无限跑的,一般设定为10秒或20秒仿真时长,时间到了整个Episode结束,得到这个Episode的总奖励。同时还要设置Stop Training条件,比如平均奖励超过阈值就自动终止,避免练了百万步还在那白白烧CPU。
3.3 训练超参数与完整训练流程
训练超参数直接影响收敛速度和稳定性。下面是我测试下来比较靠谱的一组起始参数,针对单参数调节场景效果明显,多参数场景也可以在这个基础上微调:
| 超参数 | 推荐值 | 说明 |
|---|---|---|
| 采样时间 | 0.02s | 与SMC调参周期一致 |
| ExperienceBuffer长度 | 100000 | 经验池容量,至少容纳几十个Episode |
| MiniBatchSize | 256 | 训练时每次采样的样本量 |
| Actor学习率 | 0.0001 | 不要设大了,容易震荡 |
| Critic学习率 | 0.001 | 可以比Actor稍大 |
| Target平滑因子 | 0.005 | 目标网络软更新速度 |
| 探索噪声 | 高斯噪声均方差0.1 | 也可以用OU噪声 |
| Episode最大时长 | 20s | 太长拖慢训练,太短学不到稳态效果 |
训练流程分三步走。
第一步,用固定参数SMC先跑通模型。先在纯SMC模式下用一组保守参数(比如k=5、lambda=10、c=10)跑仿真,确认系统稳定、无发散、无非线性异常。这个基准结果会直接决定DDPG训练时初始探索的成功率。
第二步,在Simulink里把SMC参数改成由RL Agent模块输出,先不训练,用固定的探索噪声跑几次Episode,观察观测量的数值范围是否合理、奖励是否在一个像样的区间内、有没有出现NaN或者无穷大。这个步骤很关键,相当于“冒烟测试”。
第三步,正式训练。打开rlTrainingOptions,设置最大Episode数(比如500)、每次Episode仿真长度、保存Agent的阈值等。训练时如果发现奖励曲线长时间原地踏步,果断停掉,去检查奖励权重或动作范围,不要傻等。
3.4 训练完成后的策略部署与验证
训练完成后,得到的是Actor网络训练到一定程度的参数集合。直接把这个Actor网络当成一个“在线调参器”接到SMC上看效果。方法很简单:SMC参数不再用固定值,而是每个采样周期读取当前观测值,用getAction(agent, observation)得到动作,再映射成SMC参数。
验证时分三种工况:标称工况、参数摄动工况(比如被控对象增益突变为原来的1.5倍)、外部扰动工况(在某一时刻叠加阶跃扰动)。把“DDPG调参+SMC”和“固定增益SMC”的误差曲线、控制量曲线放一起对比,重点观察:收敛时间有没有缩短、最大超调有没有减小、控制量抖振幅度有没有恶化。
这里要提醒一下:训练阶段用DDPG,部署阶段只用Actor网络,不使用Critic网络。Critic只负责训练时提供梯度方向,部署时完全可以扔掉。这样部署模型体积小一半,推理速度也更快。如果部署目标是嵌入式实时控制器,可以进一步把Actor网络导出为C代码,或者转换成ONNX之后部署到边缘设备上。
4. 训练与仿真中的高频问题排查
4.1 训练不收敛或奖励长期不增长
这个问题我遇到最多的情况,其实不是算法问题,而是奖励函数量纲没调对。你设了r = -w1*e^2 - w2*de^2,但e是0.01量级、de是1量级,奖励被de主导,Agent优化了半天,误差其实没降下来。解决办法是先把观测量和奖励各项做一次数据分布统计,跑固定SMC时记录下各状态的分布范围,再根据范围调整权重,让误差项在奖励里占主导地位。
另外,收敛慢也可能跟动作空间初始探索很差有关。如果DDPG初始策略随便输出的参数让系统发散,那个Episode连5秒都撑不过去,根本收集不到有效经验。建议用高斯噪声初始化(噪声均方差0.2)和较短的Episode时长,让Agent在早期尽量多积累“活着”的样本,然后再逐步拉长Episode。
4.2 Episode发散与仿真中断
Simulink碰到数值发散往往直接报错停止,整个训练进度全部作废。我在这上面栽过跟头,后来总结出两个补救措施。
第一个是观测范围保护:在观测进入RL Agent之前加饱和模块,限制观测的最大绝对值,防止系统真发散了还能把一个超大值塞给Agent。第二个是仿真异常终止逻辑:用sim命令的回调函数检测到状态超过设定阈值(比如|e|>5)就提前终止Episode,把这个Episode标记为失败,让它拿到一个很差的奖励,而不是让Simulink一直崩下去。
还有一个更稳妥的做法:在训练环境里加入“自动重置”。如果系统发散,就保持被控对象状态在安全范围内重新初始化,然后让Agent从头开始体验。这个方法需要你在环境函数里多写几行判断,但能显著提高训练过程的稳定性。
4.3 仍存在抖振或控制量突变
训练收敛之后,如果控制量还是抖,第一检查的是(Δa)^2惩罚项够不够大。我测试过,把这条惩罚项的权重从0加到100,控制量突变率能下降好几倍。第二检查的是动作更新频率,如果每个控制周期都改参数,增益变动频率过高,SMC的切换控制会被催化剂一样放大。把DDPG决策周期放宽到20ms或者50ms,让参数在两次调整之间保持恒定,控制量平滑度提升非常明显。
如果抖振源头是SMC本身的符号函数项k*sgn(s),可以考虑在验证阶段把符号函数替换为饱和函数sat(s/ε)。这个不属于DDPG的调整,但会让控制量更平滑。关掉符号函数的代价是鲁棒性略微下降,不过配合DDPG在线调参,整体性能仍然可以接受。
4.4 Simulink仿真卡顿与提速实践
训练DDPG往往要跑几百个Episode,每个Episode20秒仿真时长,如果模型太复杂,训练时间可能按天计算。提速经验优先级如下。
第一优先,把仿真类型改成固定步长Discrete。连续求解器精度高但太慢,离散求解器只要步长选得合理,误差完全在可控范围内。第二优先,减少RL Agent模块内部的日志输出,尤其是不要保存每个时间步的所有观测和奖励矩阵,真需要曲线,用局部变量记录再汇总。第三优先,开启并行训练,MATLAB支持多个Worker并行跑不同Episode,如果机器配置允许,训练速度能接近线性提升。
如果在强化学习工具箱里使用GPU训练,注意不是所有网络结构都适合GPU加速。小规模的Actor/Critic(每层几十个神经元)在GPU上可能反而比CPU慢,因为每次前向反向传播的数据量太小,GPU的传输开销把优势抵消了。
4.5 高频踩坑清单
- Actor最后一层的激活函数选择要谨慎。如果要输出范围在[-1,1]的动作,用Tanh;如果范围在[0,1],用Sigmoid。不要用ReLU,否则动作很容易被截断在最小值,导致Agent永远只能输出一个参数方向。
- 奖励函数里不要加太稀疏的“成功奖励”。DDPG对稀疏奖励的适应能力有限,如果只在控制任务完全成功时才给1分,训练效率非常差。应把奖励拆成密集的、每个时刻都有信号的反馈。
- 训练中途修改奖励函数,之前积攒的经验样本就基本废了一半。所以奖励函数要在训练前充分想清楚,不要边练边改,否则经验池里的样本分布和目标完全对不上。
- Episode的初始条件多样化很重要。如果每次都是从零初始条件开始,学到的策略只对零初始状态有效。我会让每个Episode的初始状态在合理范围内随机化,比如初始误差在[-0.3,0.3]之间随机取,这样学到的策略泛化性能好很多。
- 别忘了保存Agent。每训练50个Episode就保存一次,防止电脑断电或者训练崩溃导致白练。
我自己的感觉是,DDPG给SMC调参这件事,本质上是把“调参”从一个试错过程变成一个数据驱动的最优决策过程。中间最花时间的不是搭模型,也不是写算法调用代码,而是把奖励函数这一个细节反复打磨到符合你的控制直觉。奖励函数里每一项权重的调整,都是在教Agent你在意什么:在意误差多一些,还是在意控制平滑多一些。一旦它学明白了,生成的调参策略就会给你一种“原来有些参数组合我永远也想不到”的既视感。
这个方向后面还能继续扩展。比如把多个控制器的参数一起在线协调,或者把训练过程放到真实控制器上做在线微调,又或者把DDPG的策略和传统自适应律融合,让系统在快速调节的同时保持可解释性。有机会我再把这几块的实践单独拿出来写一写。如果你正在做SMC相关的项目,希望这篇里提到的步骤和坑能帮你少走几段弯路。