拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

无人机飞控仿真三件套:SITL+MAVProxy+QGC从环境搭建到航线规划实战

无人机飞控仿真三件套:SITL+MAVProxy+QGC从环境搭建到航线规划实战

做无人机飞控二次开发这一年多,我最大的体会是:真机调试不是第一选择,能在地面上把逻辑跑通就别急着上天。

DRONEKIT-SITL、MAVPROXY、QGroundControl这三件套,就是我用得最顺手的仿真组合。简单说,DRONEKIT-SITL让我的笔记本变成一架虚拟无人机,MAVPROXY在后台搬运和翻译MAVLink数据流,QGroundControl把这架虚拟飞机的姿态、高度、航点实时投到屏幕上,操作起来和真机几乎没差别。这篇文章就把这套模拟飞行环境从安装到自动航线跑通的完整过程记录下来,适合刚接触无人机二次开发、或者想先调通算法再碰真机的朋友参考。

1. 用软件在环模拟飞行,为什么是这套组合

1.1 三个工具各自扮演什么角色

很多朋友第一次接触仿真时容易懵,因为这三个名词听起来都有点“专业”,但把它们拆开看就简单了。

DRONEKIT-SITL是Software In The Loop的缩写,软件在环仿真。它把ArduPilot飞控固件直接编译成电脑上可运行的程序,用软件模拟出GPS信号、气压计、加速度计、陀螺仪这些传感器数据,同时虚拟出一架飞机的动力学模型。也就是说,你运行它的时候,电脑里真的“飞”着一架虚拟无人机,可以通过MAVLink协议读出它的姿态、位置、速度,也可以向它发送解锁、起飞、转向等指令。

MAVPROXY是一个MAVLink通信网关。它的核心价值是“转发”和“拆包”。无人机端产生的是MAVLink二进制数据流,地面站需要这些数据来做显示和控制。MAVPROXY可以同时接收多个MAVLink源,再转发给多个终端,而且能在转发过程中记录日志、修改参数、注入指令。你可以把它理解成一个“中间路由器”,让数据在多个软件之间顺畅流动。

QGroundControl是地面站,英文简称QGC。它负责把MAVLink数据翻译成人能看懂的画面——地图、航点、姿态仪表、电池电压、飞行模式,全部图形化显示。同时它也承担航线规划任务,你可以在地图上直接拖动航点,生成一条航线,然后一键上传给无人机。 QGroundControl是目前开源飞控领域最主流的图形化地面站之一,对ArduPilot和PX4都支持得很好。

这三者的关系其实是一条数据链路:DRONEKIT-SITL用软件生成一架虚拟飞机,MAVPROXY把飞机的数据流转发给外部端口,QGroundControl在这个端口上接收数据并做可视化显示。命令行工具、图形界面和通信网关各管一段,但组合起来就是一个完整的闭环仿真系统。

1.2 脱离真机调试,收益和边界在哪

有人会问,为什么不直接用真机测试?成本和安全是最重要的两个原因。一套稍微像样点的四旋翼,加上电池、数传、遥控器,少说也要几千块,万一飞控逻辑写错,炸机就是瞬间的事。而软件仿真里把无人机参数调乱、让飞机翻滚坠地,只需要重启一个进程,成本趋近于零。

另一个容易被忽视的价值是可重复性。真机飞行受天气、磁场、GPS信号影响很大,同一段代码在上午和下午飞出来的结果可能完全不一样。仿真环境里你可以固定一个起始坐标、固定一组环境参数,反复验证同一条航线、同一个控制逻辑,跑十次结果都应该一致。这对定位逻辑bug非常有帮助。

但也要说清楚边界。仿真毕竟是模型,传感器数据是理想化的,缺乏真实环境中的磁场干扰、风场突变、GPS多径效应。最简单的例子:真机上经常出现的GPS短时丢星,仿真里就很难模拟得足够真实。所以仿真通过是基础,真机试飞仍然是必要环节。更合理的做法是先用这套软件仿真把逻辑层面的问题全部过滤掉,再带着相对可靠的代码去真机上做小范围试飞。

2. 环境配置不走弯路:安装与端口规划

2.1 基础环境与Python虚拟环境

三件套里,DRONEKIT-SITL和MAVPROXY都是Python生态下的工具,QGroundControl是独立编译的图形程序。所以系统环境的第一优先级是把Python环境弄干净。

