尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MAVSDK+PX4开发实战:从环境搭建到Offboard自主飞行

MAVSDK+PX4开发实战:从环境搭建到Offboard自主飞行 先扔一个我自己的判断如果你已经在PX4上折腾过几周大概率会产生一个困惑——固件改了一堆QGroundControl也能连上但真正想让飞机自己做点事比如自主起飞、按航点飞、识别到目标后自动降落好像总隔着一层。这个“层”就是MAVSDK解决的东西。MAVSDK是PX4官方维护的SDK它把PX4底层的MAVLink通信协议封装成了Python、C、Swift、Java这些语言的API。通俗点说PX4飞控是飞机的“小脑”负责姿态控制、位置控制、传感器融合MAVSDK是你写的程序用来命令“小脑”干活的“手柄”。你想让无人机做什么在PC/机载电脑上写MAVSDK代码发给飞控飞控负责执行和稳定飞行。这篇文章就围绕“写MAVSDK/PX4应用”这件事拆解从环境搭建、第一个Demo、到Offboard模式自主飞行的完整路径顺手把我踩过的坑也一起说了。适合刚学PX4开发、想在Ubuntu 22.04上跑通仿真、或者准备在树莓派/Jetson上写无人机的读者参考。1. 写PX4应用前先搞懂MAVSDK在整个系统里的位置很多新手一上来就急着编译PX4固件想把C代码塞进飞控里。这个方向不是不行但成本和风险都很高——一个数组越界可能直接让飞机空中断电。而MAVSDK走的是一条轻量得多的路应用代码跑在飞控之外的电脑或计算模块上通过无线数传或串口与飞控通信。1.1 PX4和MAVSDK各干各的活PX4是运行在飞控硬件比如Pixhawk系列上的实时操作系统它处理的事情包括姿态解算融合陀螺仪、加速度计、磁力计得出当前姿态角位置估计融合GPS、气压计、视觉里程计得到位置和速度控制输出根据目标姿态/位置输出PWM/Oneshot等信号给电调状态机管理地面待机Manual、自稳Stabilized、定高Altitude、Offboard等的切换MAVSDK不碰这些底层逻辑。它只做一件事把MAVLink协议封装成一系列高级API。比如调takeoff()SDK会帮你组装好MAVLink消息MAV_CMD_NAV_TAKEOFF、设定起飞高度、确认命令等然后通过串口或UDP发送给PX4。PX4收到后执行实际起飞动作。这样一个分层的好处很明显你写应用的时候不需要关心MAVLink报文的格式细节也不用担心某条消息的确认机制怎么处理。MAVSDK把这些都藏起来了。1.2 为什么说用MAVSDK而不是直接发MAVLink我在刚开始接触PX4时也试过自己拼MAVLink报文用的还是mavlink-router加pymavlink。但搞到后面发现处理不同消息的超时、确认、重传逻辑非常繁琐。比如你发一个起飞命令要等ACK确认还要同时监听飞行模式的切换状态——这些逻辑自己写至少几百行。MAVSDK把这类协议交互封装好了你看到的是同步的takeoff()调用底层自动完成命令发送、ACK等待、超时重试。这个抽象价值巨大尤其是在写复杂任务时你的注意力应该放在逻辑编排上而不是协议细节上。注意MAVSDK的抽象也有代价——它支持的功能比全量MAVLink少。但实际开发中90%的需求起飞、降落、Offboard控制、任务上传、遥测订阅它都覆盖了。2. 在Ubuntu 22.04上搭建MAVSDK/PX4开发环境网上搜“PX4 编译环境 Ubuntu 22.04”能搜出一堆教程但很多都没把MAVSDK依赖单独说清楚。这里我给出一份我自己验证过的、可以少走弯路的方案。2.1 环境准备不用装完整的PX4固件也能跑MAVSDK这是很多新手的第一个误区以为用MAVSDK就必须先把PX4固件源码编译一遍。其实分两种情况只写MAVSDK应用你只需要MAVSDK库本身然后用一个模拟器如PX4 SITL或MAVSDK自带的仿真来测试。同时改PX4固件才需要编译PX4源码。更重要的认知是MAVSDK支持通过mavsdk_server连接真实飞控或仿真器。你可以把mavsdk_server当成一个翻译网关——你的Python/C程序连上mavsdk_server它再转发给PX4。这样反而更容易排查连接问题。我的建议是先编译一次PX4 SITL软件在环仿真因为这样你能在Gazebo里看到飞机模型动起来确认整条链路通了再开始写MAVSDK代码。这一步的价值远大于直接买一块飞控接真机调参。2.2 编译PX4 SITL的实战操作我用的就是Ubuntu 22.04按下面的顺序操作基本一次过。先装基础依赖sudo apt update sudo apt install -y \ git zip qtcreator cmake build-essential genromfs ninja-build \ exiftool astyle python3-pip python3-setuptools \ python3-jinja2 python3-yaml python3-numpy \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ libsqlite3-dev libopencv-dev \ protobuf-compiler libprotobuf-dev \ libeigen3-dev libgazebo11-dev \ ros-${ROS_DISTRO}-gazebo-ros-pkgs然后拉PX4源码这里注意别直接clone最新main分支选v1.14.3这个稳定版本git clone --recursive https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursive如果拉子模块特别慢可以给git配置代理或换镜像源但不建议跳过--recursive缺少子模块几乎必然编译失败。编译SITL仿真固件make px4_sitl gazebo-classic第一次编译大约需要15到30分钟取决于机器配置。如果编译中途报错不要急着重来先看具体是哪个模块失败。我在二次编译时遇到过mavlink子模块版本不匹配解决方式是把子模块更新到和v1.14.3匹配的版本。这个匹配关系一般会在PX4的Tools/setup/ubuntu.sh里自动处理手动操作时容易忽略。编译成功后启动仿真make px4_sitl gazebo-classic启动后会在终端里看到pxh提示符同时Gazebo窗口里出现一架带旋翼的多旋翼模型。到这一步你的PX4 SITL已经跑起来了可以被MAVSDK连接。重要提示SITL默认在UDP 14540端口监听MAVLink消息MAVSDK的mavsdk_server默认连接的也是这个端口。理解端口号的作用后面排查连接问题会有帮助。2.3 安装MAVSDK-Python与验证连接我推荐新手用Python版本快速上手因为调试方便。安装很简单pip3 install mavsdk验证环境是否通的脚本如下它能探测到PX4并打印飞控版本import asyncio from mavsdk import System async def run(): drone System() await drone.connect(system_addressudp://:14540) print(Waiting for drone to connect...) async for state in drone.core.connection_state(): if state.is_connected: print(Connected to drone!) break async for version in drone.info.version(): print(fFirmware version: {version.version_string}) break asyncio.run(run())这个脚本能跑通说明你的PX4 SITL已经对MAVSDK开放了。后续所有的应用代码都是在这个连接基础上叠功能。3. 第一个MAVSDK应用起飞、悬停、降落既然环境通了就来写一个真正能操控飞机的程序。这里用Python示例C版本的API设计几乎一样只是代码结构更繁琐一些。3.1 完整流程拆解一个典型的基础飞行程序需要这几步连接飞控订阅飞控状态等待连接就绪获取GPS定位信息和健康状态解锁arm起飞到指定高度悬停几秒降落land下面这个例子就是完整的起飞-悬停-降落程序可以直接套用import asyncio from mavsdk import System from mavsdk.action import ActionError async def run(): drone System() await drone.connect(system_addressudp://:14540) print(等待飞控连接...) async for state in drone.core.connection_state(): if state.is_connected: print(飞控已连接) break print(等待定位信息...) async for health in drone.telemetry.health(): if health.is_global_position_ok and health.is_home_position_ok: print(定位正常准备起飞) break print(解锁...) await drone.action.arm() print(起飞...) await drone.action.takeoff() await asyncio.sleep(10) print(降落...) await drone.action.land() asyncio.run(run())这段代码虽然只有二十多行但已经把MAVSDK的异步事件模型体现出来了用async for订阅telemetry用await等待命令执行完成。理解这个异步模型是写复杂应用的关键。3.2 为什么必须等待health就绪再起飞我见过不少初学者把上面的程序简化为“连接后直接起飞”结果飞控没有报错但飞机纹丝不动。原因就在于GPS定位和Home点还没准备好PX4不执行起飞命令。PX4内部的逻辑是起飞前必须确认全局位置和Home位置都有效。如果这个条件不满足MAV_CMD_NAV_TAKEOFF会被飞控直接拒绝。MAVSDK的takeoff()虽然不会抛异常但命令根本没有被PX4接受。所以is_global_position_ok代表飞控已经拿到了有效的GPS定位is_home_position_ok代表飞控已经设置了Home点通常是解锁位置在室内测试仿真时GPS是仿真的通常几秒内就能满足条件。真机测试时要等GPS定位星数够多室内没有GPS信号就飞不了这是新手踩坑重灾区。3.3 真机测试时要注意的额外步骤如果你把这段代码从SITL换到真机上有几个参数必须提前确认在QGroundControl里确认安全开关Safety Switch状态。有些遥控器需要拨动安全开关才能解锁否则arm()会超时或被拒绝确认电池电压正常低电量保护会阻止起飞确认飞行模式切换通道设置好。如果MAVSDK的arm()失败先切到手动模式排查原因建议把MAV_ARM_AUTH相关配置检查一遍确认PX4没有启用额外的解锁鉴权实操心法我第一次真机测试时忘了把遥控器的安全开关拨到位arm()一直失败排查了半小时才发现是硬件保护在起作用。所以用MAVSDK测试前先通过遥控器把飞机解锁一遍确定硬件链路通畅再切到MAVSDK控制能省掉大量无效排错时间。4. Offboard模式真正自由写应用的入口起飞降落只是热身真正让开发者兴奋的是Offboard模式。这个模式下你不再只是发单个命令而是以指定频率持续地向飞控提供位置或速度设定值飞控会尽力跟随。这几乎是所有自主飞行应用的基座。4.1 Offboard模式的工作原理PX4的Offboard模式接受来自外部计算机的设定值来源通常是MAVLink消息SET_POSITION_TARGET_LOCAL_NED或SET_POSITION_TARGET_GLOBAL_INT。MAVSDK把这两类消息封装成了offboard.set_position_local()和offboard.set_velocity_ned()。关键点在这里Offboard模式要求你以持续频率建议10Hz以上发送设定值如果飞控连续500毫秒没有收到新设定值会自动退出Offboard模式并切换回之前的落定模式通常是Land或Hold。这个机制是为了防止通信链路中断后飞机变成“无头苍蝇”。理解了这个机制你就能明白为什么MAVSDK的Offboard API长这样——它需要你维护一个send_*循环而不是只调用一次就结束。4.2 用MAVSDK实现Offboard位置控制下面是一段让飞机飞到指定相对位置的代码这个方法我用来做过自主巡检的雏形import asyncio from mavsdk import System from mavsdk.offboard import (OffboardError, PositionNedYaw) async def run(): drone System() await drone.connect(system_addressudp://:14540) async for state in drone.core.connection_state(): if state.is_connected: break async for health in drone.telemetry.health(): if health.is_global_position_ok and health.is_home_position_ok: break print(-- 起飞到10米高度) await drone.action.arm() await drone.action.takeoff() await asyncio.sleep(10) print(-- 开启Offboard模式) try: await drone.offboard.start() except OffboardError as error: print(f启动Offboard失败: {error._result.result_str}) return print(-- 飞到当前位置东侧10米) # 相对当前机头方向? 这里用的是NED系, N北, E东 await drone.offboard.set_position_local(PositionNedYaw(0.0, 10.0, -10.0, 0.0)) await asyncio.sleep(10) print(-- 回归到Home点上方) await drone.offboard.set_position_local(PositionNedYaw(0.0, 0.0, -10.0, 0.0)) await asyncio.sleep(10) print(-- 退出Offboard并降落) try: await drone.offboard.stop() except OffboardError as error: print(f停止Offboard失败: {error._result.result_str}) await drone.action.land() asyncio.run(run())这段代码里有两个细节值得展开。第一个是坐标系的含义。PositionNedYaw里传入的四个值分别是北向位移、东向位移、向下高度、偏航角。NED坐标系下“向上”是负数所以-10.0代表10米高度。新手很容易把-10写成10导致飞机直接往地里钻。我在SITL里试过写反模拟器里能明显看到飞机先下降再拉升如果发生在真机上大概率已经炸鸡了。第二个是offboard.start()和arm()的先后关系。PX4要求必须先解锁、再进入Offboard顺序反了会报错。另外在SITL里如果尝试从地面直接进入Offboard模式飞控会拒绝常见表现是start()超时。正确做法是从起飞后的稳定模式比如Takeoff切到Offboard所以我上面的代码里先takeoff()再start()。4.3 速度控制模式更适合做动态跟踪位置控制适合航点巡航但如果你要做目标跟踪、避障绕行速度控制会更自然。MAVSDK里对应的API是set_velocity_ned()它接收北向速度、东向速度、向下速度、偏航速率。一个简单的悬停速度保持循环如下import asyncio from mavsdk import System from mavsdk.offboard import VelocityNedYaw async def run(): drone System() await drone.connect(system_addressudp://:14540) # ... 省略连接检查 ... await drone.offboard.set_velocity_ned(VelocityNedYaw(0.0, 0.0, 0.0, 0.0)) await drone.offboard.start() # 持续发送1m/s北向速度 for _ in range(50): await drone.offboard.set_velocity_ned(VelocityNedYaw(1.0, 0.0, -0.5, 0.0)) await asyncio.sleep(0.1) # 停止 await drone.offboard.set_velocity_ned(VelocityNedYaw(0.0, 0.0, -0.5, 0.0)) await asyncio.sleep(0.1) await drone.offboard.stop() asyncio.run(run())注意这里我故意没有用死循环而是发送50次然后停下来。实际应用中如果你需要连续控制应该把发送循环放在一个while True里并加上退出条件否则通信异常时飞控会因收不到设定值而自动退出Offboard。我做过一个简单的“键盘控制无人机”项目就是这个思路用键盘的WASD更新目标速度然后以20Hz频率持续发送。整个过程非常直观飞控对速度指令的响应也很快。5. 调试MAVSDK程序的几个高频问题与排查方法写代码总会遇到问题但MAVSDK的报错信息不算友好很多时候只是“timeout”或“connection error”新手容易一头雾水。下面把我见过的高频问题整理成表附上排查思路现象可能原因排查方法connect()后一直卡在等待连接端口不对或PX4 SITL没启动检查SITL终端是否有pxh提示符确认UDP端口是14540arm()超时飞控安全开关锁定或GPS未就绪检查QGC里是否有解锁报错确认health检查通过takeoff()没反应Home点未设置或飞行模式不允许确认is_home_position_ok为True手动起飞一次验证Offboardstart()失败未先解锁或已处于Offboard模式先arm()再start()检查是否重复调用飞行中突然退出Offboard设定值发送中断超过500ms检查发送频率建议10-20Hz检查网络延迟Python脚本卡死无输出异步事件循环被阻塞避免在async for循环里放入CPU密集计算5.1 连接不上优先查端口和IPMAVSDK的connect(system_addressudp://:14540)表示在本机的14540端口监听UDP数据。PX4 SITL默认将MAVLink消息广播到14540端口所以本机连接没问题。但如果你是在树莓派上运行MAVSDKPX4在另一台机器上就要改成await drone.connect(system_addressudp://192.168.1.100:14540)这个IP是运行PX4的电脑地址。如果连不上先在本机测试再排查防火墙。我遇到过一台Ubuntu机器上防火墙默认拦截UDP直接导致连接失败sudo ufw disable才解决当然这只在可信局域网里建议这样做。5.2 起飞后飞机偏航或漂移这个问题大多不是MAVSDK代码的锅而是PX4的位置估计还没收敛。SITL里可以等GPS定位稳定但真机需要等待磁力计校准完成。另有一个容易被忽略的点起飞前如果云台或其他负载的磁干扰比较大PX4的航向估计会漂导致视觉上飞机“转圈”。排除方法很简单在QGC中查看姿态和航向曲线确认解锁后没有明显的漂移。如果漂移严重重新校准磁力计并检查桨叶下方是否有磁铁等干扰源。5.3 Offboard模式下电机突然抽动一声然后停转这通常是飞控进入了降落保护或紧急停止。可能的原因是你发送的速度/位置设定值非常不合理比如让飞机以极快的速度撞地PX4的内置保护机制检测到异常强制切出Offboard并降落。我自己的排查顺序是先检查发送的坐标/速度数值方向是否正确然后看QGC的告警日志最后再看代码里的循环频率是否稳定。大部分情况都是数值符号搞反比如NED坐标系的“北向”写成了负值导致飞机反向飞。6. 从仿真到真机的几个关键经验仿真跑通了不代表真机就能飞。我自己在从SITL到真机切换时至少踩了三次坑这里展开说说。6.1 通信链路数传和串口的配置差异仿真用的是UDP真机一般用串口。MAVSDK连接串口的地址形如await drone.connect(system_addressserial:///dev/ttyUSB0:57600)注意波特率Pixhawk默认的TELEM2口一般是57600有些飞控是921600。搞错波特率的表现是连接时不报错但收不到任何数据或数据乱码。我建议先用QGroundControl连一次数传确认串口设备名和波特率再在MAVSDK里配置。奉行的原则是先让QGC连上再让MAVSDK连上不要两个终端同时抢一个串口。6.2 安全冗余别把命压在一根链路上写自主飞行程序时最容易忽略的是“如果MAVSDK程序崩溃了怎么办”。PX4有内置的Geofence和RTLReturn to Launch机制你的应用一定要在起飞前把RTL触发条件设置好。MAVSDK里可以通过action.set_return_to_launch_altitude()设置返航高度也可以通过mavsdk.telemetry监听遥控器信号。如果遥控器信号丢失PX4会自动触发RTL。但如果你用的是纯数传遥控而没有遥控器接收机就需要提前设置好Geofence和降落保护。我的做法是在MAVSDK的主循环里加一个看门狗如果状态机连续几秒没有收到健康的遥测数据就调用action.land()或action.return_to_launch()。这样即使程序逻辑出错也有兜底动作。6.3 Offboard频繁失控检查RC信号覆盖PX4有个默认参数COM_RC_OVERRIDE在遥控器收到RC输入时会优先响应遥控器信号并退出Offboard。这个机制是好是坏取决于场景。如果飞行过程中有人碰了遥控器的摇杆Offboard模式会突然失守飞机跟随遥控器操作可能会不听程序指挥。如果你的应用是完全自主飞行建议把遥控器切换到Hold模式或干脆关掉遥控器开关或者检查COM_RC_IN_MODE参数确认为Disabled时不会因RC输入切换模式。更稳妥的方案是在QGC中设置一个物理的紧急降落开关而不是依赖程序里的逻辑判断。7. 扩展MAVSDK能做的远不止起飞降落写到这里基础部分已经覆盖了。如果你追求更进一步MAVSDK还提供了几个我实际用过的能力值得研究任务上传mission模块支持上传完整的航点任务到飞控飞控自己执行不需要外部持续发送设定值。这和Offboard是两种不同思路前者适合预设航线巡检后者适合动态决策传感器遥测订阅GPS、IMU、电池、姿态、速度这些数据都能通过telemetry模块实时订阅方便你在机载电脑上做状态监视或日志记录摄像头触发camera模块可以触发拍照或录像用于航测或者数据采集自定义MAVLink消息如果MAVSDK功能不满足需求你可以直接使用send_mavlink_message()发送原始MAVLink消息相当于“跳过SDK”直接面对协议我也见过团队用MAVSDK做多机编队控制一架地面站电脑同时连接多架飞机的MAVSDK实例通过分配不同端口实现同步控制。这种场景下MAVSDK的异步模型反而比单线程的逻辑好写很多因为每架飞机都是一个独立System对象用asyncio.gather()并发控制就行。MAVSDKPX4的组合短期内不一定会取代底层的MAVLink开发和PX4固件开发但作为应用层的开发方式它已经足够稳定和高效。尤其是现在ROS2和PX4的官方接口也在逐渐成熟MAVSDK补齐了最轻量化的那一环——不需要装ROS一个Python文件就能驱动飞机这非常适合快速验证想法和做开发原型。从我个人经验来说在还没完全理解PX4内部状态机之前MAVSDK是一个非常好的进修路径。通过它你能直观地感受到“命令飞控”和“维护飞控”之间的界线在哪里。等有了这个手感再回头去读PX4固件源码理解深度会完全不同。这个顺序比一开始就钻进制C堆栈里要高效得多。
返回列表