上一节,我们在纸面上理解了执行器、编码器、关节和自由度。但纸面上的理解终究是隔着一层的。你知道了电机是肌肉,但你还从来没有亲手让一块“肌肉”收缩过。
这一节,我们要做一件激动人心的事情:在仿真环境里,唤醒你的第一个机器人。
别担心硬件成本。不需要花一分钱买机械臂,不需要申请实验室权限,甚至不需要担心机器人摔坏了怎么办——仿真的世界里,摔坏了按下重置键就好。你需要的,只是一台装好了仿真环境的电脑,以及一颗准备好动手的心。
初学者常常有一个执念:我要摸到真机才算做机器人。
但真相是,今天全球顶尖的机器人实验室,绝大多数工作都是在仿真里先跑通的。原因很简单:仿真让你失败得起。 真实机器人的一个控制参数设错了,它可能一头撞上墙壁,维修费几百上千。而在仿真里,撞了就撞了,你可以在十分钟内尝试几十组参数,快速迭代。这种高频率的试错反馈,是真实硬件永远无法提供的学习体验。
更重要的是,仿真器和真实物理引擎的差距正在快速缩小。MuJoCo、Isaac Sim这些平台,已经能够相当精确地模拟接触力学、摩擦力、惯性等物理效应。你在仿真里训练出来的行走策略,有相当的概率迁移到真实机器人上依然管用。
所以,放下对真实硬件的执念。你接下来的第一个机器人,将是虚拟的——但它身体里的每一个关节、每一根连杆,遵循的都是和你我一样真实的物理定律。
市面上的机器人仿真平台有好几个,各有侧重。PyBullet轻量入门,Isaac Sim画面精美但需要好显卡,Gazebo和ROS生态深度绑定。而我们的教程选择MuJoCo作为主战场。
原因有三个。第一,它是DeepMind和很多顶尖具身智能实验室的首选平台,你学到的技能直接对标前沿研究。第二,它的物理引擎精度很高,尤其在接触力模拟方面表现优异。第三,它原生支持Python,API简洁,几分钟就能跑出第一个场景。
MuJoCo全称是Multi-Joint dynamics with Contact,意思就是“带接触的多关节动力学”。这个名字已经把它的核心能力交代清楚了:模拟多个关节连接起来的物体,在碰撞和摩擦中如何运动。
在继续之前,请确保你已经按附录的指南配置好了MuJoCo环境。如果你输入 import mujoco 没有报错,就可以继续了。
MuJoCo安装包里自带了好几个预置的机器人模型。这些模型都是用XML文件描述的,详细定义了每一根连杆的长度、质量、惯量,每一个关节的类型、运动范围、驱动力上限。你不需要自己从头建模——就像你不需要自己造一台真车才能学开车。
我们先选一个最简单的:双足机器人。在MuJoCo的模型库里,它叫 humanoid.xml。这个名字有点误导——它其实不是一个完整的人形机器人,而是一个简化版的双足行走模型,有躯干、大腿、小腿和脚掌,大约有十几个自由度。
打开你的Python编辑器,输入以下代码:
import mujoco
import mujoco.viewer
# 加载预置的双足机器人模型
model = mujoco.MjModel.from_xml_path('humanoid.xml')
data = mujoco.MjData(model)
# 启动交互式可视化窗口
with mujoco.viewer.launch_passive(model, data) as viewer:
while viewer.is_running():
# 步进仿真一步
mujoco.mj_step(model, data)
# 同步可视化
viewer.sync()
运行这段代码。你会看到一个窗口弹出来,里面站着一个灰白色的双足机器人。它有头部、躯干、两条手臂和两条腿,全身由大大小小的圆柱体和球体拼接而成,连接处标着彩色的旋转轴。
这个简陋的灰白色小人,就是你在具身智能世界里拥有的第一个身体。
试着用鼠标拖动视角——按住左键旋转,滚轮缩放,按住中键平移。你可以从任何角度观察它:俯视、仰视、从侧面看它的腿是怎么连接到大腿上的。这个简单的旋转观察动作,在真实机器人面前你永远做不到——你不可能悬浮到天花板上去俯视你的机械臂。
光看是不够的。我们要钻到机器人的“身体内部”,看看它到底有哪些关节。
在MuJoCo中,你可以通过编程接口遍历所有的关节定义。在刚才的代码里,仿真窗口还在运行的同时,我们另开一个Python脚本来检查模型结构:
import mujoco
model = mujoco.MjModel.from_xml_path('humanoid.xml')
print("=" * 50)
print("机器人关节清单")
print("=" * 50)
for i in range(model.njnt):
# 获取关节名称
name = model.jnt(i).name
# 获取关节类型(0=自由, 1=球铰, 2=滑移, 3=旋转铰)
jnt_type = model.jnt(i).type
# 获取关节的运动范围
range_min = model.jnt(i).range[0] if model.jnt(i).range is not None else "无限制"
range_max = model.jnt(i).range[1] if model.jnt(i).range is not None else "无限制"
type_names = {0: "自由关节", 1: "球铰关节", 2: "滑移关节", 3: "旋转关节"}
type_str = type_names.get(jnt_type, "其他类型")
print(f"关节 {i}: {name}")
print(f" 类型: {type_str}")
print(f" 运动范围: [{range_min}, {range_max}]")
print()
print(f"机器人的总自由度(总关节驱动数): {model.nv}")
你会看到类似这样的输出:
==================================================
机器人关节清单
==================================================
关节 0: root
类型: 自由关节
运动范围: [无限制, 无限制]
关节 1: hip_y_r
类型: 旋转关节
运动范围: [-1.22173, 0.523599]
关节 2: hip_x_r
类型: 旋转关节
运动范围: [-0.523599, 0.174533]
关节 3: hip_z_r
类型: 旋转关节
运动范围: [-0.785398, 0.436332]
...
让我们解读这些信息。
root 是一个特殊的“自由关节”。它不是机器人身上的某个具体旋转轴,而是整个机器人在空间中的6自由度位姿——$(x, y, z)$ 位置加上三个旋转角。它让机器人可以在空间中自由移动,而不仅仅是在原地做动作。
后面那些 hip_y_r、hip_x_r、hip_z_r 是真正的旋转关节,它们长在机器人的髋部——也就是大腿根。r 后缀表示右腿,相应地还有 _l 后缀的左腿关节。y、x、z 表示旋转轴的方向。三条旋转轴交汇在髋部,构成了一个3自由度的球形关节群,让大腿可以前后摆、左右开合、内外旋转。
顺着大腿往下,你会看到膝关节 knee 和踝关节 ankle,通常各有1到2个自由度。一条腿从头到尾大约有5到6个独立驱动的关节。两条腿加一个躯干,总共大约十几个驱动自由度,再加上代表全局位姿的自由关节,整个系统的状态空间维度通常是几十维。
每一个你看到的关节名称,在后面的控制章节里,你都会直接向它发送角度指令。 当你的代码说“让 knee_r 转到30度”,那条右腿的膝关节就会真的弯下去。你即将亲身体验这种造物主一般的操控感。
除了双足,仿真环境里往往还预置了轮式机器人模型。MuJoCo自带一个 car.xml(或者你可以找开源社区的差分驱动小车模型),它的身体结构比双足简单得多:一个底盘、四个轮子、几个旋转关节驱动车轮转动。
轮式机器人的自由度比双足少很多。它不需要操心平衡、步态、脚掌落地检测这些复杂的动力学问题。它只需要控制轮子的转速差——左轮比右轮快就右转,右轮比左轮快就左转,两轮同速就直行。
在你的学习路径上,我建议你先玩双足,再碰轮式。双足的复杂度会让你全面理解关节、力矩和平衡控制,这些概念搞懂了,轮式只是减法。如果一上来就玩轮式,你会错过很多核心的动力学直觉。
当然,如果你对轮式更感兴趣,不妨自己去探索MuJoCo模型库里的其他模型。mjModel 的 from_xml_path 函数可以加载任何合法格式的模型文件,你完全可以去GitHub上找开源的机械臂模型、四足狗模型、甚至人形机器人全尺寸模型,放进你自己的仿真场景里。
在进入下一节之前,请你完成以下三个任务:
这三个任务都不需要你写复杂的代码。它们的目标只有一个:让你建立和这具虚拟身体的熟悉感。 你越熟悉它,后面操控它的时候就越得心应手。
这一节,你迈出了从“纸上谈兵”到“上手操作”的关键一步。你在MuJoCo仿真环境里唤醒了第一个虚拟机器人,观察了它的关节结构,读出了它的自由度清单。你没有花一分钱,没有任何硬件会摔坏,但你拥有了一具随时可以操作的机器人身体。
下一节,我们要让这具身体真正动起来。你将亲手写代码,向膝关节、髋关节发送角度指令,看到机器人在仿真里做出挥手、抬腿、下蹲的动作。造物主的时刻,即将到来。