模块 2:认识 T800 机器人仿真模型

第一章:T800中的Link(body)和Joint(joint)

第二章:代码实操

第三章:MujocoAPI介绍


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言

第一章:T800中的Link(body)和 Joint(joint)

        1.1 Link /body(刚体连杆)

        1.2 Joint(关节)

        1.3T800 机器人树形结构简单示意

        1.4编程要点

第二章:代码实操

        2.1打印机器人所有关节信息

        2.2打印机器人所有连杆信息

第三章:MujocoAPI

        3.1两大核心对象

                3.1.1. MjModel(静态模型)

                3.1.2. MjData(动态数据)

        3.2模型加载类 API

                3.2.1. from_xml_path(加载本地机器人模型)

                3.2.2. MjData(model)(初始化仿真状态)

        3.3仿真核心 API

                3.3.1. mujoco.mj_step(model, data)

                3.3.2. viewer.sync()

        3.4名称与索引互查 API

                3.4.1. mj_name2id(通过名称查索引|最常用)

                3.4.2. mj_id2name(通过索引查名称|调试用)

        3.5核心数据数组 API

                3.5.1. data.qpos 关节位置/姿态数组

                3.5.2. data.qvel 关节速度数组

                3.5.3. data.ctrl 控制指令数组

        3.6模型维度参数 API

        3.7关节偏移查询 API

                3.7.1. model.jnt_qposadr

                3.7.2. model.jnt_dofadr

总结


前言

上一节课我们成功把众擎 T800 人形机器人 MJCF 模型加载进 MuJoCo 仿真环境,了解了MjModel静态模型、MjData动态数据对象,学会了基础参数打印。

本节课重点搞懂 MuJoCo 仿真里面最核心两个概念:link(body 刚体连杆)joint(关节)。这是理解人形机器人运动学的基石,后续读取关节角度、写 PD 控制器、设计强化学习观测空间全部建立在这两个概念之上。

前置环境:已经激活t800conda 虚拟环境,T800 的 xml 模型与 mesh 网格文件完整。


提示:以下是本篇文章正文内容,下面案例可供参考

第一章:T800中的Link(body)和 Joint(joint)

在 MuJoCo MJCF 模型语法中,body 就等价于我们常说的 link(连杆)

1.1 Link /body(刚体连杆)

  • body(连杆):机器人物理刚体单元,对应现实机器人的躯干、大腿、小腿、上臂、小臂等实体零部件;它保存质量、惯性物理属性。
  • geom几何体依附在 body 上面,决定这个连杆长什么样子、碰撞边界;body 本身没有外观。
  • MuJoCo 模型是树形嵌套结构:子 body 写在父 body 标签内部,表达父子从属关系,类似 ROS 的 TF 树。

类比人体:每一块骨头就对应一个 body(link)。

1.2 Joint(关节)

  • joint 写在子 body 内部,用来定义「子 body 相对于父 body」可以怎么运动;如果 body 内部不写 joint,那么这个刚体就会被焊死在父刚体上,完全不能动。
  • joint 决定自由度:铰链hinge只能绕轴旋转;free自由关节给机身根部 6 个自由度(3 平移 + 3 旋转)。

类比人体:骨头和骨头中间的韧带、转动部位就是 joint 关节。

⚠️重要区分(新手极易混淆)

  • body(link):刚体零件,有质量惯性
  • joint:运动约束,负责产生相对运动,本身没有质量

1.3T800 机器人树形结构简单示意

plaintext

worldbody(仿真世界根节点)
└── root_body(机器人根刚体,free自由关节)
    ├── torso 躯干(body)
    │   ├── head 头部(body)
    │   ├── left_arm 左手臂(body)
    │   └── right_arm 右手臂(body)
    ├── left_leg 左腿(body)
    └── right_leg 右腿(body)

整个机器人就是一棵运动学树,一层一层嵌套。

1.4编程要点

操作 T800 机器人关节,一律使用名称查找 ID,禁止硬编码数字下标,提升代码健壮性,适配后续模型迭代修改。

在操作 T800 机器人关节的时候,我们会遇到一个非常典型的开发痛点:人认识关节名字,但是 MuJoCo 底层只认数字索引

从示意图可以看到:

  • 程序员视角:我们可以直观理解语义名称,比如右肩关节,需求是 “抬起右胳膊”,可读性很高,符合人的思维。
  • 计算机底层视角:程序内部没有 “右肩关节” 这个文字概念,它只识别数字编号,例如关节#17,通过数组下标去读写角度、施加力矩。

很多新手会直接把数字索引写死在代码里面,例如直接写data.qpos[17]去操作右肩关节。

❗这是一个高风险错误写法。

