桌上没有 Q1。
在这里插入图片描述

这是开发者最常见的第一天:SDK 下好了,文档读了两章,接口看懂了,然后卡住。挥手指令发给谁?22 个关节的角度对不对?相机看到的画面是什么样?没有真机,这些都没法回答。

答案是把仿真先跑起来。官方给了完整的一套:URDF、24 个 STL 网格、三份 MJCF、三套场景。把它们加载起来,22 个关节的骨架能立住,相机能出图,数据链路能先跑通。

这篇文章讲四件事:仿真包怎么拆、三份 MJCF 怎么选、关节名为什么是契约、仿真验证到真机部署怎么衔接。文中的数字都来自实际加载模型和渲染,脚本在 实操/scripts/,截图在 results/。

先说清楚边界:这篇文章验证的是运动学、几何、相机和数据链路。动力学闭环和真机执行,我没有跑,文末单独列出来,不含糊。

一、先想清楚仿真能替你验证什么

没有真机,最贵的不是设备钱,是试错成本。

真机上试错的代价有三层。摔一次,修一周。改一版参数,要等设备排期。同一个 bug,明天复现不出来,因为姿态和电量都变了。仿真把这三层都绕开了:摔不坏、随时跑、每次从同一个初始状态开始。

但仿真不是万能的。它能验证的和不能验证的,必须分清楚。

能验证不能验证
关节数量、顺序、限位电机真实响应和发热
连杆几何、重心位置减速器回差、摩擦非线性
相机安装位姿、视场真实光照、反光、自动曝光
话题、消息、关节名契约DDS 延迟、控制周期抖动
数据生成和训练链路真机安全兜底

这张表决定了用法。仿真里跑通了,说明"结构对、契约对、链路对"。真机上跑不通,问题大概率在动力学和时序,不在你写的业务逻辑。

这个分工很省时间。我后面几节的顺序也按这个来:先几何,再契约,再链路,最后才是动力学。

二、官方仿真包拆开看

Q1 的仿真包在 inputs/q1urdf/q1_v3/ 下,三块东西。

第一块,URDF。 urdf/q1_v3.urdf,25 个 joint,其中 22 个 revolute,3 个 fixed。22 个可动关节就是整机的自由度。

第二块,网格。 meshes/ 下 24 个 STL,pelvis、大腿、小腿、脚、躯干、大臂、小臂、头,一段连杆一个文件。MJCF 和 URDF 都引用同一套网格,所以两种格式渲染出来的外形是一致的。

第三块,MJCF 和场景。 xml/ 下六个文件。三份机器人模型 q1_v3.xml、q1_v3_full_range.xml、q1_v3_mc.xml,三套场景 _scene_lab.xml、_scene_home.xml、_scene_test.xml。

三份模型加载出来的结构完全一样。

文件nqnvnjntnbody执行器用途
q1_v3.xml292823270可视化、运动学
q1_v3_full_range.xml292823270全范围限位校核
q1_v3_mc.xml2928232722 个 motor动力学、运控

这张表是 mujoco_check.py 跑出来的,三份文件一致:nq 29、nv 28、njnt 23、nbody 27。

在这里插入图片描述

图 1,三份 MJCF 的加载结果

nq 是 29,不是 22。差的 7 个是浮动基座:3 个位置加 4 个四元数。nv 是 28,浮动基座占 6 个速度自由度,加上 22 个关节。njnt 是 23,22 个 hinge 加 1 个 freejoint。nbody 27,是 26 个机器人本体加 1 个世界坐标系——骨盆 1 节、双腿各 6 节、腰 1 节、躯干 1 节、双臂各 5 节(含腕部固定节)、头 1 节。注意 q1_v3.xml 本身不带场景,桌子、墙、杯子在 _scene_*.xml 里。

看懂这四个数,你就知道模型里到底装了什么。22 个关节负责动作,浮动基座负责平衡,24 个网格负责外形。

三、三份 MJCF 怎么选,这是最容易选错的一步

三份模型的关节数一样,差别在两处:限位和驱动。

q1_v3.xml 是默认版,没有执行器,纯运动学。适合拿来做可视化、算正运动学、验证相机视角。它的限位是软件限位,比机械限位收一点。

q1_v3_full_range.xml 是机械全范围版,限位放宽到机械极限。适合做可达性校核:这条轨迹在物理上到底能不能到。

q1_v3_mc.xml 是运控版,最完整。23 个 joint(多了浮动基座),22 个 motor,时间步 1 毫秒,关节加了 armature="0.004"。适合做动力学仿真和控制算法验证。

三份的限位差异不是小数点后的差别,随手挑四个关节看。

