
1. 内容整体设计与思路拆解1.1 这篇论文到底在解决什么问题先聊一个我最近做实验时遇到的真实情况。我在用开源VLA模型Vision-Language-Action Model视觉-语言-动作模型做桌面抓取任务时模型对常规物体的操作成功率其实已经相当可观但一旦场景里出现玻璃杯、易碎品、或者人的手臂突然进入工作空间模型就像完全没看见一样照常输出动作。不是它看不见而是它根本不知道这个动作在这个情境下是危险的。这就是VLA模型安全对齐问题的典型痛点。举几个更具体的场景机械臂抓取药瓶时模型预测的动作轨迹会直接穿过旁边的人手倒水任务中模型生成的动作在杯口附近反复震荡容易打翻容器导航任务里机器人为了抄近路贴着桌沿走稍微一个扰动就翻车。这些不是模型笨而是训练目标里压根没有安全这个维度。主流的VLA训练范式还是走预训练微调路线在大量互联网视频或机器人遥操作数据上预训练视频生成或动作预测能力再在下游任务数据上用行为克隆微调。行为克隆的目标函数是最大化动作与专家动作的似然换句话说只要专家演示里没出现过危险情况下的正确避让动作模型就学不会在危险情况下应该怎么避让。这篇题为《Towards Safety Alignment of Vision-Language-Action Model via Constrained Learning》的论文切入角度正是这个盲区把安全问题从损失函数里的一个加分项升级为一个显式的硬约束条件。这套思路在传统强化学习里叫约束马尔可夫决策过程CMDP但把CMDP引入VLA模型的动作生成阶段在业内还属于比较新的尝试。论文适合谁读我的判断是三类人。第一类是正在用OpenVLA、π0、RT系列等模型做机器人真实部署的工程师他们最需要知道怎么让模型少闯祸第二类是研究机器人安全、人机交互安全的同学可以借这篇论文梳理约束学习这个方向的切入方式第三类是自己训练VLA微调流程的团队这篇论文里关于数据构造、约束设计的思路对改进数据配比和训练目标非常有参考价值。1.2 为什么是约束学习而不是加个惩罚项我见过很多人在做安全对齐时第一反应是在损失函数里加一项安全惩罚loss task_loss λ * safety_cost然后发现效果很差。为什么因为惩罚系数λ非常难调。λ调小了模型无视安全λ调大了模型变得畏手畏脚什么任务都不做或者只输出保守动作任务成功率雪崩。而且这个λ对整个数据集是同一个值没法根据当前样本的危险程度动态调节等于用一刀切的方式处理一个高度上下文相关的问题。约束学习换了一个数学框架不再把安全当作损失函数里的一项而是把它写成独立的不等式约束比如期望代价不超过某个阈值。优化目标最大化任务性能 满足条件安全代价期望 ≤ d这个形式在数学上对应带约束优化问题可以用拉格朗日对偶方法求解。你不需要手动去猜λ是多少而是让模型在训练过程中自适应地调整一个安全乘子。当模型违反安全检查时这个乘子自动加大迫使优化过程更重视安全当模型表现安全时乘子回落把更多容量还给任务性能。这个机制是我认为这篇论文最值得仔细品的地方。一个生活化的类比约束学习就像一个家长在教孩子做家务。惩罚式学习相当于家长站在旁边做错了就吼一句孩子会为了避免吼叫而变得不敢动手约束学习则是你可以做家务但不能打破碗——打破碗的后果被明确定义成不能逾越的底线只要不越线家长不会干预你怎么做。这样一来孩子既学会了干活也知道底线在哪里。1.3 技术路线俯瞰从整体架构来看这篇论文的思路可以分为四步在冻结或部分微调的VLA模型上额外引入一个安全评估模块通常是一个安全奖励函数或安全评分模型对每个动作输出计算安全代价把最大化任务性能和满足安全约束定义成一个带约束的优化目标使用拉格朗日对偶方法对约束进行处理生成自适应的加权系数在训练过程中动态平衡任务性能与安全要求用带有安全标注的数据集进行微调使VLA模型在保持任务能力的前提下学会规避不安全动作。这套流程里我理解最核心的工程难点是第1步怎么定义安全代价。论文里提出了一个比较实用的做法定义几种典型的不安全状态比如机械臂末端离障碍物过近、动作速度超限、关节角度越界然后通过距离度量或边界函数来为每个状态计算一个连续的代价分数。这里的关键是连续而不是二值的二值的安全/不安全标签无法提供梯度信息而连续代价可以让模型学会越接近危险就越要调整动作。2. 核心细节解析与实操要点2.1 输入输出设计VLA模型是怎么动作的要理解这篇论文的约束学习得先清楚VLA模型的输入输出结构。拿当前比较常见的VLA设计来说输入一般包含三个部分一张或一段多帧视觉观察图一条自然语言指令比如把红色方块放进篮子里可选的机器人本体状态比如机械臂当前关节角度、夹爪开合度。模型的输出通常不是直接输出连续关节位置而是输出动作token序列或者按照离散动作token的方式来预测。预测目标要么是末端执行器的位置增量delta pose要么是关节角速度具体取决于训练数据的动作表示方式。在论文提出的约束学习框架中每个动作token在训练时都会被送入安全代价函数计算一个代价值并且这个代价会作为约束项参与梯度更新。这里面有一个实操上的坑VLA模型的输出token是离散的而安全代价通常是围绕连续动作来定义的比如说末端执行器到障碍物的欧氏距离所以实测中必须先对动作token做解码得到连续动作后再去计算代价而不是直接在token空间里算代价。我在自己的实验里就把这个顺序搞反过一次直接对离散token算距离结果安全约束和任务目标在梯度方向上互相冲突训练直接发散。2.2 安全代价函数的设计是成败关键论文中的约束学习效果很大程度上取决于安全代价函数的质量。一个太宽松的代价函数会漏掉危险情况一个太严格的代价函数会让模型学不到任务。从我自己的实践和论文的表述来看一个好的安全代价函数应该具备几个特征第一连续性。代价值应该随着状态趋近危险而平滑增加而不是在越过某个阈值时突然从0跳到1。连续性保证了梯度可以从安全约束传到模型参数上。第二可解释性。每一条代价最好能对应到具体的物理意义比如末端距离障碍物表面最近距离、关节速度与安全上限的余量、夹爪接触力预估。这样当约束违反时你能快速定位是哪个安全维度出了问题。第三场景上下文相关。同一个动作在不同语境下的安全代价应该不同。例如机械臂在空旷区域快速移动可能是安全的但同样速度在有人在场时风险就很高。所以代价计算需要把环境信息人、障碍物、易碎品位置编码进来。这里有一个我在实际操作中发现的变体直接用原始图像算像素级别的安全区域来构造代价可能不太稳比较稳的做法是先经过一个视觉感知模型提取障碍物位置再用欧氏距离场来生成连续代价。更高效的替代方案是使用signed distance functionSDF生成三维空间的代价场这样可以同时包含距离和方向信息代价函数的梯度对模型有清晰的推离引导。一个具体的设计示例def compute_safety_cost(ee_pose, obstacle_points, safety_margin0.05): 计算末端执行器相对于最近障碍物的安全代价 ee_pose: 末端执行器位置 [x, y, z] obstacle_points: 障碍物点云坐标 [n, 3] safety_margin: 安全边界米 distances np.linalg.norm(obstacle_points - ee_pose, axis1) min_dist np.min(distances) if min_dist safety_margin: return 0.0 return (safety_margin - min_dist) / safety_margin这个函数会生成0到1之间的连续代价当末端离障碍物距离大于安全边界时代价为0越靠近障碍物代价越大贴到表面时接近1。2.3 拉格朗日对偶的机制解读拉格朗日对偶方法是这个框架的核心。我稍微展开讲一下这里的数学直觉。假设原来要做的是无约束优化问题min_θ L_task(θ)其中L_task是VLA模型的行为克隆损失衡量模型预测动作与专家动作的差距。现在加上安全约束min_θ L_task(θ) s.t. L_safe(θ) ≤ d其中L_safe是平均安全代价d是预设的安全阈值。这个带约束问题很难直接做梯度下降因为约束条件没法直接放进反向传播里。拉格朗日对偶的核心做法是构造一个拉格朗日函数min_θ max_λ ≥ 0 [ L_task(θ) λ * (L_safe(θ) - d) ]内层的max是对安全乘子λ外层的min是对模型参数θ。从直观上理解λ自动调节当前该多重视安全。如果L_safe(θ)明显大于d也就是模型不安全那么λ会上升把安全代价的重要性放大优化器就会优先降低安全代价如果模型足够安全λ会降低任务损失重新占据主导。在工程实现上内层的max被替换成一个梯度上升步骤外层的min是正常的梯度下降步骤。两个循环交替进行# 每轮训练中的简化流程 # λ是安全乘子初始化为较小值比如0.1 # lr_theta是模型学习率lr_lambda是乘子学习率 for each batch: task_loss compute_task_loss(...) safety_cost compute_safety_cost(...) # 更新模型参数最小化任务损失和安全代价的加权和 total_loss task_loss lambda * safety_cost theta - lr_theta * grad(total_loss, theta) # 更新安全乘子最大化拉格朗日函数对λ的部分 lambda_grad safety_cost - safety_budget lambda lr_lambda * lambda_grad lambda max(0, lambda) # 乘子必须非负我补充一个实操参数建议λ的初始值不能太大也不能太小我试过从1.0初始化前期安全收敛很快但任务性能停滞了很久从0.01初始化又会导致安全乘子升温太慢前期出现大量危险动作。折中下来初始化为0.05~0.1配合乘子学习率设成模型学习率的1/10左右效果比较稳定。另外一定要给λ加非负约束否则负的乘子会让约束学习完全失效。2.4 评估指标不能只看任务成功率很多VLA论文汇报结果时只会报一个任务成功率但做安全对齐的论文如果只报这个指标基本等于没做。这篇论文在评估指标设计上给了我一个很好的提示至少要用三种不同的指标来呈现模型的表现。第一个是任务成功率Success Rate看模型在保证安全的前提下是否还能完成任务。第二个是安全违反率Constraint Violation Rate在所有测试回合中有多少比例至少发生了一次不安全事故。第三个是平均安全代价Mean Safety Cost在任务过程中累计的安全代价均值用来衡量模型的危险程度是不是整体降低了。只看任务成功率的问题很明显一个什么都不做、完全静止的机器人永远是安全的任务成功率是0但安全违反率也是0。反过来一个无视一切约束的模型可能任务成功率很高但安全代价爆炸。所以三个指标必须并排看我甚至建议在实验中额外记录一条安全成功率的帕累托曲线横轴是安全违反率纵轴是任务成功率这样能直观看出方法在效率和安全的权衡上处于什么位置。我在自己的复现实验里遇到过很典型的指标分布方法任务成功率安全违反率平均安全代价基线行为克隆无安全约束82.3%41.7%0.386惩罚式task_loss λ*safety73.4%30.2%0.241约束学习本文思路79.6%8.4%0.072可以看到基线模型成功率虽然最高但几乎每两次任务里就有一次不安全行为这在真实机器人上完全不可接受。惩罚式方法改善了一些但效果不稳定。约束学习在只损失不到3个百分点的任务成功率下把安全违反率从41.7%压到了8.4%这才是实际落地时真正关注的空间。3. 实操过程与核心环节实现3.1 实验场景与任务设计论文里设计的实验场景我按照论文的公开描述和常见工程实践做了还原主要在桌面操作类任务上比如把杯子从桌垫推到指定位置、倒水、搬运易碎物品、避让突然出现的人手或者障碍物。任务设计的核心是制造安全-任务冲突。如果任务本身就不存在冲突比如桌面很空、附近没有障碍物那安全约束再强也体现不出价值。所以实验场景里需要有意识地加入一些事故高发地带目标物体旁边放一个不可触碰的禁区物体比如一个玻璃杯机器人末端必须从狭窄通道通过通道两侧都靠近高风险区域偶尔有动态障碍物比如人伸手去拿另一个物体出现在机械臂的运动轨迹上。在这些场景下基线模型大概率会选择最短路径直接冲过去无视禁区而带安全对齐的模型需要学会绕路或者减速以稍微增加路径长度为代价换取安全。我个人的经验是安全对齐的收益在这种冲突场景下会成倍放大。在无冲突任务上约束学习和基线模型几乎没有区别但在冲突任务上基线的安全违反率会迅速飙升而约束学习模型能维持较低水平。3.2 环境与数据准备如果要在本地复现类似实验我建议按下面的组合来准备环境# 环境配置参考以常见开源方案为例 # Python 3.10 # PyTorch 2.x CUDA 11.8/12.1 # 预训练VLA权重如OpenVLA的7B版本或更轻量的3B版本 # 机器人仿真环境如MuJoCo/Isaac Lab 桌面操作场景 pip install torch transformers einops pip install mujoco数据准备是整个流程中工作量最大的部分。你需要两类数据第一类是任务演示数据这部分可以沿用公开的机器人操作数据集比如桥接数据集BridgeData、或者自己用遥操作设备采集。每一条数据格式为观测图像语言指令动作序列。第二类是安全标注数据。这篇论文思路的一个重要实现步骤是给每条演示数据标注安全代价。具体做法是在回放演示轨迹时用第2节提到的安全代价函数计算每一步的安全代价生成一个对应的代价序列。如果某些示范轨迹本身就穿过了禁区或者是危险示范可以额外构造一些负样本。我在这里踩过一个坑直接用遥操作数据里的原始动作去计算安全代价会出现人操作时本来就不太安全的情况导致安全代价整体偏高模型会学得过度保守。解决办法是在标注安全代价之前先对演示轨迹做一次预处理剔除或修正那些不必要的危险动作把这些轨迹当作危险反例单独管理而不是让它们污染正常演示。一个有用的工程小技巧安全标注不一定要做在动作级别可以先做在状态级别。即只需要标注哪些状态是安全的哪些是不安全的然后让模型自己学习从安全状态向不安全状态滑动的动作是不好的。这样可以减少标注成本因为状态标注往往比动作标注更直观。3.3 训练流程中的三个关键环节整个微调流程我拆成三步来走每一步对应一种不同级别的干预。第一步冻结预训练VLA模型的视觉编码器和语言编码器只微调动作头部分。这个阶段的目标是让模型先学会输出基本框架的动作同时保持对安全约束的大致感知能力。因为VLA模型的视觉语义理解能力是预训练阶段就建立起来的如果一开始就解冻全部参数安全约束可能会破坏已经学好的视觉-语言对齐。第二步解冻语言-动作之间的投影层联合优化任务损失和安全约束训练轮次我最开始用600步后续稳定后逐步增加到2000步左右。这一步是核心安全乘子在这个阶段会经历一个先升后降的典型曲线因为模型一开始有很多危险行为乘子会被推高随着模型学会避让安全代价下降乘子再回落到平衡点。第三步如果需要部署到真实机器人建议在仿真里加上扰动再来一轮对抗性评估比如随机改变目标物体位置、新增障碍物观察模型的安全表现是否依然稳定。训练过程中的监控指标我建议每20个batch就记录一次task loss、safety cost、lambda的取值画成折线图放在一起看。lambda的走势特别重要如果lambda长期处于很低的水平说明安全约束没有被激活模型在走回头路如果lambda一路飙升不回落说明模型无法在安全约束下完成任务这时候要回去检查代价函数设计多半是代价给得太苛刻了。3.4 一个典型调参过程的复盘我第一次训练时用的是比较粗糙的公式safety_budget安全预算d给的是0.05结果发现模型在简单的抓取任务里成功率从80%掉到了65%。排查之后发现问题出在我定义安全代价时把机械臂自身的机身距离也算进了障碍物距离导致模型把靠近自己身上某处也当成了危险信号于是模型学会了整天把机械臂缩在初始位置附近。修正方式很简单把安全代价的障碍物检测范围限定为环境中非本体障碍物也就是排除机械臂自身的碰撞体。改完之后任务成功率立刻回到75%左右安全违反率保持在了12%以下。另一个调参目标是安全阈值d的选择。d太小比如0.01任务成功率暴跌d太大比如0.2模型会在安全边界附近反复试探。我最终的取法是先跑一个对照实验画出安全代价的分布直方图然后选一个处于中位数附近的值作为d这样既不苛刻也不松散。实际操作中d0.05到d0.08在我测试的多组场景里效果都还可以。我还尝试过一个变体不把d设成固定值而是设定一个训练过程中逐渐衰减的阈值从0.1开始每100步降低到目标值0.05。这个做法的好处是前期给模型一点犯错空间让它先保证任务学习的推进再逐步收紧安全要求。实验结果证明衰减式阈值比固定阈值效果更好任务成功率的损失进一步收窄到了1到2个百分点。4. 常见问题与排查技巧实录4.1 安全乘子不更新或者直接发散这是我在实现拉格朗日对偶时遇到最频繁的问题。安全乘子λ在训练中一动不动安全代价也一直在高位徘徊——这说明整个约束学习机制没有真正被激活。排查步骤通常是这样第一检查代价函数的数值范围。如果安全代价函数输出的最大最小值都集中在0到0.01之间那么这个代价对梯度更新的贡献本来就很小λ就算增大十倍也没用。这种情况下需要重新设计代价函数或者对代价做归一化处理。第二检查safety_budget和目标安全代价之间的关系。如果模型初始平均安全代价已经小于d那λ就没有上升的动力这时候需要先提高代价难度或者降低d来激活约束。第三检查乘子优化器的学习率。λ的学习率太小会导致更新过慢模型在整个训练过程中都感知不到安全约束的存在。我推荐λ的learning rate设置在模型学习率的1/10到1/5之间并且给λ单独建一个优化器不要和模型参数共用同一个优化器。如果λ出现发散震荡越来越剧烈通常原因是λ的更新步长太大或者安全代价噪声太大。这时候可以对λ的更新做平滑例如使用指数移动平均来稳定λ的走势。4.2 任务性能下降太多模型变保守了约束学习最常见的副作用就是模型变得什么都不敢做。这通常是安全阈值d设得太严格或者安全代价函数的定义范围过宽把很多本来中性的行为也标记成了危险行为。我的排查经验是把安全代价按维度拆开分别看是哪一类代价贡献了最多的约束违反再针对性地调整那一类代价的权重或阈值。例如如果发现80%的安全代价都来自末端速度过高但实际任务里这个速度并没有造成真正的危险就需要把速度上限放宽一些或者给速度代价加一个更平滑的梯度。另外有一个很有用的工程建议在数据集里混合比例上做文章。把完全安全的任务数据和带有潜在危险的冲突任务数据按7比3混合不要把所有数据都设定为高危险场景。这样可以避免模型把安全等同于不动让它学会区分哪些场景要求高度警惕哪些场景可以放开手脚。4.3 安全对齐在仿真里表现好真实环境失效这种接口漂移问题在任何仿真到真实的迁移中都会遇到但安全相关的迁移失败更致命因为代价函数依赖的环境模型障碍物距离、接触力在真实世界中测量噪声更大。我从预设角度给一个具体建议在仿真训练时就引入传感器噪声模型对代价函数的输入加高斯扰动。这样训练出的模型对噪声更鲁棒不会因为视觉定位误差几厘米就产生完全不同的安全判断。另一个是可以用域随机化调整障碍物的摩擦系数、重量、颜色等属性让模型学会关注几何边界而不是颜色特征。如果真实环境的计算资源有限另一个降级方案是在真实机器人上只部署一个轻量级的安全否决器safety filter当代价计算模块检测到即将进入不安全区域时降低机器人速度或者直接冻结动作。这种硬保护虽然不算端到端对齐但作为安全底线非常有效也方便和约束学习微调后的模型叠加使用。4.4 为什么奖励减去惩罚在很多任务上效果不佳在阅读这篇论文之前我也一直是奖励减去惩罚路线的践行者。但经历了多个任务后我越来越认同论文里的观点简单的reward-shaping式惩罚项存在两个结构性短板。第一个短板是尺度敏感。任务损失和安全代价通常不在一个数量级上需要手工调整权重λ而且这个λ对不同的任务、不同的数据分布都很敏感。同一个λ在任务A上有效换到任务B上模型可能完全忽视安全。第二个短板是梯度耦合。当安全惩罚加到总损失里后任务梯度和安全梯度会混合在一起共同更新参数而且没有反馈机制去告诉优化器现在安全是否已经满足要求了。模型可能为了降低安全惩罚而偏离任务本质产生过度安全但无用的行为。约束学习的最大优势在于它把安全当成一个动态调节的边界而不是静态的惩罚项。模型在安全边界内自由优化任务只有越界时才受到压力。这让我想到一个比喻一个合理的教师不会在孩子每次做题时都唠叨别粗心惩罚式而是明确告知低于80分需要补考约束式孩子的目标变成在保证不挂科的前提下争取高分——这才是安全的正确形态。4.5 安全收敛检查清单最后分享一份我自己整理的安全对齐实验检查清单每次跑新场景前我都会逐条过一遍安全代价函数是否输出连续值是否在0到1之间有足够的区分度安全约束的阈值d是否明显低于模型初始安全代价如果初始就是0约束无法激活λ是否单独设置了优化器初始值是否在建议范围0.05~0.1训练过程中是否定期记录task loss、safety cost、λ三条曲线安全代价是否排除了机器人自身本体的碰撞体测试评估里是否同时汇报任务成功率、安全违反率、平均安全代价三个指标是否有单独的冲突场景目标与禁区重叠来验证安全对齐是否真的起作用部署前是否加入噪声扰动来评估安全性对扰动的鲁棒性每次实验前花10分钟过一遍这份清单能省下大半天排查问题的时间。这些坑我已经踩过不止一轮希望看到这篇笔记的同仁不用再走一遍弯路。我个人在实际操作中的体会是VLA模型的能力边界在互联网规模的数据上已经基本解决干什么的问题不再是唯一瓶颈不干什么或者说在什么约束下干什么正在成为下一步竞争的关键。这篇论文用约束学习的方式提供了一个足够优雅的数学框架但真正决定框架能不能落地的仍然是安全代价函数的设计质量和数据集的构造水平。这两块没有捷径只能靠大量的场景实验和细致的bad-case分析慢慢打磨。