☰
PX4+ROS2无人机仿真环境搭建与联调实战指南
2026/10/27 19:44:00 网站建设 项目流程

简介:这份资源面向无人机飞控开发、机器人方向的学生与研究人员,提供一套基于PX4与ROS2的完整仿真环境,用于在实飞前验证控制算法、开展毕业设计与课程实践。压缩包共154个文件,约46.62MB,以99个png贴图、11个sdf场景模型、9个config参数配置、6个py脚本及obj、mtl、dae等三维模型资源为主,另含Dockerfile、Readme说明与build、run、entrypoint等shell脚本,以及launch_files启动配置,覆盖环境构建、场景加载与节点启动全流程。资源通过话题、服务、动作等机制打通ROS2与PX4的通信,可实现飞行模式切换、姿态与位置控制、导航控制等功能的仿真测试,并借助容器化保证环境一致复现。目前已有132人学习,适合希望低成本搭建可控实验平台、理解自主飞行机制的开发者参考。

1. 从零搭一套 PX4 + ROS2 无人机仿真:这套环境到底解决什么问题

很多人第一次接触无人机开发,卡住的地方不是算法,而是环境。飞控固件编译不过、ROS2 话题对不上、Gazebo 里飞机一解锁就翻、MAVROS 连不上……这些琐碎问题消耗的精力远超写控制律本身。基于 PX4 和 ROS2 的无人机仿真环境,本质是把飞控、通信中间件、物理引擎和可视化工具打包成一条可复现的链路,让你在没有真机的情况下完成从起飞到路径跟踪的全流程验证。它适合三类人:想入门无人机软件栈的学生、需要快速验证算法的研究者、以及做硬件在环之前想先把软件跑通的工程师。这套环境的核心价值在于——把“炸机成本”换成“重启成本”,调参、改代码、重跑,几分钟一轮。下面我从选型、安装、联调、避坑到进阶,把这条链路拆开讲清楚。

2. 环境选型与依赖关系:为什么是 PX4 + ROS2 + Gazebo 这个组合

2.1 三个组件各自负责什么

PX4 是飞控固件,跑在仿真里时它扮演“虚拟飞控”的角色,负责姿态控制、位置估计、混控输出。ROS2 是通信框架,负责把传感器数据、控制指令、状态信息以话题和服务的形式暴露出来。Gazebo 是物理引擎,提供刚体动力学、传感器噪声模型和三维场景。三者之间的关系是:Gazebo 算出飞机当前状态 → PX4 读取并解算 → 通过 MAVLink 发给 ROS2 → ROS2 里的算法处理后把指令发回 PX4 → PX4 输出电机转速 → Gazebo 更新状态。这个闭环里任何一环版本不匹配,都会出现“能起飞但飘”“话题有数据但不动”这类玄学问题。

常见做法是锁定 Ubuntu 22.04 + ROS2 Humble + PX4 v1.14 以上 + Gazebo Classic 11。不要混用 Gazebo Sim(Ignition)和 Gazebo Classic,两者的插件接口不同,PX4 的gz_bridge只认其中一种。我一般会先确认系统版本,再按官方文档顺序装 ROS2、装 Gazebo、最后编译 PX4,顺序反了容易出依赖冲突。

2.2 版本对应关系与安装顺序

组件推荐版本安装方式备注
Ubuntu22.04 LTS系统镜像不要用 24.04,ROS2 Humble 官方支持到 22.04
ROS2Humbleapt 源装桌面版,包含 rviz2 和 demo 节点
GazeboClassic 11apt与 PX4 的sitl_gazebo插件匹配
PX4v1.14 或 v1.15git clone + 子模块编译前先make distclean
MAVROSros-humble-mavrosapt需要额外装 geographiclib 数据集

安装顺序建议:系统更新 → 装 ROS2 → 装 Gazebo → 装 MAVROS → 编译 PX4。每一步做完都跑一个最小验证,比如 ROS2 装完跑ros2 run demo_nodes_cpp talker,Gazebo 装完跑gz sim看能不能出界面,PX4 编译完跑make px4_sitl gazebo看能不能出飞机。不要等全部装完再一起调,那样出问题很难定位。

2.3 工作空间与包结构规划

ROS2 的工作空间用 colcon 管理,建议单独建一个px4_ros2_ws,里面放你自己的功能包,不要和 PX4 源码混在一起。PX4 源码单独放PX4-Autopilot目录,编译产物在build/下。MAVROS 通过 apt 安装,不需要放进工作空间。这样分层的好处是:PX4 升级不影响你的算法包,ROS2 重装也不用重新编译飞控。