推荐使用Ubuntu 20.04或22.04。Windows下虽然也能跑,但权限配置和网络端口的坑比较多。Mac也可以,但后续一些ArduPilot固件工具链在macOS上兼容性略差。如果手头只有Windows电脑,建议直接装虚拟机,性能上完全够用。

Python版本选择上,3.8到3.10是比较稳的范围。太新的Python版本有时候会和较老的DroneKit库产生兼容问题,太旧的又跟不上依赖包。安装之前先确认一下:

python3 --version pip3 --version

强烈建议新建一个虚拟环境,不要图省事直接怼到系统Python里。因为DroneKit、pymavlink这些库会有一些特定版本的依赖,稍不注意就污染了系统环境,后续装其他项目时会出现一堆奇怪的冲突。

mkdir ~/uav-sim && cd ~/uav-sim python3 -m venv venv source venv/bin/activate

之后所有的安装和运行都在这个虚拟环境里进行,干净又安全。

2.2 安装DRONEKIT-SITL与MAVPROXY

先装DroneKit-SITL,以及配套的DroneKit库:

pip install dronekit dronekit-sitl

DroneKit-SITL本身会从ArduPilot源码仓库拉取对应的固件并编译,所以系统里需要具备基础编译环境。如果之前没装过编译工具,先执行:

sudo apt update sudo apt install build-essential git cmake python3-wxgtk4.0

装MAVProxy有两种方式。一种是apt直接装,版本相对旧但稳定;另一种是pip安装,能用上最新功能:

pip install MAVProxy

安装完成后,验证一下:

dronekit-sitl --help mavproxy.py --help

能正常打印帮助信息,说明两个核心工具已经就位。这一步如果报错,大概率是依赖缺失,根据提示补装即可。

2.3 安装并连接QGroundControl

QGroundControl的安装最简单,去官网下载AppImage格式的Linux版本,给执行权限后直接运行:

chmod +x QGroundControl.AppImage ./QGroundControl.AppImage

首次打开会进入一个欢迎界面,建议先跳过相机和参数设置的向导。QGC默认会尝试自动检测串口和网络上的无人设备,但仿真环境里没有物理链路,需要手动配置UDP连接。

这里的端口规划非常关键。整条数据链路的默认约定是:DRONEKIT-SITL监听TCP端口5760,MAVPROXY连接这个TCP端口,然后通过UDP端口14550向外转发。QGC连接时就走UDP 14550这个口。

点开QGC右上角的齿轮图标,进入“Comm Links”设置,添加一个UDP连接,端口号填14550,点击连接。此时如果MAVPROXY已经在运行并转发数据,QGC左侧就会弹出模拟飞机,地图上会出现home点的绿色图标。

3. 完整实操:从冷启动到自动航线

3.1 启动SITL并验证MAVLink数据流

安装完成只是第一步,真正复杂的是启动流程和端口对接。我的习惯是先开SITL,再开MAVPROXY,最后连QGC,按顺序来不容易乱。

开一个终端,激活虚拟环境,启动模拟四旋翼:

dronekit-sitl copter-3.3 --home=35.123456,-120.123456,0,180

这条命令的含义是:启动一架模拟三号版本固件的四旋翼,起始坐标设为北纬35.123456、西经120.123456,海拔0米,机头朝向正北。home坐标后面的数字180表示初始偏航角,单位是度。

如果启动成功,终端会持续输出姿态、GPS状态等仿真信息。看到类似“GPS lock”的字样,说明虚拟GPS已经完成了定位。

注意,这个过程中电脑会编译下载一些组件,第一次跑可能比较慢,耐心等待即可。

此时SITL已经在TCP 5760端口等待连接。再开一个新终端,激活虚拟环境,启动MAVPROXY:

mavproxy.py --master tcp:127.0.0.1:5760 --out udp:127.0.0.1:14550

--master tcp:127.0.0.1:5760表示从本机的TCP 5760端口读取数据源,也就是SITL。--out udp:127.0.0.1:14550表示把数据以UDP形式转发到本机的14550端口,QGC后续就从这里收数据。

MAVPROXY启动后,会进入交互式命令行模式。不要急着操作,先输入status查看数据状态。正常情况应该能看到MAVLink数据线在持续跳动,说明数据流已经通了。