关节q1_v3(默认)q1_v3_full_range(机械)
髋 pitch-169.0° ~ 84.2°-175.0° ~ 90.0°
膝5.7° ~ 134.1°0° ~ 140.0°
踝 pitch-40.1° ~ 21.8°-45.0° ~ 25.0°
踝 roll±17.2°±20.0°

默认版比机械版每边收了几度。为什么收,文档没写,但工程上常见:软件限位给控制误差留余量,避免规划贴着极限跑,真机上撞限位。

选哪个,看你要验证什么。

你要做的事用哪份
看外形、算 FK、调相机q1_v3.xml
校核轨迹可达性、查越界q1_v3_full_range.xml
跑动力学、验证控制器q1_v3_mc.xml
渲染数据集q1_v3.xml + 场景文件

还有一个细节值得记。三份模型的关节力矩上限不一样,髋和膝 36 牛米,踝 22 牛米,腰 36 牛米,手臂 22 牛米。这个数写在 actuatorfrcrange 里,做力矩规划时别超,超了仿真里可能过,真机上电机会先热。

四、第一帧:加载、前向运动学、拿到骨架

加载模型只要两行。

import mujoco
model = mujoco.MjModel.from_xml_path('inputs/q1urdf/q1_v3/xml/q1_v3.xml')
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)

mj_forward 是正运动学。给定关节角,算出每个连杆在世界坐标里的位置和朝向,结果在 data.xpos 里。

把所有连杆的位置连起来,就是骨架。

在这里插入图片描述

图 2,Q1 的正运动学骨架

这张图不是渲染,是纯几何。每个点是一个连杆的原点,每条线是父子关系。它能验证三件事:连杆长度对不对、关节旋转轴对不对、初始姿态合不合理。

渲染出来是另一个样子。

在这里插入图片描述

图 3,MuJoCo 渲染的第一帧

白色人形、网格地面、脚下有影子。看到这张图,说明网格、材质、光照、相机全部加载正确。第一公里的验证到此为止,剩下的都是契约问题。

五、关节名就是契约,仿真和真机一字不差

这是整套仿真里最值钱的一条。

真机的关节指令走 /aima/hal/joint/command,消息类型 JointCommandArray,里面是一个 JointCommand 数组。每个 JointCommand 长这样。

string  name
float64 position
float64 velocity
float64 effort
float64 stiffness
float64 damping

注意第一个字段是 name。指令是按关节名下发的,不是按数组下标。

这意味着什么。你在仿真里调好的 22 个关节轨迹,搬到真机上,不需要重新排 22 维数组的顺序。只要名字对上,指令就落对。

我核对过三处的关节名:SDK 的 joint_control.py 示例、URDF、MJCF。三处一字不差。

left_hip_pitch_joint    right_hip_pitch_joint
left_hip_roll_joint     right_hip_roll_joint
left_hip_yaw_joint      right_hip_yaw_joint
left_knee_joint         right_knee_joint
left_ankle_pitch_joint  right_ankle_pitch_joint
left_ankle_roll_joint   right_ankle_roll_joint
waist_roll_joint        waist_yaw_joint
left_shoulder_pitch_joint   right_shoulder_pitch_joint
left_shoulder_roll_joint    right_shoulder_roll_joint
left_shoulder_yaw_joint     right_shoulder_yaw_joint
left_elbow_joint            right_elbow_joint

22 个名字,四段:左腿 6、右腿 6、腰 2、双臂各 4。

所以仿真到真机的迁移,业务代码基本不用改:关节名、单位(弧度)、消息类型全一致,真机上认的是同一套名字。要改的是连接参数——把节点挂进机器人的 DDS 域,也就是开发者模式加网段那两步。

这条契约还解释了 joint_control.py 示例的用法。它是拿 joint_names 和 target_positions 两个参数配对的。

python3 examples/python/joint_control.py --ros-args \
  -p joint_names:="['right_hip_pitch_joint', 'right_knee_joint']" \
  -p target_positions:="[-1.6, 0.0]"

示例里还有三个安全前提,写在脚本头注释里,很多人会跳过。

第一,必须先关闭机器人本体的运控模块。第二,机器人要平躺或悬挂。第三,轨迹用 Ruckig 做平滑,默认刚度 20、阻尼 2、最大速度 3 弧度每秒。

这三条不是建议,是前提。直接站着发 22 个关节的目标位置,运控和你的指令会打架,结果不可预期。

六、把相机装进仿真,数据链路先跑通

几何和契约验证完,下一步是感知。

Q1 的仿真包里带了三套场景,_scene_lab、_scene_home、_scene_test。场景里有桌子、墙、杯子,还有一盏方向光,diffuse 0.95,ambient 0.35。

我拿它们做了一件事:渲染数据集,训一个找杯子的模型。细节写在微调那篇,这里只讲和仿真有关的部分。

