
做四旋翼定点降落这个方向的人应该都有同感视觉位姿解算搞得再漂亮最后能不能落准很大程度卡在飞控和机载电脑之间的那根“数据链”上。上一篇文章我写了单目视觉如何做目标检测和位姿估计从图像里拿到无人机相对降落标志的x、y、z偏移和偏航角。但拿到这些数据只是完成了“感知”怎么把这些数据喂给Pixhawk怎么让飞控乖乖按着这个偏移去调整位置最后平稳落到标志点上这才是实战里最磨人的部分。这篇文章就聚焦在MAVROS和Pixhawk这一侧把我踩过的坑、验证过的配置和完整的数据流实现讲清楚。这篇内容的核心链路是机载电脑运行视觉节点→ MAVROS → Pixhawk → 电机。适合已经能跑通视觉识别、手里有Pixhawk或同类飞控、准备把视觉结果真正接进飞控控制环的开发者。无论你是用PX4还是ArduPilot固件这篇文章里的思路基本通用只是话题名称和参数有所区别我会在正文里同步标注差异。1. 系统整体设计与通信链路解析1.1 单目视觉定点降落到底需要哪些模块协同单目视觉定点降落听起来好像只是“看到一个点然后飞过去”但实际上整个系统至少包含四个层次感知层、估计层、控制层和执行层。感知层就是摄像头和视觉检测算法负责从图像中识别降落标志常见做法是用ArUco码、AprilTag或者自训练的深度学习目标检测网络。检测到标志之后要用PnP算法或几何关系解算出无人机相对标志的位姿。单目相机没有深度信息所以测距一般依赖已知标志的物理尺寸这是一个很关键的约束我在上一篇文章里专门提过。估计层负责把视觉输出的位姿在时间轴上做平滑和滤波。单目视觉单帧解算出来的数据往往伴有抖动和跳变直接拿去控制飞机会造成机身晃动甚至失控。一般用扩展卡尔曼滤波或者互补滤波融合视觉数据和IMU数据也可以先在PX4内部把视觉数据作为外部位置测量源接入让飞控自带的EKF2来做融合这样实现更简洁也是我最终采用的方式。控制层就是飞控内部的位置控制器。Pixhawk运行PX4或ArduPilot固件它根据机载电脑发给它的期望位置、速度或姿态指令计算油门和电机转速。MAVROS在这里起的作用是“桥梁”和“翻译官”它把机载电脑上由视觉位姿生成的控制指令翻译成飞控能理解的MAVLink消息。执行层是电调和电机这一层飞控自己处理但作为开发者你得理解控制链路延迟和响应特性因为视觉控制天然比手动遥控多了一截通信和计算延迟整条链路的时序分配一定要心里有数。1.2 MAVROS在整条链路中的位置与作用MAVROS是运行在机载电脑比如NVIDIA Jetson、树莓派或Intel NUC上的ROS节点它通过串口UART或USB与Pixhawk连接将MAVLink协议转换为ROS的话题、服务和参数让你可以用ROS标准方式访问飞控。在这套系统里MAVROS承担三类核心任务。第一类是读取飞控状态包括无人机当前的位置、姿态、速度、电量、飞行模式等这些信息以mavros/state、mavros/local_position/pose、mavros/imu/data等话题对外发布。第二类是发送控制指令包括解锁/上锁arming/disarming、切换飞行模式比如从Offboard切到Land、发送期望位置/mavros/setpoint_position/local、期望速度/mavros/setpoint_velocity/cmd_vel或期望姿态加推力/mavros/setpoint_raw/attitude。第三类是参数读写与任务服务通过mavros/param读取飞控参数或者调用/mavros/cmd/arming、/mavros/set_mode等服务完成特定操作。对于定点降落我最常用的消息是/mavros/setpoint_position/local。这条消息里携带的是无人机的期望位置相对于起飞点或机体原点视觉节点计算出无人机相对标志的偏移把它换算成期望位置再把期望位置以10Hz到30Hz的频率持续发给飞控。这里的核心逻辑是飞控处于Offboard模式时会以机载电脑发送的指令为控制基准。所以你把期望位置发给它飞控内部的位置控制器就会自动驱动无人机飞到那个位置。视觉检测到无人机偏左了就更新期望位置让无人机往右飞这样形成一个闭环。1.3 为什么选MAVROS而不是直接串口或其它SDK有人会问视觉算出来的结果直接通过串口发给飞控不就行了为什么要引入MAVROS这一层这个问题我一开始也纠结过。如果你的控制逻辑极其简单完全可以在飞控固件内部做视觉位置融合比如PX4可以直接接收视觉里程计MAVLink消息。但一旦你需要在机载电脑上做逻辑判断、做落点切换、做GPS失效保护、做视觉丢失后的应急策略MAVROS提供的工程框架优势就非常明显了。MAVROS的优势至少有三点。第一ROS话题机制天然适合多节点协作。视觉节点、决策节点、控制节点、日志节点可以解耦运行视觉算法崩溃了不拖垮控制链路这在实际飞行中非常重要。第二MAVROS内部已经处理了MAVLink协议的大量细节包括消息分帧、重传、心跳检测、时间戳同步这些工作如果自己从串口层面做工程量很大而且容易出错。第三ROS生态里现成的工具链可以加速调试比如用rqt看话题曲线、用rosbag记录飞行数据、用tf做坐标变换这些都是自己写串口协议很难快速实现的。当然MAVROS也有开销它依赖ROS环境意味着机载电脑上得装一套完整的ROS系统。对于资源受限的嵌入式平台来说这会比较吃力。但对四旋翼定点降落这种典型的研究级项目机载电脑一般是Jetson或NUC级别运行ROS完全没有问题。2. 环境搭建与MAVROS配置要点2.1 飞控固件版本与机架参数校准先说固件选择。PX4和ArduPilot都能做Offboard控制但我的经验是做视觉定点降落这类偏研究性质的项目PX4的可配置性和MAVROS的配合度更好一些。PX4对MAVLink外部控制指令的支持非常完整而且文档和社区案例也更多。ArduPilot也不是不行但GUIDED模式下的消息格式和PX4的Offboard模式略有差异调试起来要额外注意。我用的是PX4 1.13及以上的稳定版本差配套的QGroundControl地面站做固件升级和参数校准。收到一套新Pixhawk先别急着接视觉我习惯按这个顺序做基础校准加速度计校准QGC里按照机架朝向逐步旋转机体补偿安装误差。陀螺仪校准保持机体静止采集零偏。磁力计校准在室外或远离大金属物体的地方按六个方向旋转机体这个步骤在室内有强磁场干扰时容易失败建议直接跳过或使用外部罗盘。遥控器校准确认摇杆行程映射正常并在失控保护里设置好RC Lost动作。电调校准如果用的是PWM电调需要对油门行程做一次统一校准如果是DShot或CAN电调这一步可以省去。校准完成之后有一个参数很关键SYS_AUTOSTART机架类型。选错机架类型会导致飞控的输出通道和电机映射混乱起飞时直接翻机。四旋翼X型选4001左右对应的机架具体要看固件版本里的定义。2.2 MAVROS安装与参数配置MAVROS的安装在官方wiki上有标准流程Ubuntu环境下我建议用二进制包安装省去编译时间。如果使用ROS NoeticUbuntu 20.04sudo apt install ros-noetic-mavros ros-noetic-mavros-extras sudo /opt/ros/noetic/lib/mavros/install_geographiclib_datasets.sh第二个脚本是下载GeographicLib数据集很多人装完MAVROS后会卡在这一步导致启动报错。如果你网络状况不好这个数据集可能下载很慢甚至需要手动下载放到指定目录具体的路径脚本运行后会提示。MAVROS的启动配置文件是px4_config.yaml和apm_config.yaml分别对应PX4和ArduPilot。我通常不在系统安装目录改配置而是把整个配置目录拷到自己的工作空间里然后在launch文件里指定路径这样方便版本管理。我的启动launch文件大致长这样launch arg namefcu_url default/dev/ttyACM0:921600 / arg namegcs_url default / arg nametgt_system default1 / arg nametgt_component default1 / node pkgmavros typemavros_node namemavros requiredtrue outputscreen param namefcu_url value$(arg fcu_url) / param namegcs_url value$(arg gcs_url) / param nametarget_system_id value$(arg tgt_system) / param nametarget_component_id value$(arg tgt_component) / rosparam file$(find mavros)/launch/px4_config.yaml / /node /launch这里最容易出问题的是fcu_url参数的配置。/dev/ttyACM0是Pixhawk通过USB连接Linux电脑时最常见的设备节点波特率通常用921600这是Pixhawk USB接口的默认速率。如果是接在Jetson的UART口上设备节点一般是/dev/ttyTHS1波特率要根据你在飞控上配置的SER_TEL1_BAUD参数来对应设置。还有一个非常实用的启动参数是gcs_url。如果你希望通过MAVROS同时把数据发送到QGroundControl显示可以在启动时指定gcs_url : udp://127.0.0.1:1455014555相当于MAVROS又当作一个地面站的转发网关。这个功能我实际使用中特别有用调试时可以同时用QGC看到飞行状态、NAVIO或者自己的ROS节点同时算位置不用来回切换。2.3 机载电脑与飞控的连接方式连接方式直接影响整条链路的稳定性和实时性这里值得展开讲讲。首选是USB直连。Pixhawk板载的USB口连接到机载电脑的USB口Linux会枚举出/dev/ttyACM0设备节点。这种方式的优点是不需要额外配置飞控的串口参数即插即用而且USB供电理论上能同时给飞控供电但我不建议用USB给飞控供电做动力飞行因为USB线的压降和接触不良会带来意外断电风险。USB最大的问题是在高振动环境下容易松动飞控附近电机转动带来的震动可能让Micro USB口接触不良一旦断连整条链路就废了。第二种是UART串口连接。这是我最推荐的稳定方案。飞控上有专门的TELEM1或TELEM2接口接三根线TX、RX、GND到机载电脑的串口引脚上。NVIDIA Jetson系列提供比较多串口树莓派有40Pin GPIO上的UART。这种连接不依赖USB枚举稳定性很高而且可以同时保留USB口给地面站调试。飞控端需要把对应的SER_TEL1_BAUD参数设为921表示921600或576表示57600机载电脑的MAVROS启动文件里把波特率匹配上。串口连接有几点需要注意电平和电压。飞控的UART一般是3.3V TTL电平而有些电脑串口是5V电平直接接会烧飞控的串口芯片需要加逻辑电平转换模块。另外TX和RX要交叉连接飞控的TX接电脑的RX飞控的RX接电脑的TX接反了会收不到任何数据这类问题在论坛里经常见到。第三种是通过数传电台连接。如果飞控和机载电脑之间距离较远或者不方便布线可以用一对数传模块飞控端接TELEM口机载电脑端接USB或串口。这种方式的延迟比前两种高不少对视觉控制这种需要快速响应的场景不太适合我只在地面调试阶段用来做远程监控不用于实际控制。3. 从视觉算法到控制指令的实操实现3.1 视觉位姿估计结果如何进入MAVROS这一步是整个系统的“翻译”环节。视觉节点解算出的数据是无人机相对降落标志的平移向量和旋转向量而飞控期望的是自身在某个坐标系下的位置。两者之间的桥梁就是坐标变换。我使用的视觉输出形式是geometry_msgs/PoseStamped话题名/vision/pose。坐标定义采用“相机系到机体系到导航系”的链路视觉算法输出的是标志在相机坐标系下的位姿我先通过相机到机体的外参矩阵标定得到把标志位姿转换到机体坐标系下再根据无人机当前在导航系下的位置和姿态把“标志在机体系下的位置”转换到“标志在导航系下的位置”。因为在定点降落场景中降落标志通常固定在地面上所以这个导航系下的标志位置就是一个固定点。无人机要做的就是飞向这个点。这一层转换如果用代码表达在ROS里最优雅的方式是TF树。相机坐标系camera_link固定在机体坐标系base_link下这两者之间是静态变换启动时发布一次即可。标志坐标系target_marker由视觉节点实时发布相对于camera_link的变换就是PnP解算出来的结果。飞控自身的姿态位置则由MAVROS发布的比例TF来维护。关键点来了视觉计算出的“标志位置”最终要换算成/mavros/setpoint_position/local里的期望位置。设定local坐标系的原点是起飞点所以你得知道标志点相对于起飞点的坐标。如果每次起降都在同一个固定点这个坐标可以提前量好写死。如果是移动降落点或随机投放那就需要在视觉锁定的那一刻用无人机当前位置加上相对偏移来推算出标志的绝对位置。换算公式很简单标志在导航系下的位置 无人机当前在导航系下的位置 无人机姿态旋转矩阵 × 标志在机体系下的位置。具体到代码里如果视觉位姿的平移向量描述的是“标志相对相机的平移”t_cam_marker那么import tf from tf.transformations import quaternion_matrix # 假设已经得到视觉输出的位置和四元数 # 变换链: camera_link - base_link - map/local_origin # 先转成4x4矩阵连乘变换再取平移分量这个过程最容易出错的就是坐标系的轴方向和单位。ROS标准坐标系是右手系X向前、Y向左、Z向上。但相机的OpenCV坐标系是Z向前、X向右、Y向下。从OpenCV的POSIT或PnP结果转到ROS相机坐标系通常需要将Y轴取反、Z轴取反做变换具体要看你的视觉算法输出的定义。越是这种细节越容易让无人机飞歪我在这上面吃过不止一次亏建议你写完转换后先做一个静态验证把无人机放在标志正上方看视觉输出计算出的标志在导航系下的位置是否和实际量测值一致。3.2 Offboard模式下如何用setpoint控制四旋翼PX4的Offboard模式是视觉定点降落的核心控制模式。它的意思是位置、速度或姿态的期望值由外部机载电脑提供飞控只负责内部的姿态和电机控制。MAVROS发送的setpoint消息到达飞控后PX4会按消息类型进入不同的控制回路。对于定点降落我用的是/mavros/setpoint_position/local话题消息类型是geometry_msgs/PoseStamped。发送频率建议保持在20Hz以上。PX4内部有检测机制如果一段时间内没有收到新的setpoint会自动退出Offboard模式这个时间窗口是500毫秒。如果发得太慢飞控会认为链路中断立即切回预设的失控保护模式在空中这是非常危险的。所以即使你的视觉位姿更新频率只有10Hz控制指令的发送线程也必须独立运行在20Hz以上。如果在某个周期没有新的视觉数据就重复发送上一个有效的期望位置而不是停止发送。这个逻辑我在工程上叫“保持型setpoint”它能让控制指令流不断流视觉数据偶尔跳一帧也能维持稳定。再说控制数据结构。发送局部位置期望时我通常这样设置pose PoseStamped() pose.header.stamp rospy.Time.now() pose.header.frame_id map pose.pose.position.x target_x pose.pose.position.y target_y pose.pose.position.z target_z pose.pose.orientation.x 0.0 pose.pose.orientation.y 0.0 pose.pose.orientation.z 0.0 pose.pose.orientation.w 1.0这里frame_id理论上不影响MAVROS实际解析PX4关心的是消息里的数值不是frame_id但建议还是写map或者local_origin方便在rviz里核对数据。在降落过程中如果需要控制无人机的偏航角对准标志只需要把四元数部分改为期望偏航对应的四元数即可。如果降落标志带有方向要求这个功能就很有用。还有一个实用技巧PX4对局部坐标系下的setpoint是“增量优先”还是“绝对位置优先”取决于MPC_POS_MODE参数。默认情况下PX4在Offboard模式接收位置指令时是将收到的位置作为绝对位置控制目标。这意味着你发给飞控的坐标必须是导航系下的绝对坐标而不是增量偏移。这一点和ArduPilot的GUIDED模式有本质区别ArduPilot可以用SET_POSITION_TARGET_LOCAL_NED消息里的type_mask来决定是绝对位置还是相对偏移。所以迁移到PX4时代码逻辑要做相应调整。3.3 降落判据与油门控制切换逻辑视觉引导飞机飞到标志点上空之后不能一直停在Offboard模式下让飞控执行位置保持因为高度很低的时候气压计和GPS的垂直精度都会变差而且Offboard模式需要机载电脑持续发setpoint一旦链路出问题飞机可能会在原地悬停而不是降落。所以我设计了一套分阶段的降落逻辑。阶段一高位逼近。高度3米以上视觉锁定标志输出期望位置为标志正上方期望高度逐步下降但每次下降不超过0.5米留出视觉算法响应的时间。阶段二低位对正。高度在0.5米到3米之间视觉仍然作为主输入但我会把水平方向的控制误差阈值收紧只有当水平偏移小于0.1米时才继续下降否则先修正水平位置再下降。这是因为近距离时单目视觉测距的误差会被放大高度越低同样的像素误差对应的实际偏移越小但抖动也越明显过早下降容易砸地。阶段三切Land并接管。当视觉估计高度低于0.5米时我直接调用MAVROS的/mavros/set_mode服务把飞控从Offboard模式切换到AUTO.LAND模式。这样做的好处是最后几十厘米的触地阶段完全由飞控内部的地面检测和油门管理控制不需要依赖外部视觉指令。Pixhawk在LAND模式下会自动检测着陆电流或持续接地状态然后锁定电机比外部控制更可靠。这里我认为最重要的原则是视觉控制负责把无人机带到“足够安全”的位置触地动作交给飞控自己的降落逻辑。有人会坚持全程Offboard视觉高度持续控制到0高度我试过最大的问题是视觉测距的低度误差和地面效应会叠加无人机可能在离地20厘米处被判为已触地而电机停转直接摔下来。切LAND模式是我经历过多次摔机之后总结出来的稳妥方案。切换模式的服务调用代码大致如下from mavros_msgs.srv import SetMode rospy.wait_for_service(/mavros/set_mode) try: set_mode rospy.ServiceProxy(/mavros/set_mode, SetMode) resp set_mode(custom_modeAUTO.LAND) except rospy.ServiceException as e: rospy.logerr(模式切换失败: %s, e)注意custom_mode字符串要按固件定义传PX4里是AUTO.LAND有些老版本固件写作AUTO.LAND依然是标准形式但实际执行时QGC界面显示可能是“Land”。切换前建议把飞机高度拉到一个安全阈值内然后观察使能返回结果再判断要不要继续做视觉引导。4. 调试手段与常见问题排查实录4.1 用QGC和MAVROS同时调试的正确姿势视觉定点降落这个系统最讨厌的问题是一旦飞起来你很难判断问题出在视觉、控制还是通信哪一个环节。所以我强烈建议在地面阶段就把监控体系搭好。第一在QGC里打开“MAVLink Inspector”实时观察飞控收到的MAVLink消息流。重点看POSITION_TARGET_LOCAL_NED有没有持续收到以及消息里的数值是否和你想发的一致。如果消息没有刷新说明MAVROS和飞控的通信有问题如果数值不对说明setpoint生成逻辑有问题。第二用rosbag record记录关键话题/mavros/local_position/pose、/mavros/state、/mavros/setpoint_position/local、/vision/pose。降落完成之后回放bag把所有话题按时间轴对齐回放可以很清楚地看到整个过程中视觉数据和飞控期望数据的对应关系定位到底是哪一环先崩溃的。第三成本最低但也最有必要的调试方式桌面推演。把飞机固定在一个可以自由转动但不会升空的测试台架上或者干脆只推油门低于起飞阈值让飞控处于Offboard模式却不起飞。这时你可以手动移动标志板观察视觉节点输出的目标坐标变化再观察MAVROS发出去的期望位置是否同步变化。我在实验室里用一个纸箱和一台固定无人机做了无数次的推演把所有坐标系转换问题、消息频率问题、模式切换问题全部在地面暴露干净才上真机。4.2 常见问题速查表我在调试过程中遇到过的问题整理成一张速查表基本涵盖了大部分新手会踩的坑。现象可能原因排查手段与解决思路MAVROS启动后一直等不到飞控心跳串口设备名不对、波特率不匹配、线序错误先检查ls /dev/ttyACM*再用screen或minicom看串口是否有数据输出飞控能收到心跳但/mavros/state里connected长期为falseMAVROS只连上了地面站通道飞控和目标系统ID不匹配确认target_system_id和target_component_id与飞控一致通常都是1视觉节点正常但无人机不动Offboard模式没进入、setpoint频率太低、type_mask设置不对在QGC手动切Offboard试一次并用rostopic hz检查setpoint频率无人机往错误方向飞坐标系方向定义反了、视觉输出符号错了在地面台架上逐步验证每个轴把标志板向左移看期望位置是否也向左高度越低抖动越大视觉测距噪声放大、控制增益需要调整视觉端加大滤波飞控端降低MPC_XY_P和MPC_Z_P响应或者提高视觉数据频率Offboard模式进入后1秒内退出PID参数未整定、飞机在剧烈振荡、RC摇杆干扰先检查/mavros/state里的mode看是否真的切到了Offboard并检查是否收到OFFBOARD使能确认降落触地后电机不停LAND模式触发条件没满足、接地检测参数不匹配检查LNDMC_*相关参数或收到落点信号后手动上锁4.3 实测心得哪些坑我建议你提前避开先说通信层面。很多人做第一次视觉降落时会直接用USB连接机载电脑和Pixhawk图方便。但飞机一震动USB口就容易接触不良导致MAVROS断连飞控自动切出Offboard模式。建议所有实机飞行都走UART串口并且用扎带或热熔胶固定接头。这一个小小的改变直接决定了你以后是“反复重试”还是“顺利落地”。其次是视觉数据的时间戳。MAVROS内部对消息时间戳有一定容忍度但如果视觉节点发布的消息时间戳明显滞后或跳变会引起飞控端EKF2的异常表现为位置漂移或拒绝融合视觉数据。所以视觉节点发PoseStamped时header.stamp必须用当前时间不要用图像采集时间戳除非你做了精确的时钟同步。我们一般直接用rospy.Time.now()保证时间戳单调递增。再就是降落坡度问题。我见过很多人在模拟器里做得好好的真机一上就翻车原因就是真机存在桨效和地面效应而且视觉延迟比模拟器里高得多。我建议第一次真机降落时先在飞行日志里确认从视觉数据发布到飞控收到setpoint的延迟如果这个时间超过200毫秒就必须优化链路减少视觉图像处理的降采样时间、关闭不必要的ROS日志打印、提升MAVROS和视觉节点的线程优先级。还有一个关于安全的小建议把你的失控保护动作设置成“悬停”Hold而不是“返航”RTL。在视觉降落场景里飞机通常就在地面站附近如果链路断了但RTL起飞点很远飞机可能会在位置估计漂移的状态下自主飞行反而更危险。保持在当前位置悬停给地面人员更多手动接管的时间。5. 与仿真和后续方向的小结关于标题里搜到的“四旋翼仿真 滑模控制 simulink”这些热词简单说一句我的看法。在真正接通MAVROS和Pixhawk实机之前先用Simulink做控制算法验证是非常值得的。你可以用Simulink的PX4 PSP模块直接生成控制代码或者用Simulink和Gazebo做联合仿真。滑模控制对于视觉降落这种存在模型不确定性和外部干扰的场景鲁棒性确实有优势我后续也打算把滑模控制器嵌到Offboard控制指令生成层里替代目前的PID位置环。不过仿真里的时间戳是理想化的真机上有通信延迟和丢包直接照搬仿真参数基本不可行还是得回到实机调试一步步调。这套MAVROS Pixhawk的方案目前已经能稳定支撑单目视觉定点降落从3米高度开始最终落点偏差控制在0.2米以内。如果后面把视觉换成立体相机或者加入IMU紧耦合精度还有提升空间。希望这篇文章能帮到正在调这条链路的人至少让你少走我走过的那些弯路。