可达鸭1
(可达鸭1)
1
我是C++部署了usb传图+yolo实时推理 之前都是好好的 正常推理帧数也有200+ 然后昨天系统突然出现问题 开发板断电重启之后绿灯常亮进不去系统 我就重新烧录了最新版本的系统 26年4月的,在新系统上部署了我的老代码推理帧率只有10-20了 BPU占用很低只有个位数 模型 代码什么的完全一样 系统是server版不是桌面版的 但是我py测试的单次推理是330+的帧率 很奇怪 是server版系统不行吗
我之前的系统是ubuntu22 desktop-3.3.3
我现在的新系统是ubuntu22 server-3.5.0
系统版本:
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy
板卡版本:Rdkx5
问题分析
你的问题非常典型:Python 推理正常(330+ FPS),但 C++ 部署帧率极低(10-20 FPS),BPU 占用仅个位数。这说明模型和 BPU 本身没问题,问题出在C++ 推理流程的数据链路上。
核心原因(按概率排序)
1. USB 摄像头取图成为瓶颈(最可能)
- Server 版系统 USB 驱动/带宽调度策略与桌面版不同
- USB 摄像头在高负载下可能降帧或阻塞
- 验证方法:在 C++ 代码中单独测试取图耗时
2. 图像预处理未走硬件加速
- Server 版可能默认未启用 GPU2D/N2D 硬件加速
- 如果预处理用 OpenCV CPU 做 NV12→RGB 转换,会严重拖慢帧率
- 验证方法:检查代码中图像格式转换的耗时
3. 推理流程同步阻塞
- C++ 代码可能是同步等待推理完成才取下一帧
- Python 测试可能是异步或跳帧处理
- 验证方法:检查是否有
WaitTaskDone 阻塞调用
4. 系统调度策略差异
- Server 版 CPU 频率调度更保守
- 可能未开启高性能模式
排查步骤
步骤 1:定位瓶颈(加时间戳)
在你的 C++ 代码关键位置添加耗时统计:
#include <chrono>
auto t1 = std::chrono::high_resolution_clock::now();
// 1. USB 取图
auto img = get_usb_image();
auto t2 = std::chrono::high_resolution_clock::now();
// 2. 预处理(格式转换等)
auto processed = preprocess(img);
auto t3 = std::chrono::high_resolution_clock::now();
// 3. BPU 推理
auto result = inference(processed);
auto t4 = std::chrono::high_resolution_clock::now();
// 打印各阶段耗时
std::cout << "取图:" << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count() << "ms" << std::endl;
std::cout << "预处理:" << std::chrono::duration_cast<std::chrono::milliseconds>(t3-t2).count() << "ms" << std::endl;
std::cout << "推理:" << std::chrono::duration_cast<std::chrono::milliseconds>(t4-t3).count() << "ms" << std::endl;
步骤 2:检查 USB 摄像头实际帧率
# 使用 v4l2-ctl 测试摄像头最大帧率
v4l2-ctl -d /dev/video0 --list-formats-ext
# 使用 ffmpeg 测试实际取图帧率
ffmpeg -f v4l2 -framerate 30 -i /dev/video0 -f null -
如果这里帧率就低,说明是 USB 带宽或驱动问题。
步骤 3:检查是否启用硬件加速
# 查看 GPU2D/N2D 相关服务状态
systemctl | grep gpu
systemctl | grep n2d
# 检查是否有硬件编解码相关库
ldconfig -p | grep hobot
步骤 4:对比 Python 和 C++ 的推理流程
关键差异点:
- Python 是否跳过了某些预处理步骤?
- Python 是否使用了异步推理?
- 输入图像格式是否一致(NV12 vs RGB)?
步骤 5:尝试优化方案
方案 A:启用硬件预处理(推荐)
参考社区案例:X5 使用 gpu2d 进行图像格式转换
// 使用 N2D/GPU2D 替代 OpenCV 做格式转换
#include "hobot/gpu2d/gpu2d.h"
// 具体 API 参考官方文档
方案 B:改为异步推理流程
// 使用多缓冲队列,取图和推理并行
std::queue<Image> frame_queue;
// 取图线程和推理线程分离
方案 C:降低 USB 摄像头分辨率/帧率
# 尝试降低分辨率换取稳定帧率
v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480
步骤 6:检查系统性能模式
# 查看 CPU 频率调度
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# 临时设置为性能模式
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
补充信息请求
为了更精准定位,请提供:
- C++ 代码中图像预处理部分(特别是格式转换)
- 耗时统计结果(取图/预处理/推理各多少 ms)
- USB 摄像头型号和分辨率设置
- Python 测试代码的推理流程(是否也做了同样的预处理)
参考资源
建议优先执行步骤 1 的耗时统计,90% 的情况下能直接定位到瓶颈环节。如果是取图慢,考虑换 MIPI 摄像头;如果是预处理慢,改用硬件加速。
可达鸭1
(可达鸭1)
3
[USB] 接收:98.8 fps | 推理:20.0 fps | 队列:0 | CPU:15%
[耗时|线程0] 等待:83.5ms 预处理:0.0ms BPU:3.3ms 后处理:0.4ms
[USB] 接收:238.8 fps | 推理:29.0 fps | 队列:0 | CPU:22%
[耗时|线程1] 等待:106.7ms 预处理:0.0ms BPU:3.3ms 后处理:0.4ms
[USB] 接收:235.3 fps | 推理:29.0 fps | 队列:0 | CPU:21%
[USB] 接收:217.6 fps | 推理:28.9 fps | 队列:0 | CPU:20%
[耗时|线程0] 等待:73.0ms 预处理:0.0ms BPU:3.3ms 后处理:0.4ms
[耗时|线程1] 等待:52.8ms 预处理:0.0ms BPU:3.3ms 后处理:0.4ms
[USB] 接收:235.2 fps | 推理:29.0 fps | 队列:0 | CPU:21%
[耗时|线程1] 等待:52.3ms 预处理:0.0ms BPU:3.2ms 后处理:0.4ms
[USB] 接收:235.5 fps | 推理:28.9 fps | 队列:0 | CPU:21%
[耗时|线程0] 等待:88.6ms 预处理:0.0ms BPU:3.2ms 后处理:0.4ms
[USB] 接收:235.5 fps | 推理:28.9 fps | 队列:0 | CPU:22%
[耗时|线程1] 等待:65.6ms 预处理:0.0ms BPU:3.2ms 后处理:0.4ms
[USB] 接收:212.0 fps | 推理:28.4 fps | 队列:0 | CPU:21%
[耗时|线程0] 等待:59.8ms 预处理:0.0ms BPU:3.2ms 后处理:0.4ms
[USB] 接收:141.5 fps | 推理:29.0 fps | 队列:0 | CPU:21%
问题定位完成!
从日志看,瓶颈非常清晰:
| 环节 |
耗时 |
状态 |
| USB 取图 |
- |
正常 (200+ FPS) |
| 等待 |
52-106ms |
严重瓶颈 |
| BPU 推理 |
3.3ms |
正常 (对应 300+ FPS) |
| 预处理 |
0.0ms |
正常 |
| 后处理 |
0.4ms |
正常 |
结论:BPU 推理本身没问题(3.3ms 对应 300+ FPS),问题出在线程同步等待上,等待时间占总时间的 80%+。
根本原因
你的代码是双线程架构(线程 0/线程 1),但存在同步阻塞:
线程 0: 取图 → 等待锁 → 推理 → 释放锁
线程 1: 取图 → 等待锁 → 推理 → 释放锁
Server 版系统的线程调度策略更保守,导致线程间切换和锁等待时间显著增加。
解决方案
方案 1:改用无锁队列(推荐)
使用 std::atomic 和环形缓冲区替代互斥锁:
#include <atomic>
#include <array>
template<typename T, size_t N = 4>
class LockFreeQueue {
private:
std::array<T, N> buffer;
std::atomic<size_t> head{0};
std::atomic<size_t> tail{0};
public:
bool push(const T& item) {
size_t current_tail = tail.load(std::memory_order_relaxed);
size_t next_tail = (current_tail + 1) % N;
if (next_tail == head.load(std::memory_order_acquire)) {
return false; // 队列满
}
buffer[current_tail] = item;
tail.store(next_tail, std::memory_order_release);
return true;
}
bool pop(T& item) {
size_t current_head = head.load(std::memory_order_relaxed);
if (current_head == tail.load(std::memory_order_acquire)) {
return false; // 队列空
}
item = buffer[current_head];
head.store((current_head + 1) % N, std::memory_order_release);
return true;
}
};
// 使用示例
LockFreeQueue<cv::Mat> frame_queue;
方案 2:减少线程数量(快速验证)
先改为单线程测试,确认是否是线程同步问题:
// 暂时注释掉第二个推理线程
// std::thread infer_thread_1(infer_loop, 1);
std::thread infer_thread_0(infer_loop, 0);
如果单线程帧率恢复正常,说明是线程同步问题。
方案 3:优化同步机制
如果必须用双线程,改用条件变量替代忙等待:
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool frame_ready = false;
// 生产者
{
std::lock_guard<std::mutex> lock(mtx);
frame = get_frame();
frame_ready = true;
cv.notify_one();
}
// 消费者
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return frame_ready; });
frame_ready = false;
// 处理 frame
}
方案 4:检查是否有隐式同步
常见坑点:
// ❌ 避免:在推理线程中调用阻塞的 USB 读取
auto frame = usb_camera.read(); // 可能阻塞
// ✅ 改为:取图和推理完全分离
// 线程 1: 只负责取图 → 放入队列
// 线程 2: 只负责从队列取图 → 推理
验证步骤
1. 先测单线程
# 修改代码只保留一个推理线程,重新编译运行
# 预期:帧率应该接近 100+ FPS
2. 检查锁的使用
# 搜索代码中的锁相关调用
grep -rn "mutex\|lock\|wait" your_cpp_code/
3. 使用 perf 分析线程等待
# 安装 perf
sudo apt install linux-tools-generic
# 运行程序时采样
sudo perf record -g -p <your_pid>
# 查看线程等待时间
sudo perf report
参考案例
社区有类似优化案例:
下一步
请尝试方案 2(单线程测试),5 分钟内就能验证是否是线程同步问题。如果是,再实施方案 1(无锁队列)。
需要我帮你审查具体的线程同步代码吗?可以贴出关键的线程创建和同步部分。
可达鸭1
(可达鸭1)
5
我把系统刷回desktop的3.3.3版本能解决问题吗
可以直接回答:能解决 
回退到 desktop 3.3.3 大概率能恢复正常帧率,因为:
- 你的代码在这个版本上验证过(之前 200+ FPS)
- Desktop 版线程调度更友好,互斥锁/条件变量的开销更小
- USB 驱动策略不同,Server 版更保守
但这是「绕路」方案,建议先做两个测试
测试 1:单线程验证(5 分钟)
在 Server 3.5.0 上先改单线程测试:
// 临时注释掉第二个推理线程
// std::thread infer_thread_1(infer_loop, 1);
std::thread infer_thread_0(infer_loop, 0);
预期结果:
- 如果单线程帧率 → 100+ FPS:说明是线程同步问题,回退系统能解决
- 如果单线程帧率 → 还是 20-30 FPS:说明有其他问题,回退可能也没用
测试 2:检查系统调度策略
# 查看当前 CPU 调度器
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Server 版通常是 powersave,改为 performance
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# 再跑测试看帧率是否提升
回退系统的利弊
| 方案 |
优点 |
缺点 |
| 回退 desktop 3.3.3 |
快速解决问题,代码不用改 |
系统更重,占用更多资源 |
| 优化代码适配 Server |
系统轻量,长期更稳定 |
需要改线程同步逻辑 |
我的建议
优先级:
- 先做单线程测试(5 分钟)→ 确认问题根源
- 如果能接受 Desktop 版 → 直接回退 3.3.3,最快解决问题
- 如果需要 Server 版 → 用无锁队列优化代码(参考我之前给的方案)
回退步骤
# 1. 下载 desktop 3.3.3 镜像
# https://archive.d-robotics.cc/downloads/os_images/rdk_x5/
# 2. 烧录(和之前一样)
sudo dd if=rdk-x5-ubuntu22-preinstalled-desktop-3.3.3-arm64.img of=/dev/sdX bs=4M
# 3. 重新部署代码测试
总结:回退能解决,但建议先花 5 分钟做单线程测试,确认问题根源后再决定。需要我帮你分析单线程测试结果吗?