2. 仿真环境搭建:Gazebo与PyBullet对比、安装配置、URDF模型导入
做四足机器人,仿真这一步绕不开。说白了,你不可能每次改个参数就直接往实物上烧程序——摔几次电机就废了。我个人习惯是先跑通仿真,再往实物迁移。这一章我们就聊聊两个主流的仿真器:Gazebo 和 PyBullet。
2.1 Gazebo vs PyBullet:怎么选?
先给结论:两个都能用,但场景不同。Gazebo 更接近真实物理,PyBullet 更轻量、更适合算法快速迭代。我刚开始做四足的时候,两个都试过,踩了不少坑。
| 对比维度 | Gazebo | PyBullet |
|---|---|---|
| 物理引擎 | ODE / Bullet / DART / Simbody | Bullet(自带) |
| 渲染能力 | OGRE 3D,支持传感器仿真 | OpenGL 基础渲染 |
| ROS 集成 | 原生支持,ros_control 无缝对接 | 需手动桥接 |
| 安装复杂度 | 较高,依赖多 | pip install 即可 |
| 仿真速度 | 较慢,尤其多机器人场景 | 快,适合强化学习训练 |
| 适用阶段 | 实物部署前的整机验证 | 算法原型、RL 训练 |
我的建议:如果你做足式机器人运动控制算法,先用 PyBullet 快速迭代;等算法稳定了,再搬到 Gazebo 做整机联合仿真。我在项目里就是这么干的——PyBullet 跑 1000 次训练,Gazebo 只做最后 10 次验证。
2.2 Gazebo 安装与配置
Gazebo 的安装,嗯,这里要注意版本匹配。ROS 1 配 Gazebo 9/11,ROS 2 配 Gazebo 11+。我见过太多人装错版本,折腾一整天。
2.2.1 安装步骤(Ubuntu 20.04 + ROS Noetic)
# 1. 安装 ROS Noetic(略,假设已装好)
# 2. 安装 Gazebo 11
sudo apt-get install gazebo11
sudo apt-get install libgazebo11-dev
# 3. 安装 ROS-Gazebo 桥接包
sudo apt-get install ros-noetic-gazebo-ros-pkgs
sudo apt-get install ros-noetic-gazebo-ros-control
# 4. 验证安装
gazebo --version
# 输出应包含 Gazebo multi-robot simulator, version 11.x
我曾经踩过的坑:装完 Gazebo 后直接 roslaunch,结果黑屏。后来发现是显卡驱动问题。如果你用虚拟机,记得开启 3D 加速;如果是双显卡笔记本,用 prime-select 切到独显。
2.2.2 启动测试
# 启动空世界
gazebo --verbose worlds/empty.world
# 或者通过 ROS launch
roslaunch gazebo_ros empty_world.launch
启动后你应该看到一个灰色网格地面。这时候可以手动插入模型测试。我个人习惯先放一个方盒子,看看物理效果是否正常。
2.3 PyBullet 安装与配置
PyBullet 就简单多了。你想想看,一个 pip 命令搞定,多省心。
# 安装
pip install pybullet
# 验证
python -c "import pybullet as p; print(p.__version__)"
装完就能跑。我经常在笔记本上开个 PyBullet 调算法,连 GPU 都不需要。
2.3.1 快速启动一个四足机器人仿真
import pybullet as p
import pybullet_data
import time
# 连接物理引擎
p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 加载地面和机器人
p.loadURDF("plane.urdf")
robot = p.loadURDF("quadruped/quadruped.urdf", [0, 0, 0.5])
# 设置重力
p.setGravity(0, 0, -9.81)
# 仿真循环
for i in range(10000):
p.stepSimulation()
time.sleep(1/240.)
小技巧:PyBullet 默认的 time.sleep 是实时仿真。如果你做强化学习训练,用 p.setRealTimeSimulation(0) 关闭实时模式,然后自己控制步进频率,速度能快几十倍。
2.4 URDF 模型导入——核心环节
不管用哪个仿真器,URDF 都是绕不开的。URDF 就是机器人的「身份证」,描述了关节、连杆、惯量、碰撞模型等信息。
2.4.1 URDF 基本结构
<robot name="my_quadruped">
<link name="base_link">
<visual>
<geometry>
<box size="0.3 0.2 0.05"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</visual>
<collision>
<geometry>
<box size="0.3 0.2 0.05"/>
</geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.02"/>
</inertial>
</link>
<joint name="hip_joint" type="revolute">
<parent link="base_link"/>
<child link="hip_link"/>
<origin xyz="0.15 0.1 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit effort="10" velocity="5" lower="-1.57" upper="1.57"/>
</joint>
</robot>
关键点:URDF 里 <collision> 和 <visual> 可以不同。仿真器用碰撞模型做物理计算,用视觉模型做显示。我一般把碰撞模型简化成圆柱或长方体,能大幅提升仿真速度。
2.4.2 在 Gazebo 中导入 URDF
<!-- 在 URDF 中增加 Gazebo 标签 -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/quadruped</robotNamespace>
</plugin>
</gazebo>
<gazebo reference="base_link">
<material>Gazebo/Blue</material>
</gazebo>
然后在 launch 文件中加载:
<launch>
<!-- 启动 Gazebo 世界 -->
<include file="$(find gazebo_ros)/launch/empty_world.launch"/>
<!-- 加载机器人模型 -->
<param name="robot_description"
textfile="$(find my_quadruped)/urdf/quadruped.urdf"/>
<node name="spawn_urdf"
pkg="gazebo_ros"
type="spawn_model"
args="-param robot_description
-urdf
-model quadruped
-x 0 -y 0 -z 0.5"/>
</launch>
2.4.3 在 PyBullet 中导入 URDF
PyBullet 更直接,一行代码搞定:
robot_id = p.loadURDF("quadruped.urdf",
basePosition=[0, 0, 0.5],
useFixedBase=False,
flags=p.URDF_USE_SELF_COLLISION)
注意:PyBullet 默认不开启自碰撞检测。如果你的四足机器人腿会互相穿透,记得加上 flags=p.URDF_USE_SELF_COLLISION。我曾经因为这个原因,仿真里机器人走路姿势看着正常,但实际物理上腿已经交叉了——浪费了两周训练时间。
2.5 知识体系总览
下面这张图概括了本章的核心逻辑:
2.6 常见问题与避坑
- Gazebo 启动后黑屏:检查显卡驱动,或者用
export SVGA_VGPU10=0临时解决。 - URDF 加载后机器人塌陷:惯性参数设置不合理。检查
<inertial>里的质量是否为正数,惯量矩阵是否正定。 - PyBullet 仿真速度慢:关闭实时模式,减少
time.sleep调用,或者用p.setPhysicsEngineParameter(numSubSteps=4)调整子步数。 - 关节限位不生效:URDF 里
<limit>标签的lower和upper必须写对。我遇到过单位搞错——弧度写成了角度,结果机器人腿直接反关节。
最后一个小建议:不管用哪个仿真器,先把 URDF 在 rviz 里可视化一遍。如果 rviz 里显示正常,再往仿真器里导。这样能快速定位是模型问题还是仿真器配置问题。