摘要
启元 Q1 官方下载页的 q1_v3 模型包里,除了给 RViz 用的 URDF,还躺着三份 MuJoCo 模型------其中
q1_v3_mc.xml自带 22 个力矩执行器(±40Nm)、60 个传感器通道(IMU + 22 关节位置 + 22 关节速度)和 1kHz 仿真步长,官方明说"适用于运动规划及强化学习训练",但此前没有任何公开文章加载过它们。本文是这套模型的第一次系统性实测:零修改加载体检、无控制瘫倒对照、22 关节 PD 位置伺服站立(5 秒波动 ±0.0000m)、平滑轨迹挥手(力矩峰值 1.05Nm,对照跳变目标的 40Nm 饱和)、推力梯度实验(30N 站稳、40N 摔倒------位置 PD 的抗扰边界),以及一座把 MuJoCo 物理仿真桥接进 ROS2/RViz 的桥。文末给出仿真验证到真机部署的完整对应关系。
环境: Ubuntu 22.04.5 LTS / MuJoCo 3.16.0 / 启元 Q1 SDK v1.0.0.0(q1_v3 模型包) 配套仓库:https://atomgit.com/user3401/q1_mujoco_lab(实验脚本 + ROS 桥 + 交互 viewer) ROS2 侧配套:https://atomgit.com/user3401/q1_simulator

一、验证:官方模型包里的三份 MuJoCo 文件
下载页对 q1_v3 包的描述是"URDF 描述、STL 网格、MuJoCo XML 配置,适用于运动学分析、运动规划及强化学习训练 ",多数人只用过它的 URDF(进 RViz),xml/ 目录下的三份 MJCF 一直没人碰。
1.1 加载体检:零修改直接通过
按第二节搭好环境后,逐份加载:
Bash
python3 experiments/01_load_check.py

三份文件零修改直接加载成功,体检查出的规格:
| 模型 | 关节 | 刚体 | 执行器 | 传感器通道 | 总质量 | 步长 |
|---|---|---|---|---|---|---|
| q1_v3.xml | 23 | 27 | 0 | 0 | 16.92 kg | 0.002s |
| q1_v3_full_range.xml | 23 | 27 | 0 | 0 | 16.92 kg | 0.002s |
| q1_v3_mc.xml | 23 | 27 | 22 | 60 | 16.92 kg | 0.001s |

官方 q1_v3 包里的三份 MuJoCo 模型:被动版做运动学分析、全限位版放宽探索空间、控制版(_mc)自带 22 执行器与 60 传感器通道------各司其职,且此前无人加载过。
1.2 规格里值得划的重点
几处值得划重点的规格:23 关节 = 22 个铰接关节 + 1 个浮动基座(freejoint)------这是"真机器人"的标志,允许整体六自由度运动与摔倒;16.92 kg 这个总质量是官方没有公开宣传过的参数,从模型直接读出;每个关节都标了 actuatorfrcrange(如髋关节 ±36Nm、踝 ±22Nm),执行器 ctrlrange 统一 ±40Nm------力矩上限在建模时就分了层次。
二、环境搭建:从零到加载成功
2.1 MuJoCo 安装
Bash
pip3 show mujoco | head -3 # 查看环境是否已经安装 MuJoCo
pip3 install --user mujoco # 实测版本 3.16.0
python3 -c "import mujoco; print(mujoco.__version__)" # 验证版本

mujoco 是官方 Python 绑定,自带渲染所需的二进制,不依赖系统级 MuJoCo 安装。本文的实验全部走无 GUI 的 headless 路线;第八节的离线渲染另需 sudo apt install -y libosmesa6(软件渲染库)和 pip3 install --user pillow。
2.2 官方模型包的准备
从启元开发者社区下载页取 q1_v3 模型包(与 URDF 同一个包),解压到约定路径:
Bash
unzip q1_v3.zip -d ~/workspace/
解压完后的文件:

容易踩的细节 :MJCF 文件头写着 meshdir="../meshes/"------网格资源用的是相对路径 ,所以 xml/ 和 meshes/ 两个目录必须保持包内的相对结构,不能只把 xml 文件单独拷走,否则加载直接报 mesh 找不到。这也是我们所有实验脚本都从模型包根目录加载的原因。

