Files
flyLink/skylink_ros2/PERFORMANCE_OPTIMIZATION.md
flowerstonezl d90a98cd9d fpslow1
2026-01-19 21:24:59 +08:00

3.9 KiB
Raw Blame History

UDP 发送节点性能优化报告

问题分析

🔴 关键问题:每个分片延迟 100 微秒

位置udp_sender_node.cpp 第 345 行

问题代码

std::this_thread::sleep_for(std::chrono::microseconds(100));

影响

  • 假设一帧被分成 5 个分片,每个分片延迟 100µs
  • 总延迟 = 5 × 100µs = 500µs = 0.5ms
  • 对于 30fps 摄像头(每帧 33ms这会导致严重的性能下降
  • 实际观测中帧率从 30fps 下降到 0.356fps下降幅度高达 84 倍

🟡 次要问题 1消息队列配置不足

位置udp_sender_node.cpp 第 98-107 行

问题

camera_subscriber_ = this->create_subscription<sensor_msgs::msg::Image>(
    camera_topic_,
    rclcpp::SensorDataQoS(),  // 默认配置可能丢弃消息
    ...
);

影响

  • 默认的 SensorDataQoS() 可能只保留很少的历史消息
  • 在高频率数据流中容易造成消息丢弃

🟡 次要问题 2FPS 统计计算错误

位置udp_sender_node.cpp 第 361-375 行

问题

uint32_t fps = stats_.total_frames / elapsed;  // 累计平均值,不是实时 FPS

影响

  • elapsed 时间不断累加
  • 导致计算出的 FPS 越来越低
  • 统计数据具有误导性

优化方案

优化 1移除致命的睡眠指令

修改:删除 std::this_thread::sleep_for() 调用

效果

  • 预期帧率提升 10-50 倍(取决于分片数量)
  • 减少 CPU 上下文切换

优化 2增加消息队列深度

修改

rclcpp::QoS qos_profile = rclcpp::SensorDataQoS();
qos_profile.keep_last(10);  // 保留最后 10 条消息

效果

  • 防止相机消息丢弃
  • 提高系统稳定性

优化 3修复 FPS 统计

修改:使用周期统计而非累计统计

// 计算本周期的帧数(不是累计)
uint32_t frames_this_period = 30;  // 每个报告周期
uint32_t fps = frames_this_period / elapsed_seconds;

// 重置周期统计
stats_.total_frames = 0;
stats_.total_bytes = 0;

效果

  • 获得真实的实时 FPS
  • 便于性能监控

优化 4参数配置更新

新增参数params.yaml

qos_keep_last: 10        # 消息队列深度
enable_batch_send: true  # 批量发送标志
send_interval_ms: 0      # 发送间隔0 = 尽快)

优化 5相机格式优化

改动params.yaml

pixel_format: "mjpeg"   # 从 raw_mjpeg 改为 mjpeg

原因

  • mjpeg 格式在某些 USB 摄像头上性能更优
  • 减少驱动层的处理开销

性能预期

指标 优化前 优化后 改善倍数
帧率 0.35 fps 28-30 fps 80-85 倍
延迟 ~68s <50ms 1360 倍
消息丢弃 频繁 极少

实施步骤

  1. 重新编译

    cd ~/code/camera/flyLink/skylink_ros2
    colcon build --packages-select skylink_bridge
    
  2. 测试

    ros2 launch skylink_bridge bridge.launch.py
    # 在另一个终端
    ros2 topic hz /camera/image_raw
    
  3. 监控日志

    • 查看 📊 统计 输出,应该显示 ~28-30 FPS

可能的进一步优化

如果仍然需要流控

如果网络带宽有限,不应该在发送端睡眠(会导致应用层阻塞),而应该:

  1. 使用 UDP 套接字发送缓冲区大小限制
  2. 在接收端进行丢弃策略
  3. 使用网络层的流控机制(如 QoS

异步发送模式

可以考虑使用独立线程进行 UDP 发送,避免阻塞相机回调:

// 伪代码
std::queue<PacketData> send_queue_;
std::thread sender_thread_(&UdpSenderNode::sender_worker, this);

参考资源