# 创建工作空间 mkdir -p ~/px4_ros2_ws/src cd ~/px4_ros2_ws colcon build source install/setup.bash # 克隆 PX4 源码(单独目录) cd ~ git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot make px4_sitl gazebo

上面命令的逻辑:先建 ROS2 工作空间并编译空空间,确认 colcon 可用;再克隆 PX4 并递归拉子模块,最后编译 SITL + Gazebo 目标。参数说明:--recursive必须加,否则子模块缺失会导致编译报错;make px4_sitl gazebo里的gazebo是机型目标,换成gazebo_iris可以指定具体机型。如果编译卡在子模块下载,检查网络或改用make px4_sitl gazebo前先git submodule update --init --recursive。

3. 从编译到起飞:PX4 SITL 与 ROS2 联调的最小闭环

3.1 启动仿真并验证 MAVLink 链路

PX4 编译成功后,启动 SITL 仿真会同时拉起 Gazebo 和飞控进程。默认的 MAVLink 端口是 14550(UDP),MAVROS 通过这个端口连接。启动命令有两种:一种是在 PX4 目录下直接make px4_sitl gazebo,另一种是用ros2 launch拉起 MAVROS 和 PX4。我一般先手动启动 PX4,确认 Gazebo 里飞机能解锁,再启动 MAVROS,这样出问题容易判断是哪一层。

# 终端 1:启动 PX4 SITL + Gazebo cd ~/PX4-Autopilot make px4_sitl gazebo # 终端 2:启动 MAVROS 连接 PX4 ros2 launch mavros px4.launch fcu_url:="udp://:14540@127.0.0.1:14557"

逻辑说明:PX4 启动后会监听 14540 端口等待 MAVROS 连接,同时把 Gazebo 的传感器数据通过gz_bridge送入飞控。MAVROS 的fcu_url参数格式是udp://绑定地址@目标地址:目标端口,这里绑定本地 14540,目标指向 PX4 的 14557。参数怎么改:如果 PX4 启动时提示端口占用,改fcu_url里的端口;如果 MAVROS 连不上,先用netstat -anu | grep 14540确认 PX4 在监听。

3.2 用 ROS2 话题读取飞控状态

MAVROS 连上后,ROS2 里会出现一批话题,比如/mavros/state、/mavros/local_position/pose、/mavros/imu/data。用ros2 topic list能看到完整列表。验证链路是否通,最直接的方法是看/mavros/state里的connected字段是否为true,以及armed是否随解锁指令变化。

# 查看话题列表 ros2 topic list | grep mavros # 读取飞控状态 ros2 topic echo /mavros/state # 读取本地位置 ros2 topic echo /mavros/local_position/pose --once

逻辑说明:/mavros/state是 MAVROS 从飞控心跳包里解析出来的状态,包含连接状态、解锁状态、飞行模式。/mavros/local_position/pose是飞控估计的本地位置,仿真里由 Gazebo 的真值加噪声生成。参数说明:--once表示只打印一条消息就退出,适合脚本里做检查。如果connected一直是false,检查fcu_url和防火墙;如果armed不变化,检查解锁指令是否发到了正确的话题。

3.3 发送起飞与位置指令

解锁和起飞通过 MAVROS 的服务和话题完成。常见做法是先用/mavros/cmd/arming服务解锁,再用/mavros/set_mode切到OFFBOARD模式,最后往/mavros/setpoint_position/local发位置设定点。注意 OFFBOARD 模式要求设定点以不低于 2Hz 的频率持续发送,否则飞控会自动切回其他模式。

import rclpy from rclpy.node import Node from geometry_msgs.msg import PoseStamped from mavros_msgs.srv import CommandBool, SetMode class OffboardControl(Node): def __init__(self): super().__init__('offboard_control') self.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') self.timer = self.create_timer(0.1, self.send_setpoint) # 10Hz def send_setpoint(self): msg = PoseStamped() msg.pose.position.x = 0.0 msg.pose.position.y = 0.0 msg.pose.position.z = 2.0 # 目标高度 2 米 self.pub.publish(msg) def arm_and_offboard(self): arm_req = CommandBool.Request() arm_req.value = True self.arm_client.call_async(arm_req) mode_req = SetMode.Request() mode_req.custom_mode = 'OFFBOARD' self.mode_client.call_async(mode_req) def main(): rclpy.init() node = OffboardControl() node.arm_and_offboard() rclpy.spin(node) rclpy.shutdown() if __name__ == '__main__': main()

