☰
从零搭建PX4+ROS+Gazebo无人机通用仿真平台
2026/10/18 14:15:21 网站建设 项目流程

简介:本资源是面向无人机开发者、高校科研人员及自动化专业学习者的通用仿真平台,聚焦自动驾驶与智能无人系统研发,解决真实飞行测试成本高、风险大、迭代慢等核心痛点。平台基于PX4飞控、ROS通信框架与Gazebo高保真物理仿真器深度集成,支持多旋翼建模、传感器仿真(IMU/摄像头/LiDAR)、自主导航算法验证及硬件在环测试,适用于教学实验、路径规划研究、避障策略开发与应急响应模拟等场景。压缩包为910.34MB的ZIP文件,包含完整源码、启动脚本、世界模型配置、ROS节点定义及PX4固件适配文件,结构清晰,开箱即可部署XTDrone仿真环境。目前已有3215人学习下载,用户可直接复用整套仿真流程,快速开展控制律调试、SLAM验证或视觉伺服实验,显著降低从理论到实机部署的研发门槛。 先交代一下背景:我最近在调试一套无人机视觉识别方案,但手头没有合适的飞机平台,总不能每次算法迭代都真机炸机。后来把PX4、ROS和Gazebo这套仿真环境完整搭起来之后,发现这套“基于PX4、ROS和Gazebo的无人机通用仿真平台”几乎成了我所有无人机开发工作的主阵地——飞控调参、路径规划、视觉识别、传感器测试,全部可以在里面先跑一遍。这篇文章就是想把从零搭建这套平台的过程、原理和踩过的坑一次性说清楚。

这套平台解决的核心痛点是:没有真机也能做无人机开发。PX4提供飞控逻辑,ROS负责算法节点之间的通信,Gazebo构建带物理属性的虚拟世界。三者组合起来,就形成了一个可以随时拆装传感器、更换机型、注入故障的“通用仿真平台”。适合正在入门飞控开发的学生、做视觉SLAM和路径规划的算法工程师,以及想快速验证无人机方案但不想莽撞试飞的人。

1. 为什么是PX4、ROS和Gazebo这个“铁三角”组合

1.1 三者的角色边界

很多人第一次接触这套组合的时候,容易把三个组件混为一谈。实际上它们分工非常明确:

PX4是飞控固件,是无人机的“大脑”。它负责姿态解算、位置控制、任务调度,以及在失去外部指令时自行稳住飞机。它最关键的属性是可以在SITL(Software In The Loop)模式下运行——也就是飞控代码原封不动地在电脑上跑,不依赖任何飞行控制硬件。

ROS(Robot Operating System,机器人操作系统)是通信和算法层。无人机上所有“高级智能”,比如目标检测、路径规划、自主避障,都是跑在ROS节点里的。它们通过话题(Topic)、服务(Service)和动作(Action)相互通信,和飞控之间通过MAVLink或DDS协议交换数据。

Gazebo是物理世界模拟器。它负责把无人机放进一个有重力、有空气阻力、有地面碰撞的虚拟空间里,同时模拟IMU、GPS、相机、激光雷达等传感器数据。PX4里跑的控制算法感知到的“世界”,完全来自Gazebo提供的虚拟传感器流。

打个比方:PX4是飞行员,ROS是副驾驶和任务规划师,Gazebo就是那个能模拟气流、颠簸和靶场的飞行模拟器。三者通过标准化接口协作,形成一个完整的闭环。

1.2 “通用仿真平台”到底通用在哪

很多人一开始不明白“通用”两个字的分量。我以为最大的价值在于:你只需要调整配置,不需要重写代码,就能在同一个平台上完成多种任务。

我在实际使用中验证过这几类场景:

  • 换机型:把四旋翼iris模型换成固定翼plane模型,甚至换成无人车,一条make命令就能切换;
  • 换传感器:给无人机模型加一个双目相机,Gazebo立刻输出两个带畸变的图像话题,视觉算法可以零改动接入;
  • 换算法:从简单的PID位置控制切换到基于模型预测控制的轨迹跟踪,只需要替换ROS侧的planning节点;
  • 多机协同:同时启动多个PX4 SITL实例,每个实例绑定不同的MAVLink system ID,就能做编队仿真。

这套平台的通用性来自PX4和Gazebo之间的标准仿真接口,以及ROS的话题抽象。只要模型文件写对了,任何机器人都能被拉进仿真。

1.3 和纯数学仿真、硬件在环仿真的区别

我之前也用Matlab/Simulink做过纯数学仿真,也用过Pixhawk硬件。这里把三者的差异摆出来:

