作者 / 邵羿茗
📂 项目地址
GitHub:Xbotics-Embodied-AI-club/Xbotics-Embodied-AI-Handbook/tree/main
本讲目标:让一段 action 安全地变成 SO-101 的关节运动
上一讲讨论机器人系统怎样把状态、决策、控制和记录组织成闭环。本讲继续追问其中最容易被忽略的一段:策略给出一组数以后,为什么还不能直接把它送给机械臂?
贯穿本讲的任务是:为 SO-101 的六个关节写清一份动作执行约定,在 MuJoCo 中让一个小幅关节目标到达,并留下能解释成功或失败的记录。在具备经教师批准、已经校准的硬件时,再以官方 LeRobot 工作流观察主从关节的小幅跟随。两条路径讨论同一组问题,但仿真结果不代替真机校准或安全检查。
学完本讲后,学生应当能够回答:
机械结构、驱动、传感、控制和计算接口怎样共同限定机器人“能读什么、能写什么、不能做什么”? observation(观测)、policy action(策略动作)、controller command(控制器参考)和执行器命令为什么不能混为同一种数值? 关节顺序、单位、参考坐标系、每步尺度、控制频率和限位为什么要同时写入动作执行约定? 为什么同一个关节位置目标需要状态反馈、限幅、超时判定和 episode(一次实验记录)才能构成可复盘的闭环? 从 MuJoCo Menagerie 的 SO-101 模型中读取关节名、关节范围、position actuator(位置执行器)的控制范围和仿真步长。 把受限的关节位置增量转换为绝对位置参考,运行一次低幅关节目标到达,并保存成功 episode 与跟踪曲线。 触发一次受控拒绝或超时,依据日志中的最早异常,而不是凭最终姿态猜测原因。 在真机条件满足时,按 LeRobot 官方校准、观测和记录流程完成空载、小幅主从跟随;不直接下发串口命令,不修改伺服器电流、力矩或 PID 参数。
核心知识点
表 3-1 核心知识点与 SO-101 主任务的对应关系
| 知识点 | 在 SO-101 主任务中的对应物 | 本讲怎样验证 |
|---|---|---|
课堂任务与最低交付
本讲不是要求学生记住一套固定接口,而是要求用同一份 action contract 完成一条可检查的关节运动证据链。默认路径在 MuJoCo 中完成;真机路径只在教师批准、设备已校准且现场条件满足时开放。两条路径都从“动作语义是否写清”开始,而不是从“机械臂是否动起来”开始。
表 3-2 课堂任务与最低交付
| 课堂任务 | 输入与操作 | 最低产物 | 通过判据 |
|---|---|---|---|
课堂时间围绕同一任务分配,而不是把理论、仿真和真机排成互不相干的三个活动。约三成时间用于从反例填写术语和契约草案;约四成时间用于读取模型、运行基线、查看 episode 与曲线;其余时间用于单变量对照和失败复盘。真机只作为条件满足后的观察路径,不能挤占每位学生完成无硬件基线和失败证据的时间。每个时段结束时都应留下可保存的产物:合同草案、模型事实、episode 或复盘单,而不是仅留下口头结论。
方法框架:状态、动作、安全与记录
贯穿本讲的四层框架把“动作为什么不能直接下发”变成四个可检查的问题。状态层回答机器人现在处在什么构型、这些读数是否仍有效;动作层回答策略给出的候选变化作用于哪些关节、采用什么单位和时间尺度;安全层回答候选变化是否可被当前模型或现场条件接受;记录层回答这次运行凭什么可以复现与复盘。
State layer : qpos, joint name, timestamp, validity
Action layer : action_delta, unit, order, control rate
Safety layer : executable range, step limit, timeout, reject -> hold
Record layer : target, state, action, position reference, status, reason
四层不是四个必须独立部署的软件进程。它们是检查接口时的责任划分:同一个脚本可以在仿真中实现多个层,但不应把 qpos、候选 action_delta、受约束后的 position_reference 和最终任务状态写成同一个没有语义的数组。后续每个 Demo、曲线和失败复盘都沿用这四层,以便读者判断错误发生在状态读取、动作适配、安全检查还是记录解释。
3.1 一段 action 为什么不能直接驱动机械臂?

3.1.1 观测、action 与执行器命令不是同一层
假设控制程序刚刚读到 SO-101 的六个关节位置,并计算出一个长度同为六的数组。这个数组是否可以直接写入执行器?不能。数组长度相同,只说明它们都能用六个数表示;并不能说明数的顺序、单位、参考对象和时间含义相同。
先看四类对象。观测(observation)描述当前时刻实际测到的状态,例如关节位置、夹爪状态、图像或故障状态。策略动作(policy action)是模型或规则在观测条件下给出的候选改变。控制器参考(controller command)规定控制器下一步应跟踪的位置、速度或力矩目标。执行器命令则是驱动器可以接受的低层量,例如由驱动器内部转换和使用的使能状态、电压或电流。它们依次处在估计、决策、适配、控制和驱动的不同层级。
本讲的 MuJoCo 实验将这层关系具体写成一条短链:当前关节位置 qpos 产生候选 action_delta,安全约束把它转换为绝对的 position_reference,position actuator 再接收该参考并推动仿真前进一步。这里的 data.ctrl 是模型 position actuator 的控制输入;它不等同于真实 SO-101 伺服器上的电流命令,也不能由此推断真机的内部控制参数。
这个区分首先避免关节顺序错误。若数组的第一个数在策略侧表示 shoulder_pan,而适配层把它写给 shoulder_lift,数值即使处在允许范围,机器人仍会沿错误的关节运动。其次要检查单位:关节角常以弧度表示,而同一个小数若被当成角度或归一化量解释,动作幅度会改变。最后还要检查时间语义。每控制周期允许的增量为 0.03 rad,在 20 Hz 下的连续运动尺度,与在 50 Hz 下并不相同;只有把每步尺度和频率写在一起,才能讨论速度和轨迹的变化。
因此,动作在进入机器人之前至少要说明六件事:它作用于哪些关节、关节排列顺序是什么、数值采用什么单位、参考系或零位在哪里、每步允许变化多少、多久更新一次。关节范围、速度边界、通信状态和超时条件则决定候选动作在当前时刻能否执行。缺少其中任何一项,都不能把 action 当作可执行命令。
3.1.2 贯穿本讲的 SO-101 关节目标任务
课堂不把这些概念拆成彼此无关的定义,而是围绕一个低风险任务建立证据:让 SO-101 的 shoulder_lift 从当前状态向一个小幅、合法的目标位置移动。任务不要求抓取、逆运动学、视觉识别或策略训练。这样可以把注意力集中在动作解释、闭环反馈和记录上。
任务开始前,程序先从当前 MuJoCo 模型读取六个关节和对应 position actuator 的名称、关节范围、ctrlrange 与仿真步长。它不会把计划中的示例范围当成硬编码参数。随后,学生在 action_contract.yaml 中写明关节顺序、角度单位、20 Hz 基线、每步最大增量、有效目标范围来源以及拒绝后的保持行为。合同通过检查后,程序才计算候选增量、形成位置参考并运行仿真。
每个控制周期的记录至少包括时间戳、当前 qpos、目标位置、action_delta、position_reference、任务状态和拒绝原因。运行结果只可能被判定为 running、reached、timeout 或 rejected,而不是以“窗口里看起来动了”作为通过标准。成功 episode 用来检查目标、参考和状态是否按预期收敛;失败 episode 用来定位候选动作是在范围检查、控制周期还是状态更新阶段最先出现异常。
后文会依次解释本体为何决定这些字段,关节和连杆为何决定动作向量的对象,位置控制为何需要反馈,以及尺度和频率为何改变同一个增量的物理效果。到第 3.7 节,所有字段将进入可运行的 MuJoCo 实验;第 3.8 节再把同样的检查思路用于已经校准的 LeRobot SO-101 主从跟随。
3.2 机器人本体如何决定动作的含义?
同样写成“六个关节数值”的 action,放在两种机器人上未必表示同一种运动。区别不在数组的外形,而在本体提供了什么机构、什么状态测量、什么控制接口和什么安全边界。为了把这种关系说清楚,可以把机器人本体看作五层相互连接的接口:机械结构规定可动的对象;驱动系统把参考变成关节运动;传感系统报告实际状态;控制系统把候选动作变成受约束的参考;计算系统负责让状态、参考和记录在正确的时间抵达相应接口。
这五层不是五个彼此独立的盒子。机械限位会成为控制器的拒绝条件,编码器状态会成为位置闭环的反馈,计算延迟会改变控制周期中的状态新鲜度,驱动器的保护动作会改变真正执行的参考。因此,写 action contract(动作执行约定)之前,先要知道每个字段由哪一层产生、由哪一层使用,以及它在失效时留下什么证据。
同一个字段有时会跨越多层,但它在每一层的角色不同。以 position_reference 为例,控制层负责根据候选 action 和状态生成它,驱动层或 MuJoCo actuator 负责接收它,机械结构限制它最终可以形成的姿态,记录层保留它以便与 qpos 比较。把这些角色写在同一个数组里,会让“参考写错”“执行器受限”和“状态读错”在日志中失去区别。反过来,字段名称写得很完整也不等于接口可靠:它仍需有来源、单位、更新时间和可检查的范围。表 3-1 的作用正是把这四个问题固定下来,使学生在读模型、看代码和复盘 episode 时使用同一组提问。
图 3-2 将这些层放回同一台机械臂中观察。图中的连线不是硬件内部接线图,而是本讲使用的接口关系:机构和驱动决定参考可以作用到哪里,传感状态返回控制判断,计算与记录为每个周期保留可追溯的上下文。