另一个常用验证命令是mavlink info,可以查看当前数据收发数量和丢包率。如果丢包率持续为0,数据链路就是健康的。

3.2 用MAVPROXY命令行完成解锁、起飞与航线

链路打通之后,在MAVPROXY命令行里就可以像操作真机一样操控这架虚拟飞机了。

先把飞行模式切到GUIDED,也就是“自动驾驶指令引导”模式:

mode guided

接着解锁电机:

arm throttle

这里想多解释一下为什么需要先切模式再解锁。ArduPilot默认情况下禁止在STABILIZE等手动模式之外解锁起飞,因为它需要知道“你要干什么”,GUIDED模式会基于自动驾驶指令来控制飞机,让后面的takeoff指令可以被正确执行。如果先解锁再切模式,有些固件版本会直接拒绝起飞指令。

解锁成功后会看到“ARMED”反馈。然后输入起飞高度,单位是米:

takeoff 10

虚拟飞机会开始缓慢爬升到10米高度,MAVPROXY会打印当前高度和姿态数据。这个时候切换到QGC窗口,就能看到代表飞机的三角形图标已经从地面升起来了,姿态仪表盘也在实时变化。

如果要让它飞到指定经纬度位置,可以用guided命令:

guided 35.123456,-120.123456,10

这相当于在地图上指定一个目标点,飞机会自动调整机头方向并飞过去。航线规划也可以用命令输入多个航点,不过图形化操作在QGC里更直观,我一般只在调试命令行接口时才这么做。

这里有个实操心得:MAVPROXY命令行是排查数据链路问题的首选工具,因为它在最底层,任何数据流异常都能第一时间暴露。

3.3 用DroneKit脚本实现自动化任务

命令行模式用来验证链路没问题,但真正做自动化测试时,还是要靠DroneKit写Python脚本。

先看一段完整的起飞并飞向目标点的代码:

from dronekit import connect, VehicleMode, LocationGlobalRelative import time # 连接到MAVPROXY转发的UDP端口 connection_string = '127.0.0.1:14550' print("Connecting to vehicle on %s" % connection_string) vehicle = connect(connection_string, wait_ready=True) # 读取基本信息 print("Vehicle initialized") print("Autopilot version: %s" % vehicle.version) print("Mode: %s" % vehicle.mode.name) # 切换到GUIDED模式 vehicle.mode = VehicleMode("GUIDED") # 解锁电机 vehicle.armed = True while not vehicle.armed: print("Waiting for arming...") time.sleep(1) print("Vehicle armed!") # 起飞到10米高度 target_altitude = 10 vehicle.simple_takeoff(target_altitude) # 等待达到目标高度 while True: current_alt = vehicle.location.global_relative_frame.alt print("Altitude: {:.2f}".format(current_alt)) if current_alt >= target_altitude * 0.95: print("Reached target altitude") break time.sleep(1) # 飞行到指定经纬度 target_location = LocationGlobalRelative(35.123456, -120.123456, target_altitude) vehicle.simple_goto(target_location) # 持续监控一直到达到目标点附近 while True: dist = abs(vehicle.location.global_relative_frame.lat - 35.123456) if dist < 0.0001: print("Reached target location") break time.sleep(1) print("Returning to launch") vehicle.mode = VehicleMode("RTL") time.sleep(20) vehicle.close()

这段脚本的逻辑非常清晰:连接、读信息、切模式、解锁、起飞、goto目标点、返航、关闭连接。其中几个重要的点:

wait_ready=True参数的作用是让连接过程等待飞控参数下载完成,再继续执行后面的代码。如果去掉这个参数,可能出现飞行模式显示错误或高低空切换异常。

simple_takeoff函数是DroneKit封装好的命令,底层其实是发送MAVLink的MAV_CMD_NAV_TAKEOFF指令。执行它之前,飞机必须在GUIDED模式下且已解锁。

LocationGlobalRelative是相对高度坐标系,第二个参数是地面高度,我们传10表示相对家点高度10米。如果要用海拔绝对高度,就得用LocationGlobal,但一般航线任务都建议用相对高度,避免地形变化导致高度异常。

vehicle.close()不能忘。很多朋友跑脚本时出现“端口被占用”的报错,就是因为上一次实例没有正常关闭。仿真环境虽然重启SITL就能解决,但养成写资源释放的好习惯总是没错的。

