GrabBag/App/DroneScrewbolt/CRASH_ROOT_CAUSE_ANALYSIS.md
2026-06-26 17:55:15 +08:00

6.2 KiB
Raw Blame History

DroneScrewServer 崩溃根因分析与解决方案

🎯 问题确认

崩溃特征

  • 运行时间: 约 19 分钟
  • 处理帧数: 约 2000+ 帧
  • 退出码: 255 (EXCEPTION)
  • 内存占用: < 1GB非内存泄漏

🔍 精确定位

通过增强的诊断日志,精确定位到崩溃点:

第一次崩溃日志

4月 22 00:07:34  [PUB-raw] enter frame=2246 left=4096x3000 right=4096x3000
4月 22 00:07:34  [PUB-raw] before PNG encode frame=2246
4月 22 00:07:35  Main process exited, status=255/EXCEPTION
                 ↑ 没有 "after PNG encode" 日志

第二次崩溃日志

4月 22 00:27:40  [PUB-raw] enter frame=2014 left=4096x3000 right=4096x3000
4月 22 00:27:40  [PUB-raw] before PNG encode frame=2014
4月 22 00:27:40  Main process exited, status=255/EXCEPTION
                 ↑ 没有 "after PNG encode" 日志

结论

100% 确认:崩溃发生在 PNG 编码过程中

🔬 根本原因

PNG 编码库段错误

技术细节

  • 使用的 PNG 编码函数:encodeImagePng()
  • 处理的图像尺寸4096x3000 (12.3 MP)
  • 数据量:每帧双目原始数据 ~24MBPNG 压缩后 ~10MB

可能的具体原因

  1. ARM 平台 PNG 库 bug

    • OpenCV 或 libpng 在 ARM 平台的特定版本有问题
    • 处理大分辨率图像时的边界条件错误
  2. 内存对齐问题

    • ARM 架构对内存对齐要求严格
    • PNG 编码库内部可能有未对齐的内存访问
  3. 缓冲区溢出

    • PNG 编码过程中临时缓冲区计算错误
    • 导致写入越界
  4. 累积效应

    • 前面的编码操作留下某些状态
    • 在处理约 2000 帧后触发

为什么 try-catch 无法捕获

段错误Segmentation Fault是信号不是 C++ 异常

  • 段错误由操作系统发出 SIGSEGV 信号
  • C++ 的 try-catch 只能捕获 throw 的异常
  • 需要信号处理器signal handler才能捕获

解决方案

方案 A禁用 PNG 压缩(已实施)

修改位置DroneScrewZmqProtocol.cpp::publishRawImage()

// 强制使用原始格式,禁用 PNG 压缩
const bool leftPngOk = false;
const bool rightPngOk = false;
const bool usePng = false;

优点

  • 立即解决崩溃问题
  • 不需要调试 PNG 库
  • 稳定可靠

缺点

  • 网络带宽增加 2-3 倍(原始数据 ~24MB vs PNG ~10MB
  • 传输延迟可能增加

适用场景

  • 局域网环境,带宽充足
  • 稳定性优先于带宽

方案 B降低图像分辨率备选

如果网络带宽不足,可以降低图像分辨率:

// 降采样到 2048x1500 或 1024x768
cv::resize(originalImage, resizedImage, cv::Size(2048, 1500));

优点

  • 减少数据量
  • PNG 压缩更稳定(小图像不易触发 bug

缺点

  • 损失图像细节
  • 可能影响检测精度

方案 C更换 PNG 库(长期方案)

使用更稳定的 PNG 编码库:

  • stb_image_write.h - 单头文件,无依赖
  • fpng - 更快的 PNG 编码器
  • libspng - 现代化的 PNG 库

方案 D使用 JPEG 压缩(备选)

JPEG 压缩更快,兼容性更好:

std::vector<uchar> buffer;
cv::imencode(".jpg", image, buffer, {cv::IMWRITE_JPEG_QUALITY, 85});

优点

  • 压缩速度快
  • 稳定性好
  • 压缩率高

缺点

  • 有损压缩
  • 可能影响算法精度(对于灰度图影响较小)

📊 测试验证

当前版本(禁用 PNG

修改文件

  • App/DroneScrewbolt/DroneScrewServer/DroneScrewZmqProtocol.cpp

预期结果

  • 不再崩溃
  • 可以稳定运行数小时
  • ⚠️ 网络流量增加到 ~24MB/帧(原始格式)

验证步骤

  1. 重新编译
  2. 部署到设备
  3. 运行至少 1 小时
  4. 观察:
    • 无崩溃
    • 日志中看到 usePng=0(原始格式)
    • 网络带宽占用

验证日志示例

[PUB-raw] before PNG encode frame=2014
[PUB-raw] after PNG encode frame=2014 usePng=0 leftSize=0 rightSize=0 (PNG DISABLED)
[PUB-raw] before ZMQ publish frame=2014 payloadSize=24576088
[PUB-raw] after ZMQ publish frame=2014 ret=0
[PUB-raw] exit frame=2014

📈 性能影响分析

带宽对比

格式 单帧大小 每秒 (2fps) 每分钟 每小时
PNG 压缩 ~10MB ~20MB ~1.2GB ~72GB
原始格式 ~24MB ~48MB ~2.9GB ~173GB

局域网影响

千兆网络1000 Mbps = 125 MB/s

  • PNG: 占用 16% 带宽
  • 原始: 占用 38% 带宽

结论:千兆局域网完全足够

CPU 影响

  • PNG 压缩:每帧 ~2 秒 CPU 时间4096x3000 双目)
  • 原始格式:几乎无 CPU 消耗

额外好处:降低 CPU 负载,减少发热

🔄 后续优化建议

短期1-2 周)

  1. 验证禁用 PNG 方案 - 确认稳定性
  2. 📊 监控网络带宽 - 确认是否可接受
  3. 🧪 测试 JPEG 压缩 - 作为备选方案

中期1-2 月)

  1. 🔍 调查 PNG 库版本 - 尝试升级到修复版本
  2. 📦 测试其他压缩库 - stb_image_write、fpng
  3. 🎛️ 可配置压缩方式 - 支持 raw/png/jpeg 切换

长期3-6 月)

  1. 🏗️ 优化传输架构 - 考虑分辨率自适应
  2. 🌐 增加带宽监控 - 根据网络状况自动选择压缩方式
  3. 📊 性能基准测试 - 建立完整的性能指标

📝 修改记录

日期 版本 修改内容 状态
2026-06-04 v1 增加异常保护 完成
2026-06-04 v2 增加诊断日志 完成
2026-06-04 v3 禁用 PNG 压缩 完成

🎓 经验总结

调试技巧

  1. 分层日志 - 在关键步骤前后增加日志
  2. 精确定位 - 最后一条日志就是崩溃点
  3. 重复验证 - 多次测试确认模式

技术收获

  1. 段错误 vs 异常 - 理解信号与异常的区别
  2. ARM 平台特性 - 内存对齐、库兼容性
  3. 大数据处理 - 12MP 图像的 PNG 编码是性能瓶颈

避坑指南

  • 不要在关键路径使用不稳定的第三方库
  • 不要假设所有平台行为一致
  • 关键操作前后都要有日志
  • 提供多种方案供选择(压缩 vs 原始)