3.2.1 机械结构和自由度限定了可达运动
机械结构是动作的物理载体。固定基座机械臂由基座、连杆、关节和末端执行器构成运动链;关节轴的位置和方向、连杆尺寸和关节限位共同限定末端能够达到的几何范围。对 SO-101 而言,本讲先把每个可动关节当作一个有名称、有范围、有当前位置的对象,而不把“第 1 到第 6 个数”当作天然正确的关节定义。
工作空间描述的是在机构几何和关节限位给定时,末端理论上可能到达的位置或位姿集合;任务可达性还要进一步考虑末端姿态、负载方向、自碰撞、障碍物、线缆干涉和当前支撑条件。因而,即使一个末端目标在几何上可达,也不能据此认为它可以在当前负载和安全条件下执行。本讲的核心实验刻意只使用小幅关节位置目标,避开对逆运动学、碰撞规划和抓取接触的额外要求;这些限制不是降低标准,而是让动作接口中的关节顺序、范围和反馈能够被单独观察。
软件描述结构时也要区分用途。CAD 模型主要服务于设计和制造;URDF 或 MJCF 等机器人模型描述连杆、关节、质量、惯性和几何,以便状态发布、可视化和运动学计算;碰撞模型通常采用更简单的近似,以便在线检查。它们可以相互校核,但不能任意替换。模型的工具中心点、零位、负载或几何一旦与实物不一致,原来正确的坐标、范围和可达性判断就可能失效。
3.2.2 驱动、编码器与控制器怎样组成局部闭环
候选动作不会自己转化为关节运动。典型链路是:控制器提供目标位置、速度或力矩等参考;驱动器结合编码器、电流或温度等反馈产生电机驱动;电机经减速器或其他传动机构带动关节;编码器再报告实际状态。电机、传动比、摩擦、背隙、负载和温升都会改变参考与实际运动的关系,所以“关节没有到目标”不能直接归因于高层模型。
许多伺服系统内部存在电流、速度和位置等不同时间尺度的回路。高层策略通常只通过规定接口提供较慢的目标,局部控制器和驱动器负责更快的跟踪与保护。本讲使用 MuJoCo 的 position actuator,是为了观察“位置参考和关节状态之间存在闭环”这一关系;它不是让学生修改真实 SO-101 的电流环、速度环或 PID 参数,也不能据此推断具体硬件的内部实现。
保护机制也是执行链的一部分。位置、速度、加速度、温度、通信和急停状态都可能使候选 action 被限幅、保持、拒绝或停止。若只记录原始 action 而不记录经过约束后的参考与拒绝原因,就无法判断失败发生在策略、接口、控制还是硬件。因此,本讲的 episode 同时保存候选 action_delta 与实际交给 position actuator 的 position_reference。
3.2.3 不同本体为什么会有不同的状态和约束
传感系统提供控制和策略都要使用的状态。对固定基座机械臂,编码器给出的关节位置、速度和夹爪状态通常是最基本的本体状态;相机、力/力矩传感器和接触传感器则按任务需要补充环境或接触信息。每一个状态字段都要携带来源、单位、关节名或坐标系、时间戳和有效性。没有标签的数组即使数值看起来合理,也不能安全地用于运动学计算或命令适配。
控制系统把任务目标或策略 action 转成控制器能够跟踪的参考。它需要读取状态,检查关节范围、控制模式和安全条件,并在状态失效、命令超限或超时时进入保持、降级或停止等预定义状态。计算系统则负责通信、时间同步、策略推理和数据记录。消息能够抵达并不表示它仍然可用:旧图像配合新关节状态、队列堆积造成的延迟,或时钟偏差,都可能让同一 action 对应错误的物理状态。
在本讲的最小仿真里,状态新鲜度可以直接由 state_timestamp_sec 与控制更新时间比较:两者来自同一个 MuJoCo 时间轴,正常情况下随步进一同增加。这个设计刻意避开相机和网络传输,却保留了一个重要判断:任何 state 在参与下一次 action 计算前,都必须知道它对应哪一个时刻。到了真机观察路径,时间戳、设备时钟和记录格式由当前平台决定;若无法证明 leader 变化、follower 状态和视频处于可比较的时间窗口,就只能报告“记录尚不足以解释跟随关系”,而不能靠画面印象补出因果。
表 3-3 把五层本体接口收束为本讲的检查问题。后续 MuJoCo 实验不需要完整复现真机的全部硬件细节,但必须从当前模型读取与这张表有关的关节、执行器、范围和仿真步长;真机路径则额外依赖官方校准和安全流程。
表 3-3 本体五层接口的字段与证据
| 本体层 | 它提供或限制什么 | 在本讲主任务中的字段 | 首先应检查的证据 |
|---|---|---|---|
在本讲中,动作执行约定只覆盖一个 position-control 仿真闭环。它不是通用机器人 API,也不写入未经验证的真机限位或驱动参数。有效目标范围由当前模型的 joint range 与 actuator ctrlrange 的可执行交集确定,而不是由讲义中的示例数值确定。
robot: menagerie_robotstudio_so101
model_path: external/mujoco_menagerie/robotstudio_so101/scene.xml
joint_order: [shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper]
state_field: qpos
state_unit: rad
action_mode: joint_position_delta
action_unit: rad_per_control_step
actuator_command: joint_position_reference_rad
simulation_timestep_sec: read_from_model
control_rate_hz: 20
max_delta_per_step_rad: 0.03
target_range_source: model_joint_range_and_actuator_ctrlrange
on_reject: hold_current_reference
episode_schema_version: 2
这份约定还没有替学生判断每一个数值是否合理。它的作用是把原来容易藏在代码中的假设变成可检查字段:哪个关节、使用什么单位、在多长周期内变化、由什么范围裁剪,以及拒绝后保持什么参考。下一节从关节、连杆和末端的关系出发,说明为什么同样的动作值还必须知道它所作用的对象和参考系。
3.3 关节、连杆与末端:机器人究竟能怎样运动?
把 SO-101 的状态写成六个数,并不等于已经说明机械臂的构型。一个构型至少还要指出每个数对应哪一个关节、该关节如何连接相邻连杆、零位和正方向怎样定义,以及末端工具点相对于基座处在哪里。没有这些关系,数组只能表示一组抽象数值,不能解释为真实机构的姿态。
图 3-3 用一条串联运动链把这些对象区分开来。关节变量属于相邻连杆之间的相对运动;TCP 属于工具定义;从关节向量到基座坐标系中工具位姿的关系则由正运动学给出。三者不能因同时出现在一条机械臂上而混为同一类数值。

