
搞无人机开发的人迟早都会遇到一个问题想调飞控逻辑、写地面站功能、验证航线规划但手头没有飞机或者不敢直接拿真机试。买一套Pixhawk加机架倒是不贵可每炸一次机都是成本更别提在室内跑高速航线测试有多危险。我当时就是在这种状态下开始折腾DRONEKIT-SITL、MAVProxy和QGroundControl这套模拟飞行环境的折腾完之后最大的感受是这套东西学一次后面几乎所有的飞控开发前期验证都能在电脑里完成。这套环境的核心思路很简单——用软件模拟一架完整的ArduPilot飞控DRONEKIT-SITL让它跑在电脑上通过MAVLink协议对外输出飞行数据再用MAVProxy做数据链路的转发中枢把模拟飞控的数据转给地面站最后由QGroundControl来实时显示姿态、地图位置、飞行模式并且支持鼠标点击指令直接下发。整个过程里所有数据包都在本机网络里流转不碰任何真实硬件却能把从“飞控”到“链路”再到“地面站”的完整闭环跑通。我梳理了一下这篇文章更适合这么几类人看刚开始接触ArduPilot/PX4开发、想用模拟环境跑通飞控流程的学生正在写地面站或通信中间件的开发者需要一个稳定可控的虚拟飞机做联调还有单纯想低成本体验航点规划、失控返航、参数调优的无人机爱好者。下面把我从安装到排错再到进阶用法踩过的坑、走过的路都写清楚。1. 为什么是“三件套”而不是一个软件打天下很多第一次接触模拟飞行的朋友会问QGroundControl本身不就有模拟器插件吗ArduPilot官方也提供了sim_vehicle.py一键启动的工具为什么还要专门再把MAVProxy塞进来搞出一套“三件套”这里面的分工逻辑其实非常清晰搞清楚之后你对整条数据链路就会有立体认识。1.1 三件套的分工逻辑DRONEKIT-SITL是这整套环境的“飞机”。它本质上是ArduPilot飞控固件的软件在环模拟版本把原本跑在STM32芯片上的飞控代码编译成电脑上的原生程序再接入一个模拟的气动模型。你给它一个起飞指令它会按照真实的飞控控制律去计算姿态变化、电机转速、速度与位置然后把结果通过MAVLink消息发出来。它还能响应参数修改、模式切换、故障注入跟真实飞控的行为非常贴近。MAVProxy是这整套环境的“通信枢纽”。这个名字直译是“MAVLink代理”它做的事情就是把MAVLink数据流从一个端口中转到另一个端口过程中可以做协议分析、数据记录、指令注入、消息过滤。为什么要中转而不直接让SITL连QGC因为SITL的对外接口通常是一个动态的TCP端口而QGC更喜欢监听UDP端口更关键的是你经常在同一时间既开QGC又跑一个Python脚本去控制飞机如果飞机只能连接一个地面端那整个调试流程就堵死了。MAVProxy能同时向多个端口转发数据相当于把“一对多”的连接问题解决了。QGroundControl是这整套环境的“驾驶舱”。它是一个成熟的开源地面站支持MAVLink协议能显示姿态仪表、地图轨迹、飞行模式、电池电压、GPS星数等所有关键信息。你给它接上数据流之后就能用鼠标点击地图设置航点、拖动滑块改变飞行参数体验跟拿着遥控器飞真机没什么区别。QGC负责把链路中的数据转成人能看懂的画面同时把你的操作翻译成飞控能执行的MAVLink指令。这三个组件的关系可以用一条流水线来描述SITL产生飞行数据MAVProxy负责搬运和分发QGC负责显示和交互。你在QGC上点一下“起飞”指令先通过UDP包发到MAVProxyMAVProxy再把它转交给SITL模拟出来的飞控飞控经过计算后把新的姿态数据原路返回整个过程实时闭环。1.2 这套环境的能力边界先说能做到的事情你可以完整验证飞控的模式切换逻辑比如从Stabilize切到Loiter再切到Auto可以跑一个包含十几二十个航点的航线任务让飞机自动一个点一个点巡航可以打开QGC的MAVLink Inspector面板逐条查看位姿、速度、加速度消息这对写地面站解析代码来说简直是神仙帮手还可以通过MAVProxy的module命令注入RC遥控信号、模拟遥控器丢信号观察飞控的失控保护动作。做不到的事情也要清楚DRONEKIT-SITL的气动模型相对简化它不会精确还原真实机架在复杂风场中的抖动它模拟的传感器数据是理想化的不会像真机一样出现磁罗盘受干扰、GPS漂移、震动噪声等问题所以你不能用它去验证传感器滤波算法的边界效果。另外SITL跑的是ArduPilot的固件代码PX4系列的模拟飞行通常走的是jMAVSim或Gazebo不在这次讨论范围内。清楚了这些边界你才不会被“模拟器能跑通”误导成“真机也能一次就飞好”但作为一个开发调试工具它已经足够胜任90%的前期工作。2. 安装部署版本选择与平台差异这套环境最大的历史包袱是Py2/Py3的切换。很多早年间的教程还在用Python 2写MAVProxy和DroneKit-SITL照抄的话在现在的系统上大概率直接报错。我给出一套经过验证的、在当前主流系统上能跑通的部署组合。2.1 从零搭好三个组件的常用方式建议在Linux或macOS上运行Windows虽然也能跑但比较折腾我重点说Linux下的方式。如果你用的是Ubuntu/Debian系先确保系统里有git、python3-pip这些基础工具。MAVProxy的安装很简单官方源和pip源都有。当前版本推荐用pip安装sudo apt update sudo apt install python3-pip git python3-dev pip3 install MAVProxy安装完之后执行mavproxy.py --version确认一下。注意有些发行版的包管理器会带一个旧版MAVProxy比如apt install mavproxy装出来的可能是Python 2版本启动时会直接提示找不到模块。保险起见直接用pip装并且确认which mavproxy.py指向的是pip所在的路径。DRONEKIT-SITL有两种获取方式。一种是直接用pip装已编译好的模拟器程序pip3 install dronekit-sitl这个包下载的是预编译的SITL可执行文件和特定版本的ArduPilot固件对应。启动命令一般是dronekit-sitl copter-3.3。另一种方式是直接从ArduPilot源码目录拉最新的SITL这种方式能获取到最新的固件功能和参数但编译环境要求高一些git clone --recurse-submodules https://github.com/ArduPilot/ardupilot.git cd ardupilot git submodule update --init --recursive Tools/environment_install/install-prereqs-ubuntu.sh -y source ~/.profile在ArduPilot目录下用sim_vehicle.py -v ArduCopter就能启动最新的Copter模拟器。这个方式我更推荐因为sim_vehicle.py自带一个更友好的参数体系和MAVProxy联动逻辑后面排错的时候会省很多事。QGroundControl就更直接了去qgroundcontrol.com下载对应平台的安装包。Linux版是一个AppImage文件下载后赋可执行权限就行chmod x QGroundControl.AppImage ./QGroundControl.AppImage注意QGC对系统库有要求Ubuntu 20.04及以上一般没问题老系统可能要装一些libssl、libgstreamer相关的依赖启动报错时根据提示补齐即可。2.2 老教程最大的坑Python 2 与 Python 3这个坑我印象太深了。我第一次折腾这套环境是在一个刚装好的Ubuntu 18.04上照着网上特别老的一篇教程用apt install mavproxy装完再启动mavproxy.py --master tcp:127.0.0.1:5760结果报了一堆类似No module named pymavlink的错误。后来发现apt源里那个MAVProxy是绑定Python 2的老版本而系统里的Python 3环境和它完全不兼容。正确的处理思路是把所有组件都统一到Python 3生态。MAVProxy用pip3装最新版pymavlink会作为依赖自动装好DroneKit-SITL如果走pip3安装它会自动匹配当前ArduPilot的Python 3环境如果是源码编译SITL它的构建脚本在Ubuntu 18.04上默认走Python 3。统一之后再不会有“某个模块只在Python 2里才有”这种问题。另外如果你以后想写DroneKit-Python脚本去控制这架模拟飞机也要注意DroneKit库本身对Python 3的支持已经比较成熟了pip3 install dronekit即可。2.3 装完之后先做一次“空跑”正式启动完整链路之前建议先把SITL单独拉起来做一个冒烟测试。比如在ArduPilot源码目录下执行sim_vehicle.py -v ArduCopter --console --map--console会弹出一个原生的飞控状态汇总窗口--map会打开一个简易地图用来显示飞机当前经纬度。如果这两个窗口能正常弹出并且控制台里持续刷新类似AP: ArduCopter V4.3.2 (f3f0e4b1)这样的启动日志说明SITL核心已经能跑起来了。用CtrlC停止再进入下一步。这里强烈建议不要跳着来因为后续所有问题排查都是基于对“SITL本身是否正常”的判断展开的。SITL能启动但QGC连不上问题通常出在链路SITL启动一秒就崩溃问题几乎都在环境依赖SITL起来了但控制台刷出大量红色报错那要检查固件编译是否完整。空跑一次的最大价值就是帮你把“飞机本体”和“链路”这两类问题先切开。3. 启动与连接的正确姿势很多人拿到教程后第一件事就是同时开三个软件然后发现QGC一直转圈、MAVProxy报错、SITL闪退根本不知道问题出在哪一个环节。正确的打开方式应该是有先后顺序的先把SITL跑起来确认“飞机”已经升空在等指令再把MAVProxy接到SITL上确认中转链路已经建立最后才是QGC连接到MAVProxy的转发端口。三步按序执行每一步都有明确的验证信号。3.1 第一步拉起模拟飞机以ArduPilot源码方式为例打开终端进入ardupilot目录执行sim_vehicle.py -v ArduCopter --console --map -I0-I0表示这是第0号模拟飞行实例如果你同时开了两架模拟飞机第二架用-I1以此类推。启动成功后终端里会有一行关键信息类似Ready to fly说明ArduPilot模拟器已经进入待命状态。此刻它内部的状态是“未解锁、无GPS、在原点位置”但飞控程序确实在正常循环运行。sim_vehicle.py在启动过程中会自动拉起一个MAVProxy会话这是ArduPilot官方工具链的默认行为。它内部已经创建了一条从SITL输出端口到本机某个TCP端口的通道这个端口通常默认是tcp:127.0.0.1:5760。我们自己搭DRONEKIT-SITL场景时会单独控制MAVProxy所以这里要用一个参数把它自带MAVProxy的行为先关掉sim_vehicle.py -v ArduCopter --console --map -I0 --no-mavproxy加了--no-mavproxy之后SITL只保留一个TCP服务端口在等待外部连接不会自己再开MAVProxy。这样才能在后面演示出MAVProxy作为独立枢纽的价值。3.2 第二步MAVProxy作为链路枢纽新开一个终端启动MAVProxy把它当作SITL与地面站之间的透明代理mavproxy.py --master tcp:127.0.0.1:5760 --out udp:127.0.0.1:14550 --out udp:127.0.0.1:14551这里每个参数都值得拆开说。--master指定MAVProxy从哪个数据源读取MAVLink数据现在指向的就是SITL开启的TCP端口5760--out指定MAVProxy向哪些地址转发数据udp:127.0.0.1:14550就是发给本机的14550端口QGC默认监听这个UDP端口我额外开了一个14551端口留着给后面的Python控制脚本用。一条命令就把“一对多”的转发关系建立起来了。启动成功后MAVProxy会在自己的终端里刷出收到的MAVLink消息比如Detected vehicle 1:1、online等信息。最直接确认链路通没通的方法是输入status并回车STABILIZE 0%如果你的页面里能看到一个非零的高度、速度或姿态角数值说明SITL的数据确实通过MAVProxy输出出来了。此时链路中端已经在工作。3.3 第三步QGroundControl接手显示与控制QGC默认会在UDP 14550端口监听MAVLink数据。打开QGC之后正常情况下它应该会自动发现MAVProxy转发过来的数据流界面右上角的通信状态图标从灰色变成绿色还会自动弹出“Vehicle 1 connected”之类的提示。如果没有自动发现需要手动添加通信连接打开左上角的齿轮图标进入“Comm Links”设置新建一个UDP连接监听端口填14550点击连接。这一步做完后主界面上方的飞机状态图标应该变为绿色姿态仪表开始实时转动。在QGC中你能看到的画面是这样的地图上有个小飞机图标显示当前经纬度左侧的仪表盘实时显示Roll/Pitch/Yaw角飞机类型、模式、电池电压、GPS状态等参数都在顶栏刷新。此时整套三件套链路已经通了可以开始真正的模拟飞行。3.4 快速自检链路是否真的通连接通不通别只看QGC的图标是否变绿我建议做两件更靠谱的自检。第一件回到MAVProxy终端输入MAV module list看看当前加载了哪些模块至少应该有wp、param、arm、geo这些常用模块。第二件在QGC里打开右上角的“Analyze Tools - MAVLink Inspector”如果里面能看到持续刷新的HEARTBEAT、GPS_RAW_INT、ATTITUDE消息说明一整套双向通道都正常工作。我遇到过一种情况QGC图标变绿但地图上的飞机位置是固定的MAVLink Inspector里只有HEARTBEAT没有GPS数据。这种问题通常是SITL没有加载GPS模拟模型或者模拟器固件本身没跑到ARMED状态。在MAVProxy里执行module load sim然后再试基本能解决。链路自检就是要做到“肉眼可见的数据流”而不是只看连接状态。4. MAVLink数据在链路里是怎么走的跑通了整套环境之后建议停下来想一想刚才敲的那几条命令背后发生了什么。理解了MAVLink数据在SITL、MAVProxy、QGC三者之间的流转过程后面写代码、调参数、排查问题都会从容得多。4.1 一个飞控数据包的生命周期想象一下SITL模拟出的飞控内部有一个循环每秒钟跑几十次每一次都会计算最新的姿态和位置然后打包成MAVLink消息比如ATTITUDE、GLOBAL_POSITION_INT、GPS_RAW_INT。这些消息通过SITL开启的TCP Server端口发出等待外部程序来连接。MAVProxy以TCP客户端身份连接到这个端口从这里读走所有字节流。MAVProxy内部有一套pymavlink解析逻辑能识别出一条消息从哪里开始到哪里结束完成拆包再按照--out参数配置的目标端口重新打包发送。因为协议格式相同QGC收到的数据跟在SITL TCP口收的数据没有任何区别。QGC作为一个UDP Server在14550端口上监听来自MAVProxy的数据收到了就解析并更新界面。反过来你在QGC上点击“Takeoff”按钮QGC会生成一条MAV_CMD_NAV_TAKEOFF指令消息把指令发给前面那条UDP链路的“另一端”——也就是MAVProxy在14550端口上的发送端。MAVProxy收到指令后判断这是给飞机的就原样转发给TCP链路那头的SITL飞控。飞控收到指令、执行计算、把最新的执行结果再封装成状态消息回传。这样一圈下来就是一个完整、实时的双向闭环。4.2 端口分配与协议转换的设计逻辑这套链路里出现的三个端口需要理清楚5760、14550、14551。5760是SITL自带的TCP服务端口提供给像MAVProxy、QGC或自定义脚本这样的客户端去连接它的特点是“必须主动连别人”14550是QGC监听的UDP端口特点是“收包无需建立连接”14551是我们额外开的UDP转发口用于接其他MAVLink客户端。为什么MAVProxy要从TCP读、却用UDP发这部分是典型的链路设计需求TCP有握手、重传机制适合一对一的可靠通信但多端同时接收时会让每个连接都占用一份资源、还容易互相拖慢UDP是广播式地“扔包”每个监听端口各取所需天然适合一对多传输。MAVProxy做的正是把“从TCP拿到的可靠数据流”转换成“一对多UDP广播”这也是地面站与飞控通信中最经典的数据分发方式。这种设计的另一个好处是你可以在MAVProxy后面无限挂其他调试工具。比如第三个--out udp:127.0.0.1:14552加一个端口给Mission Planner用再加一个给wireshark抓包用每个工具都独立地从自己的UDP端口收数据互不干扰。4.3 再加一个开发者地面站之外的扩展口实操过程中我最常用的扩展场景是QGC看着飞机同时用一个Python脚本自动跑一些重复性测试。实现方式就是在MAVProxy命令里多带一个--out udp:127.0.0.1:14551然后用DroneKit-Python去连接这个端口from dronekit import connect, VehicleMode vehicle connect(127.0.0.1:14551, wait_readyTrue) print(Vehicle connected:, vehicle.version) print(Mode:, vehicle.mode) print(Altitude:, vehicle.location.global_relative_frame.alt) vehicle.mode VehicleMode(GUIDED) vehicle.armed True vehicle.simple_takeoff(10)这段脚本不需要直连SITL只要从MAVProxy的扩展端口拿数据就行。整个链路里QGC负责显示、Python脚本负责自动化、MAVProxy负责当枢纽各司其职。我通常在自动化测试场景下会写一个Python脚本循环执行“起飞到10米-飞向A点-返航”这样的流程同时用QGC盯着实时画面两边并行互不影响。5. 排错实战五个典型故障与排查链路模拟环境搭建过程中踩坑是必然的。这节我把从实践里碰到的、以及身边朋友经常问到的几个故障完整复盘一遍每个都给出一条可执行的排查链路。这里不做报错问答式的碎片回复我会按照“现象—定位—解决”的顺序讲清楚。5.1 连接一直转圈心跳包到底来没来现象QGC启动后图标一直灰白色转圈提示连接中MAVProxy和SITL都显示正常。这种情况十有八九是QGC没有收到MAVLink心跳包。MAVLink里有一条HEARTBEAT消息每秒以1Hz频率广播地面站就是靠它来判断链路是不是通的。没有心跳地面站即使收到了其他数据也会认为“飞机不在线”。排查链路分三步走。第一步在MAVProxy终端里输入status看有没有类似1 amber的提示。如果是No data说明MAVProxy没有从SITL拿到任何数据问题在SITL侧的启动方式回到3.1检查。第二步确认MAVProxy的--out的端口号和QGC监听端口是否一致很多人会把udp:127.0.0.1:14550写错成14551这种低级错误很隐蔽看配置就能发现。第三步查看系统防火墙是否放行UDP 14550端口。Linux下执行sudo ufw status如果防火墙开着用sudo ufw allow 14550/udp放行。5.2 SITL进程秒退与依赖环境问题现象sim_vehicle.py -v ArduCopter启动后终端刷几行日志就退出或报错。SITL启动失败最常见的原因是缺少编译依赖或者Python包依赖不完整。如果是编译阶段报错比如缺少libtool、autoconf、g这些用安装脚本重新执行一次即可Tools/environment_install/install-prereqs-ubuntu.sh -y如果是启动时Python模块导入失败检查pymavlink、numpy这些包是否装了当前Python版本对应的版本pip3 install --upgrade pymavlink numpy还有一个很容易被忽略的问题ArduPilot源码目录更新到新版本后SITL可能不再支持老的操作系统库版本。这种情况下建议直接切换到稳定分支而不是执着于最新master分支。我在Ubuntu 20.04上用Copter 4.3.x稳定分支跑得很稳升级到4.4测试版时反而出现过随机退出问题虽然最后定位是参数文件兼容性问题但这种折腾成本不如从一开始就选稳定版。5.3 MAVProxy能连上但QGC一片死寂现象MAVProxy显示Detected vehicle并持续刷新数据QGC里却什么都看不到一直在“No data”。这种不对称现象通常出在MAVProxy的--out参数和QGC的实际连接模式上。QGC的“Comm Links”里如果选择的是UDP它默认是监听模式也就是它只接收数据不需要主动发送。但如果QGC配置成了TCP连接模式它就会在指定端口上等一个TCP连接进来而MAVProxy默认不会主动去连QGC的TCP端口。这两种模式不匹配就会出现MAVProxy这边疯狂发数据、QGC那边压根没接收的情况。我的排查经验是在QGC的Comm Links设置里确认连接类型选的是UDP并勾选监听端口QGC作为UDP接收方同时确认MAVProxy那边的--out用的也是udp:开头而不是udpin:或tcp:。这里udp:和udpin:的区别经常让人头晕简单说--out udp:127.0.0.1:14550表示MAVProxy主动向目标端口扔数据包--out udpin:0.0.0.0:14550表示MAVProxy去监听这个端口等别人给它发包。QGC作为监听方时我们必须用前一种udp:。5.4 模拟起飞后数据异常真的是bug吗现象按了Takeoff之后飞机高度在QGC上乱跳或者姿态角剧烈震荡有时飞机的全球位置数据跑到莫名其妙的地方。这种异常数据首先要想清楚一件事SITL的GPS初始化位置是哪个点。默认情况下SITL的原点设置在地球的某个固定坐标通常是在澳大利亚某处而且飞机刚启动时GPS状态是No GPS直到你解锁后才会借助内部模拟器算出坐标。如果你在QGC里设置的Home点与SITL模拟出来的实际起点不一致地图上就会看到飞机“瞬移”的问题。更常见的异常是SITL默认参数和飞控的PID没调好。Copter的默认PID参数在理想气动模型下是能飞的但如果你使用高版本ArduCopter并加载了某些非默认机架参数模拟出来的飞行动作就会很离谱。处理方式是在MAVProxy里执行param set ARMING_CHECK 0 param set INS_HNTCH_ENABLE 0 reboot关掉部分预检查项和航向传感器模拟限制再试一次Takeoff。很多“模拟起飞就乱飞”的问题到这里就能解决。如果还是乱飞检查一下是否有多个MAVProxy实例在同时操作SITL指令冲突会导致飞控状态非常混乱。5.5 端口冲突与重复实例干扰现象启动第二个MAVProxy时提示Address already in use或者之前的QGC突然断连。当你同时调试多架模拟飞机、多个地面站时端口管理会变成一个隐性坑。SITL默认的TCP端口是5760MAVProxy默认输出UDP端口是14550如果第二个模拟器实例还用同样的端口必然产生冲突。解决办法是给不同实例分配不同端口sim_vehicle.py -v ArduCopter -I0 --no-mavproxy --out 127.0.0.1:14550 sim_vehicle.py -v ArduCopter -I1 --no-mavproxy --out 127.0.0.1:14555用-I参数隔开不同实例同时在MAVProxy的--master里指定对应的端口。每架飞机只允许有一个MAVProxy以相同的--master连接它防止多写者的竞争。另外如果之前运行过的MAVProxy进程没有被完全杀掉它会继续占着端口需要先用ps aux | grep mavproxy查出来再kill掉。对这类问题我个人的习惯是每启动一个组件前后都看一眼端口占用情况ss -ulpn | grep 14550 ss -tlpn | grep 5760这两个命令能快速确认链路端口是否被预期进程占用。多加这一眼很多诡异的“偶发断连”都能在几十秒内定位。6. 别停留在起飞把这套环境变成开发测试台模拟飞行跑通之后这套环境的价值才真正开始展现。把它当作一个可以反复折腾、随便乱来都不会炸机的测试台你会发现它能做的远远不止“看飞机飞起来”。6.1 用DroneKit-Python脚本自动接管飞机前面已经提到用DroneKit连接MAVProxy扩展端口、执行简单的起飞指令。再往深走一步你可以用它做完整的自动化航线测试。下面这段脚本跑起来后飞机起飞到10米高度依次飞到两个设定航点然后自动降落from dronekit import connect, VehicleMode, LocationGlobalRelative import time vehicle connect(127.0.0.1:14551, wait_readyTrue) def arm_and_takeoff(target_altitude): print(Basic pre-arm checks) while not vehicle.is_armable: print(Waiting for vehicle to initialise...) time.sleep(1) print(Arming motors) vehicle.mode VehicleMode(GUIDED) vehicle.armed True while not vehicle.armed: print(Waiting for arming...) time.sleep(1) print(Taking off!) vehicle.simple_takeoff(target_altitude) while True: print(Altitude: , vehicle.location.global_relative_frame.alt) if vehicle.location.global_relative_frame.alt target_altitude * 0.95: print(Reached target altitude) break time.sleep(1) arm_and_takeoff(10) waypoint1 LocationGlobalRelative(47.398039, 8.545572, 10) waypoint2 LocationGlobalRelative(47.398139, 8.545672, 10) vehicle.simple_goto(waypoint1) time.sleep(10) vehicle.simple_goto(waypoint2) time.sleep(10) print(Returning to Launch) vehicle.mode VehicleMode(RTL) time.sleep(15) vehicle.close()这套环境的好处在于脚本里跑飞逻辑、QGC里实时看状态、MAVProxy日志里抓数据三位一体开发效率极高。我写地面站的时候就是用这个脚本反复验证自动飞行逻辑和地面站显示的同步性不用真机也能把bug清得七七八八。6.2 模拟故障场景验证飞控的兜底逻辑这套环境最值钱的部分是做“故障注入”测试。用MAVProxy的sim模块能模拟各种传感器异常比如模拟GPS丢失MAV sim on MAV sim gpsfix 0GPS瞬间变成无定位状态飞控从Auto模式切入到AltHold或Land模式的失控保护逻辑会被触发。你可以观察QGC上模式的切换过程、MAVProxy日志中的告警信息、以及飞控最终的落点位置。类似地还能模拟电池低电压MAV param set SIM_BAT_VOLTAGE 10.5电压低于低电量阈值后飞控会触发低电量保护自动切换模式或执行返航。这套测试中你可以验证QGC上的警告提示是否正常、RTL航线是否按预期执行、MAVProxy日志是否记录了准确的保护触发原因。这些在真机上做很危险且很难复现的场景在模拟环境下可以反复演练到完全有把握。6.3 为地面站开发提供一个稳定的调试后场如果你在开发自己的地面站或MAVLink数据解析模块这套三件套环境更是必需品。你可以把MAVProxy当作数据源模拟出各种真实场景下的MAVLink数据流测试你写的消息解析、航线显示、告警逻辑。最方便的一点是MAVProxy支持log模块可以记录完整的数据流回放的时候还能模拟之前的飞行过程MAV module load log MAV log list MAV log replay 00000001用录制好的历史数据回放来调试自己的UI或逻辑比每次都要重新飞一遍的测试方式高效得多。而且由于数据是真实飞控产生的回放数据的复杂度远超手工构造的测试消息能暴露出很多解析逻辑的边界问题。6.4 多人协作与持续测试思路还有一个很多团队没有意识到的用法把MAVProxy的转发端口开放给局域网内的其他机器。假设你的SITL跑在一台高性能服务器上MAVProxy设置在启动时加了--out udp:192.168.1.100:14550另一台电脑上的QGC就能通过局域网连接到这架模拟飞机。这样多个开发人员可以同时看到同一架飞机的状态共享同一套测试环境很适合团队联调、教学演示和远程协同排查问题。持续集成的思路也能嵌进来我见过有个团队把SITL跑在CI流水线里每次代码提交后自动启动模拟飞行跑一个固定的航线脚本验证新改动是否导致了飞行控制逻辑回归。飞机本身是软的随便折腾这就是这套环境最大的底气。我自己的习惯是任何时候开始一个新功能都会先在DRONEKIT-SITL MAVProxy QGroundControl这套环境里“飞”一遍流程确认逻辑再上真机。SITL的代码更新、MAVProxy的端口配置、QGC的显示设置都用文本或脚本固化下来遇到环境问题能十分钟内重装恢复。这套组合玩熟了之后你会觉得无人机开发的调试门槛真的低了一大截。