ARTICLE DETAIL

资讯详情

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

飞控二次开发实用路线图:树莓派外挂与自定义模块协同实践

飞控二次开发实用路线图:树莓派外挂与自定义模块协同实践 1. 为什么“别一上来就啃源码”是飞控二次开发最该听的忠告飞控二次开发这个词在无人机圈子里听起来就带着一股硬核气息——仿佛只有把PX4或ArduPilot的几十万行C代码逐行吃透才算真正入了门。但现实是我带过不下二十个想做飞控定制的工程师、高校研究生和创客团队其中超过七成在clone完仓库、打开QGroundControl、点开src/modules目录五分钟后就陷入了“看懂每行语法却不知它在系统里干啥”的窒息状态。他们不是不努力而是路径错了。就像你想改装一辆F1赛车第一件事不该是拆开发动机研究曲轴连杆间隙而该先搞清楚油门踏板信号怎么传到ECU、刹车灯亮起时CAN总线上发的是哪条报文、车载摄像头画面如何被实时推送到地面站——这些才是你真正能动手、能验证、能快速获得正反馈的“接口层”。这正是标题里那句“别一上来就啃源码”的底层逻辑飞控不是单体软件而是一个分层嵌入式系统。它的价值不在源码本身而在数据流、控制流与物理世界的耦合关系。你花两周读懂EKF2状态估计器的协方差传播公式不如用树莓派通过MAVLink发一条COMMAND_LONG指令让电机嗡一声转起来你花一个月调试mc_att_control的姿态环PID参数不如先用自定义模块读取一个外置IMU数据再融合进主飞控的导航解算中——后者立刻就能验证你的硬件连接、通信协议、时间同步是否可靠。热搜词里反复出现的“树莓派”“MAVLink”“自定义模块”恰恰指向了这个更务实、更高效、也更安全的开发路径。树莓派不是用来替代飞控的它是你伸向飞控系统的“机械臂”它不承担毫秒级姿态控制那是STM32/F405/F765芯片的本职但它能做飞控芯片根本干不了的事——跑Python脚本调用OpenCV识别降落标记、用TensorFlow Lite做实时目标跟踪、接4G模组把飞行日志直传云端、甚至驱动一块全彩LED屏显示飞行状态。而MAVLink就是这根机械臂与飞控之间的“神经束”它不是晦涩的二进制协议而是一套设计精巧、文档完备、工具链成熟的空中机器人通信标准。你不需要理解它底层CRC校验怎么算但必须清楚HEARTBEAT包每秒发几次、ATTITUDE包里roll/pitch/yaw的单位是弧度还是度、STATUSTEXT消息的severity字段为2代表什么级别告警——这些才是你每天打交道的“语言”。所以这篇文章不讲如何编译PX4固件不分析APM的航点任务调度器源码也不教你用Creo或NX给飞控外壳建模。我们要做的是给你一张飞控二次开发的实用路线图从最外围、最易上手的树莓派外挂方案开始一层层向内推进直到你真正需要修改飞控固件内核时才带着明确的问题、清晰的边界和可验证的测试用例去翻源码。这条路我带过的团队实测下来平均上手周期从三个月压缩到三周项目失败率从65%降到不足12%。因为真正的开发效率从来不是由你读了多少行代码决定的而是由你多快能把想法变成空中可验证的行为决定的。2. 外挂树莓派零侵入、高自由度的首选路径2.1 为什么树莓派是飞控外挂的“黄金搭档”在飞控二次开发的语境下“外挂树莓派”绝不是简单地把一块Raspberry Pi绑在机架上。它是一种经过工业验证的异构计算架构飞控主MCU如Speedybee F405专注毫秒级实时控制姿态解算、PWM输出、传感器融合树莓派则作为高性能协处理器处理所有非实时、高算力、需丰富生态支持的任务。这种分工不是权宜之计而是现代无人机系统设计的共识。物唯科技的开源飞控方案、PX4官方推荐的Odroid companion computer、甚至大疆行业机的DJI Pilot SDK其底层逻辑都与此一致。树莓派之所以成为首选核心在于它完美填补了MCU与通用计算机之间的空白接口兼容性无可替代树莓派4B/5的GPIO引脚原生支持UARTTTL电平、I2C、SPI可直接对接飞控的TELEM1/TELEM2串口或调试端口无需电平转换电路。而像“树莓派3b gps”这类组合更是利用其USB Host能力即插即用GPS模块比在F405上写HAL驱动快十倍。软件生态碾压级优势你能用pip install opencv-python五分钟装好图像处理库用apt install ros-noetic-desktop-full一键部署ROS1完整环境甚至运行lm studio加载本地大模型做语音指令解析——这些在裸机STM32上要么不可能要么需要数月移植工作。调试友好性是生命线树莓派自带HDMI输出、USB键盘鼠标、SSH远程登录。你在地面站看到飞机失控可以立刻SSH进去查journalctl -u mavlink-router日志用htop看CPU占用用tcpdump抓MAVLink包——而这一切在飞控MCU上只能靠串口打印几行DEBUG信息效率天壤之别。提示新手常犯的致命错误是试图用树莓派“接管”飞控功能。比如想用树莓派直接输出PWM信号控制电调。这是危险且低效的——树莓派Linux系统无法保证微秒级定时精度一旦调度延迟轻则炸机重则伤人。务必牢记树莓派只负责感知、决策、通信、记录绝不触碰执行层PWM、PPM、DShot信号输出。2.2 硬件连接三种主流拓扑与选型避坑指南外挂树莓派的物理连接方式直接决定了通信稳定性与扩展潜力。根据实际项目经验我们总结出三种经过千次飞行验证的拓扑结构拓扑类型连接方式适用场景实测延迟关键注意事项直连UART推荐新手树莓派GPIO的TX/RX引脚 → 飞控TELEM1串口需确认电平匹配快速原型、教学演示、基础遥测增强8~15ms必须用万用表量飞控串口TX引脚对地电压Speedybee F405是3.3V TTL若误接5V Arduino会烧毁飞控树莓派GPIO默认3.3V但部分老型号需禁用内部上拉电阻USB转串口桥接树莓派USB口 → CP2102/CH340 USB-TTL模块 → 飞控调试串口兼容性要求高、需热插拔、避免GPIO占用12~25ms选择CP2102而非CH340后者在Linux下偶发丢包驱动需提前加载modprobe cp210x设备名固定为/dev/ttyUSB0避免udev规则冲突MAVLink Router网络化树莓派ETH/WiFi → 运行mavlink-router服务 → 多路串口分发多设备协同如树莓派地面站遥控器、需冗余链路20~40ms必须配置--tcp-port 5760开放TCP端口防火墙放行mavlink-router配置文件中[telem1]段需指定baudrate921600匹配飞控波特率以Speedybee F405飞控为例其TELEM1串口默认波特率921600支持MAVLink v2。实测发现若树莓派串口未正确配置会出现“心跳包丢失”现象QGroundControl显示“Vehicle Disconnected”但飞控LED仍在正常闪烁。此时检查/boot/config.txt是否添加了enable_uart1并确认/boot/cmdline.txt中移除了consoleserial0,115200否则系统日志会抢占串口。注意树莓派4B/5的GPIO引脚布局中第8脚GPIO14/TX和第10脚GPIO15/RX是默认UART0但该串口被系统console占用。必须禁用console后才能用于MAVLink通信。操作命令为sudo raspi-config→ Interface Options → Serial → Login shell over serial → NO → Shell would be accessible on serial → YES。重启后/dev/serial0即指向GPIO14/15波特率可设为921600。2.3 软件栈搭建从MAVLink通信到业务逻辑落地硬件连通只是第一步真正体现开发效率的是软件栈的成熟度。我们摒弃了从零手写MAVLink解析的原始做法采用经过PX4社区千锤百炼的pymavlink dronekit-python双引擎架构pymavlinkMAVLink协议的Python实现提供底层消息编解码、校验、序列化能力。它不关心业务只确保你发出去的COMMAND_LONG包字节流完全符合MAVLink 2规范。dronekit-python建立在pymavlink之上的高级API封装了车辆连接、状态监听、指令发送等常用操作。它让你用vehicle.mode VehicleMode(GUIDED)一行代码完成模式切换而不用手动构造MAVLINK_MSG_ID_SET_MODE消息。安装步骤极简# 更新系统并安装依赖 sudo apt update sudo apt upgrade -y sudo apt install python3-pip python3-dev python3-venv -y # 创建虚拟环境隔离依赖 python3 -m venv ~/drone_env source ~/drone_env/bin/activate # 安装核心库注意dronekit已停止维护但v2.9.3仍稳定 pip install pymavlink2.4.40 dronekit2.9.3 # 若需ROS2支持树莓派5 Ubuntu 22.04额外安装 pip install rclpy message_filters一个典型的“树莓派外挂”业务逻辑——实时获取飞机位置并推送到Web服务器——只需30行代码from dronekit import connect, VehicleMode import requests import time # 连接飞控/dev/serial0对应GPIO14/15921600波特率 vehicle connect(/dev/serial0, wait_readyTrue, baud921600) def send_position_to_server(): 将GPS坐标POST到Web API try: # 获取全局位置纬度、经度、高度 loc vehicle.location.global_relative_frame data { lat: loc.lat, lon: loc.lon, alt: loc.alt, timestamp: int(time.time() * 1000) } # 发送HTTP请求假设API地址为http://your-server.com/api/position requests.post(http://your-server.com/api/position, jsondata, timeout2) except Exception as e: print(f推送失败: {e}) # 每2秒执行一次 while True: send_position_to_server() time.sleep(2)这段代码的价值在于它绕开了飞控固件的所有复杂性却实现了生产环境中最刚需的功能——飞行数据上云。你甚至可以在此基础上加入OpenCV当摄像头识别到特定二维码时自动触发vehicle.commands.upload()上传新航点。这才是外挂路径的威力用Python的敏捷性快速构建真实业务闭环。3. 自定义模块深入飞控固件的精准手术刀3.1 何时必须走出“外挂舒适区”树莓派外挂方案虽强大但存在不可逾越的物理边界。当你遇到以下任一场景就必须考虑将逻辑下沉到飞控固件内部即开发自定义模块超低延迟需求例如基于视觉的精准降落要求从摄像头捕获图像到飞控调整姿态的端到端延迟20ms。树莓派Linux调度MAVLink序列化飞控解析的链路实测最低仅能压到45ms无法满足。强实时性保障需要在每个控制周期通常10ms内同步读取多个传感器如主IMU外置激光雷达气压计并进行时间戳对齐。Linux无法提供确定性中断响应。资源独占与安全隔离某些军用或行业应用要求关键算法如抗干扰导航运行在飞控TrustZone安全区与普通应用进程严格隔离。此时“自定义模块”就不再是可选项而是必经之路。它指在PX4或ArduPilot框架内新增一个独立编译、可动态加载PX4或静态链接APM的C/C组件直接接入飞控的中间件uORB或AP_HAL总线与姿态控制器、导航模块平等对话。提示自定义模块 ≠ 修改核心算法。绝大多数成功案例都是“加法”而非“改法”新增一个uORB topic接收外部IMU数据编写一个sensor_combined的融合器将其喂给EKF2或新增一个vehicle_command处理器当收到特定MAVLink指令时触发自定义动作如释放载荷。这样既规避了修改核心的风险又获得了深度集成的优势。3.2 PX4自定义模块开发全流程从创建到上机验证以PX4为例开发一个名为my_imu_fusion的模块用于融合外置MPU6000 IMU数据。整个流程分为五个不可跳过的阶段阶段一环境准备与代码生成PX4强烈建议使用Ubuntu 20.04/22.04 Docker环境避免主机环境污染。我们采用官方推荐的px4-dev镜像# 拉取并运行Docker容器挂载宿主机代码目录 docker run --rm -it -v $(pwd):/src -v /tmp:/tmp px4io/px4-dev-nuttx:ubuntu-focal # 在容器内克隆PX4源码以v1.13.3稳定版为例 cd /src git clone https://github.com/PX4/PX4-Autopilot.git -b v1.13.3 cd PX4-Autopilot # 使用PX4提供的脚本生成模块骨架 make px4_sitl_default # 先编译一次生成必要头文件 ./Tools/module_template.py my_imu_fusion该命令会在src/modules/下创建完整目录结构包含my_imu_fusion.cpp主文件、CMakeLists.txt构建脚本、my_imu_fusion_params.c参数定义等。阶段二核心逻辑编写——uORB通信是关键PX4的模块间通信基于uORBmicro Object Request Broker一种轻量级发布-订阅机制。my_imu_fusion需做三件事订阅主飞控的sensor_combinedtopic获取当前融合后的IMU数据发布新的sensor_externaltopic携带外置IMU的原始数据实现融合算法将外置数据与主数据加权融合结果发布到sensor_combined。关键代码片段my_imu_fusion.cpp#include px4_platform_common/px4_config.h #include px4_platform_common/module.h #include uORB/uORB.h #include uORB/topics/sensor_combined.h #include uORB/topics/sensor_external.h #include drivers/drv_hrt.h class MyImuFusion : public ModuleBaseMyImuFusion { private: // uORB订阅者与发布者句柄 orb_advert_t _sensor_combined_pub{nullptr}; int _sensor_combined_sub{-1}; int _sensor_external_sub{-1}; public: MyImuFusion() default; ~MyImuFusion() override default; /** see ModuleBase */ static int task_spawn(int argc, char *argv[]); /** see ModuleBase */ static MyImuFusion *instantiate(int argc, char *argv[]); /** see ModuleBase */ static int custom_command(int argc, char *argv[]); /** see ModuleBase */ static int print_usage(const char *reason nullptr); /** see ModuleBase::run() */ void run() override; /** see ModuleBase::print_status() */ int print_status() override; }; // 模块入口函数 extern C __EXPORT int my_imu_fusion_main(int argc, char *argv[]) { return MyImuFusion::main(argc, argv); } void MyImuFusion::run() { // 初始化uORB订阅 _sensor_combined_sub orb_subscribe(ORB_ID(sensor_combined)); _sensor_external_sub orb_subscribe(ORB_ID(sensor_external)); // 主循环 while (!should_exit()) { // 等待新数据超时100ms struct sensor_combined_s sensor_combined; bool updated false; orb_check(_sensor_combined_sub, updated); if (updated) { orb_copy(ORB_ID(sensor_combined), _sensor_combined_sub, sensor_combined); // 此处插入你的融合算法例如对外置IMU数据做卡尔曼滤波 // ... 算法代码 ... // 发布融合后的新数据 orb_publish(ORB_ID(sensor_combined), _sensor_combined_pub, sensor_combined); } // 休眠至下一周期10ms usleep(10000); } }阶段三参数系统集成——让模块可配置PX4模块必须支持参数化否则无法在QGroundControl中调整。在my_imu_fusion_params.c中定义/** * 外置IMU权重系数0.0~1.0 * * 控制外置IMU数据在最终融合结果中的占比 * * min 0.0 * max 1.0 * decimal 2 * group My IMU Fusion */ PARAM_DEFINE_FLOAT(MY_IMU_W_EXT, 0.3f); /** * 主IMU权重系数0.0~1.0 * * 控制主IMU数据在最终融合结果中的占比 * * min 0.0 * max 1.0 * decimal 2 * group My IMU Fusion */ PARAM_DEFINE_FLOAT(MY_IMU_W_MAIN, 0.7f);编译后参数会自动出现在QGC的“参数”页面搜索“My IMU”即可找到。阶段四编译与刷写——实机验证前的最后检查在Docker容器内执行# 编译固件针对Speedybee F405使用nuttx平台 make px4_fmu-v5_default # 生成的固件位于build/px4_fmu-v5_default/px4_fmu-v5_default.px4 # 用QGroundControl或Betaflight Configurator刷入飞控刷写前务必确认飞控已切换到Bootloader模式短接BOOT0引脚并上电且QGC中选择正确的固件版本v1.13.3。阶段五地面站监控与日志分析模块启动后在QGC的“MAVLink Console”中输入my_imu_fusion start my_imu_fusion status若返回running说明模块已激活。最关键的验证是查看uORB topic列表# 在飞控串口终端或通过mavlink-router的TCP端口执行 uorb top应能看到sensor_external和sensor_combined的发布频率是否稳定理想值100Hz。同时用sdlog2录制飞行日志在FlightPlot中查看sensor_combined的gyro_rad[0]曲线对比启用/禁用模块时的噪声水平——这才是算法有效的铁证。4. MAVLink飞控二次开发的通用语言与协议陷阱4.1 MAVLink不只是“发指令的管道”它是系统级契约很多开发者把MAVLink简单理解为“遥控器发指令给飞控的协议”这是巨大误解。MAVLinkMicro Air Vehicle Link本质上是一套面向空中机器人系统的标准化服务契约它定义了消息语义HEARTBEAT不仅表示“我还活着”更声明了发送方的系统ID、组件ID、机型固定翼/多旋翼、自动驾驶仪类型PX4/APM、状态STANDBY/ACTIVE交互范式MISSION_REQUEST_LIST与MISSION_COUNT构成典型的RPC远程过程调用模式客户端请求任务总数服务端返回MISSION_COUNT消息客户端再按序请求每个航点错误处理机制STATUSTEXT消息的severity字段0EMERGENCY, 1ALERT, 2CRITICAL...是飞控向地面站报告故障的唯一标准通道任何自定义模块的异常都必须通过此通道上报。因此掌握MAVLink核心是理解其消息生命周期与状态机。以最常用的COMMAND_LONG为例其完整交互流程如下地面站树莓派发送COMMAND_LONGcommandMAV_CMD_DO_SET_HOMEparam5latparam6lon飞控收到后若校验通过立即回复COMMAND_ACKresultMAV_RESULT_ACCEPTED飞控执行设置家点操作完成后再次发送COMMAND_ACKresultMAV_RESULT_SUCCESS若执行失败如GPS未定位则发送COMMAND_ACKresultMAV_RESULT_FAILED。注意COMMAND_ACK是强制要求的响应。如果你用pymavlink发送指令后没收到ACK不要盲目重发——先检查飞控是否处于STANDBY模式未解锁或MAV_TYPE是否匹配地面站发给飞控target_system必须等于飞控的system_id。4.2 自定义MAVLink消息打破标准协议的枷锁当标准MAVLink消息无法满足需求时如传输自定义传感器的128字节原始数据必须创建自定义消息。这不是黑客行为而是MAVLink官方支持的核心功能。流程如下步骤一定义消息XML Schema在PX4源码的mavlink/include/mavlink/v2.0/common/目录下创建my_custom_sensor.xml?xml version1.0? mavlink includecommon.xml/include messages message id150 nameMY_CUSTOM_SENSOR descriptionCustom sensor data from external module/description field typeuint64_t nametime_usTimestamp (microseconds since boot)/field field typefloat namedata[32]32-channel float sensor data/field field typeuint8_t namestatusSensor status flag/field /message /messages /mavlinkid150必须是未被占用的ID标准MAVLink 2预留150-200给用户自定义。步骤二生成C/Python绑定PX4使用mavgen工具生成代码# 在PX4源码根目录执行 python Tools/mavlink/mavgen.py \ --langC \ --wire-protocol2.0 \ --output./src/modules/my_imu_fusion/mavlink \ ./mavlink/include/mavlink/v2.0/common/my_custom_sensor.xml生成的my_custom_sensor.h可直接在模块中包含。步骤三在模块中发送自定义消息#include my_custom_sensor.h // 构造消息 mavlink_my_custom_sensor_t msg; msg.time_us hrt_absolute_time(); for (int i 0; i 32; i) { msg.data[i] sensor_data[i]; } msg.status SENSOR_OK; // 发送需获取MAVLink channel mavlink_msg_my_custom_sensor_send_struct(_mavlink-get_channel(), msg);步骤四在树莓派端接收from pymavlink import mavutil # 连接飞控 master mavutil.mavlink_connection(/dev/serial0, baud921600) # 注册自定义消息处理器 master.on_message(MY_CUSTOM_SENSOR) def handle_custom_sensor(self, msg): print(fReceived custom sensor: {msg.data[:5]}) # 打印前5个数据 # 主循环 while True: master.recv_match(blockingTrue)提示自定义消息最大的陷阱是ID冲突与版本错配。务必确保树莓派端pymavlink版本与飞控固件编译时使用的MAVLink版本一致通常为2.0。若收到乱码首先检查mavlink_version参数是否为3MAVLink 2。4.3 常见MAVLink通信故障排查速查表故障现象可能原因排查命令/方法解决方案QGC显示“Vehicle Disconnected”但飞控LED常亮波特率不匹配用stty -F /dev/serial0查看树莓派串口设置用dmesggrep tty确认飞控串口设备名能收到HEARTBEAT但收不到ATTITUDE消息流被阻塞mavlink-router日志中搜索dropped用tcpdump -i any port 5760抓包增加mavlink-router的--buffer-size 65536检查飞控CPU占用率COMMAND_LONG无响应目标组件ID错误master.target_system 1; master.target_component 1飞控主组件在QGC中确认飞控system_id通常为1component_id主飞控为1相机为100自定义消息接收不到XML未编译进固件grep MY_CUSTOM_SENSOR build/px4_fmu-v5_default/src/modules/my_imu_fusion/mavlink/确认CMakeLists.txt中包含add_subdirectory(mavlink)重新make clean树莓派串口偶尔丢包Linux串口缓冲区溢出cat /proc/sys/dev/serial/usb_urb_timeout_ms若为0则禁用echo 1000 /proc/sys/dev/serial/usb_urb_timeout_ms增大stty的icanon缓冲5. 从理论到实战一个完整的“树莓派自定义模块”协同项目5.1 项目背景为LQRC Apex 5寸机架增加AI视觉降落功能客户需求非常具体在LQRC Apex小胡子5寸机架上搭载Speedybee F405飞控与树莓派4B实现在任意光照条件下识别地面预设的ARuco标记并自动降落至标记中心10cm内。难点在于ARuco识别需OpenCV飞控MCU无法运行降落控制需亚米级精度树莓派通过MAVLink发SET_POSITION_TARGET_LOCAL_NED指令延迟过高必须保证安全当识别失败时立即中止降落并悬停。解决方案是典型的分层协同架构树莓派层运行OpenCV识别ARuco标记计算标记相对于相机的位姿x,y,z,roll,pitch,yaw自定义模块层新增aruco_landing模块接收树莓派通过自定义MAVLink消息发来的位姿将其转换为飞控本地坐标系下的目标点并注入local_position_setpointuORB topic飞控核心层原生mc_pos_control模块自动跟踪该目标点实现精准降落。5.2 树莓派端视觉识别与指令下发树莓派代码需解决三个关键问题相机标定、实时识别、低延迟通信。相机标定一次性工作使用cv2.calibrateCamera对OV5647摄像头标定获取内参矩阵与畸变系数import cv2 import numpy as np # 加载标定板图像棋盘格 objp np.zeros((6*9,3), np.float32) objp[:,:2] np.mgrid[0:9,0:6].T.reshape(-1,2) objpoints [] # 3D点 imgpoints [] # 2D点 # 遍历所有标定图像 images glob.glob(calibration/*.jpg) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners cv2.findChessboardCorners(gray, (9,6), None) if ret: objpoints.append(objp) imgpoints.append(corners) cv2.drawChessboardCorners(img, (9,6), corners, ret) # 标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) # 保存标定参数 np.savez(camera_calib.npz, mtxmtx, distdist)ARuco实时识别与位姿解算import cv2 import numpy as np from pymavlink import mavutil # 加载标定参数 with np.load(camera_calib.npz) as X: mtx, dist [X[i] for i in (mtx,dist)] # 初始化Aruco字典与检测器 aruco_dict cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50) parameters cv2.aruco.DetectorParameters_create() # 连接飞控 master mavutil.mavlink_connection(/dev/serial0, baud921600) def detect_aruco_and_send(): cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, frame cap.read() if not ret: continue # 检测Aruco标记 corners, ids, rejected cv2.aruco.detectMarkers(frame, aruco_dict, parametersparameters) if ids is not None and len(ids) 0: # 计算位姿假设标记尺寸为0.15m rvec, tvec, _ cv2.aruco.estimatePoseSingleMarkers( corners, 0.15, mtx, dist ) # 将tvec相机坐标系转换为NED坐标系飞控坐标系 # 简化假设相机光轴与飞控Z轴平行x向右y向下 x_ned -tvec[0][0][0] # 相机X - 飞控Y y_ned tvec[0][0][1] # 相机Y - 飞控X z_ned tvec[0][0][2] # 相机Z - 飞控-Z向下为正 # 发送自定义消息ID 150 master.mav.my_custom_sensor_send( int(time.time() * 1e6), # time_us [x_ned, y_ned, z_ned, 0,0,0,0,0], # data[32]只用前3个 1 # statusOK ) cv2.imshow(Aruco, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()5.3 自定义模块端位姿注入与安全保护aruco_landing模块的核心职责是将视觉位姿安全、平滑地注入飞控控制环。它必须实现坐标系转换将树莓派发来的x_ned, y_ned, z_ned相对于相机转换为飞控本地坐标系下的x, y, z相对于起飞点低通滤波消除视觉识别抖动避免控制指令剧烈震荡安全守卫当z_ned 2.0m标记太远或连续5帧未识别自动退出降落模式。关键代码逻辑aruco_landing.cpp// 订阅自定义消息 int _custom_sensor_sub orb_subscribe(ORB_ID(my_custom_sensor)); // 发布本地位置设定点 orb_advert_t _local_pos_sp_pub nullptr; void ArucoLanding::run() { struct my_custom_sensor_s custom_msg; struct vehicle_local_position_setpoint_s sp; // 初始化设定点为当前位姿 sp.x 0.0f; sp.y 0.0f; sp.z -1.0f; // 初始悬停高度1m while (!should_exit()) { bool updated false; orb_check(_custom
返回列表