3.3.1 关节空间中的状态怎样对应真实机构
自由度(degree of freedom, DoF)是确定机器人构型所需的独立变量数量。对简单串联机械臂,受控关节数常常与自由度数相同;对闭链、差动、耦合或带被动关节的机构,两者则可能不同。因而,描述控制接口时应写明可独立控制的变量,而不是只数电机或模型节点的数量。
关节是连接相邻连杆、规定相对运动方式的机构单元。转动关节通常用角度表示,单位为弧度;移动关节通常用线位移表示,单位为米。关节轴、正方向、零位、有效范围和驱动方式都属于关节状态的语义。轴方向或零位配置错误时,即使控制器收到的正数与预期相同,末端也会朝错误方向运动。
关节空间用向量 q 表示各独立关节变量。以本讲的仿真模型为例,qpos 不是“六个按位置排列的数”,而是必须经 joint name 映射后读取的六个关节位置。关节顺序、单位、零位或控制模式只要有一项不同,两个同维向量就不能相互替换。后续代码会因此先读取模型中的关节名称,再建立状态向量和控制参考的对应关系。
外部约束还会改变实际可用的自由度。固定基座机械臂把基座看作已知约束;移动底盘受到地面和非完整运动约束;人形机器人则随双脚、单脚或腾空等接触状态改变约束条件。这也是人形系统不能只按“关节更多”来理解复杂度的原因:手臂目标还必须与支撑、姿态、碰撞和接触条件同时相容。本讲只把这一点作为动作约定需要扩展的原因,不将平衡或全身控制加入必做实验。
3.3.2 连杆、TCP 与末端位姿怎样建立联系
连杆把各个关节连接成运动链。其几何信息给出长度、形状和关节之间的相对位置;质量、质心和转动惯量描述重力负载和加速时的动力学影响;刚度、传动弹性和装配误差则说明真实机构与理想刚体模型可能出现的差别。只知道连杆长度不能推断控制负担,也不能保证末端定位准确。
给定关节状态、关节轴、连杆几何、基座坐标系和工具安装关系,可以沿运动链计算末端位姿,这个过程称为正运动学(forward kinematics, FK)。输出必须说明它表达在哪个参考坐标系中。一个末端点在基座坐标系、世界坐标系和相机坐标系中的数值通常不同,不能把其中一种数值直接拿来与另一种比较。
工具中心点(tool center point, TCP)是工具真正执行任务的位置或坐标系。二指夹爪的 TCP 可定义在两指之间的抓取中心,吸盘可定义在吸附面的接触中心,螺丝刀则应对应尖端或轴线。工具更换、法兰偏置、相机支架变化或线缆重新布置,都可能使原有 TCP、碰撞几何和质量参数不再适用。关节跟踪误差很小而工具仍然没有到达目标时,应先检查模型版本、TCP 和参考坐标系,而不是直接把问题归为控制器故障。
因此,模型文件不是只在第一次加载时使用的背景资料。它把关节名称、轴方向、连杆几何、工具安装关系和控制接口放在同一份可追溯的描述中。CAD、URDF 和 MJCF 可以服务于不同阶段,但不能因为外观看起来相同就随意互换:若模型中的关节零位或法兰偏置变化,旧的 joint_order、TCP 定义、碰撞判断和实验曲线都需要重新说明。本讲只用关节空间完成到达实验,正是为了让这些依赖关系保持可见;以后把目标写成末端位姿时,必须把 TCP、坐标系和模型版本加入更完整的任务契约。
对于 SO-101 关节位置到达任务,学生不需要先求一个末端抓取位姿;但仍应知道为何本讲故意选择关节空间。关节状态和 position actuator 在模型中具有直接、可读取的名称和范围,能够把“目标是否到达”与“哪个关节、哪个参考、哪个约束”逐项对齐。末端任务空间更贴近抓取、插接等任务描述,却需要额外给出 TCP、坐标变换、运动学和可达性条件。
3.3.3 末端目标为什么还要经过可达性检查
末端空间或任务空间用位置和朝向描述工具的目标。位置常用指定坐标系中的三个平移分量表示;朝向可用旋转矩阵、欧拉角、轴角或四元数表示。不同姿态表示具有不同的约束和数值性质,因此接口必须固定表示方式,不能把同样长度的姿态数组当作可互换格式。
从关节状态求末端位姿是正运动学;从目标末端位姿寻找关节构型是逆运动学(inverse kinematics, IK)。逆运动学给出的只是候选构型,不是已经可执行的关节命令。同一末端目标可能有多组关节解,也可能没有满足当前限位、碰撞、工具姿态、负载和路径连续性条件的解。即使求解器返回数值结果,也还要检查目标所在坐标系、TCP、关节范围、自碰撞和运动速度。
因此,末端目标进入执行链的顺序应当是:先确认目标的参考坐标系和姿态表示,再用当前机构模型检查几何可达性,随后检查关节限位、碰撞、工具和负载约束,最后才生成连续的关节参考。任何一步不通过,都应返回可解释的拒绝或失败原因,而不是强行把末端目标转换成越界关节命令。原稿中关于 IK 多解、不可达、奇异和数值不收敛的讨论,后续会作为失败分析和扩展阅读的边界;本讲主实验不以 IK 成功与否作为通过条件。
表 3-4 对照了两种常见动作描述。它说明为何本讲把关节位置增量作为最小实验接口,也为以后学习末端增量、坐标变换和任务规划保留了准确边界。
表 3-4 关节与末端动作描述的接口条件
| 动作描述 | 直接作用对象 | 必须先说明的条件 | 转成执行参考前还需要什么 |
|---|---|---|---|
本节的结论很直接:动作向量的维数不是物理含义。物理含义来自它所对应的关节或 TCP、单位、坐标系、模型和约束。下一节把这个结构关系放入闭环,考察一个关节位置参考怎样在反馈、饱和和超时条件下成为实际关节状态。
3.4 一个关节位置目标怎样形成闭环?
给定一个合法的 shoulder_lift 目标位置,机械臂仍可能因负载、摩擦、传动误差、状态延迟或保护触发而没有到达目标。若程序只在开始时发送一次参考,随后不读取实际关节状态,这是一条开环命令链。若程序不断比较目标与测得状态,并据此更新后续参考和任务状态,才构成闭环。
图 3-4 把本讲关心的外部闭环画为“位置参考、位置执行器、关节状态”的循环。饱和和超时不属于反馈的正常部分:前者说明某层输出已经受到边界限制,后者说明在规定时间内未形成到达证据,两种情况都应留下记录。

