ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PX4+Gazebo+ROS2全栈仿真:从环境搭建到无人机轨迹跟踪

PX4+Gazebo+ROS2全栈仿真:从环境搭建到无人机轨迹跟踪 搭这种“虚拟无人机”环境有个很典型的坎网上教程不少但要么只讲PX4怎么编译要么只讲ROS2怎么装真正把PX4、Gazebo、ROS2这几样串成一条完整的轨迹跟踪链路、让一架飞机在仿真器里沿着自定义轨迹稳稳飞过很少有人从头到尾讲清楚。这篇文章就是想把这套“全栈链路”说透从环境选型、固件编译、地面站联调到ROS2轨迹跟踪节点实现一步不落。适合刚接触PX4开发、想搞无人机自主控制算法验证、或者正在被课程设计和毕业设计折腾的人参考。1. 为什么偏偏是这三件套PX4、Gazebo、ROS2的分工与选型逻辑1.1 仿真环境里的四个角色分别是谁很多人一听到“PX4GazeboROS2”就想当然地认为这是一个大而全的软件包装完就能用。实际上它们是四个独立角色各干各的活。首先PX4是一套完整的飞控软件它里面跑着姿态估计、卡尔曼滤波、位置控制、姿态控制、任务调度这些东西。在仿真里的角色就是“大脑”。其次Gazebo是物理仿真器提供刚体动力学、碰撞检测、传感器模拟、世界场景相当于给大脑装了一副“身体”和“眼睛”。ROS2是通信神经系统负责把外部算法轨迹生成、规划、视觉感知和PX4连接起来形成真正的自主闭环。除此之外还有一个容易被忽略的第四角色——QGroundControlQGC地面站它就像飞机旁边的仪表盘和遥控器负责解锁、起飞、切模式、看状态。把这几个角色放在一起才叫“全栈仿真”。后面你会发现很多时候环境起不来、轨迹跑飞、ROS2收不到数据其实都是角色之间的接口出了问题而不是某个软件本身坏了。1.2 对比一下为什么非选这套组合有人会问做无人机轨迹跟踪仿真能不能用AirSim能不能用Matlab能不能用ArduPilot都可以但每个选择都对应不同的代价。方案优势不利点PX4 Gazebo和真实飞控代码完全一致仿真验证后可直接迁移真机支持SITL硬件在环社区资料丰富环境搭建复杂版本兼容要求高ArduPilot Gazebo同样开源固定翼侧重强Offboard外部控制的接口与ROS2集成体验不如PX4顺滑AirSim 自研飞控视觉渲染逼真适合感知算法动力学模型偏简化和真实PX4固件结合需要额外桥接Matlab/Simulink建模方便控制算法开发快昂贵、不贴近真实飞控代码验证效果和实机差距大用一句话总结如果你目标是用ROS2写轨迹跟踪、路径规划、避障这些上层算法并且希望代码以后能跑到真机上PX4Gazebo基本是当下最合理的选择。1.3 “全栈”到底全在哪里我见过不少教程标题也写着“全栈搭建”结果只用QGC手动起飞手动控制飞行器转一圈就结束了完全没有ROS2的事。严格来说那只能叫“SITL仿真”不叫“轨迹跟踪仿真”。本文说的全栈至少要把这条链路走完Gazebo启动生成无人机模型模拟IMU、GPS、气压计、磁力计等传感器数据。PX4读取传感器数据内部卡尔曼滤波器融合估计出位置、速度、姿态。ROS2节点通过DDS桥和PX4通信发送Offboard控制模式和轨迹设定点。PX4的控制器跟踪这些设定点输出电机指令到Gazebo。Gazebo将无人机运动结果反馈给PX4和ROS2形成闭环。每一环都打通才谈得上“从零到一”。2. 动手前先定版本Ubuntu 22.04、ROS2 Humble与Gazebo Classic的匹配关系2.1 版本组合怎么看在做任何安装之前先把版本关系理清楚。这一步能省掉后面80%的编译报错。我自己目前在用的组合是Ubuntu 22.04 ROS2 Humble Gazebo Classic 11 PX4 v1.14.3。这个组合有三个好处一是Ubuntu 22.04的APT源里直接有Gazebo Classic 11不需要额外编译二是ROS2 Humble是目前资料最全的发行版随便搜个问题都有答案三是PX4 v1.14.3兼容新旧两套Gazebo接口Even如果你后面想切到新版Gazebo也没问题。操作系统ROS2发行版推荐Gazebo版本是否推荐初学Ubuntu 20.04FoxyGazebo Classic 11可用但偏旧Ubuntu 22.04HumbleGazebo Classic 11强烈推荐Ubuntu 24.04Jazzy新版Gazebo太新资料少2.2 系统基础依赖准备装环境前先把系统基础包补齐。以下命令在Ubuntu 22.04上直接执行即可sudo apt update sudo apt upgrade -y sudo apt install -y \ build-essential \ git \ cmake \ python3 \ python3-pip \ python3-venv \ ninja-build \ protobuf-compiler \ libprotobuf-dev \ libeigen3-dev \ libopencv-dev \ lsb-release \ wget \ curl \ xterm这里面特别说明两个包protobuf-compiler是PX4和Gazebo编译时的强依赖少了会在编译中途报“google/protobuf/message.h not found”这种错误libeigen3-dev是矩阵运算库PX4的姿态控制、卡尔曼滤波代码在编译时需要用到Eigen头文件。2.3 ROS2 Humble的安装与source坑ROS2安装有两种方式apt安装和源码编译。对于仿真开发用APT安装二进制包就完全够了没必要折腾源码。安装命令sudo apt install -y ros-dev-tools sudo apt install -y ros-humble-desktop echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc多说一句我建议装ros-humble-desktop而不是ros-humble-ros-base。虽然两个都能用但后面调试时经常需要rviz2、demo节点这些可视化工具装了desktop版省心很多。如果你只想轻量安装至少确保后续缺包时知道怎么补。ROS2有个新手必踩的坑新开的终端没有自动加载环境变量。每次新开终端如果发现ros2命令找不到说明没有source。虽然上面已经把source写进了.bashrc但在一些远程SSH、VS Code终端里仍然可能不自动生效手动执行source /opt/ros/humble/setup.bash即可。2.4 Gazebo Classic 11的安装接着安装Gazebo Classic 11。注意PX4 v1.14.3用到的还是gazebo命令不是gz sim。新版Gazebo的命令是gz sim别混了混了之后启动仿真可能会找不到模型。sudo apt install -y gazebo11 libgazebo11-dev source /usr/share/gazebo/setup.shGazebo首次启动某个模型时会自动从模型库下载模型文件。这个过程很慢如果你发现Gazebo卡在加载界面半天不动十有八九是在下载模型。提前把常用模型手动放到~/.gazebo/models目录下可以缓解但这属于可选优化第一次跑就耐心等一会儿问题也不大。3. PX4固件源码编译从拉取代码到SITL冒烟起飞3.1 拉取源码与子模块管理PX4的源码结构比普通项目复杂它引用了大量子模块来管理飞控底层库、NuttX实时操作系统、MavLink协议库等。所以拉取源码时一定用--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为什么建议用v1.14.3这个tag而不是直接用main因为main分支一直在迭代今天能编译的代码可能下周就被改坏了。固定tag能保证你遇到的所有问题和网上的教程、官方文档对得上。我在实操中遇到过换了main分支后Gazebo模型加载方式都变了的情况非常折腾。3.2 执行官方依赖安装脚本PX4官方提供了一个自动化安装脚本会在系统里装机架选择、编译工具链、Gazebo等依赖。进入源码目录后执行bash ./Tools/setup/ubuntu.sh这个脚本会跑很久中间可能弹出几次需要输入密码的提示。脚本执行完后建议注销并重新登录系统确保环境变量生效。另外注意如果你机器上已经装过ROS2尽量不要加--no-ros2参数让脚本把ROS2相关的PYTHON依赖也一起装了后面集成ROS2会省事很多。3.3 编译SITL并启动第一架仿真无人机一切就绪后编译并启动PX4 SITL命令如下cd ~/PX4-Autopilot make px4_sitl gazebo-classic_iris这里的px4_sitl是编译目标software in the loopgazebo-classic_iris指定用Gazebo Classic启动Iris四旋翼模型。第一次编译大约要10-20分钟取决于机器性能。编译完成后会自动启动Gazebo窗口和PX4的pxh控制台。看到pxh提示符就说明PX4的SITL环境已经跑起来了。此时不要急着关窗口进入下一步QGC联调。3.4 背后的状态估计链路卡尔曼滤波怎么把数据喂给控制器很多人对“PX4在仿真里到底怎么知道飞机在哪”很好奇。这就要说到PX4核心的状态估计模块扩展卡尔曼滤波器EKF。在Gazebo仿真里每个传感器都是模拟出来的。IMU在每一帧输出三轴加速度和三轴角速度GPS输出经纬度和海拔气压计输出气压高度磁力计输出航向。这些数据通过MAVLink或者共享内存传给PX4。PX4内部的EKF2模块拿到这些原始测量值后通过卡尔曼滤波算法把位置、速度、姿态这些“状态量”估计出来。注意“估计”这个词因为传感器有噪声、有延迟控制器拿到的永远是滤波后的状态而不是真实的物理状态。卡尔曼滤波的核心思想可以通俗理解成用IMU预测短时间内的状态变化再用GPS和视觉测量进行修正两者加权融合。预测噪声小就多信预测测量噪声小就多信测量。PX4里相关参数以EKF2_开头比如EKF2_GPS_CHECK、EKF2_IMU_POS_X等。搞懂这条链路对轨迹跟踪特别重要如果你的轨迹跟踪效果差可能不是控制器参数问题而是状态估计本身不准。比如在室内仿真里没有GPS信号就需要额外配置基于视觉或运动捕捉(mocap)的位置估计。这就解释了为什么有人会在Gazebo里用二维码做视觉定位本质上是在给EKF提供可靠的测量输入。4. 地面站联调QGC连接、解锁起飞与仿真数据观察4.1 QGroundControl的安装与连接PX4官方推荐的仿真地面站是QGroundControl。直接下载Linux版的AppImage文件chmod x QGroundControl.AppImage ./QGroundControl.AppImage如果你遇到AppImage无法直接运行、提示fuse错误的情况可以加--appimage-extract-and-run参数运行./QGroundControl.AppImage --appimage-extract-and-run启动QGC后如果PX4 SITL已经运行QGC会自动通过UDP 14550端口连接上模拟飞控。如果你没看到连接检查一下下端口的14550是否被占用。4.2 从“在地面”到“在空中”连接成功后QGC主界面会显示一架连接中的无人机图标但飞机还在地面。要起飞有两种方式。方式一QGC虚拟摇杆。进入“齿轮”图标 - 选择“虚拟摇杆”页面按提示设置键盘或鼠标映射然后回到主界面用摇杆解锁再推油门。这是最贴近手动飞行的方式适合验证动力学和控制通道是否正常。方式二给飞控发送起飞指令。在QGC的MAVLink控制台或PX4的pxh终端里输入commander takeoff飞控会自动执行起飞。这种方式适合后续做自主飞行实验。我建议第一次起飞用方式一。因为轨迹跟踪开发的前提是手动飞行状态一切正常如果手动都飞不稳后面发多少轨迹设定点都白搭。4.3 用地面站看什么、用Gazebo看什么QGC主要看飞行状态加速度、姿态四元数、GPS位置、电压、飞控模式。在调试轨迹跟踪时QGC最重要的是三块一是飞行模式确认当前确实处于Offboard模式或者你切换的目标模式二是位置信息对比设定点与实际位置三是日志如果轨迹跑飞了QGC会把log_record保存下来可以直接做离线分析。Gazebo则提供物理世界的直观反馈。你可以从任意角度观察无人机姿态是否倾斜、螺旋桨是否旋转、仿真环境的物理速度是否正常。这里提醒一个坐标系的问题PX4内部和MAVLink协议用的是NED北东地坐标系也就是X轴朝北、Y轴朝东、Z轴朝下。Gazebo和ROS2通用的坐标系是ENU东北天X轴朝东、Y轴朝北、Z轴朝上。所以你在写轨迹设定点时高度方向的正负一定要搞清。在PX4的TrajectorySetpoint消息里z-3表示离地3米因为NED中向下为正负号代表向上。如果你在Gazebo里看高度为负看起来很怪但从飞控角度看是完全正确的。这个坑我见过不少人踩飞行器指令发出去飞机往地面冲不是控制逻辑错了是坐标正负号反了。4.4 为什么Gazebo界面一直在闪常见原因排查关于“为什么Gazebo界面一直在闪”这个高频问题其实原因就那么几类。第一个原因是模型加载未完成时渲染线程和物理线程不同步界面会出现短暂闪烁等模型加载完就自动消失。这时候别急着关窗口观察一下终端输出。第二个原因是没有GPU硬件加速。如果你在虚拟机里跑GazeboOpenGL渲染会非常吃力要么黑屏要么闪烁。可以试着设置环境变量LIBGL_ALWAYS_SOFTWARE1强制软件渲染同时把Gazebo的渲染品质调低。第三个原因是显卡驱动不匹配。实体机上如果NVIDIA驱动没装好Gazebo也会闪烁。可以在启动前运行glxinfo | grep render检查OpenGL渲染器是否为你的显卡。如果是llvmpipe说明驱动没起来需要装驱动。5. 让无人机跟着轨迹飞ROS2轨迹跟踪节点从零实现5.1 PX4和ROS2通信的三条路线对比环境跑通以后真正的重头戏来了怎么用ROS2控制无人机沿指定轨迹飞行。PX4和ROS2通信有三条主流路线。方案实现难度适用场景uXRCE-DDS px4_msgs中等原生支持低延迟适合纯ROS2生态MAVSDK简单Python快速验证不一定要用ROS2MAVROS中等ROS1老项目迁移MAVLink通用性好本文重点讲uXRCE-DDS px4_msgs这条路因为PX4 v1.14.x默认就在跑Micro XRCE-DDS和ROS2的集成是原生支持不需要额外中间件轨迹设定点的消息也能以很高的频率收发。5.2 启动DDS桥接通道要让ROS2能收到PX4的话题需要先启动Micro XRCE-DDS Agent。这个Agent相当于PX4和ROS2之间的翻译官PX4通过UDP 8888端口把uORB消息转发出来Agent再把它们转成ROS2的话题。安装Agentgit clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git cd Micro-XRCE-DDS-Agent mkdir build cd build cmake .. make -j$(nproc) sudo make installAgent编译完成后在PX4 SITL保持运行的情况下另开一个终端执行MicroXRCEAgent udp4 -p 8888然后启动ROS2的环境用ros2 topic list查看话题。正常的话你应该能看到一堆/fmu/out/开头的PX4输出话题比如/fmu/out/vehicle_status、/fmu/out/vehicle_odometry以及/fmu/in/开头的输入话题比如/fmu/in/offboard_control_mode、/fmu/in/trajectory_setpoint。如果话题列表里一个/fmu/都没有优先检查三件事Agent是否在PX4启动之后运行的端口是否为8888PX4源码里DDS是否默认启用。大部分情况都是Agent没启动或者端口对不上。5.3 编写ROS2轨迹跟踪节点接下来是核心环节在ROS2侧写一个轨迹跟踪节点让无人机沿一个圆形轨迹飞行。我把一个能直接跑通的节点拆解出来讲。首先创建工作空间并编译px4_msgs接口包mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src git clone https://github.com/PX4/px4_msgs.git cd ~/ros2_ws colcon build --packages-select px4_msgs source install/setup.bash然后创建一个Python包在节点里发布轨迹设定点。整个控制逻辑分三步持续发布OffboardControlMode声明“我要用位置和速度控制”。持续发布TrajectorySetpoint告诉飞控“下一个目标位置在哪里、目标速度是多少”。解锁飞控并切换Offboard模式之后飞控就会跟踪设定点飞行。节点关键代码示例如下import math import rclpy from rclpy.node import Node from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleStatus, VehicleCommand class CircleTrajectoryNode(Node): def __init__(self): super().__init__(circle_trajectory_node) self.offboard_pub self.create_publisher( OffboardControlMode, /fmu/in/offboard_control_mode, 10) self.traj_pub self.create_publisher( TrajectorySetpoint, /fmu/in/trajectory_setpoint, 10) self.command_pub self.create_publisher( VehicleCommand, /fmu/in/vehicle_command, 10) self.status_sub self.create_subscription( VehicleStatus, /fmu/out/vehicle_status, self.status_callback, 10) self.status VehicleStatus() self.timer self.create_timer(0.1, self.control_loop) self.armed False self.offboard_set False self.start_time self.get_clock().now().nanoseconds * 1e-9 def status_callback(self, msg): self.status msg def send_command(self, command, param10.0, param20.0): cmd VehicleCommand() cmd.timestamp int(self.get_clock().now().nanoseconds / 1000) cmd.command command cmd.param1 param1 cmd.param2 param2 cmd.target_system 1 cmd.target_component 1 cmd.source_system 1 cmd.source_component 1 cmd.from_external True self.command_pub.publish(cmd) def control_loop(self): if not self.armed: # 先解锁 self.send_command(VehicleCommand.VEHICLE_CMD_COMPONENT_ARM_DISARM, 1.0) self.armed True self.get_logger().info(Arm command sent) return if not self.offboard_set: # 切Offboard模式 self.send_command(VehicleCommand.VEHICLE_CMD_DO_SET_MODE, 1.0, 6.0) self.offboard_set True self.get_logger().info(Offboard mode command sent) return # Offboard控制模式位置速度 offboard_msg OffboardControlMode() offboard_msg.timestamp int(self.get_clock().now().nanoseconds / 1000) offboard_msg.position True offboard_msg.velocity True offboard_msg.acceleration False self.offboard_pub.publish(offboard_msg) t (self.get_clock().now().nanoseconds * 1e-9) - self.start_time radius 3.0 omega 0.3 x radius * math.cos(omega * t) y radius * math.sin(omega * t) z -3.0 vx -radius * omega * math.sin(omega * t) vy radius * omega * math.cos(omega * t) traj_msg TrajectorySetpoint() traj_msg.timestamp int(self.get_clock().now().nanoseconds / 1000) traj_msg.position [float(x), float(y), float(z)] traj_msg.velocity [float(vx), float(vy), 0.0] traj_msg.acceleration [0.0, 0.0, 0.0] self.traj_pub.publish(traj_msg)编译之后运行python3 circle_trajectory_node.py你会看到日志输出Arm command sent然后Offboard mode command sent接着飞机应该会先原地起飞到轨迹起点再进入半径3米、高度3米的圆形轨迹跟踪。5.4 轨迹跟踪效果的参数整定思路代码跑通只是第一步。实际跑起来之后你会发现跟踪效果和理论值差很远这是正常的。PX4内部的控制器主要是多旋翼位置控制器有几个参数直接影响跟踪精度强烈建议研究一下MPC_XY_P水平位置环比例系数增大可以让飞机更快“拉回”到期望轨迹。MPC_XY_V_P水平速度环比例系数影响速度跟踪的响应速度。MPC_XY_VEL_MAX最大水平速度限制太小时飞机会跟不上圆轨迹的切向速度。MPC_ACC_HOR_MAX最大水平加速度如果期望轨迹的向心加速度超过这个值会明显切弯。MPC_Z_P高度位置环比例系数。一个重要经验单独发位置设定点时由于位置环是纯比例控制过弯会明显滞后。改进方法是把轨迹的解析速度和加速度一起发下去。我在代码里已经同时发布了速度量所以圆形轨迹的跟踪效果通常比只发位置好很多。究其原理PX4的控制器会把期望速度作为前馈信号相当于告诉它“下一个时刻你应该以多快的速度运动”而不是等误差出现后才纠正。调试建议是先从慢速小圆开始比如半径2米、角速度0.2 rad/s确认基本跟踪无误后再慢慢加码。不要一上来就甩一个高速8字轨迹炸机倒是小事调参效率极低。6. 踩坑现场仿真里的怪问题与修复经验6.1 起飞即坠落的完整排查链路“解锁之后飞机原地起飞然后立刻侧翻坠落”是我见过最多的问题我自己也踩过。第一次遇到千万别急着怀疑PX4按下面顺序排查。第一步检查Gazebo里传感器是否在正常输出。如果你在Gazebo窗口里看到飞机模型但机身下方没有传感器数据流标记比如IMU帧持续更新大概率是传感器插件没加载。重新启动仿真时先清空~/.gz或~/.gazebo缓存再试。第二步检查PX4是否真的收到了传感器数据。在pxh终端输入listener sensor_combined看传感器融合消息是否有持续输出。如果输出为空说明PX4没有和Gazebo正确连接。第三步检查状态估计器是否收敛。输入ekf2_status查看卡尔曼滤波的反馈看filter_fault_flags有没有明显异常。在正常SITL环境下GPS信号和IMU都是模拟的一般几秒钟内就能收敛。如果一直不收敛很可能你改了EKF2_AID_MASK之类的参数把它改回去。第四步检查飞机是否因为模型碰撞导致初始高度异常。如果Gazebo里飞机初始位置低于地面或者卡在物理模型里飞控会认为机体倾斜或高度异常解锁后自然会失控。重启Gazebo一般能解决。第六步才是看控制参数。如果你是全新默认参数PX4的默认PID不会导致瞬间侧翻所以默认配置下失控大概率就是上面几类问题。6.2 轨迹跟踪误差大或来回振荡如果飞机能飞但跟踪轨迹时误差忽大忽小或者来回振荡问题通常出现在参数匹配上。两个高频原因一是最大速度和加速度设置太小。PX4内部的位置控制器会把期望轨迹视为目标如果轨迹点的速度超过MPC_XY_VEL_MAX飞控会自动限速导致飞不过去、误差累积。解决办法是先算一下轨迹的最大速度和最大向心加速度再把这些参数设成至少1.5倍余量。二是发设定点的频率太低。PX4 v1.14中切换Offboard模式后如果超过0.5秒没有收到新的OffboardControlMode消息会自动退出Offboard模式。有些人的节点用1Hz或2Hz的频率发设定点飞机极限挣扎表现为来回“抽搐”。我的节点里用的是10Hz也就是100ms周期这个频率在SITL下足够稳定。6.3 仿真时间失真为什么飞着飞着变慢了还有一个很多新手容易忽略的问题Gazebo里的物理仿真时间可能和真实时间不一致。如果系统CPU占用过高仿真会变慢如果设置了headless模式有时候反而会变快。仿真时间失真的后果是轨迹跟踪的时间戳错乱。假如你的轨迹定义是x cos(0.5*t)ROS2节点按真实时间来算但PX4/Gazebo按仿真时间来算两个时间不同步飞机会不停试图追赶一个“未来”或“过去”的设定点表现为轨迹异常。最简单的方法是看QGC里显示的仿真时间或者Gazebo底部的时间倍数。正常情况下应该是1.0。如果明显偏离检查你是否同时开了多个Gazebo实例、是否在虚拟机上运行、是否加载了太多高精度的环境模型。仿真不是追求画面精致把路面纹理模型换成简单几何体性能立刻就能提升。6.4 从仿真到真机的落差提醒环境能跑通后很多人会自信地以为这套代码直接搬到真机就能用。这个心态很危险。仿真里一切数据都来得很干净传感器噪声是高斯噪声延时是固定的不存在丢包、磁干扰、震动耦合这些实际问题。真实环境里GPS在城市里有反射室内没有GPS要上视觉定位IMU会受到电机震动干扰气压计会受到螺旋桨下洗流影响。这些都是PX4里一堆“玄学”参数的来源。所以我建议把仿真环境当成“算法逻辑验证平台”而不是“真实效果模拟器”。在这个环境里你应该重点验证的是轨迹生成逻辑是否正确、Offboard控制模式是否稳定可靠、ROS2节点的时间管理和消息频率是否合理、任务中止逻辑是否有效。真机实验时再把安全逻辑、状态机加厚而不是直接抄仿真参数。最后分享一点个人体会。搭建这套环境最值钱的部分其实不是“能飞”这个结果而是你在这个过程中理解了飞控、仿真器、通信中间件这三层之间的依赖关系。后续你再做路径规划、避障、集群协同本质上都是在这条链路上增加新的ROS2节点而已。环境搭好之后多留段时间做数据分析和控制参数调优这比急着换机架、换传感器有价值得多。
返回列表