维度纯数学仿真(Matlab等)PX4+ROS+Gazebo硬件在环(HIL)
飞控代码真实性通常需要重写为仿真模型真实PX4源码直接运行真实PX4源码运行在真实硬件
传感器仿真简化模型为主包含相机畸变、IMU噪声、GPS丢星等可以接真实传感器信号
成本低低中高,需要飞控硬件和转接板
调试效率高,但结果可信度低高,且结果可信度较高中,硬件限制多
适用人群控制算法理论验证飞控二次开发、视觉、规划、编队硬件驱动和故障注入测试

在工程实践中,我的判断标准是:如果是验证纯算法逻辑,Simulink可能更快;如果是想验证“代码在真机上跑起来之后会不会崩”,就要用PX4 SITL + Gazebo这条链路;如果要测试硬件驱动或者电磁干扰问题,才需要HIL。绝大多数项目,在仿真平台这一层就已经能解决80%的问题。

2. 版本选型与环境准备:Ubuntu 22.04下先把依赖这关过了

2.1 我建议的版本组合

先说结论,再解释为什么。我在Ubuntu 22.04上最终稳定运行的组合是:

组件推荐版本说明
操作系统Ubuntu 22.04 LTS生态最成熟,踩坑资料最多
ROSROS 2 Humble22.04对应的LTS版本,官方支持到2027年
GazeboGazebo Classic 11兼容性最好,教程最多
PX4v1.13.3或v1.14.x两个版本都能用,v1.14对ROS2支持更完善
地面站QGroundControl最新AppImage用于可视化飞行状态和控制
显卡驱动NVIDIA官方驱动建议用官方PPA安装,避免开源驱动导致OpenGL渲染崩溃

为什么不推荐Ubuntu 24.04?因为24.04对应的ROS 2 Jazzy和Gazebo新版本生态还不够统一。我在虚拟机里试过,Gazebo + slam_toolbox + Nav2这套组合的编译依赖在24.04上会有不少兼容问题,相比之下22.04上几乎所有依赖都能用apt直接装到。对于入门者来说,把环境搭建的成本降到最低,比追逐新版本更重要。

2.2 安装顺序:ROS → Gazebo → PX4 → QGC

这个顺序不是随便定的。ROS是最底层依赖,Gazebo的ROS插件依赖ROS环境变量,PX4源码编译时又需要调用Gazebo的库和ROS接口,所以必须先装ROS再装Gazebo,最后编译PX4。

安装ROS 2 Humble,官方源方式:

sudo apt update sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update sudo apt install ros-humble-desktop

装完ROS 2之后记得把环境变量写进bashrc,不然后面每个终端都要手动source,非常容易漏。

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc echo "source /usr/share/gazebo/setup.sh" >> ~/.bashrc # 如果有 source ~/.bashrc

安装Gazebo Classic和ROS 2的Gazebo插件:

sudo apt install gazebo11 libgazebo11-dev sudo apt install ros-humble-gazebo-ros-pkgs

接下来是PX4的依赖。这一步我建议不要直接用官方setup.sh一把梭,而是手动装核心依赖,因为官方脚本里包含了很多临时工具链,国内网络环境下会慢到怀疑人生。手动安装的版本更可控:

sudo apt install python3-pip ninja-build cmake protobuf-compiler libeigen3-dev \ libopencv-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ libjsoncpp-dev libtinyxml2-dev liburdfdom-dev pip3 install --user kconfiglib jsonschema future pyros-genmsg

然后拉取PX4源码。注意一定要用--recursive拉子模块,否则后面编译到一半会因为缺失子模块报一堆莫名其妙的错:

cd ~ git clone --recursive https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursive

最后装QGroundControl。这个最简单,到官网下载AppImage,然后赋权运行:

chmod +x QGroundControl.AppImage ./QGroundControl.AppImage

2.3 首次编译的判定标准与常见翻车点

PX4首次编译确实非常耗时,v1.14完整编译可能需要20到40分钟,取决于CPU核心数。判断编译成功的标准是:终端输出Build finished successfully,并且build/px4_sitl_default/bin/目录下出现了px4可执行文件。

这个阶段最常见的翻车点有三个:

第一是rosdep update失败。很多人一上来就执行rosdep install --from-paths src --ignore-src -r -y,结果卡在rosdep的数据库更新上。解决办法是提前把rosdep的源切到国内镜像,或者直接跳过rosdep,用colcon build的--symlink-install参数加手动依赖管理。我自己在做仿真平台时,没有每次都跑rosdep,PX4的Gazebo仿真同样能正常跑。