3.4.1 从位置参考到实际关节状态
最小关节闭环包含五个对象:目标位置、实际关节状态、二者的误差、根据误差工作的控制器,以及被驱动的关节。以位置控制为例,编码器或仿真状态给出当前关节位置;目标与当前状态的差构成位置误差;局部控制器据此产生更低层的控制作用;关节运动后,新状态再返回闭环。闭环的价值在于它能对一定范围内的扰动和模型误差作出修正,而不是假定命令一旦发出就必然实现。
本讲中需要区分两层参考。action_delta 表示高层在一个控制周期内提出的关节位置变化;position_reference 表示经过范围检查后交给 MuJoCo position actuator 的绝对位置参考。程序每次从 qpos 读取状态,生成并裁剪 action_delta,形成 position_reference,调用仿真步进,再读取新的 qpos。这条链让目标、参考、状态和结果可以按同一时间线比较。
位置控制并不意味着位置会瞬时跳变到目标。一个过大的目标变化可能要求过高的速度、加速度或力矩,导致过冲、限幅、振动或保护触发。因此本讲使用低幅目标和每步最大增量,并将“在允许时间内进入误差阈值”定义为到达条件。它不会用“仿真窗口中机械臂看起来动了”代替状态判定。
对本讲的到达实验,目标、参考和状态各有不同用途。target_rad 表示希望最终达到的位置;action_delta_rad 表示当前控制周期提出的变化;position_reference_rad 是经过裁剪后交给 actuator 的绝对参考;qpos_rad 是步进后重新读取的状态。若目标合法但参考始终被裁剪,先检查范围交集和单步限制;若参考在变化而状态没有进入容差,再检查时间线、模型响应和 timeout。这样的判读顺序避免把“控制器没有瞬时到位”误判为数组或单位错误,也避免在没有证据时把仿真现象外推到真实伺服器。
3.4.2 反馈、饱和与超时怎样改变闭环
闭环依赖及时、可信的反馈。状态测量、通信、计算和执行都会带来延迟;如果控制器依据的是过早的状态,过高的修正可能落在已经变化的系统上,表现为过冲或振荡。传感噪声也会进入误差计算,尤其会影响对变化率敏感的控制环节。反馈频率更高并不自动更好,必须与执行器响应、计算时间和安全限幅一起考虑。
饱和说明系统不能按理想输出继续增大。关节位置、速度、力矩、电流和加速度都有范围;当某一层的候选输出超过范围,实际写入的值会被裁剪或直接拒绝。若只看最终位置误差,可能把“目标本身越界”“控制参考被限幅”“状态没有更新”和“驱动能力不足”误判为同一种控制失败。本讲的失败记录因此要保留候选 action、裁剪后的参考、状态时间戳、任务状态和拒绝原因。
超时是另一种需要显式处理的结果。一个目标在规定时间内没有达到,不表示程序可以无限期保持原命令;持续保持可能掩盖状态冻结、接口错配或模型约束。实验应当报告 timeout,保留最后一次有效状态和参考,并由学生回到最早异常的字段检查原因。对于真机,状态失效、通信异常或安全停止后更不能沿用上一周期的普通命令,而应遵循平台规定的保持、降级或去使能流程。
3.4.3 PID 负责什么,策略 action 又负责什么
比例—积分—微分(proportional-integral-derivative, PID)控制器利用误差及其历史或变化趋势帮助关节跟踪参考。比例项通常提供主要的即时纠正,积分项可减小持续偏差,微分项可增加阻尼但对测量噪声敏感。其实际响应还取决于采样周期、延迟、机构柔性、摩擦、负载、执行器范围和保护逻辑。相同的过冲或抖动现象可能来自多种因素,不能只看某一个增益就下结论。
策略 action 的责任不同。它回答“在当前状态下,希望机器人下一步怎样改变”;低层控制器回答“怎样在更短周期内、在硬件约束内跟踪已经合法的参考”。策略可以输出关节位置增量、末端增量或其他任务级候选,但不能因为存在 PID 就省略单位、范围、频率和安全检查。反过来,低层 PID 也不能替策略决定目标、坐标系或任务是否可达。
本讲不要求学生整定真实伺服器的 PID,也不把 MuJoCo position actuator 的行为解释为真实 SO-101 的内部控制器。学生需要做的是读懂一条位置闭环的外部证据:目标是否合理,候选 action 是否被约束改变,位置参考是否持续更新,状态是否在规定时间内接近目标,以及失败首先暴露在什么字段。下一节比较位置、速度和力矩三种接口,进一步说明为什么同一个数值不能在不同控制模式之间直接迁移。
3.5 位置、速度与力矩:同一个数为什么不能换着用?
0.2 这个数若被解释为关节位置、关节速度或关节力矩,分别可能表示 0.2 rad、0.2 rad/s 和 0.2 N·m。它们的作用对象、所需状态、安全边界和失败表现都不同。接口名称相近,或数组维数相同,不能证明它们可以互换。
图 3-5 把三种参考量放在同一类关节上比较。位置参考指向目标构型,速度参考规定变化率,力矩参考作用于驱动力矩;因此即使数值大小相同,也需要不同的单位、状态解释和保护条件。本讲的 MuJoCo 实验只使用位置参考。

3.5.1 位置控制:把关节移动到哪里
位置控制以关节或末端位置作为参考。对于关节位置参考,控制器需要知道关节当前位置,并在允许的速度、加速度和范围内使状态接近目标。它适合表达“把某个关节移动到哪个构型”,也是本讲 SO-101 仿真实验选用的接口。
位置目标仍必须经由连续性和安全检查。若一个参考在单个周期内跳变过大,驱动器可能需要过高的速度、加速度或力矩,出现饱和、过冲、振动或保护触发。对于接触任务,位置误差收敛也不等于任务安全完成,还要考虑接近方向、接触力和是否应切换到柔顺控制。因而,“位置模式”不是跳过轨迹和约束的快捷命令。
3.5.2 速度控制:以多快的速度运动
速度控制以变化率为参考。关节速度常以 rad/s 表示,末端速度还必须说明线速度、角速度及其坐标系,移动底盘的线速度和角速度则受自身运动学和地面接触条件限制。速度模式适合连续扫描、匀速运动或作为级联控制中的中间层,但它不直接保证最终位置到达。
若速度存在偏置、反馈延迟或执行器响应差异,位置误差会随时间累积。需要精确到位的任务通常还要由位置反馈或阶段性校正来约束。速度命令也要带有方向、坐标系、更新周期和最大允许范围;把一个位置增量误当成速度,会同时改变单位和时间语义。
3.5.3 力矩控制:何时需要直接约束作用力
力矩控制以关节力矩或与之相关的电流参考作为主要控制对象。它常用于重力补偿、接触柔顺性、碰撞响应和动态动作,但硬件前提更严格:执行器、驱动器、状态接口、热管理和保护逻辑都必须支持可验证的力矩或电流控制。关节力矩以 N·m 表示,电流以 A 表示,末端接触力以 N 表示;它们相关但不相同,不能直接互相换算或混称。
接触任务往往需要明确模式切换条件,例如在自由空间用位置参考接近,在接触确认后转入受限的力矩、阻抗或混合控制。切换前后还要保证参考连续,并记录接触状态和保护事件。若硬件没有可信的力矩或接触反馈,软件层不能把普通位置接口改名为“力矩控制”来假定柔顺性。
3.5.4 为本讲任务选择位置参考的理由
本讲的目标是辨清 action 如何经过约束进入执行闭环,而不是教授每一种伺服模式的调参方法。MuJoCo Menagerie 的 SO-101 模型提供与关节对应的 position actuator,学生可以从同一模型中读取关节范围、控制范围和状态,再观察绝对位置参考如何影响 qpos。这使动作对象、单位、范围和结果可以在一个最小闭环中逐项核对。
选择位置参考也明确了本讲的边界:它不表示所有机器人任务都应使用位置控制,更不表示 MuJoCo 的 actuator 是对真实 SO-101 电机与驱动器内部行为的完整替代。后续若使用速度、末端增量或力矩接口,应重新写明控制对象、单位、坐标系、频率、状态依赖、保护方式和日志字段;不能从本讲的 position reference 直接套用。
表 3-5 汇总三种模式的最小区别。阅读表格时,应先问“这个数作用在什么物理量上”,再问它是否允许进入当前硬件接口。
表 3-5 三种控制模式的状态依赖与边界
| 控制模式 | 参考量与典型单位 | 主要依赖的状态 | 常见用途 | 不应忽略的边界 |
|---|---|---|---|---|
控制模式说明了“参考代表什么”,还没有说明“模型输出如何形成这份参考”。下一节把关节增量、绝对目标和末端增量放在同一动作空间中比较,并把单位、顺序、坐标系、尺度和频率写成可检查的执行条件。
3.6 从模型输出到可执行参考:动作空间要写清哪些条件?
动作空间(action space)不是“模型输出多少维”的简称,而是一份从候选数值到物理运动的约定。它至少回答:每一维作用于什么对象,数组按什么顺序排列,使用什么单位,在哪个坐标系或零位下解释,允许多大范围和每步变化,多久更新一次,违反约束时怎样处理。缺少这些条件,两个长度相同的数组也可能代表完全不同的机器人动作。
把动作空间写成契约的价值,在于它把训练期和运行期的数值分开。模型可能输出归一化向量,训练数据也可能采用另一份关节排列;这些信息只能说明模型如何表示数据,不能直接提供当前机械臂的可执行范围。运行期适配器必须先依据保存的映射恢复物理单位,再使用当前模型读取到的关节范围和 ctrlrange 做检查。对于本讲的关节增量,适配器还必须保留“每控制周期”的时间语义;把它改名为位置目标、速度或电机命令,都会改变契约所说明的物理量。
契约也不替学生自动做出任务判断。它能够阻止顺序不匹配、单位未声明、范围来源缺失或拒绝处置不明等接口问题,却不能证明目标在工作空间内无碰撞,不能说明真机已完成校准,更不能替代第五讲中对初始状态、观测、成功和终止条件的完整任务定义。本讲先把候选关节动作安全地解释、执行和记录;后续课程才在这一前提上扩展任务契约与策略评测。
图 3-6 将动作契约中最容易遗漏的字段分散到它们所约束的对象旁边。关节顺序、单位和坐标系决定候选向量如何解释;单步限制和控制频率共同决定一次更新的时间尺度;可执行范围不通过时,系统应拒绝并记录,而不能将未检查的数值直接写入执行接口。

