拓冰建站拓冰建站
首页 / 资讯中心 / 正文

PX4自主避障系统实战:从Gazebo仿真到Offboard链路搭建

玩PX4避障这个方向的人十有八九都卡过同一个问题明明飞控固件、地面站、仿真器都装好了飞机也确实能起飞但一遇到障碍物它就是直愣愣地撞上去。原因很直接——PX4本身并不包含一个“打开就能避障”的开关。自主避障是一个典型的组合工程涉及传感器选型、机载感知、路径决策和飞控控制四层协作。这篇文章我会以Ubuntu 22.04环境为基准完整过一遍从Gazebo仿真配置到自主避障系统落地的链路包括如何给仿真无人机添加激光雷达、如何写一个能用的避障决策节点、如何把避障结果安全地交给PX4执行以及我在联调过程中踩过的几个典型坑。无论你是刚开始接触PX4二次开发还是已经在SITL里飞过一段时间、正想往避障方向深入这篇内容应该都能给你一个能直接参考的起点。1. 避障系统的整体架构PX4为何不能“开箱即避”很多人看到PX4支持Offboard模式、支持MAVLink、支持外部指令控制就以为“避障”应该也集成在固件里实际不是这样。PX4是一套以状态估计和控制为核心的飞控系统它擅长的是把“目标位置”或“目标速度”转化成姿态和油门指令但“目标位置该往哪里设”这件事它默认不替你想。避障本质上是路径规划问题它需要实时感知环境、构建障碍物地图、在可行空间里重新计算路径这超出了飞控固件的职责范围。理解这个边界比会敲命令更重要。1.1 感知、决策、执行三层如何分工一套完整的PX4自主避障系统可以拆成三层感知层负责回答“哪里有障碍”。在Gazebo仿真里这通常是一个激光雷达模型或深度相机模型输出雷达扫描话题、点云或深度图。它的输出是原始数据不做判断。决策层负责回答“接下来往哪飞”。它订阅传感器数据维护一个局部障碍物地图然后基于当前目标点和飞机自身位置算出一段可行的速度指令或重新选一个临时目标点。这一层可以跑在机载电脑上也可以跑在仿真环境外部本质是一个独立的计算节点。执行层就是PX4飞控负责把决策层给出的指令变成实际运动。PX4在这里只认两类东西位置设定点Position Setpoint或者速度设定点Velocity Setpoint你可以通过Offboard模式持续地喂给它。这个分层的核心价值在于解耦。换传感器不用动飞控逻辑改避障算法不用重编译固件调PID参数也不会影响上层决策。实际工程里三层各自演进出问题也方便定位。比如飞机撞上障碍物先看感知层输出的雷达话题是否有值再看决策层发布的指令是否合理最后才查飞控执行哪一层断了直接暴露。1.2 两条实现路线的取舍在PX4生态里实现避障有两条路线很多人一上来就选错后面越走越痛苦。第一条是PX4固件内部的简单碰撞预防Collision Prevention。它需要外部把障碍物距离信息封装成特定的MAVLink消息发进飞控PX4内部再用这些距离值做简单的速度限制和刹车。好处是不需要自己写决策逻辑坏处是功能很弱只能做减速和简单的方向偏转没法做真正意义上的绕行规划而且官方对这套机制的维护投入也很有限。它比较适合“低速飞行时加减速保护”不适合作为自主导航的核心。第二条是外部规划器加Offboard模式也就是本文采用的方案。决策层完全在PX4之外运行自己订阅雷达数据、自己算避障路径、自己把速度或位置指令发到飞控。PX4只负责执行最后的控制。好处是算法自由度极高想用人势场、快速探索随机树、模型预测控制还是强化学习都由你自己决定跟你选用的传感器、机载电脑完全解耦。坏处是多了一个需要自己维护的决策节点工程链路长一些。对比以下两条路线的差异对比项内部碰撞预防外部规划器Offboard开发难度低配置参数即可高需要自己写节点避障能力简单减速和偏转可绕行、可重规划算法灵活性固定不可扩展完全自由调试难度黑盒较难定位每一层都可观测适用范围低速保护真正的自主飞行做避障系统建议直接选第二条。虽然前期链路长但一旦跑通后续所有能力扩展都在你的掌控范围内。2. 仿真环境搭建Ubuntu 22.04下的版本匹配与编译环境搭建是这套系统里最容易让人放弃的环节。PX4、Gazebo、ROS桥接这几个组件版本任意一个对不上都会冒出一堆莫名其妙的编译错误和运行崩溃。很多热搜问题比如PX4编译过不了、Gazebo界面一直在闪、WSL里装PX4装不上根源多半都是版本不匹配。2.1 版本矩阵的选择理由我这边测试下来比较稳的组合是Ubuntu 22.04 LTSPX4-Autopilot v1.14.3SITL固件源码Gazebo Classic 11注意不是Gazebo Ignition也不是新版的Gazebo SimROS2 Humble用于避障节点和Gazebo数据桥接为什么选这套组合而不是最新的PX4 v1.15加新版Gazebo因为PX4 v1.14.3是v1.14系列的收尾版本该修的bug基本都修了社区资料也最全绝大多数避障相关的仿真讨论都是基于这个版本沉淀下来的。PX4 v1.15开始默认转向新版Gazebo Sim它的模型格式、桥接方式、插件体系跟Gazebo Classic差异很大你要是照着网上旧教程去配会卡在奇怪的报错上。Gazebo Classic 11则是和PX4 SITL配合最成熟的一个版本激光雷达插件、地形、物体摆放这些都有人验证过。还有一个版本细节容易踩Ubuntu 22.04系统源里的Gazebo版本可能不是Classic 11如果你的系统之前装过Gazebo 8或Gazebo 9系统里可能出现多个gazebo可执行文件启动时默认调用了老版本导致界面闪退或者模型加载不全。这也是后面要单独排查的问题。2.2 从源码编译PX4的完整步骤先更新系统依赖然后拉取PX4源码。PX4官方提供了一个自动脚本会安装大多数编译依赖sudo apt update sudo apt upgrade -y git clone https://github.com/PX4/PX4-Autopilot.git -b v1.14.3 --recursive cd PX4-Autopilot bash ./Tools/setup/ubuntu.sh这个脚本会安装包括Gazebo Classic、编译工具链、Python依赖在内的一整套环境。脚本执行过程中可能会因为网络原因卡在某些包下载上尤其是接近末尾的geographiclib数据集下载这个环节失败率很高。脚本跑完后建议单独验证一下Gazebo能否正常启动gazebo --version如果输出版本号以11开头说明Gazebo Classic装好了。如果系统里存在多个版本想办法把11设成默认版本或者用绝对路径启动。接着编译PX4 SITL固件并启动Iris仿真make px4_sitl gazebo-classic_iris第一次编译会拉取很多子模块耗时取决于机器和网络通常二十分钟到一小时。编译成功后会看到Gazebo界面弹出里面是一台默认的Iris四旋翼同时终端里会打印出PX4的NuttShell交互界面输入top可以看到每一条uORB消息的频率输入help可以查看SITL环境下的常用命令。到这里PX4的仿真环境就已经跑通了。2.3 ROS2与PX4桥接准备避障节点需要用ROS2所以还要装ROS2 Humble以及Gazebo和ROS2之间的桥接包sudo apt install ros-humble-desktop sudo apt install ros-humble-gazebo-ros-pkgs另外PX4 v1.14通过Micro XRCE-DDS协议与ROS2通信需要在系统里装Agentsudo apt install micro-xrce-dds-agentPX4 SITL启动后在另一个终端运行MicroXRCEAgent udp4 -p 8888回到PX4的NuttShell界面输入dds start等几秒就能看到Agent终端里打印出新的客户端连接信息说明PX4和ROS2已经建立了通信。这一步是后面离线控制指令能够到达飞控的关键链路没有通信避障节点发布的指令就只是ROS2世界里的一堆话题消息飞机根本看不到。3. 给Iris装“眼睛”Gazebo模型改造与激光数据通路环境能飞之后第二件正事是让飞机在仿真环境里“看得见”障碍物。默认的Iris模型没有挂载任何测距传感器所以哪怕你把障碍物摆在它正前方十厘米它也不会做出任何反应。这一步需要自己往模型里加一个激光雷达并把雷达数据发布成ROS2话题。3.1 默认模型为什么没有雷达PX4仓库里的Iris模型位于Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/iris/iris.sdf。打开这个文件会发现机体模型只包含机身、旋翼、起落架这些结构件以及IMU、磁力计、气压计等飞控传感器并没有激光雷达或视觉传感器。这是因为PX4考虑的是通用性不同避障方案有不同传感器需求它不想在基础模型里帮你定死硬件配置。在真机上传感器是物理安装的但在仿真里你需要自己定义一个雷达link再用固定joint把它“拧”在飞机上最后给这个link挂一个ray类型的传感器插件。ray传感器是Gazebo里模拟激光雷达的机制它向周围发射射线碰到物体后计算距离再把结果打包成标准消息发出来。3.2 在iris.sdf里挂载ray传感器插件打开iris.sdf在模型的link列表后面追加一个激光雷达link和固定joint。我把雷达放在机架中心上方这样水平扫描范围不会被机身遮挡link namelaser_link inertial mass0.05/mass inertia ixx0.0001/ixx iyy0.0001/iyy izz0.0001/izz ixy0/ixy ixz0/ixz iyz0/iyz /inertia /inertial visual namevisual geometry cylinder radius0.03/radius length0.05/length /cylinder /geometry material ambient0.1 0.1 0.1/ambient /material /visual /link joint namelaser_joint typefixed parentbase_link/parent childlaser_link/child pose0 0 0.1 0 0 0/pose /joint然后在模型文件末尾的/model之前追加传感器定义。这段是Gazebo的插件世界它不属于机器人模型本身而是告诉Gazebo“在laser_link下面放一个激光雷达射程20米水平视角3.6弧度发布到名为/scan的ROS2话题”gazebo referencelaser_link sensor namelaser typeray pose0 0 0 0 0 0/pose update_rate10/update_rate ray scan horizontal samples360/samples resolution1/resolution min_angle-3.1415926/min_angle max_angle3.1415926/max_angle /horizontal /scan range min0.1/min max20.0/max resolution0.01/resolution /range /ray plugin namelaser_scan filenamelibgazebo_ros_ray_sensor.so ros namespace/drone/namespace remapping~/out:scan/remapping /ros output_typesensor_msgs/LaserScan/output_type frame_namelaser_link/frame_name /plugin /sensor /gazebo这一段有两点值得注意。第一是update_rate我设的是10Hz对避障决策来说已经够用如果设得太高比如50Hz每秒钟会产生海量射线计算Gazebo仿真负载会明显上升甚至导致画面卡顿。第二是frame_name必须和link名称一致否则rqt或rviz里看不到正确的TF变换关系。修改完模型后重启PX4 SITL然后在另一个终端用命令验证雷达话题ros2 topic list | grep scan ros2 topic echo /drone/scan --once如果能看到带ranges数组的消息说明雷达已经正常工作飞机有“眼睛”了。3.3 world文件里布置障碍物雷达有了但仿真世界里还得有能让雷达“看到”的东西。默认world文件在Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/iris.world它只包含地面和少量弱纹理特征。我在测试时习惯在world文件里手动添加几个圆柱体当作障碍物比搜索官方带障碍物的world文件更可控。在world标签内、include段之后追加model nameobstacle_1 statictrue/static pose5 0 0.5 0 0 0/pose link namelink collision namecollision geometry cylinder radius0.3/radius length1.0/length /cylinder /geometry /collision visual namevisual geometry cylinder radius0.3/radius length1.0/length /cylinder /geometry material ambient0.8 0.2 0.2/ambient /material /visual /link /model你可以根据测试需求多摆几个不同位置的柱子模拟一排迎风格挡或稀疏树林。柱子的半径和高度会影响无人机绕行的可选路径。建议不要摆得太密否则人工势场法容易陷入局部极小值飞机在障碍物前左右摇晃却出不去这是势场法的固有缺陷后面整定参数时我会再细说。3.4 界面闪屏与雷达异常的排查Gazebo界面闪屏是这个阶段的高频问题我自己也遇到过。闪屏通常不是Gazebo本身的逻辑错误而是渲染后端的问题。最常见的有三种情况一是系统里同时装了多个Gazebo版本启动时选错了可执行文件导致Vendor冲突和渲染崩溃二是虚拟机或远程桌面环境下OpenGL硬件加速不可用三是NVIDIA显卡驱动版本和Gazebo引用的Ogre渲染器不兼容。推荐排查顺序是先确认gazebo --version指到了11再用软件渲染环境变量测试显卡兼容性export LIBGL_ALWAYS_SOFTWARE1如果软渲染下半界面恢复稳定基本可以断定是硬件加速或驱动问题。这个环境变量虽然会让画面变卡但对功能验证没影响SITL仿真依然可以正常跑。还有一个很隐蔽的坑WSL2里跑Gazebo。WSL2对图形界面的支持依赖WSLg但Gazebo这种重型OpenGL程序在WSLg下的稳定性很差经常闪退、黑屏、残影不建议在这个环境里折腾仿真。雷达话题没数据的排查思路也类似先看模型文件里插件路径有没有写错再看Gazebo启动日志里有没有报插件加载失败最后确认话题名称是否完全一致。如果ros2 topic list里根本没有/drone/scan多半是插件没加载成功改完SDF要完全重启PX4仿真进程Gazebo不会在飞行中热加载模型修改。4. 避障决策节点人工势场法从公式到ROS2代码雷达数据就位之后接下来是避障的核心决策节点。这一节我会用人工势场法作为示例算法因为它直观、易于实现、调试成本低非常适合作为第一套能跑通的避障逻辑。4.1 势场法原理与参数直觉人工势场法的核心思想很朴素目标点对飞机产生“引力”障碍物对飞机产生“斥力”飞机沿着合力方向移动。可以把飞机想象成一个带正电的小球目标点是负电荷障碍物是同号电荷合力方向就是小球最自然的运动方向。引力计算很简单就是目标向量乘以一个增益系数F_att k_att * (goal - position)斥力则依赖于障碍物距离。离障碍物越近斥力越强而且增长要快否则飞机还没“感觉”到危险就已经撞上了。常用公式F_rep k_rep * (1 / r - 1 / r0) * (1 / r^2) * (obstacle - position) / r其中r是飞机到障碍物的距离r0是斥力作用半径。障碍物在r0之外完全不影响飞机进入r0后斥力开始起作用越近越强。这里有两个重要直觉第一k_att和k_rep的比例决定了飞机是更激进地飞向目标还是更保守地躲避障碍第二r0决定了飞机的“警觉范围”r0太小飞机看到障碍时已经在急刹车r0太大障碍物还没挡住路就开始绕路径会变得很绕。在Gazebo仿真里雷达给出的障碍点天然带有角度和距离遍历所有雷达点就能计算每个点贡献的斥力。4.2 一个可用的ROS2避障节点示例下面这个节点可以在ROS2里直接编译运行。它订阅激光雷达话题计算合力发布TwistStamped速度指令。为了让代码更接近实际项目我在示例里预留了位置订阅接口你可以从PX4桥接话题里获取飞机当前位置也可以先固定初始位置做算法验证。import math import rclpy from rclpy.node import Node from sensor_msgs.msg import LaserScan from geometry_msgs.msg import TwistStamped class PotentialFieldAvoidance(Node): def __init__(self): super().__init__(potential_field_avoidance) self.scan_sub self.create_subscription( LaserScan, /drone/scan, self.scan_callback, 10) self.cmd_pub self.create_publisher( TwistStamped, /drone/cmd_vel, 10) self.goal [15.0, 0.0] self.position [0.0, 0.0] self.k_att 0.8 self.k_rep 0.6 self.safe_dist 1.0 self.influence_dist 2.0 self.max_speed 3.0 self.latest_scan None self.timer self.create_timer(0.05, self.control_loop) def scan_callback(self, msg): self.latest_scan msg def control_loop(self): if self.latest_scan is None: return # 引力 dx self.goal[0] - self.position[0] dy self.goal[1] - self.position[1] dist_goal math.hypot(dx, dy) if dist_goal 0.5: self.get_logger().info(Reached goal) return fx self.k_att * dx / dist_goal fy self.k_att * dy / dist_goal # 斥力 angle_min self.latest_scan.angle_min angle_inc self.latest_scan.angle_increment ranges self.latest_scan.ranges for i, r in enumerate(ranges): if r self.latest_scan.range_min or r self.influence_dist: continue if r self.latest_scan.range_max: continue angle angle_min i * angle_inc ox r * math.cos(angle) oy r * math.sin(angle) strength self.k_rep * (1.0 / r - 1.0 / self.influence_dist) / (r * r) fx - strength * ox / r fy - strength * oy / r # 限幅 speed math.hypot(fx, fy) if speed self.max_speed: fx fx / speed * self.max_speed fy fy / speed * self.max_speed cmd TwistStamped() cmd.header.stamp self.get_clock().now().to_msg() cmd.twist.linear.x fx cmd.twist.linear.y fy self.cmd_pub.publish(cmd) def main(argsNone): rclpy.init(argsargs) node PotentialFieldAvoidance() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ __main__: main()这个节点发布的是/drone/cmd_vel但PX4并不会直接订阅这个话题所以实际接入飞控前还需要一个桥接转换这是下一小节的内容。另外这个节点里self.position是写死的完整项目中应该从PX4的本地位置话题实时读取否则飞机飞远了避障算法仍然以为自己在原点斥力方向全是错的。4.3 接入PX4Offboard模式与消息桥接PX4 v1.14通过Micro XRCE-DDS与ROS2互通PX4的uORB消息会被映射成ROS2消息在px4_msgs包里定义。避障节点最终要发布的是px4_msgs/msg/VehicleVelocitySetpoint这个uORB消息对应PX4内部的vehicle_velocity_setpointOffboard模式下飞控直接跟踪它。这里先解释一下Offboard模式的使用约束。PX4设计上把Offboard视为“高可信外部命令”为了防止通信中断后飞机失控它要求外部指令必须以至少2Hz的频率持续发送。如果在期望的时间窗口内没有收到新的设定点飞控会自动退出Offboard模式并切换到降落或悬停。所以避障节点不能等到算出新指令才发布一次而是要保持固定频率持续输出即使当前没有新的避障调整也要把上一次的指令或者目标方向重新发一遍。具体桥接方式每个版本的px4_ros_com示例工程里都有一个offboard_control示例节点里面给出了两段关键代码。第一段是切换Offboard模式VehicleCommand offboard_mode{}; offboard_mode.command VehicleCommand::VEHICLE_CMD_DO_SET_MODE; offboard_mode.param1 1.0; offboard_mode.param2 6.0; offboard_mode.target_system 1; offboard_mode.target_component 1;param1为1表示启用自定义模式param2为6在PX4中代表Offboard。第二段是解锁指令param1为1表示上锁状态改为解锁。注意执行顺序通常先持续发布设定点再切换Offboard模式最后解锁。如果顺序反了飞控可能因为未收到有效设定点直接拒绝进入Offboard。在我测试的过程中发现很多新手在从TwistStamped转到VehicleVelocitySetpoint时容易犯同样的错直接把Twist的线速度填进去但忘了设置timestamp字段。PX4的uORB消息对时间戳敏感如果时间戳是0飞控会认为这是过期数据而忽略。正确写法是auto velocity_setpoint px4_msgs::msg::VehicleVelocitySetpoint{}; velocity_setpoint.timestamp static_castuint64_t(node.get_clock()-now().nanoseconds() / 1000); velocity_setpoint.x cmd_vel_x; velocity_setpoint.y cmd_vel_y; velocity_setpoint.z 0.0f; velocity_setpoint.yawspeed 0.0f;这样避障节点算出的速度指令才能真正“驱动”仿真飞机。桥梁通了以后避障系统的主链路就完整了雷达数据话题 → 避障算法 → 速度设定点 → 飞控执行。5. 联调与整定让避障从“会响应”到“飞得稳”链路接通不等于系统稳定。我见过不少项目在Gazebo里让飞机成功绕开了一根柱子但换一个障碍物布局就撞上去了或者避障时姿态剧烈晃动、来回抽搐这些基本都出在联调顺序和参数配合上。5.1 联调顺序我的经验是严格按照“底层到上层、手动到自动”的顺序推进每个环节确认无误后再进入下一环节。第一步验证雷达话题。在Gazebo里手动拖动无人机位置或者把障碍物放在不同方向观察/drone/scan数据的变化确认雷达能正确感知各个方向的障碍物。第二步验证速度指令能否驱动飞机。用一个最简单的手动发布程序向PX4发一个固定方向的速度设定点观察飞机是否朝这个方向运动。如果飞机不动先查Offboard模式有没有真的进入再看速度设定点的时间戳有没有正确填写。第三步验证避障算法。先不让飞机起飞只让避障节点跑起来用ros2 topic echo观察它输出的速度指令手动设置不同的雷达输入确认算法在“障碍物在左前方”时输出向右的避障速度“障碍物在正前方”时输出减速或者大幅变向。第四步才是真正的飞行测试。让飞机起飞并飞向目标点观察它在遇到障碍物时的动态响应。这一步如果直接出现异常不要急着改算法参数先回看前几步的验证结果问题往往出在数据链路上而不是算法上。5.2 关键参数整定表人工势场法的参数不多但每个都直接决定飞行表现。我在多次测试后总结出一组比较稳妥的起步参数参数含义推荐起始值调参方向k_att目标引力系数0.8提高则更直飞目标可能忽略障碍k_rep障碍斥力系数0.6提高则绕行更果断但会绕远路influence_dist斥力作用半径2.0m增大则提前避让路径更平滑safe_dist最小安全距离1.0m增大则更安全但通道狭窄时可能卡死max_speed最大速度指令3.0m/s降低则反应更从容测试期建议1.5需要特别说的是k_att和k_rep的比例关系。这两个参数不是独立调的它们共同决定了飞机的“性格”。k_att相对太大会让飞机顶着障碍物前进斥力被引力抵消最后撞上k_rep相对太大会让飞机离老远就开始绕飞行路径变成一个大弧线甚至出现目标点附近来回振荡。调参时建议固定一个调另一个观察变化趋势再微调。安全距离和最大速度在生产测试阶段也应该收紧安全距离设大一点最大速度设小一点给系统留足反应时间跑通后再逐步放开。5.3 EKF2状态估计对避障的影响还有一点在仿真里容易被忽略但决定这套系统能否迁移到真机的关键PX4的EKF2状态估计器。避障节点算速度指令时默认认为飞机的当前位置、当前速度是真实准确的实际上PX4拿到的都是EKF2融合之后的估计值。EKF2的工作方式可以简单理解成用IMU的加速度和角速度做状态预测用GPS、光流或视觉里程计做观测修正根据传感器噪声和协方差决定最终相信预测还是相信观测。在Gazebo仿真里传感器数据近乎完美EKF2估计的位置基本等于真实值所以避障表现很好。但如果你把同一套避障逻辑搬到真机上室外GPS受多径效应影响、室内光流容易漂移EKF2估计出的位置可能滞后真实位置几百毫秒。这意味着避障算法眼中的飞机位置和真实位置有偏差你以为飞机还离障碍物一米实际上已经只有半米了。这也是为什么很多仿真里跑得很顺的避障系统一上真机就撞墙的根本原因。所以在联调阶段就要养成看EKF2状态的习惯。在QGroundControl里查看EKF2的预测位置和GPS观测位置差值确认状态估计是健康的再飞。仿真阶段虽然没有真机那么严重但雷达数据更新率如果太低也会在时间维度上引入类似滞后的效果。避障算法本身也可以做一定的鲁棒性补偿。比如速度指令不要直接指向合力方向而是对合力方向做平滑滤波避免单帧雷达噪声引起剧烈方向跳变或者在斥力计算里只取最近的一个或两个障碍点防止密集障碍物环境下势场互相抵消导致路径来回震荡。这些小技巧本质上都是在为状态估计不确定性和传感器噪声留余量。6. 从SITL到真机的几个重要提醒Gazebo仿真跑通避障系统只是完成了一半。把这套逻辑搬到真机上之前有几个差异和红线必须提前想清楚否则轻则炸机重则伤及人员。这里不展开讲真机调试细节只列我认为最重要的三点。6.1 仿真与真机的三个关键差异第一是传感器噪声。仿真里的激光雷达数据几乎是理想的只在障碍物边界存在少量误差。真机的激光雷达或深度相机会有随机噪声、镜面反射、吸收材料导致的漏检避障算法的输入质量会直接下降。应对方式是加传感器数据过滤比如对同一角度的雷达连续多帧取中值或者把安全距离在前端故意加大。第二是执行延迟。仿真里速度指令发出后飞机的响应几乎是瞬时的因为电机模型理想、电池电压稳定。真机上从机载电脑发出指令到PX4执行到电机转速变化到飞机实际加速整个链路里有几十毫秒到上百毫秒的延迟。避障算法必须把这个延迟算进“安全距离”里否则高速飞行时飞机永远在指令之前就已经撞上了。第三是EKF2状态估计的质量。这一点在上一节详细说过真机上GPS多径、磁干扰、振动都会让状态估计变得不确定。而避障算法完全依赖这个估计值判断飞机和障碍物的相对关系估计不准避障就是无本之木。6.2 真机测试安全基线如果你决定把避障系统搬到真机下面这几条是我个人的硬性要求首次测试选在开阔空旷的场地场地里不要有人不要摆真实障碍物先用人为设置的“算法虚拟禁飞区”验证避障逻辑第一次试飞时切换Offboard模式前必须手动解锁并在悬停状态下验证模式切换是否正常发现异常立即切回手动模式必须设置一个可靠的遥控器急停开关在PX4里面把急停和手动模式映射到明显位置同时保证任何情况下遥控器都能夺回控制权避障系统里所有速度指令和位置指令做双重限幅即使算法输出异常也不会超出安全飞行包线至少安排两个人配合测试一个人盯飞机姿态另一个人盯地面站日志和避障节点输出任何一方发现异常都喊停。6.3 固件与硬件层面的注意点真机避障系统还会牵扯到PX4固件烧录和硬件接线。装机前先确认飞控的Bootloader和固件版本匹配使用QGroundControl烧录固件时不要断电电机顺序严格按照飞控的说明书接线不同飞控型号的电机编号和转向定义不同顺序接错解锁瞬间就会翻机。机载电脑和飞控之间的通信方式室外建议走UART或CAN这类确定性接口室内近距离测试用WiFi临时验证可以但正式飞行不要依赖无线链路链路抖动在高速飞行中直接等价于控制延迟增加。这些内容看起来跟避障算法无关但避障系统是典型的“最后一公里决定成败”的工程。不管你在仿真里把算法调得多漂亮硬件和通信链路不稳定一切都是零。很多团队项目死在真机调试阶段不是因为避障算法不行而是因为这些看起来“太基础”的细节没人认真对待。从我个人的实操体会来说自主避障系统的复杂度不在任何一个局部而在整条链路的打通。雷达数据进算法、算法输出进飞控、飞控状态再反馈给算法环环相扣每一环都有一堆说不完的细节。这也是为什么建议一定要先在Gazebo仿真里把它完整跑通一遍因为仿真虽然不能替代真机但它能让你把“链路逻辑”和“算法调参”这两件事彻底搞清楚等到真机上只剩“工程适配”这一个变量时你才有足够的能力去分辨问题是出在算法设计还是出在硬件工程上。我建议你按这篇文章的顺序先把环境、雷达、避障节点、Offboard链路逐项跑通再回头去优化避障效果中间每卡住一次都是一个深入了解PX4内部机制的好机会。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门