第二是Python包权限问题。Ubuntu 22.04的Python 3.10环境比较干净,但系统会启用PEP 668策略,直接pip install会报externally-managed-environment。解决办法就是用我上面写的pip3 install --user,或者建一个虚拟环境。

第三是编译时提示找不到FastRTPS或Fast DDS相关组件。这是因为PX4的ROS2接口依赖DDS实现,如果没装全就会在这里卡住。装一下即可:

sudo apt install ros-humble-rmw-fastrtps-cpp

3. 核心原理:PX4 SITL、Gazebo物理引擎与ROS通信桥

3.1 SITL模式下飞控代码怎么“假飞”

SITL模式的本质,是把PX4当成一个普通Linux进程来跑。运行时,PX4内部的传感器驱动、执行器驱动都被替换成了仿真驱动:IMU数据从UDP接口接收,电机指令输出到一个虚拟的混控器驱动,而不是真实的PWM引脚。

PX4内部的任务调度不区分真机和仿真,它仍然按照4到8毫秒的周期运行姿态控制器、位置控制器和导航任务。这意味着你在Gazebo里看到飞机姿态异常时,实际上就是真实PX4控制逻辑的响应结果——这是它比Matlab仿真有价值的地方。

启动SITL的命令非常直接:

cd ~/PX4-Autopilot make px4_sitl gazebo-classic

执行后,PX4会启动一个pxh>命令行终端,里面能看到飞控内部命令,比如commander takeoff、commander land。同时这条命令会自动拉起Gazebo,加载默认的iris四旋翼模型。

3.2 Gazebo物理引擎到底做了什么

Gazebo内部采用的刚体动力学仿真可以理解为“带物理引擎的3D游戏”。它计算重力、气动阻力、螺旋桨推力、碰撞响应,然后根据牛顿第二定律积分出无人机每一帧的位置和姿态。

对无人机仿真而言,Gazebo里最关键的是传感器插件。IMU插件会生成包含高斯噪声和零偏的角速度与加速度数据;GPS插件可以配置丢星概率;相机插件则管理内参矩阵、畸变系数和光照。这些传感器数据会通过Gazebo的传输机制发送到PX4仿真接口。千万不要用“漂亮画面”来理解Gazebo——它真正的价值是传感器数据的真实性。

3.3 ROS2与PX4的两种通信方式

这是新手最容易搞混的地方,我详细拆一下。ROS2要和PX4通信,主要有两条路:

方式一:MAVROS。MAVROS是一个成熟的MAVLink通信节点,把MAVLink消息转换为ROS2话题和服务。这是最稳定的方式,也最推荐刚接触的人使用。启动命令:

ros2 launch mavros px4.launch fcu_url:="udp://:14540@127.0.0.1:14557"

连接成功后,你可以直接订阅/mavros/state获取飞行模式,调用/mavros/cmd/arming和/mavros/set_mode服务执行解锁和模式切换。

方式二:PX4-ROS2接口(uXRCE-DDS)。这套方案直接打通PX4内部的uORB消息到ROS2 DDS,跳过了MAVLink协议转换。它适合传输大数据量传感器消息(比如点云、图像),延时更低。但需要先运行一个MicroXRCEAgent:

MicroXRCEAgent udp4 -p 8888

然后PX4编译时要加入ROS2接口支持,之后会看到/fmu/out/vehicle_local_position这样的话题。

两张口对比如下:

维度MAVROSuXRCE-DDS
协议MAVLinkDDS
话题抽象程度高,有现成的mavros_msgs低,直接暴露uORB主题
稳定性非常稳定依赖DDS实现,需自行调试
数据传输带宽受限高
适合场景控制、状态读取视觉/点云等大数据量场景

3.4 一帧数据从指令到电机转速的完整流转

我用自己的一个避障实验来说明这个流程。假设我在ROS2里发布了一个目标位置:

  1. 规划节点将目标点发布到/mavros/setpoint_position/local;
  2. MAVROS打包成MAVLink消息,通过UDP发送给PX4;
  3. PX4的commander模块解析请求,将飞行模式切换到OFFBOARD;
  4. 位置控制器生成目标加速度,姿态控制器转换为期望姿态和推力;
  5. 混控器根据机型混控矩阵,把期望力转化为四个电机转速指令;
  6. PX4的仿真输出模块把电机转速通过UDP发给Gazebo;
  7. Gazebo的电机模型产生升力和扭矩,物理引擎积分位置变化;
  8. 虚拟IMU/GPS重新产生测量值,再通过UDP返回PX4的uORB话题;
  9. 飞控进行状态估计,进入下一个控制周期。