3.6.1 绝对目标、关节增量和末端增量各在什么场景使用
绝对关节目标直接描述各关节希望达到的构型,适合结构固定、关节接口明确的任务;关节位置增量描述相对当前关节状态的小幅变化,适合把单步尺度和安全限幅明确写入闭环;末端位姿目标以 TCP 的位置和朝向表达任务意图;末端增量则描述指定坐标系中 TCP 的局部变化。它们都可以是合理的策略输出,但从策略层进入执行层所需的适配工作不同。
末端目标和末端增量需要额外说明 TCP、坐标系、姿态表示、运动学和可达性约束。它们更接近“工具怎样接近对象”的任务语言,却不能绕过逆运动学、碰撞、关节限位和连续轨迹检查。关节目标与关节增量更接近编码器和位置控制接口,但与具体本体的关节顺序、轴方向、零位和范围紧密绑定,平台迁移时必须重新映射。
本讲选择关节位置增量,不是因为它在所有任务中更好,而是因为它能把接口边界压缩到一个可观察的最小闭环:当前 qpos 加上受限 action_delta 形成 position_reference,再由 position actuator 跟踪。学生可以在同一条 episode 中比较候选增量、实际参考和关节状态,而不必同时解决 TCP 标定、IK 多解和碰撞规划。
3.6.2 单位、顺序和坐标系怎样写入动作契约
动作契约中的关节顺序必须由名称确定,不能靠训练数据或数组下标默认继承。对于关节位置,单位是 rad;对于关节速度,单位通常是 rad/s;对于末端平移,单位通常是 m;对于姿态,还必须说明轴角、欧拉角、四元数或其他表示。归一化值只在模型侧的数值空间中有意义,必须依照训练时保存的统计量恢复到物理量以后,才能进行运行期的范围和安全检查。
坐标系同样不能省略。基座坐标系中的“向前”在机器人工作台上保持固定,工具坐标系中的“向前”会随末端姿态改变,相机坐标系中的“向右”还依赖相机安装与手眼变换。对本讲的关节增量接口,坐标系问题表现为关节名称、轴方向和零位的约定;对后续末端接口,它会直接决定同一平移数值的空间方向。
表 3-6 给出一份动作契约的最小审查表。它不是让学生把所有字段背下来,而是要求每一项在模型、代码、日志和图表中有一个可以核对的来源。
表 3-6 动作契约的最小审查字段
| 字段 | 必须说明的内容 | SO-101 MuJoCo 实验中的来源 | 缺失时的典型后果 |
|---|---|---|---|
3.6.3 每步尺度和控制频率怎样共同决定速度
动作尺度不只是数值的最大绝对值。对于每一个动作分量,都要区分训练数据中的统计范围、在线动作裁剪范围和当前控制器接受范围。训练统计量描述模型看过的数据分布,不能直接当作机械安全限位;在线裁剪限制明显异常输出,也不能代替依据当前状态进行的关节范围和安全检查。
控制频率的倒数是控制周期。若每次更新允许的关节变化上界为 max_delta_per_step_rad,则在不考虑饱和、保持和实际跟踪误差的近似条件下,其单位时间变化尺度与“每步增量乘以每秒更新次数”相关。这个近似说明了为什么固定每步增量时,10 Hz、20 Hz 和 50 Hz 会产生不同的运动节奏;它不是对真实关节速度上限的替代,也不能绕过驱动器、轨迹、安全和模型范围的限制。
例如,若一次更新最多提出 0.03 rad 的关节增量,在 20 Hz 下,候选参考的理论变化尺度约为 0.60 rad/s;在 50 Hz 下则约为 1.50 rad/s。这两个数只描述“在连续多个周期都碰到增量上界时,参考最多能以多快的节奏改变”,没有计入 actuator 的跟踪误差、关节动力学、范围裁剪、保持逻辑或任何真机限制。实验中应由 episode 确认每一周期实际写入的 position_reference,再由 qpos 曲线确认模型状态怎样响应。只写公式而不保存这两类字段,无法区分候选动作变大、参考被裁剪和实际状态变化缓慢这三种现象。
还要区分策略推理频率、参考更新频率和低层伺服频率。低频策略可以由高频控制器保持或插值,但保持时间、命令有效期、状态超时和安全过滤必须明确。高频更新也不一定改善执行:若状态、通信或计算无法在周期内完成,队列积压、抖动和旧状态反而会损害闭环。后续实验只改变 max_delta_per_step 或控制频率中的一个,使用相同字段的 episode 和曲线比较结果,不用肉眼印象取代证据。
在 MuJoCo 脚本中,控制频率还必须能被当前 timestep 整数表示。这个要求不是为了追求一个特定数字,而是保证每一条记录都对应确定数量的 mj_step 调用;否则“20 Hz”只会停留在参数名上,实际时间轴可能已经偏离。该检查失败时,不补插值、不静默取整,而是让合同检查或运行前校验停止并报告原因。学生修复频率后,应重新生成 timing 图,而不是沿用失败设置下的旧曲线。
到这里,动作的对象、单位、顺序、范围、尺度和时间语义已经能够写入一份可检查的契约。下一节开始运行 MuJoCo SO-101:先让模型自己说明有哪些关节和执行器,再验证这份契约怎样把关节目标转化为带记录的闭环结果。
3.7 在 MuJoCo 中验证 SO-101 的关节目标到达
这一节的目的不是让仿真窗口中的机械臂“动起来”,而是完成一次可判定、可复现的关节位置实验。学生使用 MuJoCo Menagerie 的 robotstudio_so101/scene.xml,先读取模型事实,再运行一个 shoulder_lift 小幅目标。程序的输出必须包括模型 manifest、成功或失败 episode,以及目标、状态、action 与位置参考的曲线。
Menagerie 的 SO-101 说明要求 MuJoCo 3.1.3 或更高版本。模型路径、Menagerie commit、MuJoCo 版本和脚本参数必须与本次 episode 一同保存。课程代码不把关节范围、actuator ctrlrange 或模型步长复制为常量;若模型更新,先重新生成 manifest,再检查动作契约是否仍然成立。
无显示环境、云端工作站或 Notebook 只改变运行地点,不构成另一套实验。学生在云端仍使用同一份 Menagerie commit、action_contract.yaml、脚本与 JSONL 字段;没有 GPU 或打不开 viewer 时,仍可依靠 manifest、episode、跟踪曲线和时间曲线完成判断。交互式窗口只能帮助观察姿态,不能代替范围检查、任务状态或记录证据,也不能从云端连接真机。