相机装在 head_link 上,局部坐标 (0.145, 0, 0.03),朝向桌面中心 (1.05, 0, 0.42),用 look-at 矩阵算的四元数。分辨率 240×180,离屏渲染。

两个场景调成两种风格。实验室是亮白桌面、灰白墙、冷白光。客厅是深棕桌面、暖黄光、桌上多三件杂物。
在这里插入图片描述

图 4,实验室与客厅两个域的渲染画面

同一个模型,同一个相机,只改场景参数,画面差这么多。模型在实验室学到的是白底红杯,到客厅看到的是棕底暗杯,旁边还有它没见过的形状。

结果就是那组数字:实验室 0.95 厘米,客厅 10.2 厘米。加 120 张客厅图微调,回到 2.96 厘米。

这件事的价值不在数字,在能力。仿真能造出"部署现场的另一种画面",让你在没有客厅、没有真机的时候,提前把域偏移这个问题暴露出来。真机上你只能等,仿真里你随时能造。

七、sim-to-real 的四个坑

仿真跑通不等于真机跑通。这四个坑,我提前说清楚。

第一个,相机。 仿真的光照是简化模型,没有镜头眩光,没有自动曝光,没有白平衡漂移,没有运动模糊。真实客厅还有反光和阴影。所以仿真里 10.2 厘米的误差,真机上大概率更差,只能当下界看。

第二个,动力学。 armature 0.004、damping 0、frictionloss 0,这些是估计值。真实电机的响应、减速器回差、摩擦非线性,仿真里都没有。仿真里能站稳的控制参数,真机上可能要重调。

第三个,时序。 仿真时间步 1 毫秒,理想时钟。真机走 DDS,控制周期有抖动,服务调用有延迟。文章里反复说的"超时按失败处理",在仿真里不会遇到,真机上一定会。

第四个,场景。 桌子高度、相机位姿、杯子尺寸,在仿真里都是常量。真机上桌子会变,机器人会走动,头部会随腰部转动。视角分布比仿真宽得多。

这四个坑,正好对应真机到手后第一周该做的事:先单步验证接口,再 dry-run,再真摸。顺序不能反。

八、从仿真到真机的路线图

把上面的东西串成一条路。

阶段做什么通过标准
1 几何加载三份 MJCF,跑 FK骨架比例正确,22 关节齐全
2 契约核对关节名,消息字段三处关节名一致
3 感知相机渲染,看画面视角和真机接近
4 数据渲染数据集,训模型源域指标达标
5 动力学mc 模型跑控制关节跟踪误差可接受
6 真机PrimeConsole 单步6 个服务各调一次
7 联调dry-run,再真跑拒绝路径全部走过

前四步没有真机也能做,做完你手里就有:一个能出图的模型、一个训好的感知模型、一套对得上的关节名。

后三步要真机。但那时候你已经不是在"第一次见到机器人",是在验证一个已经跑过一遍的链路。这两件事的心理成本差十倍。

九、我的验证边界

这篇文章里,哪些是我验证过的,哪些没有,说清楚。

验证过的。三份 MJCF 加载,nq 29、nv 28、njnt 23、nbody 27,三份一致。22 个关节的正运动学骨架。MuJoCo 渲染第一帧。24 个 STL 网格全部加载。三处关节名核对一致(SDK 示例、URDF、MJCF)。两个场景的渲染和 1920 张数据集生成,以及下游的微调链路。

没有验证的。用 mc 模型跑动力学闭环控制。关节跟踪误差。真机的执行和时序。这些需要真实的控制回路和硬件,我不编。

版本也要说清楚。这台机器上的 MuJoCo 是离屏渲染(OSMesa 后端),显卡加速的渲染没有测。真机是 Ubuntu 22.04 加 ROS2 Humble,仿真这边不依赖 ROS2,纯 Python 加载,所以不受这个约束。

十、复跑清单

三个脚本,按顺序跑。

# 1. 加载三份模型,打印结构,渲染第一帧
python3 scripts/mujoco_check.py inputs/q1urdf/q1_v3/xml artifacts

# 2. 正运动学骨架图
python3 scripts/mujoco_skeleton_3d.py inputs/q1urdf/q1_v3/xml/q1_v3.xml artifacts/mujoco_q1_skeleton_3d.png

# 3. 渲染 1920 张数据集(约 12 分钟)
python3 scripts/finetune_dataset.py

依赖是 mujoco、numpy、Pillow、matplotlib。CPU 就能跑,不需要显卡。

跑完检查三个地方:artifacts/mujoco_summary.json 里的 nq 是不是 29,骨架图的四肢是不是齐全,渲染图里机器人脚下有没有影子。三个都对,仿真环境就算搭好了。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