4. 数据丢失与丢包:UDP/TCP端口配置错误、缓冲区溢出、QGC与MAVSDK的订阅冲突

数据丢包,这大概是SITL仿真里最让人头疼的问题之一。你明明看到飞控在跑,日志也在刷,但采集到的数据就是缺胳膊少腿。我刚开始搞仿真采集那会儿,经常对着缺失的时间戳发愣——明明程序没报错,数据怎么就断片了?

后来踩的坑多了,慢慢摸清了门道。说白了,数据丢失就三个源头:端口没配好、缓冲区撑爆了、还有多订阅者打架。咱们一个一个说。

4.1 UDP/TCP端口配置错误

先看端口。SITL默认走UDP,端口号是14550。但很多人一上来就改端口,或者同时开了好几个监听程序,结果数据全乱套了。

⚠ 常见错误: 端口被占用、端口号写错、UDP与TCP混用。

我遇到过最离谱的一次,是同事把UDP端口配成了TCP端口。QGC那边连上了,但MAVSDK这边死活收不到数据。查了半天才发现,MAVSDK默认走TCP,而SITL发的是UDP——两边协议都不匹配,数据当然过不来。

这里有个小技巧:用 netstat 或 ss 命令检查端口状态。启动SITL后,跑一下:

ss -tuln | grep 14550

如果看到 udp UNCONN 0.0.0.0:14550,说明端口正常。要是啥都没有,那就是SITL没启动,或者端口被别的进程占了。

💡 我的习惯: 每次启动新仿真前,先杀掉所有残留的SITL进程。用 pkill -f sitl 清场,再重新启动。这能避免90%的端口冲突问题。

4.2 缓冲区溢出

端口配对了,数据还是丢?那八成是缓冲区溢出了。

UDP本身不保证可靠传输,内核缓冲区满了就直接丢包。SITL仿真时,如果数据生成速度大于采集程序的消费速度,缓冲区就会迅速填满。尤其是当你同时采集多个话题(比如IMU、GPS、姿态)时,数据量一上来,丢包率直线飙升。

我做过一个测试:默认的UDP接收缓冲区是212992字节(约208KB)。在高速仿真下,这个值根本不够用。数据包一多,缓冲区秒满,后面的包全被内核扔掉。

参数 默认值 建议值 说明
net.core.rmem_default 212992 1048576 UDP接收缓冲区默认大小
net.core.rmem_max 212992 4194304 UDP接收缓冲区最大限制
net.core.wmem_default 212992 1048576 UDP发送缓冲区默认大小

解决办法很简单——调大缓冲区。Linux下用 sysctl 命令:

sudo sysctl -w net.core.rmem_default=1048576
sudo sysctl -w net.core.rmem_max=4194304

想永久生效,就写到 /etc/sysctl.conf 里。改完之后重启SITL,丢包率能降一个数量级。

🔑 关键点: 缓冲区溢出不是程序bug,是系统参数没调。别花时间在代码里加重传机制,先检查内核参数。

4.3 QGC与MAVSDK的订阅冲突

这个坑比较隐蔽。你同时开着QGC和MAVSDK采集程序,两个都在监听同一个UDP端口。QGC那边显示正常,MAVSDK这边却丢数据——为什么?

原因在于UDP的多播行为。UDP端口上的数据包会被所有监听者收到,但内核不保证每个监听者都能拿到完整的数据流。当QGC和MAVSDK同时订阅同一个消息时,内核会把数据包分发给两个socket。如果其中一个socket处理慢了,内核就会优先保证另一个——结果就是慢的那个丢包。

我遇到过的情况是:QGC的UI线程卡了一下,导致它的socket缓冲区满了,内核就把本该发给它的包扔了。但MAVSDK这边也受影响,因为内核在分发时做了负载均衡,两边都丢了一部分数据。

怎么解决?三个办法:

  1. 分开端口:让QGC监听14550,MAVSDK监听14551。SITL启动时用 -p 14551 参数指定第二个端口。
  2. 使用TCP:TCP有流控和重传机制,不会丢包。但延迟比UDP高,适合对实时性要求不高的采集场景。
  3. 加一个转发中间件:比如用 mavlink-router 或 mavproxy 做数据分发,避免多个程序直接抢同一个端口。

💡 我推荐的做法: 调试阶段用UDP+分开端口,方便实时查看。正式采集时切到TCP,保证数据完整性。

4.4 一张图看懂数据丢失的根因

下面这张图总结了数据丢失的三大根因和对应的解决路径。你可以把它贴在工位旁边,排查问题时对着看。

数据丢失根因分析图 数据丢失根因分析 数据丢失 端口配置错误 端口被占用 UDP/TCP混用 端口号写错 缓冲区溢出 内核缓冲区太小 消费速度跟不上 多话题并发采集 订阅冲突 QGC + MAVSDK同端口 内核分发不均 一方处理慢导致丢包 解决方案 调大缓冲区 | 分开端口 | 使用TCP | 加转发中间件

4.5 避坑指南

最后,把我这些年踩过的坑总结成几条避坑指南,你直接拿去用:

  • 启动前先清场:用 pkill -f sitl 杀掉所有残留进程,避免端口冲突。
  • 检查端口状态:用 ss -tuln | grep 14550 确认端口监听正常。
  • 调大缓冲区:把 rmem_default 和 rmem_max 改成1MB以上,别用默认值。
  • 分开端口:QGC和MAVSDK不要共用同一个UDP端口,用 -p 参数指定不同端口。
  • 正式采集用TCP:调试阶段用UDP方便实时看,正式采集切到TCP保证数据完整。
  • 加转发中间件:如果多个程序都要收数据,用 mavlink-router 做分发,别让它们直接抢端口。

📌 记住: 数据丢失90%是配置问题,不是代码问题。先检查系统参数,再怀疑程序逻辑。