3.7.1 先从模型读取 SO-101 的关节和 actuator
在代码目录中安装依赖、取得 Menagerie 后,首先运行模型检查脚本。它要求模型中存在六个同名 joint 与 position actuator:shoulder_pan、shoulder_lift、elbow_flex、wrist_flex、wrist_roll 和 gripper。对每个关节,脚本读取 joint range、actuator ctrlrange、qpos 地址和模型 timestep,并将可执行目标范围计算为前两个范围的交集。
git clone --depth=1 https://github.com/google-deepmind/mujoco_menagerie.git external/mujoco_menagerie
git -C external/mujoco_menagerie rev-parse HEAD
python -m pip install "mujoco>=3.1.3" numpy matplotlib pyyaml
cd code/lecture03
python simulation/inspect_so101_model.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--output artifacts/model_manifest.json
python simulation/check_contract.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--contract config/action_contract.yaml
--output artifacts/contract_report.json
两步都通过,才允许运行位置到达实验。inspect_so101_model.py 的通过条件不是“输出了六行文字”,而是:六个关节名称与六个 actuator 名称一一对应;每个 actuator 确实驱动同名 joint;joint range 与 ctrlrange 存在非空交集;模型 timestep 可读。check_contract.py 还要确认关节顺序、状态单位、动作单位、位置参考语义、控制周期和拒绝处置与当前模型相容。任一项不满足时,停止实验并修正模型路径或动作契约,不能通过调整目标数值绕过映射错误。
模型检查完成后,应把 model_manifest.json 和 contract_report.json 放在同一实验目录中阅读。前者说明“当前模型里有什么”,后者说明“讲义中的约定能否作用于这份模型”。两份文件都通过,只能证明字段名称、范围来源和时间基准相容;它们并不证明真实 SO-101 已经校准,也不证明任何真机的速度、负载或接触安全。
3.7.2 action delta 怎样变成 position reference
实验从当前 qpos 开始,只为选定关节指定一个合法的绝对目标。每个控制周期先计算目标与当前状态的差,再将这个差裁剪为不超过 max_delta_per_step_rad 的 action_delta。候选参考由“当前状态加上 action delta”得到,随后仍要被裁剪到 joint range 和 actuator ctrlrange 的交集。最终写入 data.ctrl 的是这个绝对 position_reference,不是原始 action_delta。
下面的代码是实验脚本中最关键的语义边界;完整实现位于 code/lecture03/simulation/so101_joint_reach.py。
positions = qpos_vector(data, mappings)
raw_delta = target_vector - positions
action_delta = np.clip(raw_delta, -max_delta, max_delta)
candidate_reference = positions + action_delta
position_reference = clamp_targets(candidate_reference, mappings)
set_position_references(data, position_reference, mappings)
for _ in range(substeps_per_control):
mujoco.mj_step(model, data)
这里 substeps_per_control 由控制频率和模型 timestep 的整倍数关系计算。若 20 Hz 对应的控制周期不能由当前模型 timestep 整除,脚本会拒绝运行,而不是悄悄改变实际更新频率。这样,曲线横轴上的每一个控制点都有明确的时间含义。
3.7.3 一次到达实验怎样留下可复盘记录
先运行一个范围内的小幅目标,再故意给出模型范围外的目标。第二次运行不是要让机械臂撞到限位,而是验证约束过滤是否在步进之前返回 rejected。命令中的 0.35 只是一个应由当前 manifest 再次核验的教学小幅示例,并不是任何真机的永久参数。
python simulation/so101_joint_reach.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--contract config/action_contract.yaml
--joint shoulder_lift --target-rad 0.35 --control-hz 20
--output-dir artifacts/reach_baseline
python simulation/so101_joint_reach.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--contract config/action_contract.yaml
--joint shoulder_lift --target-rad 4.0 --control-hz 20
--output-dir artifacts/reach_rejected
成功 episode 的每条控制周期记录至少包含时间、当前 qpos、绝对目标、action_delta、position_reference 与任务状态。它还应生成 tracking_reached.png,用于比较目标、状态和位置参考;生成 timing_reached.png,用于比较状态时间戳和控制更新时间。越界运行至少生成 episode_rejected.jsonl,其中应写明拒绝发生在步进之前及其范围原因。
一次完整的无硬件实验按下列顺序完成:
固定 Menagerie commit、MuJoCo 版本和模型路径,创建本次运行目录。 生成 manifest,检查六个 joint、六个 position actuator、范围交集和 timestep。 用同一份模型运行 contract 检查;若 joint order、单位或控制频率不相容,先修正合同而不运行机械臂。 运行 shoulder_lift 的低幅基线,只查看本次 manifest 已证明合法的目标。 先从 episode 比较 action_delta 与 position_reference,再从曲线比较 reference 与 qpos,不能跳过中间参考直接判断执行器。 给出一个范围外目标,确认记录在 t=0 标记为 rejected,并确认没有生成后续步进记录。 只改变每步增量或控制频率中的一个,保存独立的 episode 和两张曲线。 在 failure_review.md 中写明最早异常、候选原因、单变量修复、复测结果,以及当前证据尚不能证明的内容。
若某一步未通过,回到它直接依赖的上一步。例如控制周期检查失败时,回到 timestep 与频率的整除关系;目标越界时,回到 manifest 的范围交集;reference 与状态不一致时,先检查同一控制周期内的 qpos、action_delta、position_reference 与时间戳。这样做可以避免用下一轮参数调整掩盖前一轮的接口错误。

表 3-7 仿真关节到达实验的结果判据
| 结果 | 通过依据 | 首先检查的字段 |
|---|---|---|
至此,原稿中的“观测—候选动作—约束过滤—控制—反馈—记录”不再只是概念图,而变成一条能由代码和 episode 检查的闭环。下一节转向已经校准的 LeRobot SO-101,比较为什么相同的关节语义在真机上还需要校准、人工监护和官方工作流。
3.8 在 LeRobot SO-101 上观察主从关节跟随
真机路径是对仿真概念的受限观察,不是把仿真脚本直接接到电机上。它只在设备已经装配、教师确认安全条件、操作者熟悉急停位置并可全程监护时进行。任务范围固定为:校准 leader 与 follower,空载、低速、小幅地观察 1--2 个关节和夹爪的主从跟随,记录 10--20 秒 episode,并审计其中的关节状态与 action 字段。不进行抓取、接触、回放、策略训练、串口直连或低层参数修改。

