ARTICLE DETAIL

资讯详情

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

PX4功能包飞行控制模块详解:px4ctrl与MAVROS链路解析

PX4功能包飞行控制模块详解:px4ctrl与MAVROS链路解析 做无人机开发的人应该都经历过这个阶段仿真里飞机随便飞代码一到真机上要么原地抖成筛子要么切到Offboard后完全不理会机载电脑的指令。我见过太多人在这一步卡了整整一个月最后发现根本不是算法问题而是没搞懂阿木实验室PX4功能包里那套飞行控制链路是怎么运转的。这篇是ROS四旋翼快速上手指南的第四篇也是我认为最值得反复读的重点章节PX4功能包中的飞行控制模块。它到底解决什么问题一句话说清楚机载电脑里的ROS节点通常叫px4ctrl负责计算“无人机下一瞬间应该往哪飞、姿态应该是什么”然后通过MAVROS把控制意图打包成MAVLink消息发给飞控固件固件再把姿态指令解算成四个电机的PWM。飞控内部跑姿态环和角速度环机载电脑跑的是位置环和速度环两层配合起来飞机才能稳稳地悬停、跟踪轨迹。这篇内容适合刚装好ROS和PX4仿真环境、想搞清楚飞控与机载电脑控制关系的同学也适合准备把仿真代码搬上真机的开发者。我尽量把链路里每一个“为什么”都讲透而不是只贴代码。1. 从MAVROS到px4ctrl这套功能包的数据通道到底是怎么串起来的1.1 功能包目录结构里藏着的主干逻辑我在不同项目里用过好几个版本的阿木PX4功能包目录结构大同小异。一个典型的控制功能包长这样px4_control/ ├── include/ │ └── px4ctrl/ │ ├── controller.h │ ├── linear_quadratic_controller.h │ ├── pos_controller.h │ └── state_machine.h ├── src/ │ ├── px4ctrl.cpp │ ├── controller.cpp │ └── state_machine.cpp ├── launch/ │ └── px4ctrl.launch ├── param/ │ └── px4ctrl_param.yaml ├── CMakeLists.txt └── package.xml这个结构里最核心的是px4ctrl.cpp这个主节点它负责程序入口、话题订阅发布、定时器初始化以及异常状态监控。真正算控制量的是controller.cpp里面实现了位置环和速度环的计算逻辑。state_machine.cpp则负责判断无人机当前处于什么飞行阶段比如“待机”、“起飞”、“悬停”、“轨迹跟踪”、“返航”每个状态下期望位置和期望速度的生成方式是不同的。很多人拿到功能包第一件事是去读controller.cpp但我建议先把launch文件和param文件读明白。因为这两个文件决定了节点的运行参数控制频率、订阅哪些话题、最大速度限制、最大加速度限制、PID初值。很多“飞机不受控制”的现场事故根源都是param里某个限幅没改或者话题名和实际发布的不一致。先跑通数据链路再谈算法这是做无人机控制最重要的顺序。1.2 五个关键订阅话题和一个核心发布话题px4ctrl节点不是一个封闭的算法盒子它需要实时感知飞机当前的状态然后输出控制指令。我整理了一份功能包中最常见的话题清单按重要性排序话题名消息类型方向作用/mavros/statemavros_msgs/State订阅飞控连接状态、当前飞行模式、是否已解锁/mavros/local_position/posegeometry_msgs/PoseStamped订阅当前机体在ENU坐标系下的位置和姿态/mavros/local_position/velocity_localgeometry_msgs/TwistStamped订阅当前机体在ENU坐标系下的线速度和角速度/mavros/imu/datasensor_msgs/Imu订阅加速度计、陀螺仪原始数据和姿态四元数/mavros/extended_statemavros_msgs/ExtendedState订阅着陆检测、VTOL状态、失效保护状态/mavros/setpoint_raw/attitudemavros_msgs::AttitudeTarget发布期望姿态四元数期望推力真正发给飞控的指令有些功能包版本支持位置控制或速度控制的输出模式发布的话题会还多一个/mavros/setpoint_position/pose或/mavros/setpoint_velocity/cmd_vel。但阿木这套典型实现里最底层发布的都是setpoint_raw/attitude也就是期望姿态加推力。这个选择的逻辑值得说一下位置环和速度环在机载电脑里算姿态环和角速度环在飞控固件里算这是目前消费级和准工业级无人机最常见的分工方式计算实时性最好也最安全——即使机载电脑死机飞控大概率还能维持最后一次姿态指令一小段时间。2. px4ctrl的控制线程级联PID在每个控制周期里做了什么2.1 位置环和速度环的计算流程px4ctrl的核心控制线程一般是一个定时器回调频率常见的是200Hz也就是5毫秒一个控制周期。也有些版本用100Hz仿真里差别不大真机上我个人推荐至少150Hz以上因为飞控内部的控制频率通常是250Hz到400Hz机载电脑给指令的周期如果太长飞控的插值效果会打折扣。控制线程的第一步是读取事先缓存好的最新状态。这里要注意一个工程细节不能在控制回调里直接调ros::Subscriber的getLatestMessage那样的阻塞接口而是应该在话题回调里把消息拷贝到类成员变量并记录时间戳。控制线程只负责用成员变量计算不负责等待新数据。这样做的原因是把“数据到达频率”和“控制计算频率”解耦话题来了就刷新缓存控制线程只管按自己的节奏算。位置环的逻辑很清晰就是一个带限幅的P控制器// 简化版位置环 Eigen::Vector3d pos_err desired_pos_ - current_pos_; Eigen::Vector3d vel_sp pos_err.cwiseProduct(kpos_); // 限幅防止期望速度过大导致姿态指令过激 vel_sp clampVector(vel_sp, max_vel_);desired_pos_可能来自launch里的固定点也可能来自更高层的轨迹规划节点。kpos_是位置环增益通常是一个三维向量x/y/z可以分别设置。需要注意z方向的增益表面上看可以独立调但由于四旋翼的推力方向特殊性实际调试时x/y先调稳z再单独处理。速度环接在位置环后面输出的是一段时间内的期望加速度// 简化版速度环 Eigen::Vector3d vel_err vel_sp - current_vel_; Eigen::Vector3d acc_sp vel_err.cwiseProduct(kvel_); // 重力补偿ENU下重力加速度是 (0, 0, -9.8) acc_sp.z() GRAVITY;重力补偿这一步极其重要。位置环和速度环本质上都在算“需要多大的加速度才能消除误差”但四旋翼在地面系下必须额外克服重力如果不加这个GRAVITY补偿飞控收到的推力指令会整体偏小飞机就会往下坠。很多仿真实例看起来能飞是因为仿真平台里的模型参数和真实重力环境有细微差异掩盖了这个问题。2.2 从期望加速度到期望姿态的换算对四旋翼来说水平方向不能直接给“加速度”只能通过倾斜机身让螺旋桨拉力产生水平分量。所以期望加速度acc_sp必须换算成期望的横滚角和俯仰角再连同期望偏航角一起打包成姿态四元数发给飞控。换算公式在教科书里写得很复杂实际代码里核心就几行double roll_sp (acc_sp.x() * sin(yaw) - acc_sp.y() * cos(yaw)) / GRAVITY; double pitch_sp (acc_sp.x() * cos(yaw) acc_sp.y() * sin(yaw)) / GRAVITY;这里yaw是期望偏航角通常取当前机体的偏航角也可以由上层指定。这个公式隐含了“小角度近似”也就是认为横滚角和俯仰角都不大。在正常飞行情况下是成立的但如果期望加速度很大飞机倾斜角度接近二三十度小角度近似的误差会明显增大届时就该用更严谨的旋转矩阵解算而不是这个简化式。最后是推力归一化double thrust acc_sp.norm() / GRAVITY; thrust clamp(thrust, 0.1, 0.8);这里计算出的thrust是一个0到1之间的无量纲数不是物理意义上的推力。PX4飞控会把thrust乘以当前电压和电机模型对应的最大推力比例。不同固件版本对thrust的标定有细微差异通常悬停时在0.35到0.55之间如果解锁地面测试时发现电机转速离谱优先检查推力限幅。2.3 控制频率、时间戳与超时保护只讲控制率不讲异常保护是做无人机开发的大忌。px4ctrl里有个容易被忽略但非常关键的部分超时保护。控制线程每次计算前都会检查当前系统时间和最新一帧IMU或位姿消息的时间戳差。如果超过一定阈值常见的是100毫秒说明MAVROS链路出现问题可能USB掉线、串口堵塞或机载电脑负载过高。此时功能包不会继续发布盲目的控制指令而是会采取两种安全策略之一一是停止控制输出让飞控自行进入悬停或降落二是发布一个“当前点悬停”的设定值尽量减少姿态突变。我见过不少同学把超时保护直接注释掉理由是“仿真里从来没触发过”。这个想法很危险。真机上SD卡写入卡一下、外设占满CPU、WiFi干扰导致串口重传都可能出现几百毫秒的数据中断。没有超时保护飞控收到的指令要么是陈旧的、要么是畸变的轻则飞机画龙重则直接翻机。在仿真和真机上保留超时保护是功能包设计里最值得学习的一点。3. 坐标系转换与参数对齐多数人翻车的位置其实在这里3.1 ENU与NED的转换链路无人机领域最折磨人的问题之一就是坐标系。PX4飞控内部和MAVLink协议用的是NED坐标系北东地x轴指北y轴指东z轴向下而ROS生态里习惯用ENU坐标系东北天x轴指东y轴指北z轴向上。MAVROS在这两者之间做了大量的隐式转换。比如/mavros/local_position/pose这个主题MAVROS把飞控发来的NED位置发给ROS时已经转换成ENU反过来/mavros/setpoint_position/pose这个主题ROS这边发布的是ENUMAVROS会在发往飞控之前转成NED。所以只使用这些高层话题时坐标系转换基本是透明的。真正容易出问题的是setpoint_raw/attitude。这是一个比较底层的接口很多资料说法不一。以我实测过的MAVROS版本来看ROS侧发布mavros_msgs::AttitudeTarget时orientation字段按照MAVROS插件的处理逻辑会经历一次ENU到NED的四元数转换后再打包进MAVLink。也就是说你在ROS代码里构造的应该是ENU系下的期望姿态四元数。但这里有一个很大的陷阱如果你图省事直接用/mavros/imu/data里的姿态四元数微调一下就当期望姿态发回去表面上已经跑通了但一旦你自己用欧拉角去构造四元数就踩进坐标系坑了。因为从欧拉角构造四元数时偏航角的正方向定义、旋转顺序都容易搞错。我的建议是所有期望姿态统一用Eigen库的四元数运算不要手写欧拉角拼接写完坐标系转换后在Gazebo里故意把期望横滚角设为一个小角度观察飞机是否朝预期方向倾斜这是验证坐标系最直接的方法。3.2 飞控端参数检查清单就算ROS端代码写得再对飞控参数不对Offboard控制一样跑不起来。我把自己用过的一套检查清单列出来每次换新飞控都照着过一遍参数名推荐值说明SYS_MC_CTRL_GROUP1多旋翼控制组确认固件是四旋翼配置MAV_1_MODEOnboard或Normal机载电脑连接的串口模式必须允许MAVLink控制COM_RCL_EXCEPT按需设置设置遥控器对Offboard模式的抢占权限建议保留遥控器可抢占OFFBOARD_TIMEOUT0 或指定时间控制Offboard模式在丢失设定值后的退出策略建议先保留默认EKF2_AID_MASK按需是否启用视觉/外部定位辅助室内定位时必查CBRK_IO_SAFETY0安全开关旁路测试时不要乱改这里重点说COM_RCL_EXCEPT。这个参数决定了遥控器摇杆信号能不能抢占Offboard模式。对于真机初飞建议保持遥控器可以随时抢占。一旦飞机发疯你拨一下遥控器模式开关就能切回手动这是最后一道保命防线。但要注意抢占开启时遥控器摇杆位置必须处于中位否则切回手动瞬间飞机会猛冲。还有一个容易忽略的参数是串口波特率。机载电脑通过USB转串口连接飞控时MAV_1_CONFIG对应的波特率要和MAVROS配置里的/dev/ttyUSB0或/dev/ttyACM0的波特率一致。很多“MAVROS连不上飞控”的问题查到最后就是波特率不一致。4. Offboard模式切换与解锁的完整时序为什么必须先发设定值4.1 PX4对Offboard模式的安全机制Offboard模式是PX4中最强大也最危险的模式。它允许外部设备也就是机载电脑发送姿态、速度或位置设定值飞控会从外部设备获取控制目标而不是从遥控器摇杆获取。为了安全性PX4对Offboard模式有一个硬性要求飞控必须持续接收到外部设备的设定值消息才允许进入和保持在Offboard模式。如果设定值消息中断超过一定时间飞控会触发Offboard失效保护自动退出Offboard模式切回之前的模式或执行预设的降落逻辑。这个机制是整个启动流程设计的根源。你会发现很多新手犯的错是先通过QGC地面站把飞控切到Offboard模式然后再启动px4ctrl节点结果飞控立刻报错并退出Offboard。原因就是切换瞬间没有设定值数据流。正确顺序必须反着来。4.2 推荐的安全启动流程我给自己定的启动流程是下面这样已经用了很久没出过事启动MAVROS确认/mavros/state已经变成connected飞控固件版本等信息正常。启动px4ctrl节点。节点启动后第一件事不是解锁而是立刻以固定频率比如50Hz向飞控发送“当前位置保持”的期望姿态。这一步相当于告诉飞控外部设备已经在线正在持续提供控制指令。用rosservice call切换模式rosservice call /mavros/set_mode custom_mode: OFFBOARD确认返回结果中succeeded: True再执行下一步。解锁rosservice call /mavros/cmd/arming value: true解锁后电机开始低速旋转整机进入待命状态。给一个非常保守的期望位置比如解锁点正上方0.3米观察飞机是否缓慢起飞、有没有异常抖动。这个顺序的核心逻辑是先建立数据流再切模式最后解锁。每一步之间留出时间确认状态不要在一秒内把三个动作全部执行完。很多脚本化操作把解锁和切模式放在一个循环里一旦PX4固件版本更新后时序要求变化脚本就失效了排查起来非常痛苦。4.3 切模式后的100ms窗口关于设定值持续发送有一个细节值得单独拿出来强调。PX4官方文档对Offboard设定值中断时间的容忍度不同版本不一样常见的是0.5秒到1秒之间。但实际工程中我建议按“每100毫秒至少收到一帧设定值”的标准来设计。为什么标准要放这么严因为数据链路不是理想的。MAVROS和飞控之间是串口或USB一旦机载电脑有瞬时高负载数据可能延迟。如果你按0.5秒的极限去发中间出现一次用户态程序调度卡顿就可能突破飞控的容忍值导致Offboard意外退出。按100毫秒的标准给系统留足余量飞行才会稳定。px4ctrl里如果控制频率就是200Hz这个问题天然就满足了但如果在里面加了复杂的轨迹规划算法计算耗时增大就要特别关注循环周期是否还能稳定维持。切到Offboard之后也不是万事大吉。要时刻监听/mavros/state一旦发现mode字段不再是OFFBOARDpx4ctrl应该立刻停止发送过激的期望指令转为悬停或等待上位机指令。很多失控事件都发生在飞控因为某种原因退出Offboard、但机载电脑还在按原轨迹发指令的状态下。5. 真机上的故障排查链路三个高频问题的根因与验证5.1 飞机抖动发散增益调节的顺序和方法最经典的故障现象解锁后一切正常一旦切到Offboard并给出微小的期望位置飞机先是高频抖动几秒然后越来越猛最后直接掀翻。这个问题的根因九成是位置环或速度环增益过大。PX4固件内部的姿态环通常已经调好机载电脑这一层相当于给飞控加了一个外环外环增益过大会让整个闭环进入震荡状态。更麻烦的是外环震荡会激发飞控内环饱和电机输出顶到上限这时候飞控的姿态环也会失去稳定能力。调试顺序我建议这样先确认最大速度和最大加速度限幅是合理的速度限幅建议从0.5m/s开始加速度限幅从1m/s²开始然后把位置环P降到默认值的一半速度环P也降一半起飞试一下如果飞机反应“肉肉的”、跟随很慢但至少不抖说明方向对了接着逐步增大位置环P直到出现轻微回中速度变快的感觉再增加速度环P每次只动一个参数。千万不要两个环一起调否则出了新问题你根本不知道是谁引起的。如果增益已经很低还是抖就要考虑是不是IMU数据本身有高频噪声。可以在rqt_plot里看/mavros/imu/data的角速度曲线如果噪声幅度异常先检查飞控减震泡棉是不是失效或者安装架是否和机架发生共振。控制增益和机械振动耦合在一起时就是经典的结构振动问题调软件不如先解决硬件。5.2 切到Offboard却完全不响应先查这些另一种高频故障是模式切成功了飞机也解锁了但飞机就像“死”了一样既不跟随期望位置也没有明显反应遥控器推杆还能正常控制但只要切回Offboard飞机就原地缓慢漂移或者落下。遇到这种问题先别急着怀疑px4ctrl代码。按顺序排查第一步确认px4ctrl是否真的在发布数据。用rostopic hz /mavros/setpoint_raw/attitude看频率是否稳定。如果话题频率是0或者只有几Hz说明节点可能被阻塞了查一下是不是有另一个节点占用了大量CPU或者ros::spin回调被某个耗时操作卡住。第二步确认飞控收到的指令模式是否正确。/mavros/setpoint_raw/attitude里的type_mask很重要如果这个掩码设置不对飞控可能认为当前消息里不包含姿态信息而只包含角速度期望。很多功能包在初始化时会把type_mask设为“忽略横滚、俯仰、偏航角速度”的模式也就是只使用姿态和推力如果代码里误把这个掩码搞反了飞控收不到有效的姿态指令自然不会有反应。第三步看飞控日志。QGC里能看到Offboard相关的警告比如Offboard enabled失败或者Control not available之类。最常见的原因是MAV_1_MODE没有配置成允许Offboard指令从当前数据链路进入。飞控可以连接MAVROS也能解锁但某些参数配置下它默认忽略外部控制指令。如果以上都正常还有可能是px4ctrl内部的模式状态机没有进入“Offboard控制中”的状态。有的功能包设计为只有在检测到/mavros/state.mode OFFBOARD时才发布有效控制量否则只发布“零姿态期望”。这种保护逻辑本身合理但如果状态机判断条件写得过于严格比如同时要求armed为true且extended_state.landed_state为not_landed就会导致解锁后在地面阶段一直不进入控制状态。排查时看一下节点的stdout日志通常会有打印。5.3 位置缓慢漂移误差到底来自哪里飞机能飞能悬停但会以一个极慢的速度往某个方向漂像有微风在吹一样。这个现象在室外和室内都可能出现也是最难定位的一类问题。先说结论这种漂移大概率不是控制器的问题而是定位源或传感器的问题。位置环用的是“期望位置减当前测量位置”作为误差如果当前测量位置本身就一直在缓慢飘控制器只会很“尽职”地跟踪这个飘动的位置。排查第一步看飞控的EKF位置估计。在QGC的状态页面里EKF的innovation新息如果持续偏大说明位置估计和某个传感器量测不一致。常见原因是加速度计零偏没有被正确校准导致EKF在积分过程中产生漂移。重新做一次加速度计六面校准很多时候漂移就消失了。排查第二步检查磁力计环境。室外如果附近有大块金属结构或者高压线磁力计会严重受扰航向角缓慢变化位置漂移也会跟着出现。室内则要考虑强磁场源比如大功率电源、电机本身。排查第三步确认这个漂移是否与期望值没有关系。可以把期望位置设成当前点然后观察半小时如果飞机始终向同一个方向漂基本可以断定是定位问题如果漂移方向不定再回头看控制器是不是有静态误差比如速度环没有积分项、且存在持续的重力前馈误差。另外室内飞行如果只靠PX4自带的气压计和IMU做位置估计那位置本身就会缓慢漂移这是物理极限不是代码能完全解决的。这种情况可以考虑加视觉定位或UWB外部定位源。6. 把功能包改造成自己的控制器扩展的正确接缝在哪里6.1 保留通信层替换算法层阿木实验室这套PX4功能包的价值不只是“能飞”更在于它把通信层和算法层分开了。通信层就是MAVROS相关的订阅发布算法层就是controller.cpp里那一大段控制率计算。想改成自己的控制器最理想的接缝就在这个边界上。拿我自己做的一个自定义控制器举例我只改了controller.cpp里的核心计算函数把原来固定增益的PID换成了带轨迹前馈的MPC输入输出接口完全不动。订阅的还是那五个话题发布的还是/mavros/setpoint_raw/attitudelaunch文件一个没变。整个替换过程只花了一个下午剩下大量时间全花在调MPC参数上。这就是接口设计合理带来的红利。替换算法层有一个原则要守住新的控制器内部可以很复杂但对外输出的指令必须和原版一样“干净”——频率固定、时间戳准确、异常时能安全降级。MAVROS不会替你做数据平滑飞控也不会替你判断指令是否合理。6.2 接入轨迹规划与视觉定位的扩展点功能包预留的扩展点主要集中在两条线上期望值输入和定位输入。期望值输入侧默认的desired_pos_往往来自param里的固定数值。要改成轨迹跟踪控制只需把固定数值替换成一个订阅三目标话题的赋值语句比如void traj_callback(const nav_msgs::Path::ConstPtr msg) { // 把轨迹解析成当前时刻的期望位置和期望速度 desired_pos_ parsed_pos; desired_vel_ parsed_vel; }位置环那一节里如果只用了位置误差算期望速度现在可以把轨迹规划器给的前馈速度叠加进去跟踪效果会好很多Eigen::Vector3d vel_sp pos_err.cwiseProduct(kpos_) desired_vel_;定位输入侧PX4本身支持外部定位源。视觉SLAM通过/mavros/vision_pose/pose把位姿发进去然后设置EKF2_AID_MASK里的vision位飞控就会把视觉位姿和IMU融合。px4ctrl订阅的/mavros/local_position/pose在此时会自动变成融合后的位姿对上层控制代码完全透明。这也是为什么我建议别在控制层直接读SLAM原始位姿而是通过MAVROS走一遍飞控融合省心又靠谱。安全方面再加一句机载电脑和飞控之间的链路要在任何时候都有监控哪怕只是最简单的rostopic hz心跳检查也比完全不管要好。我见过不止一次USB线松动导致接触不良飞机在空中直接切到失效保护如果当时没有超时保护机制后果不敢想。调这套功能包最深的体会是控制代码本身不难难的是把坐标系统一、把启动时序讲清楚、把异常保护留好。哦对了还有一个小技巧分享给你真机初飞前在Gazebo里把位置环增益故意调大一倍看飞机怎么发散、发散前有什么前兆多做几次这种“坏试飞”你上手真机时会淡定很多。
返回列表