MuJoCo vs Gazebo vs PyBullet:机器人仿真选型与UR5e实战
2026/9/20 1:05:41 网站建设 项目流程

1. 为什么我最终选择了 MuJoCo 而不是 Gazebo 或 PyBullet

1.1 从一次机械臂抓取实验的崩溃说起

去年做 UR5e 的抓取策略训练时,我一开始用的是 Gazebo。场景搭好、控制器调通、抓取成功率在仿真里跑到 85%,结果一上真机直接掉到 30% 以下。排查了两周才发现问题出在接触力模型上——Gazebo 默认的 ODE 求解器在处理刚性接触时,穿透深度和恢复系数的组合跟真实橡胶夹爪差异太大,仿真里"捏住"的物体在真机上要么滑脱要么被弹飞。

那次之后我开始认真对比几个主流物理引擎。PyBullet 上手最快,但它的接触求解精度在高速碰撞场景下衰减明显;Gazebo 生态最全,但物理精度和实时性的平衡一直是个痛点。最后转向 MuJoCo,核心原因是它的软接触模型(soft contact model)和凸优化求解器在处理机械臂抓取、足式机器人落地这类"接触密集"场景时,稳定性和物理一致性明显更好。

MuJoCo 最早是华盛顿大学 Emo Todorov 团队开发的,2021 年被 DeepMind 收购后开源,现在是 Apache 2.0 协议,商用也没问题。它的定位很明确:面向机器人控制与强化学习的高精度物理仿真。不是游戏引擎,不追求画面,追求的是动力学计算的准确性和速度。

1.2 MuJoCo、Gazebo、PyBullet 的选型对照

选仿真平台这件事,没有"最好",只有"最合适"。我把三个平台在几个关键维度上的实际表现整理了一下,供你参考:

维度MuJoCoGazeboPyBullet
接触求解精度高(软接触+凸优化)中(ODE默认参数需调)中高(取决于求解器配置)
仿真速度(UR5e场景)约 8000-12000 步/秒约 1000-3000 步/秒约 3000-6000 步/秒
建模格式MJCF(XML)SDF/URDFURDF/MJCF
ROS 集成通过 mujoco_ros 桥接原生支持通过 pybullet_ros
强化学习生态极好(dm_control、Gymnasium)一般好(Gymnasium)
学习曲线中等(MJCF 需熟悉)较陡(依赖多)平缓
渲染质量中(但够用)

如果你的核心需求是控制算法验证强化学习训练,MuJoCo 的性价比最高。如果是要做多机器人系统集成传感器仿真(激光雷达、深度相机噪声模型),Gazebo 的生态更成熟。PyBullet 适合快速原型验证,但别指望它在接触密集场景下给你高保真结果。

1.3 MJCF 建模格式:看起来麻烦,用起来真香

MuJoCo 用自己的 MJCF 格式描述模型,本质上是 XML。很多人第一反应是"为什么不用 URDF",我一开始也这么想。但用久了发现 MJCF 有几个设计确实更合理:

  • 关节和几何体可以内联定义,不需要像 URDF 那样把 link 和 joint 拆成平行结构,可读性好很多。
  • 支持默认类(default class),同一类物体的摩擦、阻尼、接触参数可以统一设置,改一处全生效。
  • 执行器(actuator)和传感器(sensor)直接写在模型里,不需要额外的配置文件。
  • 支持程序化生成,可以用 Python 脚本批量构建复杂场景。

当然,URDF 转 MJCF 也有现成工具,后面会讲。但如果你是从零搭一个实验,我建议直接用 MJCF 写,省得转来转去丢信息。

2. 环境搭建:那些安装教程不会告诉你的细节

2.1 pip 安装 mujoco 之后为什么 import 报错

先说最省事的路径。MuJoCo 从 2.1 版本开始就不需要单独下载二进制包了,直接 pip 装:

pip install mujoco

但这里有个坑:Python 版本和 mujoco 版本的兼容性。我实测下来,mujoco 3.x 需要 Python 3.8 以上,3.10 和 3.11 最稳。如果你用的是 Python 3.12,某些旧版本的 mujoco 会报ImportError: DLL load failed或者undefined symbol

另一个常见问题是numpy 版本冲突。mujoco 依赖 numpy,但如果你环境里已经装了某个特定版本的 numpy(比如 TensorFlow 拉进来的),可能会出现 ABI 不兼容。解决办法是先建一个干净的虚拟环境:

python -m venv mujoco_env source mujoco_env/bin/activate # Windows 用 mujoco_env\Scripts\activate pip install mujoco numpy

提示:Windows 用户如果遇到mujoco.dll找不到的问题,大概率是 Visual C++ Redistributable 没装。去微软官网下载最新的 VC++ 运行库装上就行,这个在官方文档里提得很少,但踩坑率极高。

2.2 验证安装:跑通第一个仿真之前先做这三件事

装完之后别急着写代码,先做三个验证:

第一,检查版本和基本导入:

import mujoco print(mujoco.__version__)

第二,跑一个内置模型:

import mujoco import mujoco.viewer model = mujoco.MjModel.from_xml_string(""" <mujoco> <worldbody> <body> <geom type="sphere" size="0.1"/> <joint type="free"/> </body> </worldbody> </mujoco> """) data = mujoco.MjData(model) mujoco.viewer.launch(model, data)

如果弹出一个窗口,里面有个球在重力作用下掉落,说明安装没问题。

第三,检查渲染后端。MuJoCo 3.x 默认用 GLFW 做可视化,如果你在无头服务器上跑,需要设置:

export MUJOCO_GL=egl # 或者 osmesa

这个环境变量不设的话,在服务器上调用mujoco.viewer.launch会直接报错。我当初在 Ubuntu Server 上折腾了半天才想起来是这个问题。

2.3 目录结构:别把模型文件到处乱放

项目做大了之后,模型文件、脚本、日志混在一起是灾难。我现在的习惯是固定这样的结构:

project/ ├── models/ │ ├── ur5e/ │ │ ├── ur5e.xml │ │ └── assets/ │ └── scene.xml ├── scripts/ │ ├── run_sim.py │ └── controllers/ ├── logs/ └── requirements.txt

MJCF 文件里引用 mesh 或纹理时,路径是相对于 XML 文件所在目录的。所以assets文件夹放在模型同级目录下最省心。如果你用<include>标签引入其他 XML,路径规则也一样。

3. 用 MJCF 从零描述一个 UR5e 机械臂

3.1 UR5e 的运动学链拆解

UR5e 是六自由度串联机械臂,从基座到末端依次是:基座(base)→ 肩部(shoulder)→ 上臂(upper arm)→ 前臂(forearm)→ 腕部1(wrist 1)→ 腕部2(wrist 2)→ 腕部3(wrist 3)。六个关节都是旋转关节,其中肩部和腕部的轴线方向需要特别注意。

在 MJCF 里,每个 link 对应一个<body>,每个关节对应一个<joint>。关键在于关节轴方向body 原点的相对位置。UR5e 的 DH 参数网上能查到,但直接套 DH 参数到 MJCF 里容易出错,因为 MJCF 用的是相对父 body 的坐标系,不是 DH 的全局坐标系。

我的做法是:先确定每个关节的旋转轴在父 body 坐标系下的方向,再确定子 body 原点相对父 body 原点的偏移。以 UR5e 为例:

<mujoco model="ur5e"> <compiler angle="radian" meshdir="assets"/> <default> <joint damping="0.5" armature="0.01"/> <geom friction="1.0 0.005 0.0001" rgba="0.7 0.7 0.7 1"/> </default> <worldbody> <body name="base" pos="0 0 0"> <geom type="cylinder" size="0.075 0.05" pos="0 0 0.025"/> <body name="shoulder_link" pos="0 0 0.089"> <joint name="shoulder_pan_joint" type="hinge" axis="0 0 1" range="-6.28 6.28"/> <geom type="capsule" fromto="0 0 0 0 0 0.1" size="0.06"/> <body name="upper_arm_link" pos="0 0.138 0"> <joint name="shoulder_lift_joint" type="hinge" axis="0 1 0" range="-6.28 6.28"/> <geom type="capsule" fromto="0 0 0 0 0 0.425" size="0.05"/> <body name="forearm_link" pos="0 0 0.425"> <joint name="elbow_joint" type="hinge" axis="0 1 0" range="-3.14 3.14"/> <geom type="capsule" fromto="0 0 0 0 0 0.392" size="0.04"/> <body name="wrist_1_link" pos="0 0 0.392"> <joint name="wrist_1_joint" type="hinge" axis="0 1 0" range="-6.28 6.28"/> <geom type="capsule" fromto="0 0 0 0 0 0.095" size="0.035"/> <body name="wrist_2_link" pos="0 0 0.095"> <joint name="wrist_2_joint" type="hinge" axis="0 0 1" range="-6.28 6.28"/> <geom type="capsule" fromto="0 0 0 0 0 0.082" size="0.035"/> <body name="wrist_3_link" pos="0 0 0.082"> <joint name="wrist_3_joint" type="hinge" axis="0 1 0" range="-6.28 6.28"/> <geom type="capsule" fromto="0 0 0 0 0 0.082" size="0.03"/> <site name="ee_site" pos="0 0 0.082" size="0.01"/> </body> </body> </body> </body> </body> </body> </body> </worldbody> </mujoco>