2.3 ROS 桥场景的依赖
本文的物理实验与离线渲染完全不需要 ROS。只有第七节的桥接场景需要:ROS2 Humble 环境 + q1_simulator 仓库(提供 RViz 配置与 robot_state_publisher)+ 本仓库(q1_mujoco_lab)的桥脚本,三者均见文首链接。。
三、初次仿真:让它先瘫一次
物理仿真和可视化(RViz)的本质区别,在于有没有重力和接触。q1_v3_mc.xml 加载后什么都不控制(ctrl 全零),跑 2 秒:
Python
mujoco.mj_resetData(m, d)
for _ in range(int(2.0 / m.opt.timestep)):
d.ctrl[:] = 0.0
mujoco.mj_step(m, d)
骨盆高度从初始的 0.48 m 掉到 0.2283 m ------机器人瘫倒在地,关节在重力下自由摆动、腿臂拍击地面,IMU 传感器通道里留下了撞击的加速度尖峰。这一步是后面所有实验的对照组:证明接下来机器人做出的任何动作,都来自我们的控制,而不是动画。
四、让它站住:22 关节 PD 位置伺服
4.1 控制律:位置伺服 + 官方力矩限幅
_mc 版的 22 个 motor 是力矩执行器(输入目标力矩),所以要让它"站住"得自己写位置伺服:PD 控制律 + 官方力矩限幅。
Python
tau = np.clip(KP * (q_ref - q) - KD * v, -40.0, 40.0)
d.ctrl[:] = tau # KP=150, KD=8(实验值)
mujoco.mj_step(m, d) # 1kHz 推进
4.2 站立实测:5 秒波动 ±0.0000 米
以初始姿态为目标跑 5 秒,结果(experiments/02_fall_and_stand.py):
- 骨盆高度稳定在 0.419 m,5 秒内波动 ±0.0000 m
- 关节力矩均值 0.09 Nm、峰值 0.37 Nm------40 Nm 上限的 1% 不到

PD 控制闭环:读状态 → 算误差 → PD 律+限幅 → 力矩执行 → 物理引擎推进,1kHz 循环。底部是五组实验跑出来的实测账单。
一个直观的硬件洞察:静态双足站立对 Q1 这个体型(16.92 kg、腿长约 0.4 m)来说是"低价"任务------重心落在支撑面内时,22 个执行器合起来出力不到 1%。执行器 40Nm 的设计余量,显然是给动态行为(走跑、平衡恢复)留的。

PD 位置伺服下的 Q1 站立帧:物理仿真中的机器人由 22 个力矩执行器托住,骨盆 5 秒波动 ±0.0000m。
五、让它挥手:指令平滑是第一课
站立时给右臂一条"挥手"目标轨迹(肩抬起 + 肘部 1.2Hz 正弦摆动),做了一组很说明问题的对照:
5.1 反面教材:目标跳变,执行器瞬时饱和
目标角在 t=0 直接跳变 +0.6 rad:
Plaintext
全身力矩峰值 40.00 Nm(= 执行器上限,瞬时饱和)
kp×瞬时误差直接超限,肩肘执行器顶格输出。真机上这种饱和轻则减速器异响,重则过流保护。
5.2 正面版:smoothstep 平滑,38 倍改善
同一轨迹套 3 秒 smoothstep 缓动 + 挥动分量乘爬升系数:
Plaintext
力矩峰值 1.05 Nm(38 倍改善),骨盆纹丝不动(0.4194 ±0.0000 m)
同一条轨迹,只差一个"平滑",力矩账单差 38 倍。给机器人的目标永远不要跳变 ------这一课在 ROS 速度控制里叫"平滑给速",在轨迹规划里叫 Ruckig(SDK 的 joint_control 示例用它),在 MJCF 实验里叫 smoothstep:同一件事的三种说法。


平滑轨迹挥手的物理仿真实录:3 秒缓动抬臂后 1.2Hz 摆肘,全程力矩峰值 1.05Nm(执行器上限的 2.6%),骨盆纹丝不动。
六、推它一把:位置 PD 的抗扰边界
站立+挥手的机器人都稳,但那是"没人惹它"。
6.1 推力梯度:30N 站稳,40N 摔倒
给骨盆施加 0.5 秒侧向推力,从 10N 到 40N 做梯度实验(experiments/04_smooth_wave_gradient.py):
| 推力 | 骨盆末高 | Y 向偏移 | 结果 |
|---|---|---|---|
| 10 N | 0.419 m | 0.001 m | 站稳 |
| 20 N | 0.419 m | 0.003 m | 站稳 |
| 30 N | 0.419 m | 0.017 m | 站稳 |
| 40 N | 0.131 m | 0.550 m | 摔了 |

40N 侧推 0.5 秒的物理仿真实录(与上表末行同一组实验):重心出支撑面后位置伺服无以为继,机器人侧倒------边界之外,是策略学习的领地。
6.2 边界的由来:位置 PD 是"锁死的刚性棍"
边界非常清晰:30N 以内位置 PD 稳如泰山(偏移不到 2cm),40N 一推就倒。原因也不复杂------位置 PD 本质是把 22 个关节锁死的"刚性棍",重心还在支撑面内时怎么推都弹得回来;重心一出支撑面,没有任何力会去移动脚步或调整躯干,只剩倒地。
实验脚本的终端原始输出数据:瘫倒对照、PD 站立、平滑挥手与推力梯度