3.4 在QGroundControl里图形化监控

脚本跑起来的时候,切到QGC窗口,你看到的画面会非常直观。地图上飞机图标沿航线运动,右上角的高度条实时变化,左侧的飞机状态面板会显示当前模式、GPS卫星数、速率、电池电压等。

QGC还有一个很好用的功能是手动航点规划。地图上右键点击,选择“添加航点”,拖出几个点,再把高度设为10米或20米,保存后点“上传航线”。如果MAVProxy数据链路正常,飞机会按照这条航线自动飞行。这个过程不需要写一行代码,非常适合演示和快速验证航线逻辑。

地图上航点用右键拖拽的方式添加,移动时会自动吸附到鼠标位置。第一次使用建议把高度设低一些,比如10米,让飞机始终在可视范围内。

在QGC里还能查看飞控参数。齿轮图标进入“参数”面板,搜索框输关键词,就能找到对应的参数项。比如调整PID参数时,可以直接在QGC里修改RATE_PIT_I、RATE_PIT_P这些项,改写后飞控会在下次启动或指令下生效。

4. 仿真调试中的常见坑与速查表

4.1 连接不上?先按这几个顺序排查

三件套组合最常遇到的问题就是“明明都启动了,但QGC没有画面”。我遇到过的、以及身边朋友遇到过的,基本可以归纳成三类问题。

第一类是顺序问题。如果先启动QGC,再启动MAVPROXY和SITL,QGC可能已经优先占用了UDP端口,导致数据无法送达。建议严格按SITL、MAVPROXY、QGC的顺序来,或者在QGC里断开连接再重新连接一次。

第二类是端口遗漏。很多人启动MAVPROXY时只写了--master tcp:127.0.0.1:5760,忘了带--out udp:127.0.0.1:14550,结果SITL和MAVPROXY自己通信正常,但QGC收不到任何数据。这个参数遗漏很隐蔽,因为MAVPROXY不会报错,只有打开QGC才会发现问题。

第三类是虚拟环境里启动MAVPROXY后,没有保持终端常驻。命令行模式下如果输入了quit或者不小心按到Ctrl+C,整个MAVPROXY进程退出,数据链路自然就断了。仿真调试时建议单独开一个屏幕区域放MAVPROXY,方便随时观察。

如果以上都检查过还是不行,用系统工具看看端口是否实时监听:

ss -ulnp | grep 14550

有输出说明UDP 14550端口在监听,数据在哪一步断了再去排查哪一步,效率会高很多。

4.2 模拟飞行表现异常的常见原因

链路通了之后,飞行表现怪异是另一个集中的问题来源。

飞机解锁后一直在地上打转,不抬头:最常见原因是SITL的传感器数据出现异常,特别是gyro calibration没有正确完成。可以重新启动SITL,或者等待仿真环境自动完成传感器校准。更常见的一种情况是,用户在MAVPROXY里发了manual模式指令,导致飞控处于手动操控状态,但没有遥控器信号输入,飞机自然就只能在地上打转。

飞机原地起飞后一直撞向同一个方向:优先检查home坐标和目标点坐标的数值格式。--home参数里的经纬度必须是合法的全球坐标,如果填了一个错误格式的位置,飞控的EKF滤波器会报错,飞机姿态就会乱漂。这个坑我踩过很多次,后来养成了一个习惯:坐标参数一律用十进制格式,并且先在Google地图上复制真实位置,不要凭感觉输入。

脚本中takeoff后飞机不执行:多半是mode没有切换成功,或者GPS没有锁定。在脚本里最好加一段等待逻辑,确认vehicle.mode.name == 'GUIDED'之后再触发simple_takeoff。

仿真飞行速度比真实时间快很多:DRONEKIT-SITL默认以接近真实时间的速度运行,但有些版本在特定固件上会以几倍速运行。如果你发现整个任务在十几秒内就完成了,说明处于加速模式。对于大多数逻辑测试,加速模式没问题;但如果涉及实时图像处理或通信延迟测试,就必须加上--speedup 1参数让仿真保持真实时间速度。

4.3 常用命令速查表

把过程中最常用的命令整理成一个速查表,建议截图收藏。

