| name | pybullet |
| description | PyBullet 物理仿真开发技能 - Python 机器人仿真、强化学习环境、GPU 加速、URDF 导入 |
| argument-hint | pybullet仿真 OR 强化学习环境 OR 机器人仿真 OR GPU加速 |
| user-invocable | true |
PyBullet Physics Simulation Skill
用于 PyBullet 物理仿真环境的配置和开发
何时使用
当需要以下帮助时使用此技能:
- 安装和配置 PyBullet
- 创建机器人仿真环境
- 编写强化学习训练环境
- 导入 URDF/SDF 模型
- 使用 GPU 加速仿真
快速参考
安装 PyBullet
pip install pybullet
pip install pybullet-gym
pip install pybullet-gpu
基本使用
连接模式
import pybullet as p
import pybullet_data
client_id = p.connect(p.GUI)
client_id = p.connect(p.GUI, options="--width=1920 --height=1080")
client_id = p.connect(p.DIRECT)
client_id = p.connect(p.SHARED_MEMORY)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
基本仿真循环
import pybullet as p
import time
p.connect(p.GUI)
p.setGravity(0, 0, -9.81)
p.setTimeStep(1/240)
plane_id = p.loadURDF("plane.urdf")
robot_id = p.loadURDF("franka_panda/panda.urdf", [0, 0, 0])
for _ in range(1000):
p.stepSimulation()
time.sleep(1/240)
p.disconnect()
机器人模型
加载 URDF
robot_id = p.loadURDF(
"robot.urdf",
basePosition=[0, 0, 0.5],
baseOrientation=[0, 0, 0, 1],
useFixedBase=True
)
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()}")
joint_states = p.getJointStates(robot_id, [0, 1, 2])
positions = [state[0] for state in joint_states]
velocities = [state[1] for state in joint_states]
创建简单机器人
def create_mobile_robot():
body_id = p.createMultiBody(
baseMass=1.0,
baseCollisionShape=p.createCollisionShape(p.GEOM_BOX, halfExtents=[0.2, 0.1, 0.05]),
baseVisualShape=p.createVisualShape(p.GEOM_BOX, halfExtents=[0.2, 0.1, 0.05], rgbaColor=[0.3, 0.3, 0.8, 1]),
basePosition=[0, 0, 0.1]
)
wheel_radius = 0.05
wheel_positions = [
[0.15, 0.12, 0.05],
[0.15, -0.12, 0.05],
[-0.15, 0.12, 0.05],
[-0.15, -0.12, 0.05]
]
wheel_ids = []
for pos in wheel_positions:
wheel_id = p.createMultiBody(
baseMass=0.1,
baseCollisionShape=p.createCollisionShape(p.GEOM_CYLINDER, radius=wheel_radius, height=0.02),
baseVisualShape=p.createVisualShape(p.GEOM_CYLINDER, radius=wheel_radius, length=0.02, rgbaColor=[0.1, 0.1, 0.1, 1]),
basePosition=pos,
baseOrientation=[0, 0, 0, 1]
)
wheel_ids.append(wheel_id)
return body_id, wheel_ids
():
links = []
parent = -
base_id = p.createMultiBody(
baseMass=,
baseCollisionShape=p.createCollisionShape(p.GEOM_BOX, halfExtents=[, , ]),
basePosition=[, , ]
)
links.append(base_id)
parent = base_id
link_params = [
(, , [, , ]),
(, , [, , ]),
(, , [, , ])
]
mass, radius, pos link_params:
link_id = p.createMultiBody(
baseMass=mass,
baseCollisionShape=p.createCollisionShape(p.GEOM_CYLINDER, radius=radius, height=),
baseVisualShape=p.createVisualShape(p.GEOM_CYLINDER, radius=radius, length=, rgbaColor=[, , , ]),
basePosition=pos,
baseInertialFramePosition=[, , ],
baseInertialFrameOrientation=[, , , ]
)
p.createConstraint(
parent, -, link_id, -,
jointType=p.JOINT_REVOLUTE,
jointAxis=[, , ],
parentFramePosition=[, , ],
childFramePosition=[, , -]
)
links.append(link_id)
parent = link_id
links
控制器
位置控制
def position_control(robot_id, joint_indices, target_positions):
for i, target in zip(joint_indices, target_positions):
p.setJointMotorControl2(
bodyUniqueId=robot_id,
jointIndex=i,
controlMode=p.POSITION_CONTROL,
targetPosition=target,
force=100
)
position_control(robot_id, [1, 2, 3], [0.5, -0.3, 0.8])
速度控制
def velocity_control(robot_id, joint_indices, velocities):
for i, vel in zip(joint_indices, velocities):
p.setJointMotorControl2(
bodyUniqueId=robot_id,
jointIndex=i,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=vel,
force=50
)
力控制
def torque_control(robot_id, joint_indices, torques):
for i, torque in zip(joint_indices, torques):
p.setJointMotorControl2(
bodyUniqueId=robot_id,
jointIndex=i,
controlMode=p.TORQUE_CONTROL,
force=torque
)
p.setJointMotorControl2(
bodyUniqueId=robot_id,
jointIndex=1,
controlMode=p.POSITION_CONTROL,
targetPosition=0,
force=0,
positionGain=0,
velocityGain=0,
maxVelocity=10
)
PID 控制
class PIDController:
def __init__(self, kp=1.0, ki=0.1, kd=0.5):
self.kp = kp
self.ki = ki
self.kd = kd
self.integral = 0
self.prev_error = 0
def compute(self, target, current, dt):
error = target - current
self.integral += error * dt
derivative = (error - self.prev_error) / dt
output = self.kp * error + self.ki * self.integral + self.kd * derivative
self.prev_error = error
return output
传感器
获取传感器数据
states = p.getJointStates(robot_id, range(num_joints))
positions = [s[0] for s in states]
velocities = [s[1] for s in states]
base_pos, base_orn = p.getBasePositionAndOrientation(robot_id)
base_vel, base_ang = p.getBaseVelocity(robot_id)
import cv2
import numpy as np
width = 640
height = 480
img = p.getCameraImage(width, height, renderer=p.ER_BULLET_HARDWARE_OPENGL)
rgb_image = np.array(img[2], dtype=np.uint8).reshape(height, width, 4)
rgb_image = rgb_image[:, :, :3]
rgb_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR)
深度传感器
img = p.getCameraImage(
width, height,
viewMatrix=view_mat,
projectionMatrix=proj_mat,
renderer=p.ER_BULLET_HARDWARE_OPENGL
)
depth_buffer = np.array(img[3])
depth_image = far_near / (1 - depth_buffer * (1 - far_near))
激光雷达模拟
def get_lidar_data(robot_id, num_rays=360, max_range=10):
results = []
base_pos, base_orn = p.getBasePositionAndOrientation(robot_id)
euler = p.getEulerFromQuaternion(base_orn)
yaw = euler[2]
for i in range(num_rays):
angle = yaw + (2 * np.pi * i / num_rays)
direction = [np.cos(angle), np.sin(angle), 0]
ray_from = base_pos
ray_to = [
base_pos[0] + direction[0] * max_range,
base_pos[1] + direction[1] * max_range,
base_pos[2] + direction[2] * max_range
]
ray_result = p.rayTestBatch([ray_from], [ray_to])
if ray_result[0][0] != -1:
results.append(ray_result[0][2])
else:
results.append(max_range)
return results
强化学习环境
创建 Gym 环境
import gym
from gym import spaces
import numpy as np
class RobotEnv(gym.Env):
def __init__(self):
super().__init__()
self.client = p.connect(p.DIRECT)
self.action_space = spaces.Box(
low=-1, high=1, shape=(4,), dtype=np.float32
)
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf, shape=(12,), dtype=np.float32
)
def reset(self):
p.resetSimulation()
p.setGravity(0, 0, -9.81)
plane = p.loadURDF("plane.urdf")
self.robot = p.loadURDF("robot.urdf", [0, 0, 0.5])
return self._get_obs()
def step(self, action):
for i, joint in enumerate(range(1, 5)):
p.setJointMotorControl2(
.robot, joint,
p.POSITION_CONTROL,
targetPosition=action[i],
force=
)
p.stepSimulation()
obs = ._get_obs()
reward = ._compute_reward()
done = ._is_done()
obs, reward, done, {}
():
states = p.getJointStates(.robot, (, ))
np.array([s[] s states] + [s[] s states])
():
():
():
p.disconnect()
使用 Stable-Baselines3
from stable_baselines3 import PPO
from stable_baselines3.common.env_checker import check_env
check_env(RobotEnv())
env = RobotEnv()
model = PPO("MlpPolicy", env, verbose=1)
model.learn(total_timesteps=100000)
model.save("ppo_robot")
GPU 加速
GPU 渲染
p.connect(p.GUI, options="--background_color_red=0.8 --background_color_green=0.8 --background_color_blue=0.8")
p.connect(p.DIRECT, options="--gpu")
并行环境
import multiprocessing as mp
def run_env(env_id):
import pybullet as p
p.connect(p.DIRECT)
return result
with mp.Pool(4) as pool:
results = pool.map(run_env, range(4))
碰撞检测
contact_points = p.getContactPoints(bodyA=robot_id, bodyB=plane_id)
if contact_points:
for contact in contact_points:
normal_force = contact[9]
print(f"Normal force: {normal_force}")
p.setCollisionFilterGroupMask(
bodyUniqueId=robot_id,
linkIndex=-1,
collisionFilterGroup=1,
collisionFilterMask=1
)
常见问题
问题 1: 仿真不稳定
解决方案:
- 减小时间步长 (1/480 或 1/960)
- 增加迭代次数
- 调整约束公差
问题 2: 机器人穿模
解决方案:
- 启用连续碰撞检测 (CCD)
- 增加碰撞层精度
- 使用更厚的碰撞体
问题 3: GPU 内存不足
解决方案:
- 减少并行环境数
- 降低渲染分辨率
- 使用 DIRECT 模式
相关资源
另见