这段代码里几个关键点:

  • axis指定旋转轴,UR5e 的肩部 pan 关节绕 Z 轴,lift 和 elbow 绕 Y 轴,腕部交替绕 Y 和 Z 轴。
  • pos是子 body 原点相对父 body 原点的偏移,不是 DH 参数里的ad直接照搬。
  • range是关节限位,UR5e 的实际限位是 ±2π 或 ±π,这里按实际值设置。
  • site用来标记末端执行器的位置,后面做 IK 和抓取时会用到。

3.2 用 URDF 转换:省事但要注意丢信息

如果你手头已经有 UR5e 的 URDF,可以用urdf2mjcf工具转:

pip install urdf2mjcf urdf2mjcf ur5e.urdf -o ur5e.xml

但转换之后一定要检查几件事:

  1. mesh 路径:转换工具可能把路径写成绝对路径,换台机器就跑不了。
  2. 关节阻尼和摩擦:URDF 里通常没有这些参数,转换后默认值是 0,仿真会抖得厉害。
  3. 碰撞几何体:URDF 的 collision 和 visual 是分开的,转换后要确认碰撞体没有用高精度 mesh,否则仿真速度会暴跌。

我一般转换完之后手动加<default>里的阻尼和摩擦参数,再把碰撞体换成简化几何(capsule 或 box),速度能提升 3-5 倍。

3.3 执行器配置:位置控制、速度控制还是力矩控制

MuJoCo 的 actuator 配置直接决定了你能用什么方式控制关节。UR5e 实验里我常用两种:

位置控制(适合轨迹跟踪):

<actuator> <position name="shoulder_pan_act" joint="shoulder_pan_joint" kp="2000" kv="100"/> <position name="shoulder_lift_act" joint="shoulder_lift_joint" kp="2000" kv="100"/> <!-- 其余关节类似 --> </actuator>

kp是位置增益,kv是速度阻尼。这两个值的选取有个经验公式:kv ≈ 2 * sqrt(kp * m),其中 m 是关节等效惯量。UR5e 的关节惯量大概在 0.5-2 kg·m² 之间,所以 kp 取 2000、kv 取 100 左右比较稳。kp 太小跟踪不上,太大直接发散。

力矩控制(适合强化学习):

<actuator> <motor name="shoulder_pan_act" joint="shoulder_pan_joint" gear="1" ctrlrange="-150 150"/> <!-- 其余关节类似 --> </actuator>

ctrlrange是力矩限幅,UR5e 各关节的最大力矩不同,肩部大概 150 N·m,腕部只有 28 N·m。这个值设错了,要么仿真里机械臂"软绵绵",要么直接飞出去。

注意:位置控制和力矩控制不能混用在同一个关节上。如果你需要切换,得在模型里定义两套 actuator,然后通过mujoco.mj_setActuator动态启用/禁用。

4. 正向运动学、逆向运动学与 IKPy 的配合

4.1 MuJoCo 自带的 FK:一行代码拿到末端位姿

正向运动学在 MuJoCo 里简单到离谱。仿真步进之后,直接读 site 或 body 的位置:

import mujoco import numpy as np model = mujoco.MjModel.from_xml_path("models/ur5e/ur5e.xml") data = mujoco.MjData(model) # 设置关节角度 data.qpos[:6] = [0.1, -1.2, 1.5, -0.3, 0.8, 0.0] mujoco.mj_forward(model, data) # 读取末端 site 的位姿 ee_pos = data.site("ee_site").xpos.copy() ee_mat = data.site("ee_site").xmat.reshape(3, 3).copy() ee_quat = np.zeros(4) mujoco.mju_mat2Quat(ee_quat, data.site("ee_site").xmat)

