仿真工具使用
仿真是在虚拟环境中测试和验证机器人算法的关键技术。人形机器人作为复杂系统,在实机测试前需要大量仿真验证。本文深入解析 MuJoCo、PyBullet、Isaac Sim、Gazebo 等主流仿真平台的使用方法与适用场景。
一、仿真平台对比
1.1 主流平台
| 平台 | 开发商 | 物理引擎 | 价格 | 特点 | 适用场景 |
|---|---|---|---|---|---|
| MuJoCo | 自研 | 免费 | 快速、精确 | 研究、控制 | |
| PyBullet | CMU | Bullet | 免费 | 易用、Python | 教育、RL |
| Isaac Sim | NVIDIA | PhysX | 免费(学术) | GPU 加速、逼真 | 大规模训练 |
| Gazebo | Open Robotics | ODE/Bullet | 免费 | ROS 集成 | ROS 开发 |
| Webots | Cyberbotics | ODE | 免费 | 易用、跨平台 | 教育、研究 |
| Drake | MIT | 自研 | 免费 | 严谨、形式化 | 控制理论 |
1.2 选择建议
仿真平台选择指南
研究用途:
├── 控制算法:MuJoCo、Drake
├── 强化学习:PyBullet、Isaac Sim
└── 论文发表:MuJoCo(引用率高)
工程开发:
├── ROS 集成:Gazebo
├── 视觉算法:Isaac Sim
└── 快速原型:PyBullet
教育用途:
├── 入门:Webots、PyBullet
└── 进阶:MuJoCo、Isaac Sim
企业应用:
├── 大规模训练:Isaac Sim(GPU 加速)
├── 数字孪生:Isaac Sim
└── 快速迭代:PyBullet
二、MuJoCo
2.1 安装
# 1. 安装 MuJoCo(Google 收购后免费)
pip install mujoco
# 2. 安装 Python 绑定
pip install mujoco-python
# 3. 验证
python -c "import mujoco; print(mujoco.__version__)"
2.2 基本使用
import mujoco
import mujoco_viewer
import numpy as np
# 1. 加载模型
model = mujoco.MjModel.from_xml_path('humanoid.xml')
data = mujoco.MjData(model)
# 2. 创建可视化
viewer = mujoco_viewer.MujocoViewer(model, data)
# 3. 仿真循环
for _ in range(1000):
# 控制输入
data.ctrl = np.random.randn(model.nu) * 0.1
# 仿真步
mujoco.mj_step(model, data)
# 渲染
viewer.render()
# 4. 关闭
viewer.close()
2.3 人形机器人模型
<!-- humanoid.xml -->
<mujoco model="humanoid">
<!-- 选项 -->
<option timestep="0.002" gravity="0 0 -9.81"/>
<!-- 世界 -->
<worldbody>
<!-- 地面 -->
<geom name="floor" type="plane" size="10 10 0.1" rgba="0.5 0.5 0.5 1"/>
<!-- 人形机器人 -->
<body name="torso" pos="0 0 1.5">
<!-- 躯干 -->
<geom name="torso" type="box" size="0.15 0.2 0.4" rgba="0.8 0.5 0.5 1"/>
<!-- 左腿 -->
<body name="left_thigh" pos="0 -0.15 -0.4">
<joint name="left_hip_pitch" axis="0 1 0" range="-0.5 2.0"/>
<joint name="left_hip_roll" axis="1 0 0" range="-0.5 0.5"/>
<geom name="left_thigh" type="capsule" size="0.08" fromto="0 0 0 0 0 -0.4"/>
<body name="left_shin" pos="0 0 -0.4">
<joint name="left_knee" axis="0 1 0" range="0 2.5"/>
<geom name="left_shin" type="capsule" size="0.06" fromto="0 0 0 0 0 -0.4"/>
<body name="left_foot" pos="0 0 -0.4">
<joint name="left_ankle" axis="0 1 0" range="-0.5 0.5"/>
<geom name="left_foot" type="box" size="0.1 0.05 0.02"/>
</body>
</body>
</body>
<!-- 右腿(对称) -->
<body name="right_thigh" pos="0 0.15 -0.4">
<!-- ... -->
</body>
<!-- 手臂 -->
<body name="left_arm" pos="0.15 -0.25 0.3">
<!-- ... -->
</body>
<body name="right_arm" pos="0.15 0.25 0.3">
<!-- ... -->
</body>
</body>
</worldbody>
<!-- 执行器 -->
<actuator>
<motor name="left_hip_pitch" joint="left_hip_pitch" ctrlrange="-100 100"/>
<motor name="left_hip_roll" joint="left_hip_roll" ctrlrange="-50 50"/>
<motor name="left_knee" joint="left_knee" ctrlrange="-150 150"/>
<!-- ... -->
</actuator>
</mujoco>
2.4 强化学习训练
import gymnasium as gym
from stable_baselines3 import PPO
# 1. 创建环境
env = gym.make('Humanoid-v4') # MuJoCo 人形机器人环境
# 2. 创建 PPO 智能体
model = PPO(
'MlpPolicy',
env,
verbose=1,
tensorboard_log='./ppo_humanoid_tensorboard/'
)
# 3. 训练
model.learn(total_timesteps=10_000_000)
# 4. 保存
model.save('ppo_humanoid')
# 5. 测试
obs = env.reset()
for _ in range(1000):
action, _states = model.predict(obs, deterministic=True)
obs, rewards, dones, info = env.step(action)
env.render()
三、PyBullet
3.1 安装
pip install pybullet
pip install gymnasium[bullet]
3.2 基本使用
import pybullet as p
import pybullet_data
import time
# 1. 连接
p.connect(p.GUI) # 或 p.DIRECT(无界面)
# 2. 添加数据路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 3. 设置重力
p.setGravity(0, 0, -9.81)
# 4. 加载地面
plane_id = p.loadURDF("plane.urdf")
# 5. 加载人形机器人
robot_id = p.loadURDF(
"humanoid.urdf",
basePosition=[0, 0, 1.5],
useFixedBase=False
)
# 6. 获取关节信息
num_joints = p.getNumJoints(robot_id)
for i in range(num_joints):
info = p.getJointInfo(robot_id, i)
print(f"Joint {i}: {info[1].decode()}")
# 7. 仿真循环
for _ in range(10000):
# 控制关节
p.setJointMotorControl2(
robot_id,
jointIndex=0,
controlMode=p.POSITION_CONTROL,
targetPosition=0.5
)
# 仿真步
p.stepSimulation()
# 延迟(实时)
time.sleep(1./240.)
# 8. 断开
p.disconnect()
3.3 Gym 环境
import gymnasium as gym
# 创建环境
env = gym.make('HumanoidBulletEnv-v0')
# 重置
obs = env.reset()
# 仿真
for _ in range(1000):
action = env.action_space.sample()
obs, reward, done, truncated, info = env.step(action)
env.render()
env.close()
3.4 自定义环境
import pybullet as p
import pybullet_data
import numpy as np
import gymnasium as gym
from gymnasium import spaces
class HumanoidEnv(gym.Env):
"""
自定义人形机器人环境
"""
def __init__(self):
super().__init__()
# 连接(无界面,用于训练)
self.client = p.connect(p.DIRECT)
# 加载模型
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.loadURDF("plane.urdf")
self.robot = p.loadURDF(
"humanoid.urdf",
basePosition=[0, 0, 1.5]
)
# 动作空间
num_joints = p.getNumJoints(self.robot)
self.action_space = spaces.Box(
low=-1, high=1,
shape=(num_joints,),
dtype=np.float32
)
# 观测空间
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf,
shape=(num_joints * 3,), # 位置 + 速度
dtype=np.float32
)
def reset(self):
"""
重置环境
"""
p.resetSimulation()
# 重置机器人位置
p.resetBasePositionAndOrientation(
self.robot,
[0, 0, 1.5],
[0, 0, 0, 1]
)
# 重置关节
for i in range(p.getNumJoints(self.robot)):
p.resetJointState(self.robot, i, 0, 0)
# 获取观测
obs = self._get_observation()
return obs, {}
def step(self, action):
"""
执行动作
"""
# 应用控制
for i, torque in enumerate(action):
p.setJointMotorControl2(
self.robot,
i,
p.TORQUE_CONTROL,
force=torque * 10 # 缩放
)
# 仿真步
for _ in range(10): # 多步仿真
p.stepSimulation()
# 获取观测
obs = self._get_observation()
# 计算奖励(前进距离)
pos, _ = p.getBasePositionAndOrientation(self.robot)
reward = pos[0] # x 方向前进
# 检查终止
done = pos[2] < 0.5 # 摔倒
truncated = False
info = {'position': pos}
return obs, reward, done, truncated, info
def _get_observation(self):
"""
获取观测
"""
obs = []
for i in range(p.getNumJoints(self.robot)):
state = p.getJointState(self.robot, i)
obs.extend([state[0], state[1]]) # 位置 + 速度
return np.array(obs, dtype=np.float32)
def render(self):
"""
渲染(切换到 GUI)
"""
p.disconnect(self.client)
self.client = p.connect(p.GUI)
# 重新加载...
def close(self):
"""
关闭
"""
p.disconnect()
四、NVIDIA Isaac Sim
4.1 安装
# 系统要求
# - NVIDIA RTX GPU(8GB+ VRAM)
# - Ubuntu 20.04/22.04
# - CUDA 11.4+
# 1. 注册 NVIDIA 开发者账号
# https://developer.nvidia.com/
# 2. 下载 Isaac Sim
# https://developer.nvidia.com/isaac-sim
# 3. 安装(Omniverse)
# 按照官方安装指南
# 4. 验证
./isaac-sim.sh
4.2 Python API
from omni.isaac.kit import SimulationApp
# 1. 启动仿真
simulation_app = SimulationApp({"headless": False})
# 2. 导入 Isaac Sim 模块
from omni.isaac.core import World
from omni.isaac.core.robots import Robot
from omni.isaac.core.utils.stage import add_reference_to_stage
# 3. 创建世界
world = World(
stage_units_in_meters=1.0,
rendering_dt=1.0/60.0,
backend="torch"
)
# 4. 添加机器人
robot = Robot(
prim_path="/World/humanoid",
name="humanoid",
translation=[0, 0, 1.5]
)
# 5. 添加地面
world.scene.add_default_ground_plane()
# 6. 初始化
world.reset()
# 7. 仿真循环
for i in range(1000):
# 获取观测
positions, velocities = robot.get_joint_positions(), robot.get_joint_velocities()
# 控制
target_positions = [0.5] * robot.num_joints
robot.set_joint_positions(target_positions)
# 仿真步
world.step(render=True)
# 8. 关闭
simulation_app.close()
4.3 强化学习
from omni.isaac.rl import RLEnv
from omni.isaac.core.tasks import BaseTask
class HumanoidTask(BaseTask):
"""
人形机器人 RL 任务
"""
def __init__(self):
super().__init__(name="humanoid_walk")
def set_up_scene(self, scene):
super().set_up_scene(scene)
# 添加机器人
self.humanoid = scene.add(
Robot(
prim_path="/World/humanoid",
name="humanoid"
)
)
def get_observations(self):
return {
"joint_positions": self.humanoid.get_joint_positions(),
"joint_velocities": self.humanoid.get_joint_velocities(),
}
def calculate_metrics(self):
# 奖励:前进距离
pos = self.humanoid.get_world_pose()[0]
reward = pos[0]
return {"reward": reward}
def is_done(self):
# 终止:摔倒
pos = self.humanoid.get_world_pose()[0]
return pos[2] < 0.5
# 使用 SKRL 或 RSL-RL 训练
4.4 Domain Randomization
from omni.isaac.core.utils.prims import get_prim_at_path
from pxr import UsdGeom
import numpy as np
class DomainRandomization:
"""
域随机化
"""
def __init__(self):
self.ranges = {
'mass': (0.9, 1.1),
'friction': (0.5, 1.5),
'gravity': (9.7, 9.9),
}
def randomize(self):
"""
随机化环境参数
"""
# 质量随机化
for link in self.get_links():
mass_prim = get_prim_at_path(link)
mass_attr = UsdGeom.MassProperties(mass_prim)
mass_attr.GetDensityAttr().Set(
np.random.uniform(*self.ranges['mass'])
)
# 摩擦随机化
for contact_prim in self.get_contact_surfaces():
# 设置摩擦系数
pass
# 重力随机化
p.setGravity(
0, 0,
-np.random.uniform(*self.ranges['gravity'])
)
五、Gazebo
5.1 安装(ROS2 Humble)
# Gazebo 已随 ROS2 安装
sudo apt install ros-humble-gazebo-ros-pkgs
# 验证
gz sim --version
5.2 基本使用
# 1. 启动 Gazebo
gz sim shapes.sdf
# 2. 启动带 ROS2 桥接
gz sim humanoid.world --ros2
# 3. 从 ROS2 启动
ros2 launch gazebo_ros gz_sim.launch.py \
gz_args:="humanoid.world"
5.3 URDF 转 SDF
# URDF → SDF
gz sdf -p humanoid.urdf > humanoid.sdf
# 或使用 ROS2 工具
ros2 run urdf_to_gazebo urdf_to_gazebo \
--urdf humanoid.urdf \
--output humanoid.sdf
5.4 ROS2 集成
#!/usr/bin/env python3
# Gazebo + ROS2 控制节点
import rclpy
from rclpy.node import Node
from gazebo_msgs.msg import ModelState
from gazebo_msgs.srv import SetModelState
class GazeboController(Node):
"""
Gazebo 机器人控制
"""
def __init__(self):
super().__init__('gazebo_controller')
# 创建服务客户端
self.client = self.create_client(
SetModelState,
'/set_model_state'
)
def set_robot_pose(self, x, y, z):
"""
设置机器人位姿
"""
request = SetModelState.Request()
request.model_state.model_name = 'humanoid'
request.model_state.pose.position.x = x
request.model_state.pose.position.y = y
request.model_state.pose.position.z = z
future = self.client.call_async(request)
rclpy.spin_until_future_complete(self, future)
六、Drake
6.1 安装
# Ubuntu
sudo apt install lsb-release wget
wget https://drake-apt.csail.mit.edu/drake.asc
wget https://drake-apt.csail.mit.edu/drake.list
sudo mv drake.asc /etc/apt/trusted.gpg.d/
sudo mv drake.list /etc/apt/sources.list.d/
sudo apt update
sudo apt install drake
# Python
pip install drake
6.2 基本使用
from pydrake.all import (
DiagramBuilder,
Simulator,
Parser,
MeshcatVisualizer
)
# 1. 创建图
builder = DiagramBuilder()
# 2. 加载模型
plant, scene_graph = builder.AddSystem(
MultibodyPlant(time_step=0.0)
)
Parser(plant).AddModelFromFile('humanoid.urdf')
plant.Finalize()
# 3. 添加可视化
visualizer = MeshcatVisualizer.AddToBuilder(
builder, scene_graph
)
# 4. 构建图
diagram = builder.Build()
# 5. 创建仿真器
simulator = Simulator(diagram)
simulator.set_target_realtime_rate(1.0)
# 6. 运行仿真
simulator.AdvanceTo(10.0)
七、总结
7.1 平台对比
| 平台 | 优点 | 缺点 | 最佳场景 |
|---|---|---|---|
| MuJoCo | 快速、精确、RL 友好 | 图形一般 | 控制研究、RL |
| PyBullet | 易用、免费、Python | 精度一般 | 教育、快速原型 |
| Isaac Sim | GPU 加速、逼真、功能强 | 安装复杂、资源需求高 | 大规模训练、数字孪生 |
| Gazebo | ROS 集成好、免费 | 慢、精度一般 | ROS 开发 |
| Drake | 严谨、形式化验证 | 学习曲线陡 | 控制理论验证 |
7.2 推荐组合
推荐仿真组合
学术研究:
├── 控制算法:MuJoCo
├── 强化学习:MuJoCo + Gym
└── 论文验证:Drake
工程开发:
├── ROS 集成:Gazebo
├── 视觉算法:Isaac Sim
└── 快速测试:PyBullet
企业应用:
├── 大规模训练:Isaac Sim
├── 数字孪生:Isaac Sim
└── 快速迭代:PyBullet
7.3 学习路径
仿真学习路径
入门(1-2 周):
├── PyBullet 基础
├── 加载 URDF
└── 简单控制
进阶(1 月):
├── MuJoCo 深入
├── 强化学习集成
└── 自定义环境
专业(3 月+):
├── Isaac Sim 大规模训练
├── Domain Randomization
└── Sim2Real 迁移
仿真是人形机器人开发的加速器。选择合适的仿真平台,可以在虚拟环境中快速迭代算法,降低实机测试风险。建议从 PyBullet 入门,逐步过渡到 MuJoCo 或 Isaac Sim,最终实现 Sim2Real 迁移。
参考资料:
- MuJoCo 官方文档
- PyBullet 教程
- NVIDIA Isaac Sim 文档
- Gazebo 用户指南