把树莓派塞进无人机当“副驾驶”,这事儿我琢磨了很久。飞控自己能飞、能定高、能按航点走,但真要让它自己精准落回一个起飞垫上,光靠GPS是远远不够的。GPS的漂移在开阔地轻松晃悠好几米,落偏是家常便饭。后来我把树莓派加上摄像头,通过MAVLink协议和Pixhawk 2.4.8飞控对上话,才算把“自主巡航”和“精准降落”这两件事真正打通。这篇文章我会把整套系统的分工、连线、巡航航点实现、视觉精准降落的原理和代码,以及我实测踩过的坑一次讲清楚,适合手里有Pixhawk 2.4.8和树莓派、想做无人机自主任务或者毕业设计的同学参考。
1. 先理清分工:树莓派在飞机上到底扮演什么角色
1.1 飞控能飞,但飞控“不聪明”
Pixhawk 2.4.8这块板子,到今天依然是很多开源无人机的绝对主力。它内置了三轴陀螺仪、加速度计、磁力计和气压计,配合GPS模块,能稳定输出姿态和位置控制。你把它拨到Loiter模式,它能乖乖悬停;给它上传一条航线,它能一个点一个点地飞过去。这些能力足够完成日常航拍和简单任务,但它有个天生的短板:算力太弱,跑不动视觉识别,也没有办法实时处理摄像头画面。
飞控的实时性要求极高,姿态解算和控制回路通常跑在几百赫兹级别,它所有的精力都用在“让飞机别摔下去”这件事上。你让它分心去识别地面上的ArUco码、计算目标降落点的位置,它没有余力,硬件也跟不上。
所以常规做法就出来了:把“思考”和“决策”交给树莓派,把“飞行稳定”和“底层控制”继续留给Pixhawk。树莓派是一台完整的Linux小电脑,跑OpenCV、跑Python脚本、读摄像头、算目标位置,这些活儿它都干得动。两边通过串口连接,用MAVLink协议通信。
1.2 机载计算机和飞控的职责边界怎么划
我做这个项目时,给两边的分工定得很明确:
- Pixhawk 2.4.8负责:姿态控制、位置控制、速度控制、航点执行、降落油门控制、紧急返航。
- 树莓派负责:摄像头采集与ArUco码识别、目标距离与角度解算、任务级决策(什么时候起飞、飞哪些点、什么时候进入降落流程)、把视觉结果打包成MAVLink消息发给飞控。
这套分工的本质是“飞控永远有最终决定权”。树莓派可以建议飞控往左偏一点往右偏一点,但最终油门、姿态、舵面混控都由飞控自己算。一旦树莓派死机或者视觉识别中断,飞控依旧能按普通GPS降落逻辑完成任务,不会因为机载电脑挂了就失控。这个设计逻辑一定要守住,后面所有精度优化都建立在安全兜底之上。
1.3 为什么不用地面站远程接管
有人会问:QGroundControl或者Mission Planner不也能上传航线、控制飞机吗?为什么还要在飞机上塞一台树莓派?
答案很简单:通信链路不可控。远程地面站通过数传电台跟飞控通信,带宽有限、延迟大,飞行中稍微拉远一点或者有遮挡,链路就可能中断。一旦链路断了,地面站根本看不到视觉画面,更不可能实时传回目标位置。精准降落要求视觉系统以至少10Hz的频率连续输出目标角度和距离,靠数传电台那点带宽根本不现实。
机载处理的好处是:所有的图像数据在飞机本地就消化掉了,不需要传回地面,只把最终的角度偏移和距离数值通过MAVLink串口发给飞控,数据量极小、实时性极高。树莓派跑在飞机上,等于把“眼睛”和“大脑”都搬到了现场。
2. 硬件连接与固件参数:先把通信路打通
2.1 连线:TELEM2到树莓派UART
树莓派和Pixhawk的物理连接,我建议走Pixhawk 2.4.8的TELEM2口。TELEM1一般留给数传电台,TELEM2正好空出来接机载电脑。
接口顺序是按标准JST-GH 6针脚位排列的,和Pixhawk 4等新板子一样,但2.4.8出厂默认带的接口其实是杜邦线或者带焊盘的插针,需要自己接线。连接关系如下表:
| TELEM2引脚 | 功能 | 树莓派GPIO |
|---|---|---|
| 1 | VCC 5V(不要接!) | - |
| 2 | TX(飞控发送) | GPIO14(UART0_TXD) |
| 3 | RX(飞控接收) | GPIO15(UART0_RXD) |
| 4 | GND | GND |
| 5 | CTS | 可悬空 |
| 6 | RTS | 可悬空 |
这里要特别强调第一个坑:TELEM2口上那个5V是给外接设备供电用的,有些飞控教科书里会让你接,但我不建议从飞控给树莓派供5V。树莓派4B满载时电流能到2A以上,飞控的5V稳压模块大概率扛不住,强行带载会导致电压跌落,轻则树莓派频繁重启,重则连累飞控掉电压。正确的做法是树莓派用独立的5V BEC模块或者单独电池供电,TELEM2只接TX、RX和GND三根线。
电平方面,Pixhawk 2.4.8的TELEM串口逻辑电平是3.3V,树莓派GPIO也是3.3V电平,可以直接互联。接线时注意飞控TX接树莓派RX,飞控RX接树莓派TX,交叉连接,最后一定要共地。
2.2 树莓派串口配置:这一步不做后面全白搭
树莓派的系统我用的是Raspberry Pi OS Lite,不带桌面,节省资源。系统装好之后,如果要让GPIO14/15作为通用串口使用,必须做两件事。
第一件事:关闭串口控制台登录。默认树莓派开机时会让系统把启动日志和登录shell映射到串口上,这个串口如果不关掉,树莓派和飞控之间发MAVLink时会混入一堆乱码文本。运行sudo raspi-config,进入Interface Options -> Serial Port,第一问“Would you like a login shell to be accessible over serial?”选No,第二问“Would you like the serial port hardware to be enabled?”选Yes。
第二件事:禁用蓝牙,把UART归位。树莓派4B上,蓝牙默认占用了PL011 UART(也就是硬件串口ttyAMA0),GPIO14/15默认被接到mini UART(ttyS0)上,mini UART波特率跟随GPU频率,不稳定,丢包严重。在/boot/config.txt末尾加上:
enable_uart=1 dtoverlay=disable-bt重启后,/dev/ttyAMA0就是那个稳定的硬件串口,可以直接被Python程序使用。如果是树莓派3B或者老版本,同样适用。确认配置成功可以执行ls -l /dev/serial*,正常情况下能看到一个指向ttyAMA0的软链接。
2.3 飞控固件与关键参数
Pixhawk 2.4.8可以刷PX4和ArduPilot两套固件,精准降落这套方案我强烈建议用ArduPilot Copter,固件版本推荐4.3.x以上。ArduPilot的Precision Landing功能非常成熟,文档齐全,PLND_TYPE里专门有Companion模式,可以直接对接机载计算机的视觉数据。PX4虽然也有landing_target功能,但配置复杂,坑比较多,新手容易劝退。
刷好Copter固件后,用Mission Planner或者QGC连接飞控,在参数表里改这几项:
SERIAL2_PROTOCOL = 2 (MAVLink2) SERIAL2_BAUD = 57 (57600波特率) PLND_ENABLE = 1 (开启精准降落) PLND_TYPE = 1 (Companion,机载视觉输入) PLND_EST_TYPE = 0 (卡尔曼滤波器估计目标位置)波特率这里我选了57600,没上921600。串口通讯不是越快越好,树莓派和Pixhawk之间距离很短,但航模电机和电调会制造电磁干扰,高速率下误码率明显上升。57600波特率足够承载MAVLink的heartbeat、航点命令和LANDING_TARGET消息,稳定优先。
全部配置完后,连接树莓派和飞控,上电,在树莓派上跑一个最简单的heartbeat检测脚本,能收到飞控的MAVLink心跳就说明通信链路通了。
3. 自主巡航实现:MAVLink消息从哪来到哪去
3.1 巡航完整消息链路
自主巡航这套流程说穿了就是一条MAVLink消息链:先告诉飞控“我要接管”,再解锁电机,然后起飞到目标高度,接着上传一串航点,最后切到AUTO模式让它自己飞。我用的Python库是pymavlink,它是MAVLink协议的官方Python实现,底层封装得非常干净。
巡航的完整流程是这样的:
- 树莓派通过串口和飞控建立MAVLink连接,收到heartbeat确认链路。
- 发送
SET_MODE指令,把飞控切到GUIDED模式,表示“机载电脑接管”。 - 发送
MAV_CMD_COMPONENT_ARM_DISARM指令解锁电机。 - 发送
MAV_CMD_NAV_TAKEOFF指令,带一个目标高度,让飞机原地起飞。 - 发送
MISSION_COUNT和MISSION_ITEM_INT消息上传全部航点。 - 发送
SET_MODE指令,切换到AUTO模式,飞控开始执行航线。
每一步都需要确认飞控返回的ACK消息,不能连续粗暴地发一坨消息过去,飞控的处理队列是有顺序的,消息挤在一起容易丢失。我在脚本里每一步发送后都会调用recv_match(type='COMMAND_ACK', blocking=True)等待确认,超时再重发。
3.2 Python脚本实操
这是巡航部分的精简核心代码,完整代码里我还加了超时重连和日志记录:
from pymavlink import mavutil import time # 建立连接 master = mavutil.mavlink_connection('/dev/ttyAMA0', baud=57600) master.wait_heartbeat() print("飞控已连接") # 切换GUIDED模式 master.set_mode_apm('GUIDED') time.sleep(0.5) # 解锁 master.arducopter_arm() time.sleep(1) # 起飞到10米 master.mav.command_long_send( master.target_system, master.target_component, mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 0, 0, 0, 0, 0, 10 ) ack = master.recv_match(type='COMMAND_ACK', blocking=True, timeout=5) print("起飞指令ACK:", ack.result) # 准备航点:起飞点先绕两个矩形点,最后回到降落区附近 waypoints = [ (31.230412, 121.474061, 10), (31.230582, 121.474391, 10), (31.231102, 121.474398, 10), (31.231085, 121.473885, 10), ] # 上传航点列表 master.mav.mission_count_send( master.target_system, master.target_component, len(waypoints) ) for i, (lat, lon, alt) in enumerate(waypoints): master.mav.mission_item_int_send( master.target_system, master.target_component, i, # 航点序号 mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT, mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 0, 1, # current, autocontinue 0, 0, 0, 0, # 停留时间、接受半径、通过半径、偏航角 int(lat * 1e7), int(lon * 1e7), int(alt), mavutil.mavlink.MAVLINK_TYPE_MISSION_ITEM_INT ) ack = master.recv_match(type='MISSION_ACK', blocking=True, timeout=5) print("航点上传ACK:", ack.type) # 切换AUTO,飞控开始执行航线 master.set_mode_apm('AUTO')几个容易出现误会的地方:
航点上传用的是MISSION_ITEM_INT而不是老的MISSION_ITEM。新消息类型里经纬度都放大到1e7的整数,避免浮点数精度问题。MAVLink2里推荐优先用INT版本。
MAV_FRAME_GLOBAL_RELATIVE_ALT_INT表示航点高度是相对起飞点的海拔高度,不是相对海平面。这样换场地飞也不用改航点高度。
航点参数里第4个参数(accept_radius)我设为0,表示让飞控使用默认的接收半径。如果你希望飞控精确飞到某个点再切向下一个点,可以给它一个较小的数值,比如2米。
3.3 为什么用AUTO而不是GUIDED逐个发Pt来飞
GUIDED模式下逐个地发SET_POSITION_TARGET_GLOBAL_INT也能让飞机巡航,但这种方式有个致命的隐患:如果树莓派程序崩了,飞控会失去目标位置输入,进入悬停或失控保护状态。AUTO模式下航线数据是存储在飞控内存里的,树莓派只需要发送一次航点,之后就算树莓派死机,飞控照样能把整个航线飞完。
这就是我强调的“飞控有最终决定权”的体现。AUTO模式让任务执行逻辑完全落在飞控内部,树莓派只在初始阶段参与航点上传,不参与实时导航,可靠性大幅提升。精准降落阶段才需要树莓派实时介入,因为那个阶段视觉信息必须是时实的。
4. 精准降落:从ArUco识别到Precision Landing状态机
4.1 视觉识别的核心数学
精准降落的第一步,是让树莓派知道自己离目标降落点多远、偏了多少角度。我采用的方案是市面上最成熟、成本最低的ArUco码识别:在一张A4纸上打印一个20cm边长的ArUco码,放在降落点正中央,树莓派用朝下安装的摄像头俯视识别它。
这里面牵扯到一个关键转换:像素坐标到飞控能读懂的角度坐标。
假设相机内参已经标定过(焦距fx、fy,光心cx、cy),ArUco检测后得到目标中心在图像里的像素坐标(u, v),那么目标相对相机光轴的水平和垂直角度是:
angle_x = atan((u - cx) / fx) angle_y = atan((v - cy) / fy)另外,用estimatePoseSingleMarkers可以直接从ArUco码的真实尺寸(20cm)解算出目标在相机坐标系下的三维坐标(tvec),其中tvec[0][0][2]就是垂直方向的距离,这个距离值直接送给飞控,作为降落过程中的高度参考。
这里必须说明一个非常容易翻车的细节:MAVLink标准里,LANDING_TARGET消息的angle_x和angle_y单位是弧度。ArduPilot内部也是按弧度处理。我自己第一版代码里写过角度制,结果飞机在降落时不停震荡,数据分析才发现是单位错了。所有角度计算,最终送进消息之前务必统一成弧度。
4.2 树莓派侧发送LANDING_TARGET
检测到ArUco码后,树莓派要做的事情就是持续发送LANDING_TARGET消息给飞控。消息发送频率建议10Hz到15Hz,太低飞控的卡尔曼滤波器跟不上目标变化,太高树莓派CPU占用率上涨,视觉帧率反而下降。
参考代码:
import cv2 import numpy as np from pymavlink import mavutil import math import time master = mavutil.mavlink_connection('/dev/ttyAMA0', baud=57600) master.wait_heartbeat() MARKER_SIZE = 0.20 # ArUco码边长,单位米 CAMERA_MATRIX = np.array([[fx, 0, cx], [0, fy, cy]], dtype=float) DIST_COEFFS = np.zeros((5, 1)) aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_50) params = cv2.aruco.DetectorParameters() detector = cv2.aruco.ArucoDetector(aruco_dict, params) cap = cv2.VideoCapture(0) # 降低分辨率,提高处理速度 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) def send_landing_target(angle_x, angle_y, distance_m): master.mav.landing_target_send( int(time.time() * 1e6), # time_usec 0, # target_num mavutil.mavlink.MAV_FRAME_BODY_FRD, angle_x, # 弧度 angle_y, # 弧度 distance_m, # 米 0.20, 0.20 # target尺寸x/y,单位米 ) while True: ret, frame = cap.read() if not ret: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) corners, ids, _ = detector.detectMarkers(gray) if ids is not None: # 取出第一个ArUco码 rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers( corners, MARKER_SIZE, CAMERA_MATRIX, DIST_COEFFS) tvec = tvecs[0][0] dx, dy, dz = tvec angle_x = math.atan2(dx, dz) angle_y = math.atan2(dy, dz) distance = dz send_landing_target(angle_x, angle_y, distance) time.sleep(0.1)这里用atan2(dx, dz)而不是直接用tvec里的dx、dy,是因为ArUco的坐标系定义里,当目标在相机正前方时dx和dy接近0,但轻微偏移时我们需要测出真实角度,直接取tvec的x、y值在目标偏移较大时会有误差。
摄像头安装方式直接影响识别距离。朝下安装时,摄像头距离地面越远,ArUco码在画面里越小,识别距离越远但精度越低。我测试下来,4米高度以内用640x480分辨率识别20cm的ArUco码没有任何压力,超过5米最好换更大尺寸的码。
4.3 飞控侧Precision Landing的运作机制
树莓派把视觉角度和距离发过去之后,飞控这边做了一系列复杂的处理。ArduPilot的Precision Landing功能(PLND)本质上是一个卡尔曼滤波器状态机,它把外界输入的视觉轨迹和飞控自身的IMU数据进行融合,估算出目标降落点相对飞机的真实位置,然后在LAND模式里驱动飞机往目标点飞行。
整个降落的触发逻辑是这样的:
- 飞机执行到航线最后一个航点后,飞控自动进入LAND模式。
- 如果PLND_ENABLE=1,且树莓派持续发送LANDING_TARGET,飞控进入Precision Landing状态。
- 在较高高度,飞控主要依靠视觉数据修正水平位置,让飞机挪到降落点正上方。
- 下降到一定高度后,飞控逐步降低油门,同时持续用视觉数据修正位置,直到触地。
- 如果视觉信号中途中断超过一定时间(由PLND_TIMEOUT参数控制),飞控自动退出精准降落逻辑,切换到普通GPS降落,保证飞机不会因为视觉丢失而在空中乱飘。
PLND_EST_TYPE=0代表卡尔曼滤波器模式,也是我实测后推荐的。它会把单帧的识别抖动平滑掉,稳定性远超直接用原始数据。PLND_EST_TYPE=1是直接用裸数据,响应快但噪声大,不建议新手使用。
降落速度也有讲究。我会把LAND_SPEED设为60到80(单位cm/s),太慢容易被风吹偏,太快则留给飞控修正位置的时间窗口太短。理想状态是飞机在1.5米高度左右已经稳定在目标点正上方,然后以平稳速度垂直落下,最终精度可以控制在15到30厘米内。
4.4 视觉识别不出来怎么办:多高度冗余设计
实际操作中,飞机下降过程中摄像头视角会发生变化,ArUco码可能在某个高度以下跑出画面边缘。我的做法是三级冗余:
- 高空(3米以上):用ArUco码粗定位,只要识别到码,就把飞机往码的投影点上方挪。
- 中低空(1到3米):ArUco码已经在画面里占据较大面积,用tvec测距和角度,持续发送。
- 低空(1米以下):飞机已经基本在目标点上空,视觉数据主要防止侧风把飞机吹离目标,此时如果识别中断,飞控来不及大幅修正,普通降落也不会偏太远。
如果你要兼顾低空识别稳定性,建议把摄像头做成可调节俯仰角,或者直接选广角镜头模组。OV5647这颗树莓派官方CSI摄像头模组默认60度左右视场角,实测在2.5米高度识别20cm的ArUco码没问题,但如果场地风大、飞机晃动厉害,最好换100度以上的广角镜头。
5. 实测中的关键经验:数据、调参、安全
5.1 串口和供电的坑比想象中多
这个项目前后测试了大概两个月,硬件上踩过的坑比软件多得多。先说串口干扰问题。最初我把树莓派和飞控用排线连接,排线从机身中部走线绕到机臂位置,结果电机一推油门,MAVLink链路就开始丢包,heartbeat频繁超时。后来发现是电机三相线在高功率输出时产生了强电磁干扰,排线离机臂太近,被耦合进了噪声。
解决方法是:串口线全部换成带屏蔽层的杜邦线或者双绞线,走线避开电机线和电调线,尽量贴着机身中线走,长度控制在15厘米以内。如果干扰依旧存在,可以在树莓派的TX、RX线上加一个磁环,效果立竿见影。
供电问题同样隐蔽。我曾图省事,把树莓派直接接到飞控的5V输出脚上,结果每次解锁电机,树莓派就重启一次。原因是解锁瞬间电调会从电池抽取大量电流,飞控5V稳压模块输入电压被拉低,直接触发了树莓派的欠压保护。后来改成独立BEC模块给树莓派供电,这个问题彻底消失。切记,树莓派和飞控的供电要隔离,共地只是为了信号参考,不是让你共用电源。
5.2 联调顺序:先仿真后实飞,别拿螺旋桨当测试工具
整个系统的联调我推荐按这个顺序来:SITL仿真 -> 桌面串口测试 -> 手持抓机测试 -> 系留测试 -> 空旷场地实飞。
SITL仿真阶段,ArduPilot自带的Software In The Loop直接在电脑上模拟飞控,树莓派上跑的Python脚本除了串口设备路径不同,其他逻辑完全一样。我建议先在这里把航点上传、模式切换、LANDING_TARGET消息的发送逻辑全部调通,再上真机。
桌面串口测试时,飞机不需要装桨,把树莓派和Pixhawk连接好,在树莓派上跑巡航脚本和视觉识别脚本,用Mission Planner看飞控是否收到航点和LANDING_TARGET消息。这一步能验证串口电平、波特率、参数配置是否正确。
手持抓机测试最有意思:人拿着飞机在地上走,让树莓派识别地面上的ArUco码,观察Mission Planner地面站画面里飞机的目标位置是否随手中移动而变化。这能确认视觉算法和MAVLink输出链路是通的,而且没有GPS参与,飞机不会乱动,非常安全。
系留测试时把飞机用绳子系在地面锚点上,测试完整的自起降流程。我建议系留高度控制在2米以内,即使失控,绳子也能兜住飞机。
实飞场地选空旷、无人的地方,周围尽量没有金属围栏和高压线。第一次实飞前,我会在飞控里设置好地理围栏和失控保护:
| 参数 | 设置值 | 作用 |
|---|---|---|
| FENCE_ENABLE | 1 | 开启围栏 |
| FENCE_TYPE | 3 | 高度和水平围栏都启用 |
| FENCE_RADIUS | 120 | 半径120米 |
| FENCE_ALT | 30 | 最高30米 |
| RTL_ALT | 20 | 失控时返航到20米 |
| FS_THR_ENABLE | 1 | 油门失控保护 |
以上参数只是一个安全起点,具体数值要根据场地规模和当地法规调整。实飞那天遥控器务必充满电,救机时切Stabilize或者Loiter都比RTL来得直接。
5.3 数据分析是调参的终极武器
每次实飞或系留测试结束后,把飞控里的DataFlash日志导出来,在Mission Planner里打开,重点看四个关键数据:PLND状态、目标角度、目标距离、修正位移。PLND状态下,数值从0变到1以上就说明飞控确实在用视觉数据进行修正;目标角度和目标距离能直接对比树莓派端发送的值,确认数据链路真没丢包;修正位移则是最终结果检验,看飞机是否真的往目标方向挪动了。
有次测试降落总是偏左半米左右,排查半天发现是树莓派串口数据里混进了半字节垃圾数据,导致angle_x偶发跳变。飞控的卡尔曼滤波器虽然能抑制单帧噪声,但连续几帧跳变还是会让位置估计偏移。这个问题靠的是对树莓派端串口数据做校验和过滤,确保进入飞控的数据一定是有效值。
日志分析时还有一个很实用的小技巧:同时看飞控里的GPS定位数据和PLND位置估计数据,两者偏差超过1米时就要注意是不是GPS的磁偏角或安装方向影响到了飞控的坐标系对齐。视觉引导的坐标系是以飞机机头方向为基准的,如果GPS航向和磁航向存在偏差,飞控在融合这两路数据时就会打架。
5.4 安全兜底永远是第一优先级
精准降落是个锦上添花的功能,安全兜底才是飞机能反复起降的根本。我的飞控配置里,RTL(返航)永远比精准降落优先。就算树莓派完全死机,只要飞控还在,切RTL就能让飞机回到起飞点附近。如果没有开FENCE和FS_THR_ENABLE,树莓派程序一旦异常,飞机可能一直往一个方向飞出去,那种场景我完全不想再经历第二次。
实操中我用的兜底方式是:在树莓派脚本里加一个看门狗机制。每次向飞控发送LANDING_TARGET时,同时记录当前时间戳。如果连续3秒没有成功发送,脚本自动退出并释放GPIO,同时通过飞控的MAVLink消息触发RTL。这样即使视觉识别卡死,飞机也不会在原地傻等,而是自动执行预设的返航逻辑。
精准降落进入最后阶段时,尤其是高度低于1米后,我会在遥控器上一直虚握油门摇杆,一旦察觉飞机有任何异常的横向漂移,马上切回Loiter或者手动模式接管。视觉系统再准,也不如人眼的瞬间判断可靠。
这套树莓派、MAVLink和Pixhawk 2.4.8的组合完成之后,我最大的感受是:飞控把底层飞行控制做到了极致,树莓派把上层智能做到了灵活,两者结合才是开源无人机该有的灵魂。哪怕你现在预算有限,用一块2.4.8飞控加一台树莓派3B也能复现这套巡航加精准降落流程,树莓派3B跑OpenCV的帧率低一些,但只要把分辨率降到480p、牺牲一点识别距离,效果依然可用。等你跑通整个链路之后,自然会知道下一步该把算力升级到树莓派5还是Jetson,视觉识别是该换YOLOv5还是保持ArUco——那个阶段的选择,已经是另一个故事了。