简介:一款面向智能控制研究者与机器人方向学生的 MATLAB Simulink 强化学习控制实现,以自适应控制为主线,结合经验回放等机制,让机器人能在未知或变化环境中实时调整策略。压缩包共 127 个文件,其中 106 个 m 脚本是核心代码,覆盖启动清理、经验回放、核心学习算法与界面辅助等模块;其余 13 张 JPG 示意图、3 张 PNG、2 张 GIF 动图和 1 个 FIG 文件,以及少量 HTML/TXT 说明文档,可用于查看仿真界面、机器人运动效果与算法过程,整体体积仅 336KB,便于快速下载阅读。目前已有 850 人学习浏览。包内代码层次清晰,从 startuprl 初始化环境到 learn.m 主学习循环均有对应实现,再配合经验池与回放策略,可帮助读者在 Simulink 中搭建 DQN/DDPG 类自适应控制实验;Changelog 与 Contents 还能辅助追踪版本变更、快速定位文件,对理解强化学习与机器人控制结合、复现控制器训练流程都有直接参考价值。
1. 为什么强化学习控制机器人,卡在Simulink这层
做过机器人控制的人大多有这种经验:强化学习算法在纯Python仿真里跑得顺顺当当,到实物上却一碰就露馅。关节摩擦、电机饱和、通信延迟、负载变化,任何一个不确定性都可能让训练出来的策略彻底失效。这就是标题里“自适应”三个字真正的意义——策略不仅要会完成轨迹跟踪,还要能感知环境参数偏移并在线调整行为。
而MATLAB/Simulink在这个问题上的位置很微妙:它既是机械臂、移动机器人、四旋翼建模仿真的标准环境,又是强化学习工具箱(Reinforcement Learning Toolbox)能直接训练智能体的宿主。问题在于,大部分教程只教你如何在MATLAB脚本里训练一个agent,却很少讲清楚“Simulink模型如何与训练循环交互”“奖励函数怎么在仿真时钟里实时计算”“模型参数变了之后策略怎么自适应调整”。这篇文章把这三件事连成一条线,给出一套可以在本地复现的落地路径,适合那些手里已经有Simulink机器人模型但还没迈过强化学习这道坎的工程师参考。
2. 强化学习控制机器人:算法选型与状态空间设计
2.1 连续动作域里选DDPG还是PPO:先看执行器
机器人控制大多属于连续动作问题——机械臂关节力矩指令、移动机器人线速度角速度、四旋翼油门姿态角,输出都是连续向量。深度强化学习里处理连续动作域的常用候选主要有DDPG、TD3、SAC、PPO这四种,选型不只是在跑分表上比较,而是在动作更新方式、样本效率、安全约束能力上取舍。
| 算法 | 动作更新方式 | 样本效率 | 超参数敏感度 | 典型适用场景 |
|---|---|---|---|---|
| DDPG | 确定性策略梯度 | 中高 | 高 | 低维连续控制,结构简单够用 |
| TD3 | 确定性策略梯度 + 双Q网络 | 高 | 中 | DDPG的改进版,优先替代DDPG |
| SAC | 最大熵随机策略 | 高 | 中低 | 鼓励探索,适合奖励稀疏场景 |
| PPO | 随机策略梯度 | 低中 | 低 | 稳定但需要大量仿真样本 |
我一般会建议:如果Simulink模型精度尚可、采样时间小、仿真速度快,优先用TD3或SAC;如果实物上只能做短时间在线采集,样本量有限,DDPG配合高噪声探索也有操作空间。PPO需要的样本量在仿真里不成问题,但如果你的Simulink模型需要几分钟才能跑完一个episode,PPO会有训练时长焦虑。
2.2 状态空间设计:把关节摩擦和负载写进观测向量
自适应控制的核心前提是“控制器看得到变化”。如果观测向量只有关节角、角速度、目标误差,策略模型事实上无法区分“负载增加了”和“目标突然提速了”,因为没有特征能指示环境发生了变化。常见的补救方式是把能够反映模型失配的间接量加进观测:
- 实际输出与期望轨迹的一阶误差差分,量级上等效于模型偏差的变化速度;
- 控制输入的历史窗值,例如最近10步力矩均值,能间接反映阻力变化;
- 跟踪误差的积分电量,用于感知持续性的干扰。
% 状态观测定义示例:7维观测用于机械臂单关节自适应轨迹跟踪 obsInfo = rlNumericSpec([7 1], ... 'LowerLimit', [-10 -100 -2 -2 -100 -100 -inf]', ... 'UpperLimit', [10 100 2 2 100 100 inf]'); % 第1行:关节角度误差(状态估计可以包含负载变化引起的位置偏差) % 第2行:关节角速度(执行器输出受限的失控迹象会体现在这里) % 第3-4行:当前力矩与上一时刻力矩(反映负载突变的动态) % 第5-6行:误差的滑动平均和滑动方差(变化率检测的关键窗口值) % 第7行:实时奖励值(部分环境中作为策略的历史先验输入) obsInfo.Name = 'joint_obs';每个观测维度的下限和上限一定要被约束到物理边界,而不是拍脑袋填一个很大的数。强化学习对scale极其敏感,关节角误差是0.1rad量级,力矩可能是10Nm量级,二者放入同一个数组后数值量级差异会导致梯度被大数值特征主导。常见做法在进入agent之前跑一个obs_normalized = (obs - mean) ./ std,不过注意:均值方差统计必须来自环境本身的特性采样,而不是训练初期的随机轨迹,否则归一化反而引入偏移。
2.3 奖励函数怎么立:前后项拆分,别只给稀疏信号
密集奖励设计里常见写法是reward = -w1 * e^2 - w2 * tau^2 + bonus,其中e是跟踪误差,tau是控制力矩,bonus是达到目标区域的常值奖励。这个式子直接决定了自适应能力的上限,因为如果奖励函数不惩罚“力矩突变”,策略就会学会利用高带宽控制冲过扰动区间,表现为动作抖动、执行器饱和。
% 用于Simulink Gym-like environment reward计算的MATLAB函数块核心片段 function reward = computeReward(obs, action) e = obs(1); % 位置误差 de = obs(2); % 角速度误差 tau = action(1); % 当前动作(力矩) w1 = 0.7; % 位置误差权重 w2 = 0.15; % 动作幅值惩罚权重 w3 = 0.15; % 动作变化率惩罚权重 persistent lastTau; if isempty(lastTau) lastTau = 0; end dTau = tau - lastTau; lastTau = tau; reward = -(w1 * e^2 + w2 * tau^2 + w3 * dTau^2); if abs(e) < 0.02 reward = reward + 1.0; % 距目标近时给一个小额bonus平滑奖励面 end end权重w1/w2/w3是自适应调参的入口,后面第4章会说明如何在训练中途动态改变这三项权重来模拟环境适应需求。注意不要使用绝对值函数当惩罚项,绝对值的梯度在符号切换点不连续,MATLAB中rl训练计算策略梯度时容易造成振荡。
3. 在Simulink里搭一个可训练的自适应控制闭环
3.1 顶层模型结构:Agent、环境、奖励计算模块三者怎么连
Simulink下做强化学习控制的常规连接思路是:被控对象模型(机械臂/移动机器人/四旋翼)放在一个受使能信号控制的子系统里,状态信息由Output端口输出;RL Agent模块根据状态计算出控制动作;动作一方面进入被控对象,另一方面连同状态一起进入Reward计算模块,生成当前步的奖励值。三个模块之间还必须有一个“训练时钟同步”机制,否则Simulink的连续求解器会在仿真步长内多次调用agent,造成训练数据中采集到非预期的时间差分数据。
| 模块 | 端口/参数 | 作用 |
|---|---|---|
| Simulink Environment | rlObservationBus / rlActionBus | 提供给agent的状态与动作接口 |
| RL Agent模块 | Agent to Environment, Environment to Agent | 在推理模式下输出动作,训练时由外部训练循环调用 |
| Reward计算函数块(MATLAB Function) | obs, action, reward三个端口 | 按时间步计算标量reward |
| Reset函数块 | initial conditions | 每个episode开始时重置关节角度、速度、积分值 |
3.2 用rlTrainingOptions配置的仿真交互参数
训练不是在Simulink里点“Run”,而是通过MATLAB脚本调用train函数完成的。Simulink模型被rlSimulinkEnv封装成一个标准的训练环境对象,这个对象内部在每一步仿真中与agent交互。
% 连接Simulink模型与强化学习环境 env = rlSimulinkEnv('ctrl_robot_mdl', 'ctrl_robot_mdl/RL Agent', obsInfo, actInfo); % 去掉仿真过程中弹出的示波器刷新窗口,避免训练速度被GUI拖垮 env.resetFcn = @(in) resetRobotState(in); % 训练超参数:重点是EpisodeCount和LearnRate的配合 trainOpts = rlTrainingOptions(...) MaxEpisodes = 3000, ... MaxStepsPerEpisode = 500, ... ScoreAveragingWindowLength = 20, ... StopTrainingCriteria = "AverageReward", ... StopTrainingValue = -20, ... Plots = "training-progress"); % 训练时间不充裕时优先调整LearnRate而不是加Episode agent.AgentOptions.ActorOptimizerOptions.LearnRate = 1e-4; agent.AgentOptions.CriticOptimizerOptions.LearnRate = 1e-3;resetRobotState这个函数负责在每个episode开始时对Simulink模型中的积分器、状态变量赋初始值,随机化初始位置误差。注意指定StopTrainingCriteria为AverageReward而不是EpisodeReward,否则只有个别幸运的episode才会停掉训练,整体策略未必收敛。
3.3 训练过程中Simulink引擎的三个坑
第一个坑:Simulink模型的采样时间和RL训练步长必须对齐。强化学习Agent在训练中的每个step对应一个物理仿真时间间隔,这个时间间隔在模型中往往由连续求解器自动决定,但RL Agent的SampleTime属性不能设为1,而要设为0.01或更小,逼近实际机器人控制周期。
第二个坑:不要用Simulink的全局Goto/From传状态或奖励值。这些信号在训练优化中不会自动成为计算图的一部分,可能导致训练正常但推理结果异常。正确做法是让信号物理连线到RL Agent模块和Reward函数块。
第三个坑:simulation 模式必须设定为Environment外部训练模式。模型设置里如果保持普通仿真模式,train函数会报错“Simulation must be launched with the rlSimulinkEnv environment”。检查sim(env)方法能否正常单步调用,是快速验证模型是否就绪的办法。
4. 自适应机制落地:奖励函数时变与在线微调
4.1 “自适应”在强化学习里通常指两种路径
第一种叫训练时域随机化(Domain Randomization),第二种叫在线微调(Online Fine-tuning)。二者的设计目标不同,落点也不同。域随机化是训练阶段就故意改变Simulink模型里的负载质量、摩擦系数、通讯延迟等参数,让策略在变化范围里学出一个更通用的解;在线微调则是策略已经在某个标称环境下正常工作时,通过性能指标检测环境漂移,触发继续训练或切换到备用策略。
| 路径 | 适用阶段 | 优点 | 缺点 | 实现对象 |
|---|---|---|---|---|
| 域随机化 | 训练阶段 | 提高策略的泛化范围,可处理事先未知的扰动区间 | 需要较宽参数扰动设计,训练成本高 | Simulink模型内部参数(负载、摩擦、阻尼) |
| 在线微调 | 部署阶段 | 可应对训练区间之外的异常工况 | 在线训练有稳定性风险,硬件上需谨慎 | RL Agent的策略网络权重,reward函数权重 |
对于标题里的“自适应机器人控制”,两者最好结合起来:先用域随机化把策略在Simulink里训到鲁棒,上线后在实物数据上做强化学习的在线校准。但这不是一篇“从零到实物”的文章,所以重点说训练阶段和部署前仿真阶段如何实现“自适应”。
4.2 在Simulink中通过参数通道动态修改环境属性
域随机化的Simulink实现不做复杂编程,通常直接利用模型变量工作空间。给被控对象子系统的摩擦系数和负载质量定义MATLAB工作空间变量,在训练循环里每个Episode启动前,用evalin或assignin修改变量值:
function in = resetRobotState(in) % 对Simulink模型 ctrl_robot_mdl 中的模型参数做随机化 blkPath = 'ctrl_robot_mdl/Robot Plant'; loadVar = getVariable(in, 'm_load'); m_new = 0.8 * loadVar + 0.4 * randn(); assignin('base', 'm_load', max(m_new, 0.1)); loadVar2 = getVariable(in, 'fric_coeff'); f_new = 0.05 * loadVar2 + 0.02 * randn(); assignin('base', 'fric_coeff', max(f_new, 0.001)); % 重置机器人初始关节角度偏移 in = in.setVariable('q0', 0.2 * randn(2)); end这种做法的本质是让策略无法“死记硬背”单一的动力学解,提升了第2章第2节提到的“模型失配可观察性”。注意随机扰动范围不要从一开始就铺满物理极限,经验做法是先窄后宽:前500个Episode用小扰动让策略先学会基本控制,后2000个Episode逐步加宽扰动区间。
4.3 在线微调的奖励权重调度策略
在线微调阶段,如果发现奖励函数权重固定,策略在真实环境出现大规模误差时可能不知道“现在应该更注重位置误差还是更注重力矩平稳性”。一个可在Simulink中实现的自适应调度策略是:根据误差的滑动窗口标准差动态调整奖励权重。
% 自适应权重计算:误差标准差大时提高位置误差权重,否则维持手柄柔性 function [w1, w2] = adaptiveRewardWeights(errHistory) n = length(errHistory); sd = std(errHistory); if sd > 0.15 w1 = 0.85; w2 = 0.10; % 优先修正位置偏差 elseif sd > 0.05 w1 = 0.70; w2 = 0.15; % 中间状态兼顾两者 else w1 = 0.50; w2 = 0.30; % 误差较小,强化平滑动作 end end计算出新权重后,通过Simulink的Data Store Memory或者直接赋值到MATLAB Function块的参数变量,在下一次奖励计算循环时生效。需要注意:高频率更新奖励权重会造成非平稳奖励环境,策略训练反而更难稳定。在Simulink中每个Episode开头设置一次权重,比每个时间步都改要好得多。
4.4 在线微调触发机制的具体设置
部署阶段的在线微调不能无脑执行。建议在Simulink中设计一个“性能守门员”逻辑:每50个仿真步计算一次跟踪误差的滑动平均值,与预设阈值比对,只有当误差连续三个窗口超过阈值时,才触发策略网络的在线训练。
% 在线训练触发检测器(部署阶段) persistent errWin; if isempty(errWin), errWin = zeros(50,1); end errWin = [errWin(2:end); abs(e)]; winMean = mean(errWin); if winMean > 0.08 && consecutiveCount >= 3 % 触发热启动在线微调,使用当前经验缓冲 setTrainingMode(agent, true); train(agent, env, onlineOpts); else setTrainingMode(agent, false); end这个在线微调过程有条件地更新actor网络权重,而不是训练整个体系。RL Agent对象在MATLAB中可以直接调用train来继续用新采集的经验做梯度更新,但需用LearningRate衰减到1e-6左右,防止以“网络重置”的方式破坏已有策略。
5. 验证技巧:奖励曲线、动作抖动与安全切换
5.1 奖励曲线不是越高越好,而是看收敛方差
训练结束后,最关键的验证不是看平均奖励是否达到StopTrainingValue,而是回放训练曲线里窗口奖励的标准差。越是自适应的策略,在域随机化环境中后期应该是奖励缓慢上升,方差逐步收窄;如果出现窗口奖励先升后大幅回落,说明reward权重调度出了问题,而非策略本身不行。
5.2 用动作抖动率验证策略是否过度激进
在Simulink推理模式下跑一段轨迹跟踪仿真,采集动作序列,计算相邻步动作差分的绝对值均值,如果超过执行器峰值力矩的15%,策略在实际机器人上大概率会触发减速机报警。一个快速检测脚本:
tauSeq = logsout.get('torque_cmd').Values.Data; dTau = diff(tauSeq); dTauMean = mean(abs(dTau) / max(abs(tauSeq))); if dTauMean > 0.15 warning('动作抖动率%.2f超出安全阈值,建议降低动作变化率惩罚门槛', dTauMean); end5.3 安全切换:Simulink里加一个保守策略兜底
自适应强化学习落地,最容易被挑战的问题是“如果在线微调时策略突然退化怎么办”。一个工程上实用的方案是:在Simulink的控制链路中并联一个保守的PID控制器,通过一个可配置的切换模块选择输出源。平时强化学习agent接管,一旦监测到策略输出的动作值超出执行器物理限制,或在线训练的训练损失爆炸,自动切回PID。
if any(abs(action) > actuatorLimit) || trainLoss > 10 action_output = pidOutput; % 保守策略兜底 switchFlag = 1; else action_output = action; % 强化学习策略正常输出 switchFlag = 0; end这个切换器在Simulink里不需要额外工具箱,用Switch模块加上MATLAB Function即可实现。切换条件的判断要和奖励函数共用同一组观测数据,不要新增传感器,减少部署时的标定工作量。
本文还有配套的精品资源,点击获取