mj_forward会计算所有 body 和 site 的全局位姿,不需要手动做矩阵连乘。这是 MuJoCo 相比自己写 FK 最大的便利。

4.2 为什么 IK 不直接用 MuJoCo 的求解器

MuJoCo 本身没有内置的解析 IK 求解器。它有个mj_inverse可以做逆动力学,但不是逆运动学。所以做 IK 一般有两条路:

  1. 数值 IK:用 Jacobian 迭代,自己写或者用mink这类库。
  2. 解析 IK:用 IKPy 这类专门做 IK 的库。

IKPy 的优势是支持任意运动学链,你给它一个 URDF 或者自定义的链结构,它就能做数值 IK。缺点是它不感知 MuJoCo 的关节限位和碰撞,所以解出来的角度可能超出 UR5e 的实际范围。

我的做法是:用 IKPy 求初值,用 MuJoCo 做验证和限位裁剪

4.3 IKPy + MuJoCo 的完整闭环

先装 IKPy:

pip install ikpy

然后构建 IK 链。IKPy 可以直接读 URDF:

from ikpy.chain import Chain import numpy as np # 用 URDF 构建 IK 链 ik_chain = Chain.from_urdf_file( "models/ur5e/ur5e.urdf", base_elements=["base_link"], last_link_vector=[0, 0, 0.082], active_links_mask=[False, True, True, True, True, True, True] )

active_links_mask里第一个 False 是因为 base_link 是固定的,后面六个 True 对应六个关节。

求解 IK:

target_position = [0.3, 0.2, 0.5] target_orientation = [0, 0, 1, 0] # 四元数 ik_solution = ik_chain.inverse_kinematics( target_position, target_orientation=target_orientation, orientation_mode="all" ) joint_angles = ik_solution[1:7] # 去掉 base_link 对应的那个

拿到角度之后,别直接塞给 MuJoCo,先做限位检查:

def clamp_joints(angles, model, joint_names): clamped = [] for i, name in enumerate(joint_names): jnt_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, name) lo, hi = model.jnt_range[jnt_id] clamped.append(np.clip(angles[i], lo, hi)) return np.array(clamped)

然后再用 MuJoCo 做 FK 验证:

data.qpos[:6] = clamped_angles mujoco.mj_forward(model, data) actual_pos = data.site("ee_site").xpos error = np.linalg.norm(actual_pos - target_position) print(f"IK 误差: {error:.4f} m")

如果误差大于 1cm,说明 IKPy 的解在 MuJoCo 的模型里不准确,可能是 URDF 和 MJCF 的关节原点定义有差异。这时候要么调整 MJCF 的 body pos,要么用数值 IK 在 MuJoCo 里再迭代几步。

4.4 数值 IK 的补充方案:用 mink 做约束优化

IKPy 解不出来的情况(比如目标点接近奇异位形),我会用mink做补充。mink 是基于 MuJoCo 的微分 IK 库,支持关节限位、速度约束等:

import mink configuration = mink.Configuration(model) tasks = [ mink.FrameTask("ee_site", "site", position_cost=1.0, orientation_cost=1.0) ] tasks[0].set_target(mink.SE3.from_translation(target_position)) limits = [mink.ConfigurationLimit(model)] solver = "daqp" for _ in range(100): vel = mink.solve_ik(configuration, tasks, 0.01, solver, limits=limits) configuration.integrate_inplace(vel, 0.01) if np.linalg.norm(configuration.data.site("ee_site").xpos - target_position) < 1e-3: break

mink 的好处是它直接在 MuJoCo 的模型上做优化,解出来的角度天然满足限位和碰撞约束。缺点是速度比 IKPy 慢,适合做精细调整而不是实时控制。

5. 仿真发散的排查链路:从现象到根因

5.1 发散的第一表现:NaN 和爆炸的数值

仿真发散是 MuJoCo 新手最常遇到的问题。典型表现是:跑了几百步之后,data.qpos里出现nan,或者关节角度突然跳到 10^6 量级。这时候别慌,按下面的链路一步步排查。

第一步,确认是不是时间步太大。MuJoCo 默认时间步是 0.002 秒(500Hz),对于大多数场景够用。但如果你的模型里有很硬的接触或者很小的质量,需要降到 0.0005 甚至更小:

<option timestep="0.0005" integrator="implicitfast"/>

integratorimplicitfast比默认的Euler稳定得多,代价是每步计算量稍大。我现在的习惯是:只要模型里有接触,就用 implicitfast

第二步,检查质量和惯量。MJCF 里如果没显式指定massdiaginertia,MuJoCo 会根据 geom 的体积和默认密度自动算。但如果 geom 是 capsule 且 size 很小,算出来的惯量可能接近 0,导致数值不稳定。手动指定:

<body name="forearm_link" pos="0 0 0.425"> <inertial pos="0 0 0.2" mass="1.5" diaginertia="0.01 0.01 0.001"/> ... </body>

第三步,检查执行器增益。位置控制的kp太大是发散的常见原因。如果你看到关节在目标位置附近高频振荡然后飞出去,先把kp降到原来的 1/10 试试。

5.2 接触参数:solref 和 solimp 到底怎么调

MuJoCo 的接触模型由两个参数控制:solrefsolimp

  • solref="0.02 1":第一个数是时间常数,越小接触越硬;第二个数是阻尼比,1 表示临界阻尼。
  • solimp="0.9 0.95 0.001":分别是阻尼、刚度、最小值的插值参数。

默认值对大多数场景够用,但如果你做的是刚性抓取(比如金属夹爪夹金属块),默认参数会导致明显的穿透。我的调参经验:

场景solrefsolimp
软物体抓取(橡胶、布料)0.05 10.9 0.95 0.001
刚性抓取(金属、塑料)0.005 10.95 0.99 0.0001
足式机器人落地0.01 10.9 0.95 0.001
高速碰撞0.002 10.99 0.999 0.00001

调这些参数的判断标准是:看接触力曲线有没有高频振荡。如果力曲线像锯齿一样抖,说明接触太硬,把solref第一个数调大;如果物体明显陷进去,说明接触太软,调小。

5.3 一个真实的发散案例:UR5e 抓取时突然飞出去

上个月做 UR5e 抓取实验时遇到一个诡异问题:机械臂在接近物体时正常,一旦夹爪接触物体,整个系统在 0.1 秒内发散。排查过程如下:

  1. 先看日志data.qpos在接触后第 3 步就出现 10^4 量级的值,说明是接触力爆炸。
  2. 检查夹爪质量:夹爪的 mesh 是从 CAD 导出的,质量自动算出来只有 0.001 kg,但实际应该是 0.5 kg。惯量太小导致接触力计算不稳定。
  3. 修复:手动指定夹爪的massdiaginertia,同时把夹爪的solref从默认改成0.01 1
  4. 验证:重新跑,接触力曲线平滑,抓取成功率从 0% 恢复到 90%。

这个案例的教训是:从 CAD 导入的 mesh,一定要手动检查质量和惯量。MuJoCo 的自动计算对复杂几何体经常不准。

6. 可视化与调试:viewer 的正确打开方式

6.1 被动 viewer 和主动 viewer 的区别

MuJoCo 有两种 viewer 模式:

  • 被动模式mujoco.viewer.launch(model, data),viewer 在单独的线程里跑,你的主循环继续执行。适合调试和演示。
  • 主动模式mujoco.viewer.launch_passive(model, data),返回一个 handle,你需要在主循环里调用handle.sync()。适合需要精确控制仿真步进的场景。

我调试控制器时用被动模式,做数据采集时用主动模式。主动模式的一个坑是:如果你不在每步调用sync(),viewer 会卡死

6.2 在 viewer 里实时画力和轨迹

MuJoCo 的 viewer 支持自定义可视化元素。比如你想看末端执行器的轨迹:

import mujoco.viewer import numpy as np trajectory = [] with mujoco.viewer.launch_passive(model, data) as viewer: while viewer.is_running(): mujoco.mj_step(model, data) trajectory.append(data.site("ee_site").xpos.copy()) # 每 10 步更新一次轨迹可视化 if len(trajectory) % 10 == 0 and len(trajectory) > 1: viewer.user_scn.ngeom = 0 for i in range(len(trajectory) - 1): mujoco.mjv_connector( viewer.user_scn, mujoco.mjtGeom.mjGEOM_LINE, 2, trajectory[i], trajectory[i+1], np.array([1, 0, 0, 1]) # 红色 ) viewer.sync()