LeRobot 的 SO-101 官方文档明确要求先校准 follower 和 leader,使两套机械臂处在相同物理姿态时得到一致的位置值。校准不是可有可无的准备动作:零位、方向或关节范围若不一致,后续观察到的“跟随错误”无法区分是 action 解释错误、状态测量错误还是机构本身的问题。
3.8.1 校准之前为什么不能解释主从动作
开始前,检查设备固定、无负载、工作空间清空、电源和急停可用,确认不会有衣物、线缆或身体部位进入运动范围。随后按照执行当日的 LeRobot 官方文档和已安装版本,分别完成 follower 与 leader 的校准;运行任何命令前先用 --help 与官方文档核对参数名称。当前 LeRobot 仓库提供 lerobot-calibrate、lerobot-teleoperate 与 lerobot-record 入口,但端口、设备 ID、相机配置和数据集参数属于现场事实,不能从讲义示例照抄。
校准完成后,不立即做连续运动。先在只读状态下核对:每个观测字段对应的关节名称;关节正方向;中间位置和小幅手动变化是否与状态读数一致;零位和有效范围是否已写入当前设备的校准结果。任何一项不确定,都应停止在校准或只读核对阶段,而不以更大幅度的跟随运动“试试看”。
这一步有三个明确的继续条件。第一,现场人员知道停止方式和运行范围;第二,校准结果、设备身份和软件版本能够被保存;第三,只读观测与小幅手动姿态之间没有无法解释的方向或名称差异。任一条件不满足时,当前可交付物只是“停止于校准或只读检查”的记录,不应继续进入跟随,也不应把缺少的字段用推测补齐。这样保留停下来的证据,比以未知映射做一次动作试验更有价值。
3.8.2 空载小幅跟随怎样验证方向和幅度
从单关节、低速、小幅变化开始。操作者缓慢移动 leader 的一个关节,观察 follower 是否同向响应、幅度是否合理、是否出现异常噪声、抖动、保护提示或碰撞风险。一次只改变一个关节;在验证过方向和幅度后,再观察另一个关节或夹爪。任何异常出现时,停止运动并保存当前日志、视频和设备状态,不通过增加幅度或速度来判断问题。
这一过程验证的是接口一致性,而不是闭环性能指标。leader 的位置是高层动作来源之一,follower 的编码器状态是执行结果;真实伺服器内部怎样跟踪目标仍由官方驱动和设备保护负责。学生不需要、也不允许在本讲中手写串口协议、直接使能电机、调整电流、力矩或 PID。真机的 action 与 MuJoCo 的 action_delta 可以用于比较语义和记录方式,但二者不是可以直接替换的底层命令。
3.8.3 短 episode 中应当审计哪些字段
在通过前两项检查后,使用官方 teleoperate/record 工作流记录一个 10--20 秒 episode。记录结束后,先核对文件是否完整,再选择 1--2 个关节和夹爪,比较 leader 变化、follower 状态、时间戳和任务阶段。若当前版本输出的字段名称与本讲不同,应在记录说明中建立字段映射,不能为了贴合讲义修改原始数据的语义。
表 3-8 给出本讲要求的最小审计表。它不规定 LeRobot 数据集的内部字段名,而要求学生能从本次版本的文档和数据中找到等价证据。视频只用于与状态记录互相核对,不能代替时间戳和数值字段。
表 3-8 LeRobot 短 episode 的最小审计表
| 审计对象 | 应回答的问题 | 可接受证据 | 不应据此推出的结论 |
|---|---|---|---|
真机路径的通过条件是“记录和解释得清楚”,不是让机械臂完成复杂动作:已完成校准和只读核对;小幅跟随在人工监护下结束;episode 与视频可对应;异常、停止条件或缺少证据均如实记录。下一节回到无硬件实验,用相同的记录格式单独比较动作尺度和控制频率的影响。
3.9 动作尺度和控制频率会怎样改变运动?
本节只做两个单变量对照。第一个对照固定 20 Hz,只改变每控制步允许的最大关节增量;第二个对照固定每步增量,只改变控制频率为 10、20、50 Hz。目标关节、初始状态、模型版本、容差、超时和日志字段必须保持不变。不要在同一轮里同时改变增量、频率、目标、模型或控制模式,否则曲线不能支持清楚的因果判断。
两组对照回答的是不同问题。改变 max_delta_per_step_rad 时,观察的是每一次参考更新允许提出多大的关节变化;改变 control_hz 时,观察的是在同一秒内允许更新多少次。二者都会影响 reference 的时间轨迹,却不能被合并为一个“更快或更慢”的参数。实验报告必须把“每步量”和“每秒更新次数”分别写出,再结合 qpos 的实际响应讨论结果;它不报告真机最大速度,也不用于给真实伺服器设定安全上限。

3.9.1 每步动作变大时,闭环发生了什么
固定控制频率为 20 Hz,选择一个已经由 manifest 证明在范围内的小幅关节目标。分别运行较小、基线和较大的 max_delta_per_step_rad。每个值的安全含义只来自当前模型的范围与本次实验条件,不能把下面的示例数值抄到真机或其他模型中。
for delta in 0.01 0.03 0.05; do
python simulation/so101_joint_reach.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--contract config/action_contract.yaml
--joint shoulder_lift --target-rad 0.35 --control-hz 20
--max-delta-rad "$delta" --output-dir "artifacts/delta_${delta}"
done
先读 action_delta_rad 与 position_reference_rad,再读 qpos_rad。当每步增量变大时,参考通常更快接近目标;但实际状态是否同样更快,还受 position actuator、模型动力学、范围裁剪和控制周期影响。若 action_delta_rad 已经达到上界而 qpos_rad 变化很小,应检查参考、执行器、时间线和模型状态,而不是把现象简单称为“模型不够好”。
3.9.2 控制频率改变后,为什么不能只看最终位置
第二组对照固定 max_delta_per_step_rad,只改变控制频率。脚本会要求控制周期是模型 timestep 的整倍数;不满足这一条件的频率会被拒绝。这样,10、20、50 Hz 的比较是在同一个 MuJoCo 时间基准上进行的。
for hz in 10 20 50; do
python simulation/so101_joint_reach.py
--model ../../../../external/mujoco_menagerie/robotstudio_so101/scene.xml
--contract config/action_contract.yaml
--joint shoulder_lift --target-rad 0.35 --max-delta-rad 0.03
--control-hz "$hz" --output-dir "artifacts/rate_${hz}hz"
done
固定“每步”增量时,提高控制频率会改变单位时间内允许提出新参考的次数。因此不能只比较最终是否到达,还要查看达到容差的时间、tracking_reached.png 中 reference 与 qpos 的时间关系,以及 timing_reached.png 中状态时间戳是否与控制更新一致。若频率提高后结果没有改善,可能是参考已被范围或执行器响应限制,也可能是模型 timestep、控制周期或状态读取存在问题;这些原因必须由 episode 字段区分。
3.9.3 实验完成应以哪些证据判定
每次运行都将下列文件放在独立目录中。结论只能写成与这些证据一致的范围内判断,例如“在当前模型、初始状态和参数下,较大的每步增量使 reference 更快接近目标”;不能把一次仿真结论扩展为真实机械臂的通用速度或安全指标。
表 3-9 单变量实验的证据文件与判读对象
| 产物 | 它回答的问题 | 判读时必须同时查看 |
|---|---|---|
通过条件包括:两组对照各只改变一个变量;所有运行使用同一模型和动作契约;每组保留成功或超时记录;越界目标的拒绝记录存在;结论引用具体曲线或字段。下一节用这些证据处理失败,不再把“最终没有到达”当成唯一现象。
3.10 从一次失败记录中找到最早异常
失败复盘的目标不是立刻给出一个听起来合理的根因,而是缩小需要验证的假设。一个未到达的目标可能来自错误的关节映射、越界参考、控制周期不相容、状态失效或实际执行受限。若只看结束时的关节位置,这些原因会混在一起;若沿着 episode 的时间顺序查找最早的不一致字段,排查就能变成可复现的实验。
本讲使用五步证据链:描述现象,定位最早异常,列出候选原因,只改变一个变量验证,修复后复测并保留前后记录。AI 可以帮助把日志字段归类或提出候选原因,但不能代替记录、模型和复测给出结论。

