资讯动态

Ubuntu 20.04下PX4多机仿真环境搭建与编队控制实战

发布时间:2026/9/25 1:36:57 来源:尧图企业网站定制
1. 为什么选择这套多机仿真方案1.1 从单机到多机仿真环境到底难在哪单机仿真跑通之后很多人第一反应就是再加一架不就行了。我当初也是这么想的结果一上手就发现完全不是一回事。单机仿真里PX4 实例、Gazebo 模型、QGC 连接、MAVROS 话题都是一对一的你闭着眼睛都知道谁是谁。但一旦变成三架、五架问题就全冒出来了端口冲突、话题命名空间打架、模型在 Gazebo 里互相穿模、QGC 不知道该连哪个、MAVROS 订阅到了别人的位置信息。这套方案的核心价值就是解决多个无人机实例在同一台机器上和平共处的问题。它适合已经跑通过 PX4 单机仿真、想进一步验证编队算法、多机避障、协同搜索这类场景的开发者。如果你连单机 SITL 都没跑起来建议先把单机搞定再来看这篇不然会踩很多本可以避免的坑。我用的组合是Ubuntu 20.04 PX4 v1.14.x Gazebo 11classic QGC MAVROS。为什么强调版本因为 PX4 从 v1.13 到 v1.14 在启动脚本和 Gazebo 模型路径上有调整Gazebo classic 和 Ignition现在叫 Gazebo Sim的启动方式完全不同网上很多教程混着讲照着抄必然报错。Ubuntu 20.04 是这套组合里兼容性最稳的ROS Noetic 原生支持MAVROS 装起来也省心。1.2 各组件分工先理清楚再动手很多人装完一堆东西还是懵的不知道谁在干什么。我用一个生活化的类比说清楚PX4是飞控大脑负责姿态解算、位置控制、飞行模式管理。仿真时它跑的是 SITLSoftware In The Loop也就是把飞控固件编译成能在电脑上运行的二进制程序。Gazebo是物理世界提供重力、碰撞、传感器噪声。无人机在 Gazebo 里是一个带质量和惯量的刚体模型PX4 通过 MAVLink 把电机指令发给它它把传感器数据回传。QGCQGroundControl是地面站遥控器用来规划航线、切换飞行模式、看遥测。多机时它需要同时连多个 PX4 实例。MAVROS是翻译官把 MAVLink 协议翻译成 ROS 话题让你的算法代码能用/uav1/mavros/local_position/pose这种方式拿到数据。理清这层关系后多机仿真的本质就一句话启动 N 个 PX4 实例每个实例绑定不同的 MAVLink 端口和 Gazebo 模型再用 MAVROS 和 QGC 分别连上去。听起来简单魔鬼全在细节里。1.3 方案选型为什么不用 ROS2 或 Ignition现在网上有不少 ROS2 Gazebo Sim 的教程看起来更新。但我实测下来在 Ubuntu 20.04 上做多机协同ROS1 Noetic Gazebo classic 依然是踩坑最少的组合。原因有三第一PX4 官方对 Gazebo classic 的多机支持最成熟Tools/simulation/gazebo-classic/sitl_multiple_run.sh这个脚本直接帮你把多实例启动逻辑写好了改改参数就能用。第二MAVROS 在 ROS1 下的多机命名空间方案非常成熟mavros的namespace参数一设话题自动隔离。第三QGC 对多机 MAVLink 端口的支持稳定UDP 14550 到 14560 这一段端口规划已经是社区约定俗成的做法。ROS2 不是不好而是生态还在追赶多机场景下很多现成脚本还没迁移过来你等于要自己造轮子。对于想快速验证算法的人来说时间成本不划算。2. 环境搭建的关键细节与避坑2.1 Ubuntu 20.04 基础环境准备系统装好后第一件事是换源和更新。国内网络环境下不换源装依赖会慢到怀疑人生。我用的是清华源编辑/etc/apt/sources.list把archive.ubuntu.com替换成mirrors.tuna.tsinghua.edu.cn然后sudo apt update sudo apt upgrade -y接下来装编译工具链。PX4 编译需要gcc、g、make、cmake、ninja、python3-pip这些。一条命令搞定sudo apt install -y git zip cmake build-essential ninja-build \ gcc-arm-none-eabi python3-pip python3-dev python3-setuptools \ python3-wheel python3-numpy python3-yaml注意gcc-arm-none-eabi是交叉编译工具链虽然 SITL 仿真用不到但 PX4 的构建系统会检查它缺了会报错。别问我是怎么知道的。然后是串口权限。Gazebo 仿真本身不涉及真实串口但 MAVROS 和某些工具会访问/dev/tty提前把用户加入dialout组省得后面出幺蛾子sudo usermod -a -G dialout $USER这条命令需要重新登录才生效别急着往下走先注销再登进来。2.2 PX4 源码获取与版本选择PX4 版本选择是个关键决策。我强烈建议用v1.14.x这个稳定分支不要用 main 分支。main 分支天天变今天能跑的脚本明天可能就挂了做项目最怕这种不确定性。git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot git checkout v1.14.3 git submodule update --recursive--recursive一定要加PX4 依赖一堆子模块mavlink、sitl_gazebo 等漏了后面编译必挂。如果 clone 到一半断了用git submodule update --init --recursive补。编译 SITLmake px4_sitl gazebo-classic第一次编译大概要 10 到 20 分钟取决于机器性能。编译成功后会自动启动 Gazebo 和 PX4你能看到一架四旋翼停在跑道上。这时候在 PX4 终端里敲commander takeoff飞机应该能起飞悬停。这一步跑通说明单机环境没问题了。实操心得如果编译报错说找不到empy或者jinja2是 Python 依赖问题pip3 install empy3.3.4 jinja2解决。注意 empy 必须锁 3.3.4 版本4.x 版本和 PX4 的构建脚本不兼容这个坑我踩过两次。2.3 Gazebo 模型与依赖补全PX4 自带的 Gazebo 模型在Tools/simulation/gazebo-classic/sitl_gazebo-classic/models下但有些传感器模型比如相机、激光雷达需要额外下载。如果你要做视觉相关的多机仿真提前把gazebo_models拉下来git clone https://github.com/osrf/gazebo_models.git ~/.gazebo/modelsGazebo 启动时界面闪烁是高频问题热词里都有人问。原因通常是显卡驱动和 OpenGL 渲染不匹配。虚拟机里跑 Gazebo 尤其容易闪我的建议是如果只是做算法验证用gzserver无头模式跑别开gzclient图形界面省资源还不闪。需要看画面时再单独开gzclient。# 无头模式启动适合服务器或虚拟机 make px4_sitl gazebo-classic_iris__empty2.4 QGC 与 MAVROS 安装QGC 直接去官网下 AppImage 最省事别用 apt 装老版本。下载后给执行权限chmod x QGroundControl.AppImage ./QGroundControl.AppImage如果报 FUSE 相关错误装一下libfuse2就行。QGC 第一次启动会问你要不要自动连接选是它会自动扫描 UDP 14550 端口。MAVROS 用 apt 装sudo apt install -y ros-noetic-mavros ros-noetic-mavros-extras装完必须跑一遍地理围栏数据安装脚本否则 MAVROS 启动会报GeographicLib异常wget https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh sudo bash ./install_geographiclib_datasets.sh这个脚本下载的是地球重力模型和磁偏角数据大概几十兆网络不好会卡住多试几次。3. 多机仿真的核心实现3.1 多实例启动脚本拆解PX4 官方提供了一个多机启动脚本位置在Tools/simulation/gazebo-classic/sitl_multiple_run.sh。这个脚本是整套方案的核心我把它拆开讲清楚每一段在干什么。脚本的核心逻辑是根据你传入的飞机数量 N循环启动 N 个 PX4 实例每个实例分配不同的实例编号i从 0 到 N-1MAVLink UDP 端口14540 i用于 MAVROS 连接Gazebo 模型名iris_0、iris_1……系统 IDPX4 参数MAV_SYS_ID设为i1启动命令大致长这样# 启动第 i 架 px4 -i $i -d ./build/px4_sitl_default/etc \ -s etc/init.d-posix/rcS \ -w sitl_${model}_${i}其中-i指定实例号-w指定工作目录每个实例独立避免日志和参数文件打架。这就是多机不冲突的关键——每个实例有独立的 EEPROM 参数存储和日志目录。运行方式cd PX4-Autopilot ./Tools/simulation/gazebo-classic/sitl_multiple_run.sh -n 3 -m iris-n 3表示三架-m iris指定机型。跑起来后 Gazebo 里会出现三架 iris分别停在不同的起飞点。注意默认脚本里三架飞机的初始位置是写死的在Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/下的 world 文件里。如果你要自定义编队初始位置改 world 文件里每个模型的pose标签。我一般让它们间隔 2 米排成一排太近了 Gazebo 碰撞检测会互相推。3.2 MAVROS 多机命名空间配置多机 MAVROS 的核心是namespace 隔离。每架飞机一个 launch 文件通过ns参数区分。我写一个通用的 launch 文件multi_uav_mavros.launchlaunch arg nameuav_id default1/ arg namefcu_url default/dev/ttyUSB0:921600/ group nsuav$(arg uav_id) node pkgmavros typemavros_node namemavros outputscreen param namefcu_url valueudp://:1454$(arg uav_id)127.0.0.1:1455$(arg uav_id)/ param namegcs_url value/ param nametarget_system_id value$(arg uav_id)/ param nametarget_component_id value1/ param namefcu_protocol valuev2.0/ /node /group /launch关键点解释nsuav1让所有话题自动带上/uav1/前缀比如/uav1/mavros/state。fcu_url里的14541对应第一架 PX4 实例的 MAVLink 端口。PX4 多机脚本里第 i 架的端口是14540i所以第一架是 14541第二架 14542以此类推。target_system_id必须和 PX4 的MAV_SYS_ID一致否则 MAVROS 会连错飞机。启动三架roslaunch my_pkg multi_uav_mavros.launch uav_id:1 roslaunch my_pkg multi_uav_mavros.launch uav_id:2 roslaunch my_pkg multi_uav_mavros.launch uav_id:3 启动后用rostopic list | grep mavros检查应该能看到/uav1/mavros/...、/uav2/mavros/...、/uav3/mavros/...三组话题。实操心得fcu_url的格式是udp://:本地端口目标IP:目标端口。这里容易搞反。PX4 是服务端监听 14540iMAVROS 是客户端主动去连。所以 MAVROS 的本地端口随便填一个不冲突的目标端口必须是 PX4 监听的端口。我上面写的14541127.0.0.1:14551意思是 MAVROS 从本地 14541 发发到 127.0.0.1 的 14551。等等这里要小心别把 PX4 的监听端口和 MAVROS 的本地端口搞混。让我重新理一遍这是最容易出错的地方。PX4 多机脚本里第 i 架 PX4 的 MAVLink 实例监听的是14540ionboard 端口。MAVROS 要连它fcu_url应该写成udp://:14540i127.0.0.1:14540i不对这样本地和目标一样会冲突。正确的做法是 MAVROS 用不同的本地端口比如udp://:14551127.0.0.1:14541意思是 MAVROS 绑定本地 14551向 127.0.0.1:14541 发送。但 PX4 的 UDP 是双向的它收到后会从 14541 回发到 MAVROS 的 14551。所以 MAVROS 的本地端口只要不重复就行。我一般用14560i作为 MAVROS 本地端口14540i作为 PX4 端口清晰不打架。3.3 QGC 同时连接多架飞机QGC 连多机的原理是每个 PX4 实例除了 onboard 端口14540i还会开一个 GCS 端口14550i。QGC 默认扫描 14550所以第一架能自动连上但第二架、第三架需要手动加。在 QGC 里操作Application Settings → Comm Links → Add新建一个 UDP Link端口填14551对应第二架再建一个填14552第三架。每个 Link 勾选Automatically Connect。连上后QGC 左上角会出现多个飞行器图标点一下就能切换当前控制的飞机。规划航线时注意航线是发给当前选中的那架别搞错了。注意QGC 同时连多机时MAVLink 心跳包会互相干扰偶尔出现某架飞机状态刷新慢。解决办法是在 QGC 设置里把MAVLink 系统 ID过滤打开或者干脆用 MAVROS 做控制QGC 只用来监控。3.4 用 MAVROS 做编队控制的代码骨架多机编队的本质是对每架飞机发布/uavX/mavros/setpoint_position/local话题设置目标位置。我写一个简单的三机三角形编队示例#!/usr/bin/env python3 import rospy from geometry_msgs.msg import PoseStamped import math class FormationController: def __init__(self, num_uavs3): self.num_uavs num_uavs self.pubs {} for i in range(1, num_uavs 1): topic f/uav{i}/mavros/setpoint_position/local self.pubs[i] rospy.Publisher(topic, PoseStamped, queue_size10) self.rate rospy.Rate(20) def make_pose(self, x, y, z): pose PoseStamped() pose.pose.position.x x pose.pose.position.y y pose.pose.position.z z pose.pose.orientation.w 1.0 return pose def run(self): # 三角形编队边长 3 米 radius 3.0 / math.sqrt(3) targets {} for i in range(1, self.num_uavs 1): angle 2 * math.pi * (i - 1) / self.num_uavs targets[i] (radius * math.cos(angle), radius * math.sin(angle), 2.0) # 高度 2 米 while not rospy.is_shutdown(): for i in range(1, self.num_uavs 1): x, y, z targets[i] self.pubs[i].publish(self.make_pose(x, y, z)) self.rate.sleep() if __name__ __main__: rospy.init_node(formation_controller) controller FormationController(num_uavs3) controller.run()这段代码的关键点每个飞机独立的话题通过循环批量发布。发布频率 20Hz这是 PX4 offboard 模式的最低要求低于 2Hz 会自动退出 offboard。位置是 NED 坐标系还是 ENUMAVROS 的setpoint_position/local用的是 ENU东北天和 ROS 一致不用转换。要让飞机真正起飞还需要先切 offboard 模式并解锁。完整流程是先持续发布 setpoint 几秒然后调/uavX/mavros/cmd/arming解锁再调/uavX/mavros/set_mode切 offboard。这个顺序不能乱先切模式后解锁会失败。4. 常见问题与排查实录4.1 多机仿真高频问题速查表现象可能原因排查方法解决方案Gazebo 里只有一架飞机启动脚本参数错误检查-n参数确认-n 3且脚本无报错MAVROS 连不上端口不匹配netstat -anu | grep 145核对 fcu_url 端口飞机原地打转系统 ID 冲突看 PX4 日志每架设不同 MAV_SYS_IDQGC 只显示一架GCS 端口未添加QGC Comm Links 检查手动加 14551/14552话题命名空间混乱launch 文件 ns 未设rostopic list每个 launch 加 group nsGazebo 卡顿严重图形渲染占资源top看 gzclient用 gzserver 无头模式编译报 empy 错误Python 依赖版本pip3 show empy锁 empy3.3.4飞机穿模初始位置太近看 world 文件 pose间隔至少 2 米4.2 端口冲突的排查思路端口冲突是多机仿真第一大坑。我遇到过 MAVROS 启动后一直显示FCU: DeviceError查了半天发现是上一个仿真实例没退干净端口还被占着。排查步骤# 看哪些端口被占用 sudo netstat -anup | grep -E 145[0-9][0-9] # 看残留的 px4 进程 ps aux | grep px4 # 全部清理 pkill -f px4 pkill -f gzserver pkill -f gzclient养成习惯每次重新启动仿真前先pkill清理一遍。我写了个clean.sh脚本放在项目根目录一键清理#!/bin/bash pkill -f px4 pkill -f gzserver pkill -f gzclient pkill -f mavros sleep 2 echo cleaned4.3 MAVROS 时间同步与坐标系问题MAVROS 启动后如果报TM: timeout或者位置数据跳变多半是时间同步问题。PX4 SITL 用的是系统时间MAVROS 也是理论上一致。但如果你的机器负载高或者用了虚拟机时间会有漂移。解决办法是在 MAVROS 参数里关掉时间同步检查param nametime/time_sync_rate value0.0/坐标系方面MAVROS 默认把 PX4 的 NED 转成 ROS 的 ENU。但setpoint_position/local这个话题你发的是 ENUMAVROS 内部会转成 NED 发给 PX4。所以你在 RViz 里看到的位置和 Gazebo 里是一致的不用手动转换。但如果你用setpoint_raw/local那就要注意了它发的是原始 NED得自己转。实操心得调试编队时我习惯先在 RViz 里加三个PoseStamped显示分别订阅/uav1/mavros/local_position/pose等话题。这样能直观看到三架飞机的实际位置和期望位置的偏差比盯着数字看快多了。4.4 仿真性能优化技巧三架以上飞机同时仿真如果机器配置一般Gazebo 会卡成幻灯片。几个优化手段第一关掉 Gazebo 的阴影和高质量渲染。在 Gazebo 界面里Edit → Building Editor不用管直接去~/.gazebo/gui.ini里加[geometry] x0 y0其实更有效的是启动时加--verbose看看到底哪里慢。如果是物理引擎慢把 world 文件里的max_step_size从 0.001 改成 0.004物理步长变大计算量减少但精度会降一点。做编队验证够用了。第二用gzserver无头模式只在需要看画面时开gzclient。这一招能让 CPU 占用直接降一半。第三如果飞机数量超过 5 架考虑用-m iris换成更简单的机型比如plane或者自定义的简化模型减少碰撞检测的计算量。5. 从仿真到实战的经验沉淀5.1 参数调优的几个关键点多机仿真跑通之后下一步往往是调参数让飞行更稳。PX4 的位置控制参数MPC_XY_P、MPC_Z_P对编队稳定性影响很大。默认值在单机时没问题但多机时如果飞机之间距离近气流干扰仿真里其实没有但位置控制耦合会有会导致震荡。我的经验是编队飞行时把MPC_XY_P从默认的 0.95 降到 0.7 左右响应慢一点但更稳。MPC_XY_VEL_MAX限制在 3m/s 以内太快了编队容易散。这些参数可以通过 MAVROS 动态设置不用重启仿真# 设置第一架飞机的 XY 位置环 P 增益 rosservice call /uav1/mavros/param/set param_id: MPC_XY_P, value: 0.7批量设置三架就写个循环或者用mavparam工具。5.2 仿真与实机的差异提醒仿真里跑得再好上实机前一定要知道几个差异传感器噪声仿真里 Gazebo 的 IMU 噪声是理想高斯噪声实机 IMU 有零偏、温漂。卡尔曼滤波参数要重新调。通信延迟仿真里 MAVLink 走本地 UDP延迟几乎为零。实机数传链路有几十到几百毫秒延迟编队控制周期要放宽。GPS 精度仿真里 GPS 是完美的实机室外 GPS 有 1-2 米漂移室内根本没信号。室内编队要么用光流要么用动捕。电池与动力仿真里飞机永远不会没电实机飞 15 分钟就得下来。我一般建议仿真里验证算法逻辑实机前先在单机上把参数调稳再逐步增加飞机数量。别一上来就三架实机编队炸机成本太高。5.3 后续扩展方向这套多机仿真环境搭好后可以往几个方向扩展一是接入视觉传感器做多机视觉协同。在 Gazebo 模型里加camera插件MAVROS 订阅/uavX/camera/image_raw跑目标检测算法。二是接入 ROS 的导航栈用move_base或者ego-planner做多机路径规划。MAVROS 提供的位置和速度接口足够支撑。三是做硬件在环HITL把真实的 Pixhawk 飞控接上PX4 跑在飞控上Gazebo 跑在电脑上中间用串口通信。这比纯 SITL 更接近实机但配置复杂度也上一个台阶。四是多机通信拓扑研究比如用 ROS 的multimaster或者自定义的 UDP 广播模拟飞机之间的直接通信做分布式编队控制。这套环境我前后搭了大概一周踩了不下二十个坑现在回头看最耗时的不是技术难点而是版本不匹配和端口冲突这类低级问题。所以我把版本号和端口规划写得特别细你照着来能省掉大部分折腾。真正跑通三机编队那一刻看着 Gazebo 里三架飞机整齐地排成三角形悬停那种成就感还是值得的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