如果后续 T800 的 MJCF (XML) 模型文件发生改动:调整关节定义顺序、新增 / 删除连杆关节,所有关节的数字索引就会全部发生偏移错乱。原本的关节#17不再对应右肩关节,控制逻辑全部失效,而且这种 bug 很难排查。

MuJoCo 给出的解决方案:名称与索引互相转换 API:不要手动记忆、硬写数字下标,使用mj_name2id()通过关节名称反向查询运行时索引。不管模型内部顺序如何变化,只要关节名称不变,代码就可以正常工作。

import mujoco

# 通过关节名字获取id,解耦名字和数字编号
right_shoulder_id = mujoco.mj_name2id(
    model,
    mujoco.mjtObj.mjOBJ_JOINT,
    "right_shoulder"
)
# 获取该关节在qpos数组中的偏移
qpos_idx = model.jnt_qposadr[right_shoulder_id]
# 读取关节角度
shoulder_angle = data.qpos[qpos_idx]

与之对应的反向接口mj_id2name(),可以通过索引拿到关节名称,用于打印日志调试。

第二章:代码实操

2.1打印机器人所有关节信息

提示语:帮我写一个脚本05_T800_joint.py,打印t800机器人的所有关节

生成的代码如下

"""
05_T800_joint.py
打印 T800 机器人的所有关节信息(名称、类型、所属 body、qpos 起始索引与维度)
运行: D:/miniconda3/envs/t800/python.exe 05_T800_joint.py
"""
import mujoco

model = mujoco.MjModel.from_xml_path("t800.xml")

# 关节类型 -> (名称, qpos维度)
JOINT_TYPE = {
    mujoco.mjtJoint.mjJNT_FREE:  ("free",  7),
    mujoco.mjtJoint.mjJNT_BALL:   ("ball",  4),
    mujoco.mjtJoint.mjJNT_SLIDE:  ("slide", 1),
    mujoco.mjtJoint.mjJNT_HINGE:  ("hinge", 1),
}

print(f"关节总数: {model.njnt}")
print("-" * 72)
print(f"{'索引':>4} {'名称':<30} {'类型':<6} {'所属 body':<18} {'qpos_adr':>8} {'qpos_dim':>8}")
print("-" * 72)

for i in range(model.njnt):
    name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_JOINT, i) or "(无名)"
    jt = int(model.jnt_type[i])
    jtype_name, qpos_dim = JOINT_TYPE.get(jt, (str(jt), 0))
    body = model.body(int(model.jnt_bodyid[i])).name
    qposadr = int(model.jnt_qposadr[i])
    print(f"{i:>4} {name:<30} {jtype_name:<6} {body:<18} {qposadr:>8} {qpos_dim:>8}")

执行下列命令运行代码

(base) C:\Users\qx>conda activate t800      ##激活环境

(t800) C:\Users\qx>cd Desktop\T800\code2    ##跳转到代码存储位置

(t800) C:\Users\qx\Desktop\T800\code2>python 05_T800_joint.py    #运行代码

从打印信息可以看出有25个关节,T800一共是有26个关节,其中只有25个主动关节可以供我们使用进行二次开发。下文的单词需要认识。

关节英文名称中文释义
J00_HIP_PITCH_L左髋关节‑俯仰
J01_HIP_ROLL_L左髋关节‑横滚
J02_HIP_YAW_L左髋关节‑偏航
J03_KNEE_PITCH_L左膝关节‑俯仰
J04_ANKLE_PITCH_L左踝关节‑俯仰
J05_ANKLE_ROLL_L左踝关节‑横滚
J06_HIP_PITCH_R右髋关节‑俯仰
J07_HIP_ROLL_R右髋关节‑横滚
J08_HIP_YAW_R右髋关节‑偏航
J09_KNEE_PITCH_R右膝关节‑俯仰
J10_ANKLE_PITCH_R右踝关节‑俯仰
J11_ANKLE_ROLL_R右踝关节‑横滚
J12_TORSO_YAW躯干腰部‑偏航
J13_SHOULDER_PITCH_L左肩关节‑俯仰
J14_SHOULDER_ROLL_L左肩关节‑横滚
J15_SHOULDER_YAW_L左肩关节‑偏航
J16_ELBOW_PITCH_L左肘关节‑俯仰
J17_ELBOW_YAW_L左肘关节‑偏航
J18_SHOULDER_PITCH_R右肩关节‑俯仰
J19_SHOULDER_ROLL_R右肩关节‑横滚
J20_SHOULDER_YAW_R右肩关节‑偏航
J21_ELBOW_PITCH_R右肘关节‑俯仰
J22_ELBOW_YAW_R右肘关节‑偏航
J23_HEAD_PITCH头部‑俯仰
J24_HEAD_YAW头部‑偏航
英文中文说明
PITCH俯仰关节向前 / 向后摆动
ROLL横滚关节向左右翻转
YAW偏航关节水平旋转(左右转头 / 转胯)
LLeft机器人左侧
RRight机器人右侧
HIP髋关节大腿根部关节
KNEE膝关节膝盖关节
ANKLE踝关节脚踝关节
SHOULDER肩关节大臂根部关节
ELBOW肘关节胳膊肘关节
TORSO躯干 / 腰部躯体腰部关节
HEAD头部头部关节