user_scn是 viewer 的用户场景,可以用来画线、画点、画箭头。mjv_connector是画连接线的函数,参数依次是场景、几何类型、宽度、起点、终点、颜色。

6.3 录制视频:离屏渲染的正确配置

做实验记录需要录视频。MuJoCo 支持离屏渲染:

import mujoco import imageio model = mujoco.MjModel.from_xml_path("models/ur5e/scene.xml") data = mujoco.MjData(model) renderer = mujoco.Renderer(model, height=480, width=640) frames = [] for _ in range(1000): mujoco.mj_step(model, data) if _ % 10 == 0: renderer.update_scene(data, camera="track") frames.append(renderer.render()) imageio.mimsave("simulation.mp4", frames, fps=30)

几个注意点:

  • camera参数指定用哪个相机,不指定的话用默认的自由相机。
  • 离屏渲染需要设置MUJOCO_GL环境变量,Linux 服务器上用egl,本地开发机用glfw
  • 渲染很吃性能,如果仿真步进速度重要,把渲染频率降到每 10 步或每 20 步一次。

7. 从仿真到实验:几个让结果更可信的习惯

7.1 随机化:别让策略过拟合到仿真参数

如果你的目的是训练强化学习策略然后迁移到真机,域随机化是必须的。MuJoCo 里可以在每次 reset 时随机化这些参数:

def randomize(model): # 随机化质量 ±10% for i in range(model.nbody): model.body_mass[i] *= np.random.uniform(0.9, 1.1) # 随机化摩擦系数 ±20% for i in range(model.ngeom): model.geom_friction[i, 0] *= np.random.uniform(0.8, 1.2) # 随机化执行器增益 ±5% for i in range(model.nu): model.actuator_gainprm[i, 0] *= np.random.uniform(0.95, 1.05)

随机化的范围要基于真机的实际不确定性来定。比如 UR5e 的关节摩擦在出厂时就有 ±15% 的差异,那仿真里就按这个范围随机。

7.2 用真实数据校准仿真参数

仿真和真机的差距,最终要靠数据来补。我的做法是:

  1. 在真机上让 UR5e 做一组标准轨迹(比如正弦扫频),记录关节角度和力矩。
  2. 在仿真里跑同样的轨迹,记录力矩。
  3. 用最小二乘法拟合仿真的阻尼和摩擦参数,使力矩误差最小。

这个过程叫系统辨识,MuJoCo 本身不提供工具,但可以用scipy.optimize自己写。校准之后,仿真的力矩预测误差能从 30% 降到 5% 以内。

7.3 仿真速度的优化:从 1000 步/秒到 10000 步/秒

仿真速度直接影响实验迭代效率。几个实测有效的优化手段:

  • 简化碰撞几何体:把 mesh 碰撞体换成 capsule 或 box,速度提升 3-5 倍。
  • 关闭不必要的接触:用<contact><exclude>排除永远不会接触的 body 对。
  • 降低渲染频率:渲染是最大的性能瓶颈,训练时关掉 viewer,只在评估时开。
  • mj_step而不是mj_forward+ 手动积分mj_step内部做了优化。
  • 多环境并行:用mujoco.mj_step的批量版本,或者用 Gymnasium 的向量化环境。

我现在的 UR5e 抓取实验,单环境能跑到 12000 步/秒,16 个并行环境总共 80000 步/秒,训练一个抓取策略大概 2 小时收敛。

7.4 常见问题速查表

最后整理一个我踩过的坑和对应解决方案的速查表:

现象可能原因解决方案
import mujoco 报 DLL 错误VC++ 运行库缺失安装最新 VC++ Redistributable
viewer 打不开MUJOCO_GL 未设置设置 MUJOCO_GL=glfw 或 egl
仿真发散时间步太大/惯量太小/kp太大降 timestep,手动指定 inertial,降 kp
接触穿透明显solref 太大减小 solref 第一个参数
接触力振荡solref 太小增大 solref 第一个参数
IK 解误差大URDF 和 MJCF 关节原点不一致对齐 body pos 或用 mink 数值 IK
仿真速度慢碰撞体太复杂/渲染太频繁简化碰撞体,降低渲染频率
关节抖动阻尼太小增大 joint damping 和 armature

这些经验都是我在实际项目里一步步试出来的,希望能帮你少走点弯路。MuJoCo 的学习曲线不算平缓,但一旦跑通第一个完整实验,后面的迭代会越来越顺。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询