这条边界恰好说明官方为什么要在 _mc 版里配齐执行器和传感器:站立是位置控制能解决的,平衡不是。让机器人在被推之后主动落步、调躯干重心------那是策略学习(强化学习)或全身控制的活,也是这套模型"适用于强化学习训练"的真正含义。这条线我们下篇继续。
七、接回 ROS:物理仿真进 RViz
MuJoCo 是一个孤立世界
7.1 桥的设计:1kHz 仿真 + 30Hz 发布
写一座桥(mujoco_ros_bridge.py)把它接进 ROS2:1kHz 物理仿真循环内跑 PD 站立+挥手,同时以 30Hz 发布 /joint_states(22 关节)和 odom→pelvis TF(浮动基座的完整六自由度姿态,四元数记得从 MuJoCo 的 wxyz 转成 ROS 的 xyzw)。
7.2 双发布者陷阱:让出 /joint_states
RViz 侧复用 q1_simulator 仓库的 launch------但注意用上新加的开关:
Bash
# 终端 1:RSP + RViz(simulator:=false 让出 /joint_states,避免双发布者打架)
ros2 launch q1_simulator viz.launch.py simulator:=false
# 终端 2:起桥
python3 mujoco_ros_bridge.py

RViz 里渲染的不再是之前的关节镜像,而是 MuJoCo 物理引擎驱动的 Q1 :挥手时右臂摆动来自物理仿真,浮动基座姿态实时跟随 TF。配合桥做冒烟验证时注意 ros2 topic info /joint_states 的 Publisher count 必须是 1------多一个发布者画面就会跳变(q1_simulator 的 launch 加 simulator 参数正是为此)。

形成一条完整的仿真-开发链:MuJoCo(物理与策略) ↔ ROS2(接口协议) ↔ RViz(可视化),真机在最右端再挂一个真实的运控板即可。
八、交互 viewer:推倒实战
配套的交互演示(viewer_demo.py)运行后弹出 MuJoCo 官方 viewer:
Bash
python3 viewer_demo.py # 站立 + 挥手
python3 viewer_demo.py --stand # 纯站立,更好推着玩
除了观看,按住 Ctrl + 鼠标左键拖动机器人可以直接施加推力------轻推它会弹回来(第六节 30N 以内的行为),用力一甩就摔(40N 以上)。亲手复现一遍推力梯度,大概是理解位置 PD 边界最直观的方式。

踩坑 :初版演示用了 mujoco.viewer.launch(m, d)------窗口正常弹出、机器人却"从站立到往后倒",每次必倒;同一套 PD 在 headless 下稳如泰山(骨盆 0.419m 零波动),一度怀疑 WSLg 图形环境拖垮了物理。真相比想象的朴素:launch() 是 blocking 自主模式 ,viewer 自己接管物理步进,写在 with 块里的控制循环根本没执行,窗口里看到的是零控制下的自然瘫倒(恰好复现了实验 1);launch_passive() 才是"用户步进 + viewer.sync()"的协作模式。改用 passive 后同一套 PD 在窗口里稳稳站立,与 headless 严格一致。教训一句话:viewer 里看到"控制失效",先确认 launch_passive****,再怀疑环境。另有一个已知小瑕疵:WSLg 下窗口销毁阶段偶发 Segmentation fault(GL 清理问题,不影响仿真与数据)。
九、从仿真到真机:对应关系逐条清点
9.1 三阶段路径
仿真教程绕不开的问题:这套东西和真机什么关系?逐项对应如下(详见配套仓库 README):

三阶段路径与逐项对应关系:本文完成第一阶段;策略升级(RL/全身控制)是下一站;真机部署需 ARM 交叉编译、开发者模式低层授权与安全员机制。
9.2 迁移要点清单
几条迁移要点,仿真里容易忽略的都列出来:仿真步长 1kHz 与真机 HAL 关节指令标称值一致,但 PC 侧二开实测约 500Hz(低于 100Hz 触发硬件 Watchdog);真机关节指令是 stiffness/damping 两套增益语义,与仿真的裸力矩不同,迁移时需要换算核对;仿真没有电机温升、减速器背隙和通信延迟,真机全都有------策略上真机前先把增益降 20--30% 再渐进上调,并保持手柄(优先级 80)随时接管。真机部署本身走 SDK 的 C++ 路线(ARM 交叉编译到运控板),C++ 侧的全量编译与示例实战社区已有实测文章可参考。
十、总结
这套官方 MJCF 模型给人的感觉是"被认真准备过但一直没人用":惯量完整、力矩限幅分层、传感器按 RL 观测空间配置、连 1kHz 步长都调好了。本文把它跑了起来,并把几条可以带走的结论放在这:位置 PD 够它站立和挥手,但抗扰边界在 30--40N 之间;目标指令永远要平滑(40Nm 饱和与 1.05Nm 平顺之间只差一个 smoothstep);物理仿真和 ROS 开发链可以低成本打通。下一步是在这套模型上训练平衡策略------观测空间和动作空间官方都配好了,缺的只是训练循环。
参考资料
- 启元机器人开发者社区(q1_v3 模型包下载):https://dev.primebot.com/
- 《PrimeBot SDK 快速入门与集成指南》v1.0.0.0,SDK 包内文档(关节指令频率与 HAL 接口定义)
- MuJoCo Documentation: https://mujoco.readthedocs.io/(mujoco 3.16.0)
- 本文配套仓库 q1_mujoco_lab:https://atomgit.com/user3401/q1_mujoco_lab