一、PyBullet 简介

1.1 PyBullet是什么?

PyBullet 是一个用于机器人学、游戏开发和图形研究的开源物理仿真库。它是基于 Bullet Physics SDK,这是一个成熟的、广泛使用的开源物理引擎。

PyBullet 提供了 Python 接口,使得开发者能够利用 Bullet 强大的物理仿真能力,同时享受 Python 的易用性。

1.2 PyBullet的发展历程

Bullet Physics SDK 最初由 Erwin Coumans 在 2003 年开发,它从一个小型的开源项目发展为一个强大的、被广泛认可的物理引擎,被用于电影特效、游戏和机器人仿真。

PyBullet 作为 Bullet 的 Python 接口,随着 Python 在科学计算和机器学习领域的流行。

1.3PyBullet有哪些功能及特点

  • 多体动力学仿真: PyBullet 能够精确模拟多体系统的动态行为,包括刚体和软体动力学。

  • 机器人学支持: 它支持加载 URDF(统一机器人描述格式)文件,这是一种在机器人学中广泛使用的标准格式。

  • 逆向动力学和运动规划: PyBullet 提供了逆向动力学求解器和运动规划算法,这对于机器人的路径规划至关重要。

  • 渲染和可视化: 它包括一个简单的直接渲染器,也可以通过 VR 接口进行更高级的渲染。

  • 强化学习环境: PyBullet 与 OpenAI Gym 兼容,为强化学习提供了标准化的环境和接口。

  • 跨平台: 它可以在 Windows、Linux 和 macOS 上运行。

1.4PyBullet有哪些优缺点

优点:

  • 开源: 作为一个开源工具,PyBullet 有一个庞大的社区,不断有新的改进和功能添加。

  • 性能: 对于复杂的仿真任务,PyBullet 提供了良好的性能和实时仿真能力。

  • 易于学习: Python 接口简化了与物理引擎的交互,使得非专家也能轻松上手。

  • 多功能性: 可以用于研究、教育和商业项目,覆盖了从基本物理仿真到高级机器人学习的各种需求。

缺点:

  • 文档: 尽管社区支持广泛,但某些特定功能的文档可能不够详尽,新用户可能需要一段时间来熟悉。

  • 资源消耗: 对于非常大型或复杂的仿真,PyBullet 可能需要较多的计算资源。

  • 渲染限制: 内置的直接渲染器功能有限,对于需要高级图形的应用,可能需要额外的渲染工具。

  • 为什么使用它进行机器人仿真

1.5 为什么使用PyBullet进行机器人仿真

PyBullet 提供了高质量的物理仿真,包括对刚体动力学、软体、碰撞检测和摩擦等物理现象的精确模拟。并且内置有逆向动力学求解器和运动规划算法。

最重要的是:PyBullet 兼容 OpenAI Gym 接口,提供了一个标准化的环境用于开发和测试强化学习算法。

二、PyBullet 物理仿真初体验

安装 PyBullet

开始使用pybullet非常简单。如果你的Python环境已经配置好了,只需打开终端或命令提示符,输入以下命令:

pip install pybullet

如果你安装失败,很大概率上pip没有换源

第一次仿真

接下来我们将仿真一个物理世界,并在世界中放置两个球体,模拟两个球体碰撞

import pybullet as p
import pybullet_data
import time
# 启动仿真引擎的GUI
p.connect(p.GUI)
# 设置重力加速度
p.setGravity(0, 0, -9.81)
# 加载URDF模型路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 加载平面模型作为地面
planeId = p.loadURDF("plane.urdf")
# 加载第一个球体模型,并设置初始位置
ball1StartPos = [0, 0, 1]
ball1StartOrientation = p.getQuaternionFromEuler([0, 0, 0])
ball1Id = p.loadURDF("sphere2.urdf", ball1StartPos, ball1StartOrientation)
# 加载第二个球体模型,并设置初始位置稍微偏离第一个球体
ball2StartPos = [0.1, 0, 1]  # 稍微偏离第一个球体的位置
ball2StartOrientation = p.getQuaternionFromEuler([0, 0, 0])
ball2Id = p.loadURDF("sphere2.urdf", ball2StartPos, ball2StartOrientation)
# 设置模拟循环和时间步长
timeStep = 1./240.
p.setTimeStep(timeStep)
# 模拟循环,持续一定时间
for i in range(1000):
    p.stepSimulation()
    time.sleep(timeStep)
# 断开与仿真引擎的连接
p.disconnect()

image.png

运行程序,我们可以看到两个球碰撞的过程,但好像还不够直观,下面我们修改diam,给两个球体设置初始速度,使它们向对方移动之后碰撞。

import pybullet as p
import pybullet_data
import time

# 启动仿真引擎的GUI
p.connect(p.GUI)

# 设置重力加速度
p.setGravity(0, 0, -9.81)

# 加载URDF模型路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())

# 加载平面模型作为地面
planeId = p.loadURDF("plane.urdf")