逻辑说明:节点以 10Hz 发布位置设定点,满足 OFFBOARD 模式的最低频率要求。arm_and_offboard先发解锁请求,再切模式。参数说明:msg.pose.position.z = 2.0是目标高度,单位米;create_timer(0.1, ...)里的 0.1 是周期秒数,改成 0.05 就是 20Hz。注意解锁和切模式之间最好留 1 到 2 秒,否则飞控可能拒绝切换。如果飞机不起飞,先看/mavros/state的mode是否变成OFFBOARD,再看设定点话题有没有数据。

4. 避坑与排查:仿真环境里最容易翻车的五个地方

4.1 现象:Gazebo 里飞机能解锁但原地打转

原因:PX4 的磁力计模型被 Gazebo 的环境磁场干扰,或者gz_bridge没有正确转发 IMU 数据。仿真里磁力计噪声默认偏大,导致偏航估计漂移。解决:在 PX4 参数里把EKF2_MAG_TYPE设为1(关闭磁力计融合),或者用make px4_sitl gazebo时加PX4_SIM_MODEL=iris指定机型,不同机型的传感器配置不同。我一般先在 QGroundControl 里看偏航角是否乱跳,再决定要不要关磁力计。

4.2 现象:MAVROS 显示 connected 但话题没有数据

原因:MAVROS 的plugin_allowlist没有加载对应插件,或者 PX4 端的 MAVLink 流没有开启。解决:检查px4.launch里的plugin_allowlist参数,确保local_position、imu、setpoint_position都在列表里。另外在 PX4 控制台用mavlink status看流速率,如果某个流是 0,用mavlink stream -u 14540 -s LOCAL_POSITION_NED -r 10手动开。

4.3 现象:OFFBOARD 模式切不过去

原因:设定点发布频率不够,或者解锁和切模式之间没有等待。PX4 要求 OFFBOARD 切换前必须已经收到一段时间的设定点流。解决:先以 10Hz 发 2 秒设定点,再发解锁请求,等 1 秒后再切模式。代码里可以把arm_and_offboard拆成三步,用定时器控制顺序。如果还是不行,检查/mavros/state里的mode字段,看飞控是否报了COM_RC_IN_MODE相关错误。

4.4 现象:编译 PX4 时子模块报错

原因:git clone时没有加--recursive,或者子模块更新到一半断网。解决:进入 PX4 目录执行git submodule update --init --recursive,如果某个子模块卡住,删掉.git/modules下对应目录重试。国内网络环境下,可以配置 git 代理或者用镜像源,但不要混用多个源,否则版本对不上。

4.5 现象:ROS2 节点收不到 Gazebo 的传感器数据

原因:Gazebo 的传感器插件没有在 SDF 文件里启用,或者 ROS2 的gz_bridge配置里话题名不匹配。解决:检查PX4-Autopilot/Tools/sitl_gazebo/models/iris/iris.sdf里是否有libgazebo_ros_camera.so之类的插件,确认<ros><namespace>和 ROS2 节点订阅的话题一致。如果用的是 Gazebo Classic,桥接是通过gazebo_ros包做的,不是ros_gz_bridge,两者配置方式不同。

5. 进阶技巧:用 ROS2 记录数据并做硬件在环前的验证

仿真跑通之后,下一步通常是记录飞行数据做分析,或者把算法从仿真迁移到真机。ROS2 自带的ros2 bag record可以录下所有 MAVROS 话题,回放时用ros2 bag play复现飞行过程。我一般会录/mavros/local_position/pose、/mavros/imu/data、/mavros/setpoint_position/local这三个话题,足够做控制效果评估。

# 录制关键话题 ros2 bag record /mavros/local_position/pose /mavros/imu/data /mavros/setpoint_position/local -o flight_test_01 # 回放并查看信息 ros2 bag info flight_test_01 ros2 bag play flight_test_01

逻辑说明:-o指定输出目录名,回放时ros2 bag play会按时间戳重新发布消息。参数说明:如果只想录一段时间,加--duration 60表示录 60 秒。回放时注意时间同步,仿真里的时间戳和系统时间可能不一致,用--clock参数配合/clock话题。

硬件在环之前,建议先在仿真里做三件事:一是把控制频率从 10Hz 逐步提到 50Hz,看飞控是否稳定;二是注入传感器噪声,在 Gazebo 的 IMU 插件里加<noise>标签;三是模拟通信延迟,用tc命令在回环接口上加延迟。这三步做完,再上真机心里有底。我自己的习惯是每次改完控制参数,先跑一遍自动起飞降落的脚本,确认没有回归问题,再去做更复杂的路径跟踪。仿真环境最大的好处就是让你可以反复试错而不心疼,但前提是你要把仿真和真机的差异摸清楚——比如仿真里没有风扰、没有电机振动、没有 GPS 多径,这些在真机上都是翻车点。希望帮到你。

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

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

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

立即咨询