操作命令说明
启动模拟四旋翼dronekit-sitl copter-3.3 --home=纬度,经度,海拔,朝向机型、坐标可自定义
启动MAVProxymavproxy.py --master tcp:127.0.0.1:5760 --out udp:127.0.0.1:14550必须带out参数
查看数据流状态status数据线跳动表示链路正常
切换GUIDED模式mode guided自动驾驶指令引导模式
解锁电机arm throttle必须切到GUIDED后执行
起飞takeoff 10单位是米
飞向指定坐标guided 纬度,经度,高度需要MAVProxy较新版本
查看航点列表wp list可确认航线是否上传成功
修改飞控参数param set 参数名 数值比如param set RATE_PIT_P 0.1
保存参数param save 文件名仿真中同样适用

提示:MAVProxy的交互式命令是高度可扩展的。你可以用module load加载不同的模块,比如module load console会弹出一个字符图形界面的姿态显示器,调试时特别实用。

5. 进阶:多机仿真、硬件在环以及我的几点体会

5.1 多机协同仿真怎么起

单机跑通之后,多机编队仿真是很多做集群算法的朋友会遇到的场景。

多机仿真的核心是启动多个SITL实例,并且给每个实例分配不同的数据端口,避免冲突。

dronekit-sitl copter-3.3 --home=35.123456,-120.123456,0,180 --port=5760 dronekit-sitl copter-3.3 --home=35.123456,-120.123456,0,180 --port=5770

第二个实例的TCP端口设为5770,其他参数保持相同。然后在MAVPROXY里分别连接这两个数据源,并转发到不同的UDP端口:

mavproxy.py --master tcp:127.0.0.1:5760 --out udp:127.0.0.1:14550 mavproxy.py --master tcp:127.0.0.1:5770 --out udp:127.0.0.1:14560

启动两个QGC实例,分别连14550和14560,就能同时看到两架虚拟飞机。

如果你用的是ArduPilot官方SITL,还可以通过--instance参数自动分配端口,具体以你当前版本--help输出为准。多机仿真对电脑性能有一定要求,建议内存至少16G。

5.2 从软件在环到真机实践,要注意什么

SITL和真实飞行之间的差异,值得单独强调一遍。

传感器模型仿真得再好,也无法完全复现真实环境中的磁场分布、地磁干扰、GPS多径效应、气压计受风影响等现象。在SITL里调好的PID参数,到真机上往往需要重新微调。这个不是仿真环境的问题,而是所有仿真系统的固有限制——模型的复杂度再高,也不可能完全等于现实。

但是SITL有一个优势是硬件在环(HIL)没有的:完全免费、零风险、可大规模扩展。硬件在环需要一块真实飞控板,通过模拟器把仿真传感器数据灌进飞控的串口或USB口,让飞控以为自己在真实飞机上。这种方式更贴近真机,但硬件成本和环境准备复杂度都上了一个台阶。对于绝大多数算法验证场景,先用SITL跑通逻辑,再用HIL做关键环节验证,最后上真机小半径试飞,是比较稳妥的路径。

5.3 关于这套模拟环境,我的几点实操体会

最后说几个我在实际使用中总结出来的经验。

第一,先命令行后图形界面。每次搭新环境,都先让MAVPROXY命令行里能看到status、mavlink info的输出,再打开QGC。原因很简单:QGC界面信息丰富,但出现问题时定位慢,不如命令行直接。等链路确认干净了,再用图形界面做数据可视化。

第二,参数乱改了要记得保存。仿真环境里参数改坏了不会炸机,但调试效率会下降。我一般改完一组有意义的参数会尽快用param save存一份,标注文件名和日期,方便回溯。有时候来回对比不同参数组合时,这份手动保存会救你一命。

第三,把SITL当成一台“可重置的慢速真机”。仿真环境最大的价值不是替你真机飞行,而是让你把代码里的低级错误全部暴露出来。这个角度想清楚之后,你会更重视每次练习的复现性。实测下来,用这套三件套跑完10次自动航线任务,逻辑bug基本能在上真机前被过滤掉一大部分,这种效率是盲目开机试飞很难达到的。

如果你刚开始接触无人机开发,我建议从今天这篇内容的第一步开始,把环境搭好,用MAVPROXY命令行飞一次,再用DroneKit脚本飞一次,最后把QGC连上,整个过程两个小时左右就能完成。等这几条链路都通了,后续无论做航点规划、编队算法还是故障检测,你都会有一个非常顺手的试验台。

返回列表