这一套流程和真机上的唯一区别就是第6步和第7步,其余逻辑完全一样。这也是为什么仿真调参经验可以被部分迁移到真机上。

4. 从零搭建:我踩过的坑和验证方法

4.1 把三终端工作流跑起来

整个仿真平台搭建完成后,我的日常工作流是三个终端并行。第一个终端启动PX4 SITL和Gazebo:

source /opt/ros/humble/setup.bash cd ~/PX4-Autopilot make px4_sitl gazebo-classic

第二个终端启动MAVROS通信桥:

source /opt/ros/humble/setup.bash ros2 launch mavros px4.launch fcu_url:="udp://:14540@127.0.0.1:14557"

第三个终端跑自己的算法节点。强烈建议用tmux管理这三个终端,比开三个窗口清爽得多。

4.2 第一个验证动作:用QGC接管仿真飞机

这时打开QGroundControl,正常情况下会自动识别到仿真平台,连接方式显示UDP。你会看到一架飞机在虚拟环境里静止在地面,姿态数据跳动,GPS显示坐标。这个信号说明PX4 SITL → Gazebo → QGC的链路已经通了。

用QGC的滑块给飞机解锁并推油门,Gazebo里的飞机就会起飞。如果CPU占用率允许,可以把视角切到飞机的摄像头画面,确认视觉链路也正常。

4.3 从代码控制的视角:一个极简Offboard起飞程序

为了真正验证ROS2 ←→ PX4的闭环,我写过一个极简的offboard控制节点。这段代码不复杂,但作为平台自检非常有效:

#!/usr/bin/env python3 import rclpy from rclpy.node import Node from geometry_msgs.msg import PoseStamped from mavros_msgs.srv import CommandBool, SetMode class OffboardNode(Node): def __init__(self): super().__init__('offboard_node') self.pose_pub = self.create_publisher(PoseStamped, '/mavros/setpoint_position/local', 10) self.arm_client = self.create_client(CommandBool, '/mavros/cmd/arming') self.mode_client = self.create_client(SetMode, '/mavros/set_mode') def send_setpoint(self): msg = PoseStamped() msg.pose.position.x = 2.0 msg.pose.position.y = 0.0 msg.pose.position.z = 3.0 self.pose_pub.publish(msg) def main(args=None): rclpy.init(args=args) node = OffboardNode() # 注意:OFFBOARD模式要求在切换之前持续发送期望点,否则PX4不接受模式切换 for _ in range(50): node.send_setpoint() rclpy.spin_once(node, timeout_sec=0.05) # 然后进入解锁和模式切换流程,实际调用服务 node.get_logger().info('Setpoint published, ready to switch mode') rclpy.spin(node) if __name__ == '__main__': main()

这段程序本身不完整,后面还缺少调用arm和set_mode服务的部分,但核心思想是:OFFBOARD模式切换前必须连续发送至少1秒的期望位置,否则PX4会拒绝进入该模式。这是PX4故意设的安全机制,防止无人机空中突然接收到无来源指令。

4.4 踩坑实录:我遇到过的三个典型问题

问题1:Gazebo窗口一直在加载模型,屏幕全黑。

原因是虚拟机或显卡驱动不支持OpenGL渲染。解决办法有三个:升级显卡驱动、启用硬件加速、或者在启动Gazebo之前设置LIBGL_ALWAYS_SOFTWARE=1强制软渲染。多数人的开发机如果是Windows双系统装Ubuntu,优先检查显卡驱动。

问题2:PX4终端提示Comm checked但QGC连不上。

这个坑最隐蔽。PX4 SITL启动时会创建一个UDP端口通信,如果上一次仿真没有正常退出,旧进程还占着那个端口,新实例就会通信失败。处理办法是:

pkill -f px4 pkill -f gazebo pkill -f gzserver

然后重新启动。我后来养成习惯,每次仿真结束直接Ctrl+C两次,不会留僵尸进程。

问题3:编译时卡在Fetching xtensa compilers。

这是PX4编译过程中拉取工具链的环节,容易因为网络原因一直卡住。如果遇到这种情况,检查网络连接稳定性,或者直接从PX4官方镜像下载工具链包手动放置到指定目录。就我个人的体验,第一次编译建议选择网络条件好的环境,后面增量编译就不需要再拉工具链了。

5. 跑通第一个仿真任务:从起飞到拿到一帧图像

5.1 用QXGC做一次完整起降

在QGC里给飞控解锁,推油门到中位,飞机升空后再切换到定高模式,飞机就会稳定在当前高度。这个过程中你可以通过QGC的姿态表实时观察飞机响应,还能在Gazebo里用鼠标拖拽视角观察机身姿态。建议新手先做三次完整的起降循环,目的是熟悉这套平台的响应节奏。