2.2打印机器人所有连杆信息

提示语:帮我写一个脚本06_T800_body.py,打印t800机器人的所有连杆,包括名字、尺寸、重量信息,格式化输出

"""
06_T800_body.py
打印 T800 机器人的所有 body(连杆)信息(含质量、惯性、子连杆数、几何体数)
运行: D:/miniconda3/envs/t800/python.exe 06_T800_body.py
"""
import mujoco
import numpy as np

model = mujoco.MjModel.from_xml_path("t800.xml")

# 预计算每个 body 的子连杆数和几何体数
child_count = np.zeros(model.nbody, dtype=int)
for i in range(model.nbody):
    pid = int(model.body_parentid[i])
    if pid >= 0:
        child_count[pid] += 1

body_geomnum = np.zeros(model.nbody, dtype=int)
for i in range(model.nbody):
    body_geomnum[i] = int(model.body_geomnum[i])

# 总质量(除 world)
total_mass = float(np.sum(model.body_mass[1:]))

print(f"body(连杆)总数: {model.nbody}   总质量: {total_mass:.3f} kg")
print("-" * 92)
print(f"{'#':>3}  {'名称':<28} {'父 body':<18} {'质量/kg':>8} "
      f"{'子连杆':>6} {'几何体':>6} {'惯性 Ixx Iyy Izz':>24}")
print("-" * 92)

for i in range(model.nbody):
    name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i) or "(world)"
    parent_id = int(model.body_parentid[i])
    parent = (mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, parent_id)
              or "(world)") if parent_id >= 0 else "(无)"
    mass = float(model.body_mass[i])
    inertia = model.body_inertia[i]
    print(f"{i:>3}  {name:<28} {parent:<18} {mass:>8.4f} "
          f"{child_count[i]:>6} {body_geomnum[i]:>6} "
          f"{inertia[0]:>8.5f} {inertia[1]:>8.5f} {inertia[2]:>8.5f}")

连杆英文名称中文释义
world世界根坐标系(仿真全局世界)
LINK_BASE机身基座(整机根连杆)
LINK_HIP_PITCH_L左髋关节‑俯仰连杆
LINK_HIP_ROLL_L左髋关节‑横滚连杆
LINK_HIP_YAW_L左髋关节‑偏航连杆
LINK_KNEE_PITCH_L左膝关节‑俯仰连杆
LINK_ANKLE_PITCH_L左踝关节‑俯仰连杆
LINK_ANKLE_ROLL_L左踝关节‑横滚连杆
LINK_FOOT_L左脚足部连杆
LINK_HIP_PITCH_R右髋关节‑俯仰连杆
LINK_HIP_ROLL_R右髋关节‑横滚连杆
LINK_HIP_YAW_R右髋关节‑偏航连杆
LINK_KNEE_PITCH_R右膝关节‑俯仰连杆
LINK_ANKLE_PITCH_R右踝关节‑俯仰连杆
LINK_ANKLE_ROLL_R右踝关节‑横滚连杆
LINK_FOOT_R右脚足部连杆
LINK_WAIST_YAW腰部偏航躯干连杆
LINK_SHOULDER_PITCH_L左肩关节‑俯仰连杆
LINK_SHOULDER_ROLL_L左肩关节‑横滚连杆
LINK_SHOULDER_YAW_L左肩关节‑偏航连杆
LINK_ELBOW_PITCH_L左肘关节‑俯仰连杆
LINK_ELBOW_YAW_L左肘关节‑偏航连杆
LINK_WRIST_END_L左手腕末端连杆
LINK_SHOULDER_PITCH_R右肩关节‑俯仰连杆
LINK_SHOULDER_ROLL_R右肩关节‑横滚连杆
LINK_SHOULDER_YAW_R右肩关节‑偏航连杆
LINK_ELBOW_PITCH_R右肘关节‑俯仰连杆
LINK_ELBOW_YAW_R右肘关节‑偏航连杆
LINK_WRIST_END_R右手腕末端连杆
LINK_HEAD_PITCH头部‑俯仰连杆
LINK_HEAD_YAW头部‑偏航连杆
英文关键词中文含义说明
BASE基座机器人整机根部主体连杆
HIP髋关节大腿根部
KNEE膝关节膝盖部位
ANKLE踝关节脚踝部位
FOOTfoot足部
WAIST腰部躯干腰段
SHOULDER肩关节大臂根部
ELBOW肘关节胳膊肘
WRIST_END腕部末端手腕末端虚拟连杆,常用于末端点位观测
HEAD头部机器人头部
PITCH俯仰前后摆动
ROLL横滚左右翻转
YAW偏航水平旋转
LLeft机器人本体左侧
RRight机器人本体右侧

