5. PyBullet 快速上手
开始之前,先说明本节需要的基础:
- 具备 Python 基础,能够创建虚拟环境并运行脚本。
- 知道 URDF 是机器人/物体模型的一种常见描述格式;即使还没系统学过,也不影响先跑通本节示例。
- 如果当前机器没有图形界面,接受先用无头模式
DIRECT跑最小实验。
本节目标:
- 理解 PyBullet 在机器人仿真栈中的定位。
- 建立
connect -> loadURDF -> stepSimulation -> read state -> disconnect这条最核心的工作流。 - 跑通一个最小示例,把现成的 URDF 模型真正放进物理世界里。
- 分清
GUI和DIRECT两种模式分别适合什么场景。
5.1 PyBullet 是什么
PyBullet 是 Bullet Physics SDK 的 Python 接口,常被用于机器人仿真、强化学习实验和快速原型验证。
如果把它放到常见仿真工具栈里理解,可以把它看成一种“轻量、直接、脚本友好”的物理仿真入口:
Isaac Sim更偏高保真场景、渲染和传感器工作流。MuJoCo更偏动力学、接触和控制实验底座。Gymnasium更偏强化学习环境接口,而不是物理引擎本身。PyBullet则更适合快速加载 URDF、写几行 Python 就把场景跑起来。
所以如果目标是下面这些方向,PyBullet 往往会是很顺手的起点:
- 想快速验证一个机械臂或移动机器人模型能否跑起来
- 想用 Python 先把“加载模型 -> 设置重力 -> 推进仿真 -> 读状态”这条闭环打通
- 想做教学、小实验、课程 demo 或研究原型
- 想先用较低成本建立机器人仿真的基本直觉
5.2 安装
5.2.1 建议的起步方式
第一次上手时,建议优先使用独立虚拟环境,把依赖和已有项目隔开。这里用 Conda 做一个最小环境:
conda create -n pybullet python=3.10 -y
conda activate pybullet
python -m pip install --upgrade pip
pip install pybullet
如果本地已经有可用的 Python 环境,也可以直接安装:
pip install pybullet
5.2.2 为什么先不急着追求 GUI
很多第一次上手时遇到的问题,其实不是物理没跑起来,而是图形窗口没有正常打开。
因此更稳妥的顺序通常是:
- 先用
DIRECT跑通无头仿真。 - 再切到
GUI观察画面。 - 最后再进入机器人控制、相机渲染或强化学习环境封装。
这样做的好处是,能把“仿真逻辑是否正确”和“图形环境是否正常”拆开排查。
5.3 PyBullet 的核心心智模型
PyBullet 上手最快的方法,不是先记所有 API,而是先记住下面几个概念。
1. connect
PyBullet 的 API 是围绕“客户端连接到物理服务器”组织的。最常见的两种连接方式是:
p.GUI:打开图形界面,适合观察和交互。p.DIRECT:无头模式,不开窗口,适合服务器环境和最小实验。
2. pybullet_data
很多最小示例都会用到 pybullet_data 里的现成资源,例如 plane.urdf、r2d2.urdf。常见写法是:
p.setAdditionalSearchPath(pybullet_data.getDataPath())
这一步的作用,是告诉 PyBullet 去哪里找这些内置模型文件。
3. loadURDF
loadURDF 会把一个 URDF 模型加载进当前仿真世界。机器人、地面、简单刚体,都可以通过这一步进入场景。
4. stepSimulation
这一步负责真正推进物理。很多刚开始写脚本时“模型不动”的原因,不是重力没设,而是忘了循环调用 stepSimulation()。
5. 状态读取
推进若干步之后,可以通过 getBasePositionAndOrientation、getNumJoints、getJointInfo 等 API 读取机器人和物体状态。
真正最值得记住的一条主线是:
connect -> setAdditionalSearchPath -> loadURDF -> setGravity -> stepSimulation -> read state -> disconnect
5.4 第一个最小示例:先用 DIRECT 跑通
第一次上手时,推荐先不打开 GUI,直接跑一个无头脚本。这能最快确认:
pybullet是否安装成功- 模型文件能否被找到
- 重力和步进是否正常工作
新建一个 hello_pybullet.py:
import pybullet as p
import pybullet_data
physics_client = p.connect(p.DIRECT)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0, 0, -9.8)
plane_id = p.loadURDF("plane.urdf")
start_pos = [0, 0, 1]
start_orn = p.getQuaternionFromEuler([0, 0, 0])
robot_id = p.loadURDF("r2d2.urdf", start_pos, start_orn)
print("start position:", p.getBasePositionAndOrientation(robot_id)[0])
for _ in range(480):
p.stepSimulation()
end_pos, end_orn = p.getBasePositionAndOrientation(robot_id)
print("plane id:", plane_id)
print("robot id:", robot_id)
print("end position:", end_pos)
print("end orientation:", end_orn)
p.disconnect()
执行:
python hello_pybullet.py
这个例子虽然很小,但已经把 PyBullet 最核心的工作流都走了一遍:
p.connect(p.DIRECT):连接到无头物理服务器p.setAdditionalSearchPath(...):设置内置模型搜索路径p.loadURDF("plane.urdf"):加载地面p.loadURDF("r2d2.urdf", ...):加载一个现成机器人模型p.stepSimulation():推进物理p.getBasePositionAndOrientation(...):读取机器人底座姿态p.disconnect():关闭连接
如果输出里的 end position 相比初始位置发生了变化,尤其是 z 值明显降低,通常就说明重力和步进已经正常生效。
5.5 再切到 GUI 看画面
当无头模式已经跑通之后,再切到 GUI 会更稳。可以把上面的脚本改成下面这样:
import time
import pybullet as p
import pybullet_data
physics_client = p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0, 0, -9.8)
plane_id = p.loadURDF("plane.urdf")
robot_id = p.loadURDF("r2d2.urdf", [0, 0, 1])
for _ in range(2400):
p.stepSimulation()
time.sleep(1.0 / 240.0)
p.disconnect()
这里最关键的变化只有两个:
- 把
DIRECT改成了GUI - 每步之后加了
time.sleep(1.0 / 240.0),让画面按接近实时的速度播放
这一步完成后,通常会看到机器人在重力作用下落到平面上。和 DIRECT 相比,GUI 更适合:
- 观察碰撞和姿态变化
- 手动检查模型是否加载正确
- 后续做调试线、相机、键鼠交互等可视化操作
如果当前机器是远程服务器、容器或没有桌面环境,优先坚持用 DIRECT 往往更实际。
5.6 从“能跑”到“会看机器人”
PyBullet 的一个强项,是很适合把 URDF 机器人快速加载进来,然后继续往关节和控制方向走。
下面这段代码可以在加载完 r2d2.urdf 之后,查看模型包含多少关节,以及前几个关节的名字:
num_joints = p.getNumJoints(robot_id)
print("num_joints:", num_joints)
for joint_index in range(min(num_joints, 5)):
joint_info = p.getJointInfo(robot_id, joint_index)
joint_name = joint_info[1].decode("utf-8")
joint_type = joint_info[2]
print(joint_index, joint_name, joint_type)
这一步很重要,因为后续无论是做:
setJointMotorControl2控制关节calculateInverseKinematics求逆运动学- 读取 link 状态和接触信息
都需要先建立“这个机器人有哪些 joint / link”这层认知。
还有一个很容易忽略的点是:PyBullet 中很多关节默认会带着电机控制效果,不一定会完全自由下垂。如果观察到关节行为和预期不同,通常要先回头检查关节控制模式,而不是先怀疑物理引擎。
5.7 常见坑
第一次上手 PyBullet 时,最常见的问题通常集中在下面几类:
loadURDF找不到文件:通常是忘了设置pybullet_data搜索路径,或者传入的相对路径不对。- 已经设置了重力,但模型完全不动:通常是忘了调用
stepSimulation()。 - 无头脚本能跑,
GUI打不开:通常是图形环境、远程桌面或驱动问题,先退回DIRECT更稳。 - 机器人关节表现很僵硬:通常要检查默认 motor control,而不是只盯着重力或摩擦参数。
把这些问题拆开处理,排障速度会快很多。
5.8 本节小结
这一节最重要的结论有四个:
- PyBullet 是一个很适合教学、快速验证和 URDF 机器人原型的 Python 仿真入口。
- 第一次上手时,先用
DIRECT跑通,再切GUI,通常最稳妥。 - 真正需要建立的核心心智模型是
connect -> loadURDF -> stepSimulation -> read state -> disconnect。 - 当最小示例已经能加载地面和机器人、推进仿真并读出状态时,就已经跨过 PyBullet 入门里最关键的一步了。
接下来如果要继续深入,通常有三条自然路径:
- 继续往机器人控制走,学习
setJointMotorControl2、逆运动学和关节状态读取。 - 继续往感知和可视化走,学习摄像头、调试线和图像渲染。
- 继续往强化学习走,把任务环境再封装到
Gymnasium一类接口上。