5.2 给无人机加一个视觉传感器

PX4自带models里有不少现成的带传感器模型,比如iris_stereo_camera和plane_cam。使用方式:

make px4_sitl gazebo-classic PX4_SIM_MODEL=iris_stereo_camera

启动后,用下面的命令确认图像话题:

ros2 topic list | grep camera ros2 run rqt_image_view rqt_image_view

如果一切正常,你会在RQt图像窗口看到摄像头实时画面。这个画面不是游戏画面,而是模拟相机内参和畸变之后的图像流,完全可以直接送给视觉模型做推理测试。

5.3 动态障碍物和轨迹规划怎么接入

这是把这套平台从“飞机能飞”推进到“飞机有脑子”的关键步骤。做法是在Gazebo的world文件里添加动态障碍物模型,比如一个周期性往复运动的小球,然后在上层跑一个基于EGO-Planner或A*的路径规划节点。规划节点读取激光雷达或深度相机话题,实时生成轨迹,再通过offboard接口把目标位置送给PX4。

我之前用这套方式验证过一个实时轨迹规划框架:无人机在静止障碍物之间飞行时,突然给Gazebo里注入一个动态小球,规划算法能在100毫秒内重新规划路径并成功避开。整个过程在Gazebo里完全复现,不需要碰真机。

5.4 仿真卡顿的优化策略

仿真平台跑起来之后,最容易遇到的问题就是卡顿。如果你用的是新版本Gazebo,可以尝试关闭粒子特效和阴影,降低渲染分辨率。但最有效的办法是用headless模式运行Gazebo,只保留PX4的SITL进程:

headless=1 make px4_sitl gazebo-classic

这样就没有图像渲染负担。当只需要验证控制逻辑或跑长时间数据集采集时,headless模式会稳定很多。如果你需要视觉仿真,那么至少保证主机有16GB内存和一张相对主流的独立显卡,否则画面帧率会拖累整个仿真时间同步。

6. 从仿真到真机:这套平台的延伸方向

6.1 复用代码,但别照搬参数

一个经常被问的问题是:仿真里调好的PID参数可以原封不动带到真机上吗?我的建议是,逻辑代码可以复用,但参数一定要重新调。仿真里的电机响应、气流干扰、电池电压跌落都太理想化。PX4 SITL最大的价值是验证算法逻辑的完备性,而不是直接输出一套可以上机的参数。

6.2 接上SLAM和Nav2,做地面机器人也通用

因为这套平台的通信层和传感器层都是ROS2标准的,所以把旋翼无人机换成差速驱动无人车模型,上面的SLAM_Toolbox、Nav2、move_base节点几乎零成本迁移。我在这个仿真平台里也跑过基于Gazebo + SLAM工具链的自主导航验证,PX4本身不是只能用于无人机,它同样支持无人车模式。

6.3 多机编队和机械臂操作

PX4支持通过启动多个SITL实例来做多机仿真。给每个实例指定不同的-i参数和MAVLink system ID,然后在ROS2层做编队控制算法,我通过这种方式模拟过三机编队的队形保持与避撞。同样的道理,如果研究方向是无人机挂载机械臂,可以把机械臂URDF模型挂载到机身模型上,用MoveIt2规划机械臂轨迹,同时让底层飞控保持悬停——这套组合在Gazebo里做出来的效果已经非常接近实际工程场景。

6.4 一定要在仿真里做的事和不该做的事

我的经验是:应该在仿真里做的事包括算法逻辑验证、故障注入测试(模拟GPS丢失、电机失效)、传感器噪声对控制的影响评估、视觉算法快速迭代;不该在仿真里做的事情包括电池续航精确评估、风场干扰下飞行参数的最终整定、桨叶形变和飞行振动对传感器的影响分析。认识清楚这个边界,才能让仿真平台真正成为快速迭代工具而不是无效工作量来源。

整个平台搭起来之后,它会成为你后续几乎所有无人机开发工作的起点。我个人的一个小习惯是:每次调试完,都会用tmux的会话保存功能把当前的三终端工作流存下来,下次启动直接恢复,省去重复敲命令的时间。另外,仿真结束后的顺序很重要——先Ctrl+C停掉ROS节点,再停MAVROS,最后停PX4 SITL,否则残留进程会占住UDP端口,下一次启动又要pkill半天。这套平台的价值不在于它能替代真机,而在于它能让你在真机起飞之前,就把绝大多数逻辑错误消灭在虚拟环境里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询