3.10.1 先定位最早异常,而不是先猜根因
先从任务状态开始:是 rejected、timeout,还是已到达后仍出现不符合预期的轨迹?随后沿时间线依次比较当前 qpos、目标、action_delta、position_reference 与状态时间戳。第一个与动作契约不一致的字段,通常比最后的姿态更接近问题发生的位置。
例如,若第一条记录已经显示目标不在 executable range 内,最早异常是范围检查,不能再把问题解释为 position actuator 跟踪不足;若 action delta 在第一步就与配置单位不一致,应先检查配置和适配,而不是提高控制频率。若 early records 正常、position_reference 连续,后来 qpos 始终不接近目标,才需要进一步检查执行器、模型动态或 timeout 条件。
3.10.2 关节顺序、范围和周期错误怎样留下证据
本讲至少复现下列三种受控失败。每次失败都在仿真中完成,并在修改前后保留独立目录;不在真机上故意触发越限、通信中断或保护。
表 3-10 三类受控失败的最早证据与复测
| 受控失败 | 最早异常证据 | 候选原因 | 单变量验证 | 修复与复测 |
|---|---|---|---|---|
范围失败可用第 3.7 节的越界命令直接复现。周期失败则可尝试一个无法由当前模型 timestep 整除的频率;预期行为是脚本在写入控制参考前拒绝运行,而不是在内部悄悄四舍五入。joint order 失败应通过复制合同文件并仅交换两个关节名称来验证,随后恢复从 manifest 读取的顺序。这个失败只验证合同检查,不需要移动任何真实机械臂。
三种失败的复测必须保留同一模型版本和同一输出目录结构。关节顺序错误的修复证据是重新生成的 contract report 与基线 episode;范围错误的修复证据是由 rejected 转为合法运行后生成的 episode;周期错误的修复证据是兼容频率下连续的状态时间线。它们分别证明名称映射、目标范围和时间基准已经回到合同允许的条件,却不能单独证明模型中的每个动态参数都正确,也不能作为任何真机安全结论。
3.10.3 修复后为什么还要复测
修复一次错误,不等于已经证明系统恢复正常。范围错误被修复后,还要重新运行基线,检查 qpos 是否真的进入容差;控制频率被改为整倍数后,还要检查 timing 图与 episode 的时间戳;关节顺序被恢复后,还要检查对应 joint/actuator 映射,而不仅是脚本不再报错。
复测时保持初始状态、模型版本、目标、容差和其余参数不变,只替换被验证的那一个变量。这样,修复前后的 episode 才能构成有效对照。作业提交的失败复盘至少包括:失败命令或配置片段、最早异常字段、候选原因、单变量修改、复测产物和一段说明“本次证据能证明什么、还不能证明什么”。
失败记录把抽象的接口边界变成可以查询的证据。下一节利用同一份动作契约和 episode,让 AI 协助发现遗漏与提出假设,同时保留学生对模型、日志和安全判断的责任。
3.11 让 AI 帮忙检查假设,而不是替你下结论
AI 可以帮助阅读配置、解释变量、生成最小脚本骨架和归类日志现象,但它看不到本次机械臂、模型文件和 episode 以外的事实。尤其在硬件安全、关节范围、设备接口和根因判断上,可信结论必须能回到当前模型、官方文档、日志、视频或传感器记录。把一段自然语言解释当作实验结果,会掩盖最需要核对的单位、顺序和时间条件。
3.11.1 用 AI 审查动作契约中的遗漏
把 action_contract.yaml 和 model_manifest.json 一起提供给 AI,并明确要求它只列出“待人工验证的遗漏或冲突”,不要让它替代模型读取范围。可使用如下任务提示:
你是机器人接口审查助手。请比较下面的动作契约和模型 manifest,
只输出以下三类内容:
joint_order、单位、控制周期、范围来源和拒绝处置中缺少的字段; 两份文件中互相矛盾的字段; 每条问题需要由哪个文件、哪一段日志或哪项官方文档验证。
不要编造关节范围、SDK 接口、真机速度或安全参数;不确定时标记为“待验证”。
学生随后逐项回到 manifest、合同和脚本检查。AI 若说“范围不一致”,应指出哪一个 joint 的两个范围、是否确有交集,以及代码是否实际使用了交集;AI 若指出一个不存在的接口名称,应在当日平台文档中核验,而不是直接加入代码。
3.11.2 用 AI 提出失败假设,再由证据筛选
把一份 episode_timeout.jsonl 或 episode_rejected.jsonl 的少量连续记录交给 AI,让它按“现象、最早异常、候选原因、还缺什么证据”组织输出。提问中必须给出限制:AI 只能提出候选原因,不得把任何候选原因写成根因。然后由学生依次检查 episode 时间线、模型 manifest、合同版本和对应曲线,并通过只改变一个变量进行复测。
本讲可回答的 AI 提问任务限定为以下五类:
这条 action contract 是否遗漏关节顺序、单位、频率、范围来源或拒绝处置? action_delta 与 position_reference 在当前 episode 中分别表示什么,它们是否被混用? rejected 的最早证据来自范围、合同还是控制周期检查? 某次 timeout 中,哪个字段最早偏离预期,还需要哪一种记录才能区分候选原因? 这份真机短 episode 中,哪些字段只能说明小幅主从跟随,不能说明高速、带载或接触安全?
每个问题的提交都应包括 AI 输入、AI 输出、学生确认或否定的证据,以及复测命令或无法复测的原因。没有证据支持的 AI 说法只能保留为假设。
3.11.3 生成代码以后必须人工核对什么
Vibe Coding 的允许范围是生成 MuJoCo 脚本骨架、YAML schema、日志解析、单元测试、绘图代码和 README 初稿。每次生成后先在无硬件 MuJoCo 路径运行,确认模型可读、合同检查通过、越界会拒绝、成功/失败 episode 能写出,再考虑真机观察。不能让 AI 直接决定真机关节限位、最大速度、最大力矩、急停顺序、使能流程或未经验证的 SDK 调用。
人工检查清单如下:
表 3-11 AI 辅助生成后的人工作业核对项
| 生成内容 | 人工必须核对的事实 | 本讲中的验证位置 |
|---|---|---|
code/lecture03/README.md 是本讲运行命令、模型版本、合同、测试和产物定义的单一事实来源。任何修改先在 MuJoCo 通过,再更新 README 和讲义中的对应说明;“AI 生成了代码”本身不构成通过理由。下一节把这些证据组织成交付物,使一次运动实验可以由他人复现与审阅。
3.12 作业交付:把一次运动变成可复现的实验
本讲的作业不以“是否让机械臂动起来”评分,而以别人能否据此复现模型、理解 action 的语义、检查成功或失败为标准。每个层级都使用同一条 SO-101 关节目标闭环;进阶和真机路径只增加证据深度,不另换一个抓取、视觉或训练项目。
3.12.1 基础、进阶与真机可选作业
表 3-12 分层作业与最低证据
| 层级 | 必做内容 | 最低交付证据 | 不属于本讲要求的内容 |
|---|---|---|---|
3.12.2 交付文件怎样证明实验可复现
提交目录应保留原始 episode 和生成图,不用截图代替数据。文件名可按下列结构组织;其中的模型目录是外部依赖,不应把其大型资源复制进作业压缩包,但必须写明 commit 和模型路径。
lecture03_submission/
README.md
environment.txt
config/action_contract.yaml
artifacts/model_manifest.json
artifacts/reach_baseline/episode_reached.jsonl
artifacts/reach_baseline/tracking_reached.png
artifacts/reach_baseline/timing_reached.png
artifacts/reach_rejected/episode_rejected.jsonl
artifacts/delta_*/
artifacts/rate_*hz/
failure_review.md
hardware_optional/ # 仅真机可选路径提交
safety_checklist.md
episode_mapping.md
short_episode_video.mp4
README.md 至少写明操作系统、Python 与 MuJoCo 版本、Menagerie commit、模型路径、运行命令、动作契约版本、文件树和已知限制。failure_review.md 采用第 3.10 节的五步证据链。若某次实验无法完成,不要伪造成功图;应保留报错、环境信息、已完成的核查和下一步可验证假设。
评分采用证据而非设备数量:动作契约与模型映射正确 30%;基线、拒绝和两组单变量对照完整 30%;失败定位、修复和复测 20%;记录可读、版本可追溯、结论不越界 15%;AI 提问记录能区分假设和验证 5%。真机可选路径用于展示额外的校准和安全证据,不以是否拥有硬件获得替代性的基础分。
3.12.3 下一讲需要接收哪些状态和坐标信息
第四讲将把“状态是否可信”进一步展开为传感器测量、时间同步、坐标变换和标定问题。完成本讲后,应能交给下一讲一份明确的问题清单:关节状态由什么传感器产生、时间戳是否可用、基座/TCP/相机坐标系分别是什么、哪些 action 需要坐标变换、状态过期会怎样影响参考生成。
第五讲将把“单关节目标到达”扩展为完整的仿真任务与可复盘闭环。本讲交出的 action execution contract、模型 manifest、episode schema、到达/拒绝判据和失败证据链,是下一讲定义目标、初始状态、成功条件、终止条件和任务记录的基础。它们不等于完整的任务规划、策略训练或 Sim2Real 评测;后续内容应在这些接口清楚后再逐步加入。
3.12.4 本讲参考开源项目如何使用
表 3-13 参考项目在本讲中的用途与边界
| 项目或资料 | 在本讲中的用途 | 使用边界 |
|---|---|---|
往期回顾
-END-
点击下方卡片,关注【Xbotics具身智能实验室】公众号
你想要的这里都有~~
Ask Me Anything|提问箱
对文章有疑惑,或想聊更深?欢迎把你的问题丢给我们:技术方案、实操踩坑、课程与资料、项目合作、职业发展,都可以问。
怎么问:在评论区留言,或私信公众号
我们会做什么:每周集中整理高质量问题并公开回复,重点问题邀请作者或嘉宾深度解答;典型问题会加入知识库并持续更新。
提问小提示:尽量说明「你的目标—当前做法—期望产出」,附上必要信息(硬件/软件版本、数据规模等),能更快获得有用答案。
一起把问题变成知识,推动社区进步 🚀