第三章:MujocoAPI

MuJoCo 底层基于 C 语言高性能物理引擎,Python 版本是官方原生轻量绑定,所有 API 命名统一 mj 前缀,结构清晰、学习成本极低。不同于传统深度学习框架,MuJoCo API 完全围绕「物理仿真」设计,所有功能只分为三大类:

  • 模型加载 API:读取机器人 MJCF/XML 模型,生成静态结构数据

  • 仿真步进 API:物理解算、状态更新、动力学计算

  • 信息查询 API:通过名称/索引获取机器人关节、连杆、姿态数据

本章节全部为T800 机器人开发实战高频API,是后续关节控制、姿态解算、强化学习观测设计的基础。

3.1两大核心对象

MuJoCo 所有操作都围绕两个对象,也是我们项目代码的固定模板:

3.1.1. MjModel(静态模型)

只读、固定不变,代表机器人的「硬件图纸」。

存储:连杆结构、关节数量、关节限位、电机参数、质量、惯性、碰撞参数等固定信息。

3.1.2. MjData(动态数据)

每帧持续更新,代表机器人「实时状态」。

存储:关节角度、角速度、机身位置、速度、控制力矩、接触力等动态数据。

核心口诀:Model 管结构,Data 管状态

3.2模型加载类 API

3.2.1. from_xml_path(加载本地机器人模型)

最常用、项目必备,读取本地 T800.xml 模型文件。

model = mujoco.MjModel.from_xml_path("t800.xml")

作用:解析 MJCF 模型,生成完整机器人结构树(连杆、关节、执行器)。

3.2.2. MjData(model)(初始化仿真状态)

data = mujoco.MjData(model)

根据模型结构,自动初始化 qpos、qvel、ctrl 等状态数组

3.3仿真核心 API

3.3.1. mujoco.mj_step(model, data)

整个仿真项目最重要的函数

每调用一次,引擎完成一次完整物理解算:

  • 重力计算

  • 碰撞检测

  • 动力学更新

  • 更新 qpos、qvel、机身姿态

人形机器人行走、平衡、动态全部依靠该函数迭代。

3.3.2. viewer.sync()

同步渲染画面,保证仿真画面与物理计算同步。

3.4名称与索引互查 API

这一组 API 彻底解决新手写死关节下标导致模型更新就崩代码的问题。

3.4.1. mj_name2id(通过名称查索引|最常用)

根据关节/连杆的英文名字,自动获取运行时索引,代码永久兼容

joint_id = mujoco.mj_name2id( model, mujoco.mjtObj.mjOBJ_JOINT, "J00_HIP_PITCH_L" )

支持查询:关节、连杆、执行器、传感器等所有对象。

2. mj_id2name(通过索引查名称|调试用)

遍历打印所有关节、连杆名称,用于项目结构查看、日志输出。

name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_JOINT, idx)

3.5核心数据数组 API

T800 所有控制、观测全部基于以下三个数组:

3.5.1. data.qpos 关节位置/姿态数组

存储:机身位置 + 机身四元数 + 所有关节角度。

长度对应 model.nq,MuJoCo 全部使用弧度单位。

3.5.2. data.qvel 关节速度数组

存储:机身移动速度、旋转速度、各关节角速度。

长度对应 model.nv。

3.5.3. data.ctrl 控制指令数组

机器人控制唯一入口

我们写的 PD 控制、AI 策略输出的力矩,全部写入 data.ctrl,下发给机器人电机。

长度对应 model.nu(执行器数量)

3.6模型维度参数 API

这组参数对应你前面的 T800 结构图,是观测空间、算法维度设计依据:

  • model.nq:广义位置维度(姿态总参数数量)

  • model.nv:广义速度维度(运动自由度总数)

  • model.njnt:可控关节总数

  • model.nbody:机器人连杆刚体总数

  • model.nu:可控电机执行器数量

3.7关节偏移查询 API

3.7.1. model.jnt_qposadr

获取指定关节在 qpos 数组中的下标偏移,精准读取单个关节角度。

3.7.2. model.jnt_dofadr

获取指定关节在 qvel 数组中的下标偏移,读取单个关节角速度。

✅ 规范写法:通过名称查 ID → 通过 ID 查偏移 → 读取角度/速度


总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

Logo

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

更多推荐