# 设置两个球体的初始位置
ball1StartPos = [-1, 0, 0.5]
ball2StartPos = [1, 0, 0.5]

# 加载第一个球体模型
ball1Id = p.loadURDF("sphere2.urdf", ball1StartPos)

# 加载第二个球体模型
ball2Id = p.loadURDF("sphere2.urdf", ball2StartPos)

# 设置初始速度,使两个球体朝对方运动
p.resetBaseVelocity(ball1Id, linearVelocity=[5, 0, 0])
p.resetBaseVelocity(ball2Id, linearVelocity=[-5, 0, 0])

# 设置模拟循环和时间步长
timeStep = 1./240.
p.setTimeStep(timeStep)

# 模拟循环,持续一定时间
for i in range(500):
    p.stepSimulation()
    time.sleep(timeStep)

# 断开与仿真引擎的连接
p.disconnect()

运行程序,我们成功仿真了两个球体相向运动并碰撞的过程。

image.png屏幕截图 2023-12-12 131005.png

二、PyBullet 函数功能及用法

在上述示例中,我们使用了 PyBullet 来创建一个简单的物理仿真,其中两个球体相向而行并发生碰撞。这里将一一介绍代码中使用到的 PyBullet 函数及其功能:

p.commect:

这个函数用于连接到 PyBullet 仿真引擎。p.GUI 参数告诉 PyBullet 使用带有图形用户界面的模式。还有其他模式比如 p.DIRECT,它运行仿真但不会打开GUI窗口,常用于后台运行或测试。

p.connect(p.GUI)

p.setGravity:

设置整个仿真环境的重力。参数是三个浮点数,分别表示 x、y 和 z 轴方向的重力加速度。在这个例子中,我们只在 z 轴(向下)设置了重力加速度为 -9.81 m/s²,模拟地球表面的重力条件。

p.setGravity(0, 0, -9.81)

p.setAdditionalSearchPath:

PyBullet 可以加载多种格式的模型文件,包括 URDF(统一机器人描述格式)。p.setAdditionalSearchPath 函数用于设置额外的搜索路径,这样 PyBullet 在加载模型文件时会在这个路径下查找。pybullet_data.getDataPath() 返回 PyBullet 默认数据文件夹的路径。

p.setAdditionalSearchPath(pybullet_data.getDataPath())

p.loadURDF:

加载URDF模型到仿真环境中。URDF是一种常用于描述复杂机器人模型的XML格式。参数包括 URDF 文件的路径、起始位置和起始朝向。起始位置是一个三元组,表示模型在世界坐标系中的 x、y、z 坐标。起始朝向通常是一个四元数,表示模型的初始方向。

ball1Id = p.loadURDF("sphere2.urdf", ball1StartPos)

p.changeDynamics:

用于更改仿真中特定刚体的动力学属性。可以设置多种属性,如质量、摩擦系数和弹性系数等。在这个例子中,我们用它来设置球体的弹性系数。

p.changeDynamics(ball1Id, -1, restitution=restitution)

`ball1Id` 是通过 `p.loadURDF` 加载模型时返回的唯一ID,`-1` 表示改变整个刚体(而不是特定的链接),`restitution` 是弹性系数参数。

p.resetBaseVelocity:

用于设置刚体的线速度和角速度。在本例中,我们使用这个函数来给球体设置初始速度,使其相向而行。

p.resetBaseVelocity(ball1Id, linearVelocity=[5, 0, 0])

这里 `ball1Id` 是球体的唯一ID,`linearVelocity` 是线速度的三元组,分别对应 x、y、z 轴方向的速度分量。

p.setTimeStep:

设置仿真的时间步长,即每次仿真迭代过程中模拟的真实时间长度。时间步长越小,仿真越精确,但计算量越大。

p.setTimeStep(timeStep)

``timeStep` 为每个仿真步长的时间,这里设置为 1/240 秒,意味着每秒仿真240帧。

p.stepSimulation:

进行一步物理仿真迭代。在循环中调用,以连续模拟物理环境的变化。

for i in range(500):
    p.stepSimulation()

在循环中调用 `p.stepSimulation()` 使得仿真逐步前进,每次调用对应一个时间步长。

time.sleep:

这不是 PyBullet 的函数,而是 Python 标准库中的函数,用于暂停程序执行指定的时间(以秒为单位)。在这个例子中,它用于将仿真速度放慢到接近实时速度,使得我们可以观察到仿真过程。

time.sleep(timeStep)

这里的 `timeStep` 设置为与仿真时间步长相同,以便每次迭代暂停时间与物理时间步长一致。

p.disconnect:

断开当前的 PyBullet 会话,关闭 GUI 窗口。这是在仿真结束时进行清理所必须的。

p.disconnect()

所以在一个基础的仿真过程中,我们先设置环境(重力、搜索路径等),然后加载模型(球体和平面),接着设置球体的动力学属性和初始速度,然后在循环中逐步执行仿真,最后断开连接以结束仿真。