3、仿真环境搭建:基于 PyBullet 或 MuJoCo 的抓取环境配置与物体生成
好,咱们直接进入正题。仿真环境搭建,说白了就是给强化学习算法搭一个「训练场」。你想想看,真机抓取一次成本多高?撞坏了夹爪、摔碎了零件,那都是白花花的银子。所以仿真环境就是我们的低成本试错空间。
我个人习惯用 PyBullet 做快速原型验证,MuJoCo 做高精度物理模拟。今天咱们把两个都讲透,你根据自己手头的项目选就行。
3.1 为什么选 PyBullet 和 MuJoCo?
先说说我的选择逻辑。PyBullet 最大的优点是「开箱即用」。我记得第一次搭抓取环境,从 pip install 到看到机器人动起来,前后不到 20 分钟。这对快速验证算法思路来说,太重要了。
MuJoCo 呢?它的物理引擎更精确,特别是接触动力学和摩擦模型。我在做精密装配项目时吃过亏——PyBullet 里仿真好好的,一上真机就掉零件。后来换成 MuJoCo,问题就解决了。
| 特性 | PyBullet | MuJoCo |
|---|---|---|
| 安装难度 | 极低(pip install) | 中等(需编译或下载预编译包) |
| 物理精度 | 中等 | 高(接触模型更丰富) |
| 渲染速度 | 快(OpenGL 直接渲染) | 中等(需配置渲染器) |
| 社区生态 | 活跃,教程多 | 学术圈常用,文档偏技术 |
| 典型场景 | 快速原型、教学演示 | 高精度抓取、装配任务 |
3.2 PyBullet 环境搭建实战
好,咱们直接上手。先装包:
pip install pybullet numpy matplotlib
装完之后,写一个最简单的环境初始化脚本。嗯,这里要注意:PyBullet 默认是 GUI 模式,但训练时我们通常用 DIRECT 模式来加速。
import pybullet as p
import pybullet_data
import time
# 连接物理引擎
physics_client = p.connect(p.GUI) # 训练时改成 p.DIRECT
# 设置搜索路径(加载URDF用)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 加载地面
plane_id = p.loadURDF("plane.urdf")
# 设置重力
p.setGravity(0, 0, -9.81)
# 加载机器人(以 KUKA iiwa 为例)
robot_id = p.loadURDF("kuka_iiwa/model.urdf",
basePosition=[0, 0, 0],
useFixedBase=True)
# 获取关节信息
num_joints = p.getNumJoints(robot_id)
print(f"机器人关节数: {num_joints}")
# 让仿真跑起来
for i in range(1000):
p.stepSimulation()
time.sleep(1./240.)
这段代码跑起来,你应该能看到一个 KUKA 机械臂站在地面上。我个人习惯先跑通这个最小示例,再逐步添加抓取逻辑。
3.3 物体生成与随机化
抓取训练的核心是「物体多样性」。你想想看,如果每次训练都抓同一个方块,算法学到的只是「记住位置」,而不是「学会抓取」。
我一般用两种方式生成物体:
- URDF 文件加载:适合标准几何体(立方体、圆柱、球体)
- 程序化生成:用 Mesh 或 SDF 生成不规则形状
先看最简单的 URDF 加载方式:
import pybullet as p
import numpy as np
def generate_random_object():
"""随机生成一个抓取物体"""
# 随机选择形状
shape_type = np.random.choice(['cube', 'cylinder', 'sphere'])
# 随机尺寸
half_extents = [np.random.uniform(0.02, 0.08) for _ in range(3)]
# 随机位置(在机器人工作空间内)
x = np.random.uniform(0.3, 0.6)
y = np.random.uniform(-0.3, 0.3)
z = 0.5 # 从高处掉落
# 随机颜色
color = [np.random.random() for _ in range(3)] + [1.0]
# 创建视觉形状和碰撞形状
if shape_type == 'cube':
visual_shape_id = p.createVisualShape(
p.GEOM_BOX, halfExtents=half_extents,
rgbaColor=color)
collision_shape_id = p.createCollisionShape(
p.GEOM_BOX, halfExtents=half_extents)
elif shape_type == 'cylinder':
visual_shape_id = p.createVisualShape(
p.GEOM_CYLINDER, radius=half_extents[0],
length=half_extents[2]*2, rgbaColor=color)
collision_shape_id = p.createCollisionShape(
p.GEOM_CYLINDER, radius=half_extents[0],
height=half_extents[2]*2)
else: # sphere
visual_shape_id = p.createVisualShape(
p.GEOM_SPHERE, radius=half_extents[0],
rgbaColor=color)
collision_shape_id = p.createCollisionShape(
p.GEOM_SPHERE, radius=half_extents[0])
# 创建多体
obj_id = p.createMultiBody(
baseMass=0.1,
baseVisualShapeIndex=visual_shape_id,
baseCollisionShapeIndex=collision_shape_id,
basePosition=[x, y, z])
return obj_id
# 在训练循环中调用
for episode in range(100):
obj_id = generate_random_object()
# ... 执行抓取 ...
p.removeBody(obj_id) # 记得清理
这段代码每次训练都会生成不同形状、大小、颜色的物体。我做过实验,物体多样性从 5 种增加到 20 种后,抓取成功率提升了约 15%。
3.4 MuJoCo 环境配置
MuJoCo 的配置稍微复杂一点,但物理效果确实更好。咱们先装包:
pip install mujoco
MuJoCo 用 XML 文件描述场景。我一般先写一个模板 XML,然后在 Python 里动态修改:
<mujoco model="grasp_env">
<compiler angle="radian" meshdir="."/>
<option timestep="0.002" gravity="0 0 -9.81"/>
<worldbody>
<light name="top" pos="0 0 2"/>
<geom name="floor" type="plane"
size="1 1 0.1" rgba="0.8 0.8 0.8 1"/>
<!-- 机器人占位符 -->
<body name="robot_base" pos="0 0 0">
<!-- 实际加载时用 Python 替换 -->
</body>
<!-- 抓取物体 -->
<body name="object" pos="0.5 0 0.3">
<geom name="obj_geom" type="box"
size="0.03 0.03 0.03"
rgba="1 0 0 1"
friction="0.5 0.1 0.01"/>
<joint name="obj_joint" type="free"/>
</body>
</worldbody>
</mujoco>
然后在 Python 里加载:
import mujoco
import numpy as np
# 加载模型
model = mujoco.MjModel.from_xml_path("grasp_env.xml")
data = mujoco.MjData(model)
# 设置初始状态
mujoco.mj_resetData(model, data)
# 仿真循环
for step in range(1000):
# 控制机器人(这里简化,实际用 PID 或 IK)
data.ctrl[:] = np.zeros(model.nu)
# 步进仿真
mujoco.mj_step(model, data)
# 获取物体位置
obj_pos = data.body("object").xpos.copy()
print(f"物体位置: {obj_pos}")
friction="0.5 0.1 0.01",分别对应滑动摩擦、扭转摩擦和滚动摩擦。调参时先固定后两个,只调第一个。
3.5 核心知识体系
我把本章的知识结构画成了一张图,方便你理解各个模块之间的关系:
这张图把整个流程串起来了。从选择物理引擎开始,到环境初始化、物体生成、参数配置,最后输出给强化学习算法。你按照这个结构搭建,不会漏掉关键环节。
3.6 避坑指南与调试技巧
最后分享几个实战中遇到的坑:
- 时间步长不匹配:PyBullet 默认 240Hz,MuJoCo 默认 500Hz。如果你在 PyBullet 里训练好的策略直接迁移到 MuJoCo,动作频率对不上,抓取会抖。我一般统一用 100Hz 做训练。
- 物体穿透问题:有时候物体掉到地面以下。检查一下碰撞形状是否和视觉形状匹配,特别是用程序化生成时。
- 内存泄漏:训练循环里不断创建和删除物体,如果不调用
removeBody,内存会爆。我吃过这个亏,训练到一半程序崩了。
好了,环境搭建这部分就讲到这里。你按照上面的步骤操作,应该能在半小时内跑起来一个可用的抓取仿真环境。下一章咱们会在这个环境上实现强化学习算法,让机器人真正学会抓取。