机器人阻抗控制与强化学习参数整定:仿真到真机部署指南

机器人阻抗控制与强化学习参数整定:仿真到真机部署指南 1. 阻抗控制真正在调的是三个量而它们恰恰是最容易失效的地方机器人阻抗控制Impedance Control这件事很多人第一次接触时都会觉得它简单得可疑无非就是让机械臂末端表现得像一个弹簧加阻尼器位置偏了就出力偏得越多出力越大。公式写出来只有一行参数也就刚度、阻尼、质量三个看起来比PID还朴素。但真到工位上跑尤其是插入、打磨、抛光、装配这类必须和环境贴着干活的任务你会发现固定参数几乎不可能一直好使——换个工件、换个夹持姿态、甚至只是环境温度变了导致线缆硬度变化同一组参数的表现就可能从丝滑变成打摆子。强化学习Reinforcement Learning在这类问题上的介入点恰恰不是去替代阻抗控制器的位置而是去接管那组最难受的、需要人工反复试凑的增益参数。换句话说底层那套基于力反馈和位置反馈的柔顺控制回路还是原样保留强化学习策略只负责在每个控制周期给出这一时刻应该用多硬、多软的阻抗参数。这样做的好处是策略的搜索空间被压得很小六到十二维且每一维都有明确的物理上限训练时不容易飞出安全边界部署时也有现成的限幅逻辑可以兜底。1.1 从 M_d·ẍ D_d·ẋ K_d·Δx F_ext 这一行开始理解标准的笛卡尔阻抗控制目标动力学可以写成这个形式M_d · (ẍ - ẍ_d) D_d · (ẋ - ẋ_d) K_d · (x - x_d) F_ext其中 M_d、D_d、K_d 是人为设定的目标惯量、目标阻尼、目标刚度矩阵x 是实际末端位姿x_d 是期望位姿F_ext 是环境施加在末端上的外力。这个式子的物理含义非常直白把机器人当成一个可以任意捏造参数的虚拟弹簧-质量-阻尼系统环境推它一下它按你规定的软硬程度退让多少。工程上真正被反复调的就是 K_d 和 D_d。刚度决定退让幅度刚度越大机器人越倔末端偏离期望位置一点点就产生很大的恢复力刚度越小越软一点点外力就能把末端推偏。阻尼决定退让的速度品质阻尼太小会出现明显的振荡收敛过程阻尼太大则响应迟钝、显得粘。这里有个参数搭配的经验在纯线性弹簧-阻尼系统里想让响应不振荡通常取临界阻尼或略过阻尼也就是 D ≈ 2·√(K·M)。实际机械臂上因为有摩擦、关节柔性、传感器延迟往往要比这个值再大一到两倍才稳。你可以先用这个公式算出量级再在它附近用实验微调比盲目扫参效率高得多。1.2 导纳还是阻抗内环放什么决定了后面所有实现的难度同一条目标动力学实现路径有两条很多人在这里就分岔了阻抗控制Impedance Control外环读位置内环直接出力/力矩。你给的是期望位置控制器根据位置偏差算出应该施加多大的力。要求机器人本身能做力矩控制或者力矩估计也就是常说的力矩模式机械臂。导纳控制Admittance Control外环读力内环出位置。你给的是测到的接触力控制器根据这个力算出末端应该被推到哪里再把这个位置下发给底层的位姿控制器。绝大多数只开放位置接口的工业机械臂走的都是这条路。这个区分非常关键因为它直接决定你的强化学习策略能不能落地。策略输出的如果是刚度参数在阻抗控制里它是力的放大倍数在导纳控制里它是位置的放大倍数两者的量纲和数值范围完全不同网络输出的缩放系数必须分别标定。我见过不止一次有人拿一个在力矩模式机械臂上调好的策略直接搬到位置接口机械臂上结果一上电就疯狂震荡原因就是没有重新标定这个缩放。1.3 固定增益必然翻车的四种工况我在实际项目里总结下来让固定阻抗参数失效的场景基本逃不出这四类工况变化表现根本原因环境刚度差异大硬金属件上力超调、软泡棉上线缆上力不足固定 K_d 无法适应 1e3~1e6 N/m 量级的刚度跨度自由空间到接触的瞬间末端被弹开或激发振荡刚度不连续跳变冲击动能没有缓冲同一任务的阶段切换前期插不进去后期定位不准前期需要低刚度顺从后期需要高刚度定位摩擦与关节柔性仿真调好的参数上真机就变库仑摩擦、减速机回差、连杆柔性未建模这四类问题里前两类占了八成的调试时间。强化学习能起作用的地方也正好在这里它通过与环境反复交互隐式地学到了什么状态该给什么刚度的映射不需要事先知道环境刚度是多少。需要注意的是这类方法的收益不是精度提升多少倍而是同一个控制器能覆盖的工况范围变宽了。如果你的任务是单一工况、单一工件、环境刚度稳定老老实实调一组固定参数更省事上强化学习反而是过度工程。2. 把阻抗参数的整定过程写成MDP状态、动作、奖励怎么定把控制问题转成马尔可夫决策过程MDP这一步是整个项目里最需要动脑子、也最容易埋雷的地方。我见过太多项目卡在这一步算法换了三四个超参调了两周最后发现是状态设计里塞了不该塞的东西导致策略根本学不到因果关系。MDP 四要素里动作空间和奖励函数决定了策略能不能学会状态空间决定了它学得多快。这三者的设计顺序我建议是先定奖励明确任务目标再定动作明确干预方式最后定状态明确策略需要看到什么才能决策。反着来很容易做出一个状态维度爆炸但都不解决问题的方案。2.1 状态里该放什么不该放什么初学者最典型的做法是把关节角度、关节速度、末端位姿、力传感器读数全部拼成一个几十维的向量丢给网络。这个方案在仿真里可能勉强能跑但样本效率极差而且策略会过度依赖仿真里那些真机上根本不精确的量比如关节角度的绝对精度、力传感器的零漂。我的建议是把状态分为必需、可选、慎用三档必需项缺了学不会末端位姿误差 e x - x_d取平移三维加旋转三维旋转部分建议用旋转向量或四元数差值不要直接用欧拉角欧拉角在奇异位形附近会让策略看到跳变。末端线速度与角速度六个量。接触任务本质是动力学问题没有速度信息策略就无法预估冲击。滤波后的六维力/力矩读数。这里力信号一定要滤波原始信号的噪声会让策略学到错误的关联。我一般用截止频率 20~50 Hz 的二阶低通或者一阶低通加卡尔曼取决于传感器噪声水平。上一步实际输出的动作。这一项看起来多余其实非常关键它让策略有能力学会输出平滑的增益序列是抑制抖动最有效的手段之一。可选项看任务加任务阶段标志位比如插装任务里用是否已接触这个布尔量。如果强行让策略自己从力信号里推断接触发生通常会多花几十万步。末端期望轨迹的前瞻点提前给两三步的 x_d让策略有一点预判能力。慎用项能不加就不加原始关节角度与关节速度。维度高、和任务坐标系无关、冗余度高。环境刚度的估计值。除非你有一个可靠的在线辨识器否则这个量在真机上噪声很大策略会学到虚假关联。历史长序列。除非你用循环网络或者Transformer否则拼进去只会增加维度。2.2 动作空间的三种参数化方式先说清各自的边界动作空间这块三种方案我都实打实跑过各自的适用面差别很大方案A直接输出笛卡尔阻抗增益策略输出 ΔK 和 ΔD 的修正量或者输出一个缩放系数 α让实际刚度等于 α 乘以一个标称刚度。这个方案维度低六到十二维物理意义明确限幅简单是最容易跑通的。缺点是策略只能调多硬多软不能改变轨迹本身。方案B输出参考轨迹的修正量 Δx阻抗增益固定策略只负责微调期望位置。这个方案在做精定位、轴孔装配的最后几毫米时特别有效因为位置修正量对最终的接触力有直接的、单调的影响学习难度低。缺点是无法应对环境刚度的大范围变化。方案C混合输出同时给 Δx 和 ΔK。灵活性最高但动作空间维度上去之后探索效率明显下降而且两项之间存在耦合——策略可以用改轨迹或者改刚度两种方式达到同样的力效果这会稀释梯度信号训练时经常出现一项主导、另一项几乎不动的情况。我的实践建议是先用方案A的对数空间残差版本跑通再考虑加上方案B。对数空间指的是策略输出的是 log(K) 的增量然后取指数映射回线性空间。这样做的原因是刚度的有效范围往往跨越两三个数量级从 100 N/m 到 10000 N/m在线性空间里网络很难同时兼顾小值区和大值区的分辨率。2.3 奖励函数力惩罚必须用hinge而不是平方奖励函数设计是决定项目成败的关键。我先给出一个在我手上比较稳的通用形式r - w1 * ||e_p||^2 - w2 * ||e_r||^2 - w3 * max(0, ||F|| - F_safe)^2 - w4 * ||F||^2 # 小的接触力鼓励项权重很小 - w5 * ||a_t - a_{t-1}||^2 - w6 * |F · v| # 力与速度同向的功率项逐项说清楚为什么这么设计位置误差项 w1、w2 是主任务目标权重最大。旋转误差项 w2 容易被忽略但在接触任务里姿态偏差往往比位置偏差更容易引发局部应力集中建议给到平移项的 0.3~0.5 倍。安全力惩罚项 w3 用 hinge 形式只惩罚超过阈值的那部分这是我认为最容易被写错的一项。如果直接对全部接触力做平方惩罚策略学到的第一个动作就是尽量别接触表现出来就是末端接近到距目标几毫米的地方开始犹豫、来回蹭。而 hinge 形式传达的信息是阈值以下随便用超过就不行这才符合接触任务的实际需求。小的力鼓励项 w4权重必须很小通常在 w1 的百分之一量级。它的作用是防止策略躲在完全自由空间里不干活不是用来压出力大小的。动作平滑项 w5是抑制抖动的核心手段。系数太小不起作用太大会让策略变得迟钝、无法快速响应。我一般从 1e-4 开始试看训练曲线上动作差分范数的量级来调整。功率项 w6用来惩罚边压边滑这种危险动作。接触力方向与运动方向同向时表示机器人在推着环境滑动这在打磨类任务里是正常的但在装配类任务里往往意味着工件被推动了需要惩罚。2.4 终止条件要把安全约束写进环境而不是依靠外层看门狗回合终止条件的设计很多人只写超时这不够。我建议至少包含这几条接触力超过硬上限比如传感器量程的 60%立刻终止并给一个大负奖励。末端位置超出预设工作空间包围盒。期望轨迹已经完成且误差收敛到阈值内正常终止并给正奖励。固有时长超时小幅负奖励。把这些写进环境而不是只依靠外层的安全看门狗好处是策略在训练中就能感知到边界的存在学会主动规避。只靠外层看门狗的话策略会在训练里反复撞线而看门狗只在部署时兜底训练信号里完全没有这条信息。3. 仿真环境搭建接触模型比算法本身更能决定成败把 MDP 定义好之后下一步是找地方训练。这里我想强调一个在实践中反复被验证的结论对于接触密集的阻抗控制任务仿真平台里接触模型的设置对最终策略能不能迁移到真机的影响往往比选SAC还是PPO大得多。原因不复杂。强化学习策略是在仿真里摸出来的它摸到的接触手感就是仿真给出的接触手感。如果仿真里的接触是软的、无摩擦的、无延迟的策略学到的参数整定规律在真机上就是不成立的迁移必崩。3.1 四个常见平台的取舍平台接触求解特点吞吐上手成本适合的环节MuJoCo软接触模型solref/solimp可调接触稳定高单核数千步/秒低Python API成熟主力训练平台PyBullet接触参数难调易穿模中低快速原型、教学Isaac Sim / Isaac LabGPU并行接触可调极高数千环境并行高版本依赖复杂大规模域随机化训练Gazebo物理精度一般与ROS生态贴合低中接口验证、不用于策略搜索我的常规做法是主力训练放在 MuJoCo用它的软接触模型加上可调的接触刚度参数快速迭代等到策略结构和奖励都稳定了再考虑迁到 Isaac Lab 做大规模域随机化。Gazebo 只用来验证通信接口和话题频率不做策略搜索。3.2 接触刚度、solref与时间步长的绑定关系MuJoCo 里的接触模型是软约束通过 solref 参数控制接触的刚度与阻尼典型的写法是geom nametool_tip typesphere size0.01 solref0.005 1 solimp0.95 0.99 0.001/solref 的第一个数大致可以理解为接触的时间常数数值越小接触越硬。这里有一个必须记住的约束接触时间常数不能小于仿真步长的两到三倍否则求解器会不稳定出现能量爆炸。比如仿真步长是 1 ms那时间常数不要低于 2~3 ms如果你的真机末端接触刚度很高金属对金属希望仿真也硬就得把步长降到 0.5 ms 甚至更小代价是训练变慢。另一个容易忽略的点仿真里的执行器建模。很多 URDF 里把执行器写成纯力矩源但真机上的关节驱动器带宽有限、有摩擦、有小延迟。我一般会加上三样东西关节摩擦项库仑加粘滞系数按厂家数据或实测估计。执行器一阶延迟时间常数 5~20 ms用来模拟真实的位置/力矩环响应。命令缓冲把策略输出的动作延迟一到三个控制周期再施加模拟通信与计算延迟。3.3 域随机化清单与随机范围的经验值域随机化的范围设定有个原则覆盖真机上可能出现的偏差范围但不要大到让任务本身变得不可能完成。我给一份我常用的清单随机项随机范围依据连杆质量标称值的 ±10%负载变化、线缆影响关节惯量±15%减速机与负载折算误差关节库仑摩擦0.5~2 倍估计值装配公差、润滑状态力矩传感器噪声高斯σ 0.5~1.5 N 与 0.05 N·m传感器规格书力传感器零漂每回合随机偏置 ±1 N温漂、零点标定误差控制延迟1~3 个周期通信与计算链路环境刚度2e3 ~ 5e5 N/m覆盖软垫到金属环境位置偏移±5 mm工件放置误差期望轨迹起点±3 mm定位误差环境刚度这一项是最重要的也是很多人随机的范围太窄导致迁移失败的地方。跨度给到两个半数量级策略才有机会学到软环境调高刚度、硬环境调低刚度这个规律。4. 算法与网络为什么SAC通常比PPO先跑通算法选型这块我给一句可能不那么政治正确但很实用的结论在阻抗参数整定这类任务上先跑通SAC别一上来就上PPO。4.1 样本效率在接触任务里是硬约束接触任务的仿真单步耗时远高于普通的到达任务因为需要精细的时间步长和稳定的接触求解一步仿真经常要 0.5~2 ms。这种情况下一个需要五百万到一千万步才能收敛的 on-policy 算法PPO 的典型量级意味着单次训练要跑几小时到十几小时。而 SAC 这类 off-policy 算法通常几十万步就能看到明显的策略改善同样的机器上一两个小时能出第一版。另一个原因是接触任务的奖励信号噪声比较大接触求解本身有离散跳变最大熵框架下 SAC 的策略随机性有助于它跳出局部最优而 PPO 在高噪声奖励下很容易收敛到保守不动的策略。TD3 也可以但确定性策略在探索上不如 SAC接触任务里需要策略主动去试试更硬一点会怎样这个探索行为 SAC 天然就有。4.2 网络规模、超参与训练循环网络不需要大接触任务的输入维度只有几十搞个几百维的MLP纯属浪费项目取值说明Actor 网络2 层 × 256ReLU 或 ELU输出均值与对数标准差Critic 网络2 层 × 256双Q两个独立critic取最小值学习率3e-4策略与critic同回放池大小1e6接触任务经验分布窄池子大一点有好处Batch size256折扣因子 γ0.99回合长度 200~500 步时合适软更新系数 τ0.005目标熵-dim(A)SAC自动温度调节的起点梯度更新频率每环境步 1 次接触任务里可以到 2 次动作输出的处理有一步不能省用 tanh 压缩到 [-1, 1] 之后必须按每个维度的物理范围映射并且在对数空间做。伪代码大致是这样# policy_out 是网络输出的原始动作形状 (batch, act_dim) a torch.tanh(policy_out) # [-1, 1] log_k_scale a[:, :6] * 0.5 # 最多 ±0.5, 即 e^0.5 ≈ 1.65 倍 k_target k_nominal * torch.exp(log_k_scale) # 对数空间缩放 k_target torch.clamp(k_target, k_min, k_max) # 硬限幅兜底对数空间缩放的好处是无论标称刚度是多少策略的动作语义都一致网络不需要为不同的机械臂重新学习输出范围。4.3 一份可运行的训练骨架用 Gymnasium 接口包装环境配合 stable-baselines3 是最省事的路径import gymnasium as gym import numpy as np from gymnasium import spaces from stable_baselines3 import SAC from stable_baselines3.common.callbacks import EvalCallback class ImpedanceEnv(gym.Env): def __init__(self, sim): super().__init__() self.sim sim self.act_dim 6 self.obs_dim 22 # 位姿误差6 速度6 力6 上一步动作6 (示例) self.action_space spaces.Box(-1.0, 1.0, (self.act_dim,), np.float32) self.obs_space spaces.Box(-np.inf, np.inf, (self.obs_dim,), np.float32) self.action_space self.action_space self.observation_space self.obs_space self.k_nominal np.array([2000., 2000., 2000., 50., 50., 50.]) self.k_min self.k_nominal * 0.3 self.k_max self.k_nominal * 3.0 self.step_count 0 def _get_obs(self): err_p self.sim.get_pose_error()[:6] vel self.sim.get_twist() force self.sim.get_filtered_wrench() last_a getattr(self, last_action, np.zeros(6)) return np.concatenate([err_p, vel, force, last_a]).astype(np.float32) def reset(self, seedNone, optionsNone): super().reset(seedseed) self.sim.randomize_domain() # 域随机化在这里做 self.sim.reset_to_start() self.step_count 0 self.last_action np.zeros(6, dtypenp.float32) return self._get_obs(), {} def step(self, action): # 1. 动作映射到物理增益 k_target self.k_nominal * np.exp(np.clip(action * 0.5, -0.6, 0.6)) k_target np.clip(k_target, self.k_min, self.k_max) # 2. 交给底层阻抗控制器执行一个周期 self.sim.set_impedance_gains(k_target) info self.sim.step_control() # 3. 计算奖励 e_p np.linalg.norm(info[err_p]) e_r np.linalg.norm(info[err_r]) f_mag np.linalg.norm(info[force]) f_excess max(0.0, f_mag - info[f_safe]) da np.linalg.norm(action - self.last_action) reward (- 10.0 * e_p**2 - 4.0 * e_r**2 - 0.05 * f_excess**2 - 1e-4 * f_mag**2 - 0.5 * da**2 - 1e-4 * abs(info[power])) # 4. 终止判断 terminated False if f_mag info[f_hard_limit]: reward - 10.0 terminated True if info[done]: reward 5.0 terminated True self.step_count 1 truncated self.step_count self.max_steps self.last_action action.astype(np.float32) return self._get_obs(), float(reward), terminated, truncated, info env ImpedanceEnv(sim) eval_env ImpedanceEnv(sim_factory()) model SAC( MlpPolicy, env, learning_rate3e-4, buffer_size1_000_000, batch_size256, gamma0.99, tau0.005, train_freq1, gradient_steps1, policy_kwargsdict(net_arch[256, 256]), verbose1, ) model.learn( total_timesteps600_000, callbackEvalCallback(eval_env, eval_freq10_000, n_eval_episodes20), )这段骨架里有几个地方值得单独说一下。奖励函数里的各项权重我用了具体数字但这些数字绝对不能照搬它们和你的位置单位米还是毫米、力量纲、控制频率都强相关。位置误差如果用米平方之后就非常小权重自然要大如果用毫米权重就该小两个数量级。先量一下典型状态下每一项的数值量级让它们落在同一个数量级上这是奖励调参最实用的起点。4.4 判断训好了的四个指标只看平均回报曲线是不够的我一般看这四项成功率末端误差收敛到阈值内且接触力没超限的回合占比这是最硬的指标。接触力均方根与峰值分布峰值是否稳定在安全限以内。动作差分范数反映策略输出的增益序列是否平滑。这个值在训练后期应该稳定在一个较低水平如果一直在高位波动说明策略还在抖。不同域参数下的表现方差把环境刚度、质量等参数分组统计看策略在不同条件下的成功率差异。方差大说明泛化不够需要扩大域随机化。5. 训练日志背后的坑从曲线异常反推环境问题这一节说三个我实际踩过、而且排查过程有代表性的坑。5.1 策略学会了撞上去的完整排查链路第一次跑的时候奖励曲线看着还不错位置误差降得挺快但接触力峰值一直很高而且模型在评估时经常触发安全终止。这说明策略找到了一条捷径。排查思路是这样的第一步看力信号的分布确认它不是噪声。把训练过程中采样到的接触力做个直方图如果分布集中在高位且形状很窄说明这是策略的稳定行为不是偶发。第二步反推奖励函数里哪一项在鼓励这个行为。逐项计算典型状态下的贡献值最可疑的是位置误差项。如果位置误差减小带来的收益大于力惩罚带来的损失策略就一定会撞。这时候把两者的量级算一下撞上去让误差从 5 mm 降到 0.5 mm位置收益是 10 × (0.005² - 0.0005²) ≈ 2.5e-4如果这一刻的力惩罚只有 0.05 × (excess)²力超出安全阈值 5 N 才 1.25 的惩罚。数量级上位置项看起来小但关键是力惩罚项在阈值以下是零策略撞上去的瞬间可能力刚好没超阈值那就完全没有惩罚。第三步改奖励结构而不是简单地加大权重。我试过直接加大力惩罚权重结果是策略变得极度畏缩成功率大幅下降。有效的改法是两处一是加时间维度上的力累积惩罚对力在若干周期内的积分做惩罚二是加速度相关的力项接触瞬间的速度大就惩罚。这两项让高速撞击这个具体行为付出额外代价而不是把所有接触行为都一起惩罚。第四步验证。改完之后看力峰值的分布如果峰值降低但位置误差基本不变说明改对了如果两者都退化说明惩罚过重需要回调。5.2 仿真里丝滑、真机上抖动这个现象的排查方向基本是固定的几条我按我实际的检查顺序列出来排查项检查方法常见结论策略频率是否过高看真机上策略输出的更新周期策略 100 Hz 以上叠加底层高频环容易激励结构共振动作是否被低通滤波打印相邻两步动作差分未滤波的增益跳变会直接引起力抖动仿真接触是否比真机软对比同样位移下的接触力仿真是软接触学到的刚度偏大真机上就过冲延迟是否建模统计真机端到端延迟未建模延迟导致策略迟一步补偿进入振荡力传感器滤波是否足够看原始信号频谱高频噪声通过策略放大成抖动我实际遇到最严重的一次抖动根因是仿真里的接触时间常数设得太大对应很软的接触策略学到的增益在真机金属件上相当于过冲了三四倍。解决方式是把仿真接触调硬、把执行器延迟加上、并在部署时对策略输出做 10 Hz 的一阶低通三个一起改才稳住。单独加低通只能掩盖症状不能解决根本问题。5.3 完全不收敛时的二分排查法如果策略表现一直是随机水平我按这个顺序二分先验证环境本身是不是确定性可复现的。同一个初始状态、同一串动作序列跑两遍轨迹应该几乎一致。如果不一样说明环境里有未固定的随机源比如没有设置随机种子、求解器迭代次数不稳定这时候训练任何算法都不会收敛。再验证奖励函数是否真的和任务目标单调相关。构造几条人工轨迹比如不接触、轻接触、重接触、精确接触比较它们的累积奖励。如果精确接触的奖励不是最高的那奖励函数就是错的别急着调算法。然后做降维验证。把姿态维度去掉只做单方向的力跟踪把域随机化全部关掉用单一环境。如果单方向、无随机化都学不会那就一定是 MDP 定义有问题不是算法问题。最后才是调超参。SAC 的超参其实相当鲁棒学习率、批大小这些在 3e-4 / 256 这个量级附近动一动即可。如果前面几步都通过了但还不收敛我才会去动超参而且优先动的是梯度更新频率和目标熵不是学习率。6. 落到真机策略频率、安全回退与在线微调仿真训练完真正的工作才开始。6.1 分层控制策略跑低频阻抗环跑高频策略不需要跑得很快。我一般把策略放在 20~50 Hz也就是每 20~50 ms 更新一次阻抗增益底层的阻抗或导纳回路跑在 500 Hz~1 kHz。这个分工的道理是阻抗增益需要随工况变化的尺度是秒级的一次插入过程几百毫秒到几秒而力的闭环需要毫秒级的带宽把策略和力环放在同一个频率上既浪费算力又会引入额外延迟。策略输出的增益不能直接送给底层中间必须有一个插值环节。我一般用一阶低通或者线性插值把低频的策略输出平滑地铺到高频环上。突然的增益跳变会在接触状态下激起明显的力尖峰这一点在硬环境下尤其明显。6.2 安全层必须做的四件事安全层不是可选项是必选项。我一般在策略和底层之间插一个独立模块做四件事增益硬限幅不管策略输出什么实际下发的刚度、阻尼都夹在物理安全范围内。这个范围应该由机械结构和工件的脆性决定而不是由训练时的动作范围决定。变化率限幅单周期内增益的变化幅度设上限避免策略偶发的极端输出造成冲击。力超限回退一旦接触力超过阈值立刻切到一组保守的固定增益并锁定一段时间。这组保守参数必须在真机上单独整定过是最后的兜底。看门狗监控策略推论的执行时间超时或者输出出现 NaN 就切回退控制器。这四件事加起来代码量不大但缺任何一件都可能在真机上出事。6.3 残差微调与离线数据的用法真机上从头训练是不现实的样本代价太高。可行的路径有两条一条是在小范围在线微调。冻结 Actor 网络的前几层只微调最后几层用真机上采集的几百到几千条轨迹做梯度更新学习率调小一个数量级同时保持安全层始终生效。这条路的优势是简单风险是如果真机数据分布和仿真差太多微调容易把已经学好的行为破坏掉。另一条是用离线强化学习在已有的真机数据上做策略提取。真机上做示教、做人工调参、做日常运行会积累大量轨迹数据这些数据里其实藏着什么工况下用什么增益的信息。用离线RL的方法比如基于隐式Q学习的思路从这批数据里拟合一个策略再去和仿真训出来的策略做融合。这条路需要的数据量大但得到的策略在真机分布上更贴合。我个人的偏好是先用第一条路快速验证策略在真机上的基本可用性如果效果不错再考虑第二条路做长期优化。别一上来就上离线RL数据质量、覆盖度、动作分布这些问题会先把人耗死。最后分享一个我在实际部署时觉得最值钱的经验**先把策略的输出记录一整轮完整任务的日志包括每个时刻的状态、策略动作、实际下发的增益、接触力然后离线把这条时间线画出来看。**很多在实时看板上看不出来的问题比如某个特定位姿下策略反复横跳、某个阶段的增益一直顶在上限在时间线上都是一眼就能发现的。这个日志分析花的半小时往往比在真机上盲调一整天更有价值。