生产消费模型与多线程架构

3. 完整数据流向

c++ 复制代码
// ============================================================================
// 完整数据流
// ============================================================================

StreamHandler (HAL)
       ↓ getFrameData()
捕获线程 (captureLoop)
       ↓ forcePush()
rawQueue_ (无锁队列)
       ↓ tryPopBatch()
解算线程 (processLoop)
       ↓ processFrameWithSDK()  ← SDK 解算深度
       ↓ submitToWriter()       ← 提交到 AsyncFileWriter 队列
       ↓ frameCallback_()       ← 回调应用层
AsyncFileWriter 内部队列
       ↓ IO 线程异步处理
磁盘文件 (raw/depth/ir)

// ============================================================================
// 为什么需要这么多中间步骤?
// ============================================================================

StreamHandler (HAL)
       ↓ getFrameData()
CopiedFrame                    // ① 从 HAL 获取的数据格式
       ↓ 转换
RawFrameData                   // ② 放入队列前的格式
       ↓ push
LockFreeQueue<RawFrameData>    // ③ 缓冲解耦
       ↓ pop
RawFrameData                   // ④ 取出
       ↓ processFrameWithSDK() // ⑤ SDK 解算
ProcessedFrameData             // ⑥ 解算后的格式(含 depth + ir)
       ↓ submitToWriter()
AsyncFileWriter                // ⑦ 异步写文件
磁盘文件
步骤 数据类型 原因
CopiedFrame HAL 原始数据 直接从相机获取
RawFrameData 队列数据格式 带帧ID、时间戳,便于管理
无锁队列 缓冲 解耦生产和消费速度
ProcessedFrameData 解算结果 包含 depth + ir 两种数据
AsyncFileWriter 异步写 不阻塞解算线程

3. 初始化、启动和停止

scss 复制代码
构造函数
    ↓ 创建队列、计算帧间隔
    
initialize()
    ↓ 1. 创建输出目录(保存原始数据、深度图、IR图)
    ↓ 2. 初始化深度处理器(厂商SDK,用于计算深度)
    ↓ 3. 初始化 StreamHandler (EVS HAL), 启动相机流,传入相机ID、格式等
    ↓ 4. 初始化异步文件写入器
    
start()
    ↓ 启动 Pipeline - 创建工作线程
    ↓ 创建捕获线程 → 循环获取相机帧
    ↓ 创建处理线程 → 循环解算深度
    ↓ 创建统计线程 → 循环更新帧率
    
stop()
    ↓ 设置停止标志
    ↓ 等待所有线程结束
    ↓ 关闭相机、停止写入器
    
析构函数
    ↓ 调用 stop()

4. 线程整体架构图

c++ 复制代码
// ============================================================================
// ToFPipelineManager 三线程架构
// ============================================================================

┌─────────────────────────────────────────────────────────────────────────────┐
│                           捕获线程 (captureLoop)                            │
│  职责:从 StreamHandler 获取原始帧,放入原始队列                            │
│                                                                             │
│  while (!shouldStop) {                                                      │
│      frame = streamHandler_->getFrameData();  // 阻塞等待                    │
│      rawQueue_->forcePush(frame);              // 入队                       │
│  }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓
                              ┌───────────────┐
                              │  rawQueue_    │
                              │  (无锁队列)    │
                              └───────┬───────┘
                                      ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│                           解算线程 (processLoop)                            │
│  职责:从队列取帧,调用SDK解算深度,保存文件,回调应用                        │
│                                                                             │
│  while (!shouldStop) {                                                      │
│      rawQueue_->tryPopBatch(batch);  // 批量取帧                             │
│      processFrameWithSDK(raw, out);  // SDK解算深度                          │
│      submitToWriter(out);            // 提交到IO线程写文件                   │
│      frameCallback_(out);            // 回调应用层                           │
│  }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓
                              ┌───────────────┐
                              │  AsyncFileWriter │
                              │  (IO线程)        │
                              └───────────────┘
                                      ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│                           统计线程 (statsLoop)                               │
│  职责:定期计算帧率,更新统计信息,回调统计                                  │
│                                                                             │
│  while (!shouldStop) {                                                      │
│      sleep(1秒);                                                            │
│      计算每秒处理帧数 → currentFps                                          │
│      statsCallback_(stats);        // 回调统计                              │
│  }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
c++ 复制代码
// ============================================================================
// 四线程架构(实际有4个线程)
// ============================================================================

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ captureLoop │    │ processLoop │    │  statsLoop  │    │  IO Thread  │
│ (捕获线程)   │    │ (解算线程)   │    │ (统计线程)   │    │ (写文件线程) │
└──────┬──────┘    └──────┬──────┘    └──────┬──────┘    └──────┬──────┘
       │                  │                  │                  │
       ↓                  ↓                  ↓                  ↓
  获取相机帧          SDK解算深度         计算帧率          真正写磁盘
  放入rawQueue        保存文件            回调统计          不阻塞主流程
       ↓              回调应用
FrameDataTypes.h          │
-RawFrameData             ↓
-ProcessedFrameData   submitToWriter()
                     (只是提交任务,不等待)
                          │
                          └──────────────────→ IO Thread
线程 函数 创建位置 职责
捕获线程 captureLoop() start() 中创建 从 StreamHandler 取帧
解算线程 processLoop() start() 中创建 SDK 解算、保存文件
统计线程 statsLoop() start() 中创建 计算帧率、上报统计
IO 线程 AsyncFileWriter::ioLoop() fileWriter_->start() 中创建 异步写文件
arduino 复制代码
EVS 采集线程
   ↓(拷贝后帧)
mFrameDataQueue
   ↓
Capture 中转线程
   ↓(封装 RawFrameData)
【 rawQueue_ ------ 无锁队列 】
   ↓
Process 解算线程(ToF SDK)
   ↓
ProcessedFrameData(深度图 + IR图)
  ↓
显示和写入

3.1 捕获线程 (captureLoop)

  • 获取拷贝的帧数据(阻塞等待,直到有帧):CopiedFrame copiedFrame = streamHandler_->getFrameData();

  • 直接推入解算队列:RawFrameData raw;

  • 移动语义,零拷贝:raw.data = std::move(copiedFrame.data);

  • 队列满时自动丢帧,防止内存爆炸:

FrameDataTypes.文件里面有两个结构体:

  • RawFrameData 用于存原始数据帧

  • ProcessedFrameData 用于存结算后的数据帧

3.2 为什么需要RawFrameData(rawQueue_)

CopiedFrame → RawFrameData(rawQueue_) → ProcessedFrameData (procQueue_)

为什么需要 RawFrameData结构体 和rawQueue_队列?不直接把获取的 数据帧 copiedFrame进行下一步解算?生产-消费模型

因为:

  1. 解算( ToF 深度 / 图像处理 )很慢,耗时 10ms~50ms
  2. getFrameData()阻塞取帧
  3. 如果解算卡住 ,会导致队列塞满、丢帧、甚至影响相机线程
  4. 车机里相机采集、数据队列、算法解算必须三个完全独立线程

不是不能直接解算,而是直接解算会把相机回调卡崩!必须用「CopiedFrame → RawFrameData → 无锁队列」做解耦!

c++ 复制代码
// ============================================================================
// 问题:为什么不能直接解算?
// ============================================================================

// ❌ 不好的设计:在捕获线程直接解算
void captureLoop() {
    while (!shouldStop_) {
        CopiedFrame frame = streamHandler_->getFrameData();
        
        // 直接解算(耗时 30-50ms)
        depthProcessor_->processFrame(frame.data);  // 阻塞!
        
        // 问题:解算期间相机不能继续收帧,会丢帧!
    }
}

// ✅ 好的设计:生产者和消费者分离
void captureLoop() {  // 生产者:快速收帧
    while (!shouldStop_) {
        CopiedFrame frame = streamHandler_->getFrameData();  // 阻塞等待
        rawQueue_->push(frame);  // 快速入队,不处理
        // 循环结束,立即可以收下一帧
    }
}

void processLoop() {  // 消费者:慢速解算
    while (!shouldStop_) {
        RawFrameData frame = rawQueue_->pop();  // 取帧
        depthProcessor_->processFrame(frame.data);  // 耗时操作
        // 解算慢不影响收帧
    }
}
  • 核心原因:速度不匹配
线程 操作 耗时 频率
捕获线程 getFrameData() + 入队 ~1ms 30fps
解算线程 SDK 解算 + 写文件 ~50ms 可能 <30fps

无锁队列的作用:缓冲 解耦

markdown 复制代码
捕获线程(快)→ 队列 → 解算线程(慢)
     ↓                    ↓
  不用等待            处理不完的帧会积压
                     队列满时自动丢帧

3.3 正确架构:三层解耦(车机标准设计)

c++ 复制代码
1. EVS 采集线程(HAL 回调)
   → deliverFrame_1_1
   → memcpy 同步拷贝
   → 异步归还 buffer
   → 推入 mFrameDataQueue

2. Capture 线程(你代码里的 captureLoop)
   → 从 mFrameDataQueue 取 CopiedFrame
   → 封装成 RawFrameData
   → 推入 **rawQueue_(无锁队列)**

3. Process 解算线程(processLoop)
   → 从 **rawQueue_(无锁队列)** 批量取帧
   → ToF SDK 深度解算
   → 输出 ProcessedFrameData
   
   三层线程:
1. EVS 采集线程(拷贝 + 归还)
2. Capture 中转线程(封装 + 入队)
3. Process 解算线程(算法 + 输出)

无锁队列:
rawQueue_ 连接 Capture 线程 和 Process 线程,
负责低延迟、线程安全、无锁的数据传递。

为什么要分三层?

  • EVS 线程 :只管收帧、拷贝、归还,绝对不做耗时操作
  • 转发线程 :做数据格式转换,削峰、填谷、保护相机
  • 算法线程 :慢慢解算,卡住也不影响采集

3.2 解算线程 (processLoop)

  • 批量取帧减少锁竞争:size_t n = rawQueue_->tryPopBatch(batch, 4);

  • SDK 解算是 CPU 密集型操作,逐帧处理,SDK 解算深度:processFrameWithSDK(batch[i], out);

  • submitToWriter 只是提交任务,不等待写入完成。异步写文件(提交到 IO 线程):submitToWriter(out);

  • 回调应用层,推送到显示队列

3.3 统计线程 (statsLoop)

  • 每秒计算一次帧率

  • 记录峰值帧率用于性能分析

  • 通过回调上报统计信息

3.4 SDK解算包装函数

c++ 复制代码
// ============================================================================
// 直接调用 vs 包装函数
// ============================================================================

// ❌ 不好的设计:直接调用 SDK
void processLoop() {
    RawFrameData frame = rawQueue_->pop();
    
    // 直接调用 SDK,错误处理分散
    if (!depthProcessor_->processFrame(frame.data)) {
        // 处理错误...
    }
    
    const char* depth = depthProcessor_->getDepthData();
    const char* ir = depthProcessor_->getIRData();
    
    // 转换数据格式...
    // 分配内存...
    // 复制数据...
    // 错误处理...
    // 代码很长,难以维护
}

// ✅ 好的设计:包装函数
bool processFrameWithSDK(const RawFrameData& raw, ProcessedFrameData& out) {
    // 所有 SDK 相关逻辑集中在这里
    // 1. 检查输入
    // 2. 调用 SDK
    // 3. 获取结果
    // 4. 格式转换
    // 5. 错误处理
    // 6. 返回结果
}

void processLoop() {
    RawFrameData frame = rawQueue_->pop();
    ProcessedFrameData out;
    
    if (processFrameWithSDK(frame, out)) {
        // 成功,使用 out
        submitToWriter(out);
        frameCallback_(out);
    } else {
        // 失败,统计错误
        stats_.processErrors++;
    }
}
好处 说明
封装 SDK 调用细节不暴露
复用 其他地方也可以调用
测试 可以单独测试解算逻辑
维护 SDK 升级只改一个地方
错误处理集中 不用到处写错误处理

3.5 异步文件读取

为什么不把submitToWriter做成单独的线程,而是放在AsyncFileWriter.cpp中做类内部管理线程

好的设计原则:

  • 单一职责:AsyncFileWriter 只负责异步写文件
  • 封装性:线程管理封装在类内部
  • 可复用:其他地方也可以使用 AsyncFileWriter

异步文件写入器

arduino 复制代码
// AsyncFileWriter = 异步文件写入器
// 作用:把写磁盘的操作放到独立线程,不阻塞主流程

为什么是异步?

异步就是非阻塞

只是把任务丢进队列,立即返回(~0.001ms) // 调用者线程继续执行,不等待磁盘写入

为什么叫"异步文件写入器"?

关键字 含义
异步 调用者不等待结果,立即返回
文件写入 把数据写到磁盘
封装成一个类/对象

AsyncFileWriter = 负责异步写文件的类

各函数解释

函数 作用 一句话说明
构造函数 初始化 保存输出目录和队列大小
析构函数 清理 调用 stop() 停止线程
start() 启动IO线程 创建 writerLoop 线程
stop() 停止IO线程 等待线程结束,刷新剩余任务
submitWrite() 提交单个写入任务 把任务放入队列,立即返回
submitBatch() 批量提交任务 一次提交多个任务,减少锁竞争
waitForCriticalWrites() 等待关键帧写完 阻塞直到所有关键帧写入完成
getQueueSize() 查询队列大小 返回待写入任务数
writerLoop() IO线程主循环 从队列取任务,真正写磁盘
writeFileInternal() 实际写文件 打开文件 + 写入数据
ensureDirectoryExists() 确保目录存在 不存在则创建
getStatistics() 获取统计信息 总帧数、总字节数、平均耗时等

核心流程图

scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                     AsyncFileWriter 工作流程                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  解算线程 (调用者)                     IO线程 (writerLoop)                   │
│  ┌─────────────────┐                 ┌─────────────────┐                    │
│  │ submitWrite()   │                 │ while(!stop) {  │                    │
│  │      ↓          │                 │     等待队列    │                    │
│  │ 放入队列        │ ─── 任务 ───→   │      ↓          │                    │
│  │ notify_one()    │                 │ 取出任务        │                    │
│  │      ↓          │                 │      ↓          │                    │
│  │ 立即返回 ✅      │                 │ writeFile()    │                    │
│  └─────────────────┘                 │      ↓          │                    │
│                                      │ 写入磁盘 💾     │                    │
│                                      └─────────────────┘                    │
│                                                                              │
│  关键:解算线程不等待写磁盘,只把任务丢进队列就继续工作                        │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                         AsyncFileWriter 核心流程                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  调用者线程                               IO线程                              │
│  ┌─────────────────┐                    ┌─────────────────┐                 │
│  │ submitWrite()   │                    │ writerLoop()    │                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ 构造任务结构体   │                    │ unique_lock     │                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ lock_guard 加锁 │                    │ wait(等待唤醒)   │                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ 任务入队 push   │                    │ move 取出任务    │                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ 解锁(自动)      │                    │ 出队 pop        │                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ notify_one()    │ ─────── 唤醒 ────→ │ writeFileInternal│                 │
│  │      ↓          │                    │      ↓          │                 │
│  │ 立即返回 ✅      │                    │ ofs.write() 写磁盘│                 │
│  └─────────────────┘                    └─────────────────┘                 │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

完整流程时序图

scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                         完整调用时序                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  主线程                     IO线程                      磁盘                 │
│    │                         │                          │                   │
│    │ ① new AsyncFileWriter()  │                          │                   │
│    │────────────────────────→│                          │                   │
│    │                         │                          │                   │
│    │ ② start()               │                          │                   │
│    │────────────────────────→│                          │                   │
│    │                         │ ③ 创建 writerLoop 线程    │                   │
│    │                         │←─────────────────────────│                   │
│    │                         │                          │                   │
│    │ ④ submitWrite("depth")  │                          │                   │
│    │────────────────────────→│                          │                   │
│    │   [立即返回 ✅]          │ ⑤ 任务入队               │                   │
│    │                         │     notify_one()         │                   │
│    │                         │                          │                   │
│    │                         │ ⑥ 被唤醒,取任务          │                   │
│    │                         │                          │                   │
│    │                         │ ⑦ writeFileInternal()    │                   │
│    │                         │─────────────────────────→│                   │
│    │                         │                          │ ⑧ 写入文件         │
│    │                         │←─────────────────────────│                   │
│    │                         │                          │                   │
│    │ ⑨ submitWrite("ir")     │                          │                   │
│    │────────────────────────→│                          │                   │
│    │   [立即返回 ✅]          │ ⑩ 任务入队               │                   │
│    │                         │     notify_one()         │                   │
│    │                         │                          │                   │
│    │ ⑪ waitForCriticalWrites │                          │                   │
│    │────────────────────────→│                          │                   │
│    │   [阻塞等待]            │ ⑫ 处理剩余任务            │                   │
│    │                         │─────────────────────────→│                   │
│    │                         │←─────────────────────────│                   │
│    │                         │ ⑬ 关键帧写完,通知        │                   │
│    │←────────────────────────│                          │                   │
│    │   [被唤醒,继续]         │                          │                   │
│    │                         │                          │                   │
│    │ ⑭ stop()                │                          │                   │
│    │────────────────────────→│ ⑮ 退出循环,flush剩余     │                   │
│    │                         │─────────────────────────→│                   │
│    │                         │ ⑯ 线程结束                │                   │
│    │←────────────────────────│                          │                   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

函数调用关系图

scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                         函数调用关系                                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  外部调用者 (ToFPipelineManager)                                             │
│       │                                                                      │
│       ├──→ new AsyncFileWriter() ──→ 构造函数                                │
│       │                              └──→ ensureDirectoryExists()           │
│       │                                                                      │
│       ├──→ start() ──→ 创建 writerThread_                                    │
│       │              └──→ writerLoop() (独立线程运行)                         │
│       │                                                                      │
│       ├──→ submitWrite() ──→ 任务入队                                        │
│       │                    └──→ queueCV_.notify_one()                        │
│       │                              │                                       │
│       │                              ↓                                       │
│       │                      writerLoop() 被唤醒                             │
│       │                              │                                       │
│       │                              └──→ writeFileInternal()                │
│       │                                        │                             │
│       │                                        └──→ ensureDirectoryExists()  │
│       │                                                                      │
│       ├──→ waitForCriticalWrites() ──→ 等待 criticalDoneCV_                  │
│       │                                                                      │
│       └──→ stop() ──→ shouldStop_ = true                                     │
│                     └──→ writerThread_->join()                               │
│                               │                                              │
│                               └──→ writerLoop() 退出                         │
│                                     └──→ 处理剩余任务                         │
│                                           └──→ writeFileInternal()           │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

submitWrite → 入队 → 唤醒 → writerLoop → 取出 → writeFileInternal

核心数据流

scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                           数据流                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  解算线程                              磁盘                                  │
│     │                                    │                                   │
│     │  ProcessedFrameData                │                                   │
│     │         │                          │                                   │
│     │         ↓                          │                                   │
│     │  submitWrite("depth.raw", data)    │                                   │
│     │         │                          │                                   │
│     │         ↓                          │                                   │
│     │  ┌─────────────────┐               │                                   │
│     │  │   WriteTask     │               │                                   │
│     │  │  filename: ...  │               │                                   │
│     │  │  data: 指针     │ ──移动→       │                                   │
│     │  │  frameId: 123   │               │                                   │
│     │  └─────────────────┘               │                                   │
│     │         │                          │                                   │
│     │         ↓                          │                                   │
│     │  taskQueue_.push()                 │                                   │
│     │         │                          │                                   │
│     │         │                          │                                   │
│     │    IO线程独立运行                   │                                   │
│     │         │                          │                                   │
│     │         ↓                          │                                   │
│     │  writerLoop() 取任务                │                                   │
│     │         │                          │                                   │
│     │         ↓                          │                                   │
│     │  writeFileInternal()               │                                   │
│     │         │                          │                                   │
│     │         └─────────────────────────→│  depth_000123.raw                 │
│     │                                    │                                   │
└─────────────────────────────────────────────────────────────────────────────┘

lock_guard vs unique_lock 区别

arduino 复制代码
// ============================================================================
// lock_guard - 简单场景
// ============================================================================
{
    std::lock_guard<std::mutex> lock(mutex_);  // 构造时加锁
    queue_.push(task);                          // 临界区
}  // 析构时自动解锁,不能提前解锁

// ============================================================================
// unique_lock - 需要条件变量时
// ============================================================================
std::unique_lock<std::mutex> lock(mutex_);      // 可以延迟加锁
queueCV_.wait(lock, []{ return !queue_.empty(); });  // wait 需要 unique_lock
lock.unlock();                                   // 可以提前解锁
// 可以多次加锁/解锁

// ============================================================================
// 你的代码中的使用场景
// ============================================================================
// submitWrite() 中用 lock_guard ✅(只做 push,不需要 wait)
std::lock_guard<std::mutex> lock(queueMutex_);
taskQueue_.push(std::move(task));

// writerLoop() 中用 unique_lock ✅(需要 wait)
std::unique_lock<std::mutex> lock(queueMutex_);
queueCV_.wait(lock, [this]() { 
    return !taskQueue_.empty() || shouldStop_.load(); 
});
特性 lock_guard unique_lock
灵活性 低,只能构造时加锁,析构时解锁 高,可以随时加锁/解锁
条件变量 ❌ 不能用 wait() ✅ 可以用 wait()
性能 稍快(开销小) 稍慢(功能多)
移动语义 ❌ 不支持 ✅ 支持
代码简洁 ✅ 简单 稍复杂

总结:需要 wait() 就用 unique_lock,否则用 lock_guard

生产者-消费者模型

c++ 复制代码
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                           ToFPipelineManager 生产者-消费者模型                        │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                      │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐     ┌──────────────┐    │
│  │   生产者1     │     │   消费者1     │     │   生产者2     │     │   消费者2     │    │
│  │ (HAL回调)    │ ──→ │ (捕获线程)    │ ──→ │ (解算线程)    │ ──→ │ (IO线程)     │    │
│  │              │     │              │     │              │     │              │    │
│  │ deliverFrame │     │ getFrameData │     │ processLoop  │     │ ioLoop       │    │
│  │ _1_1()       │     │              │     │              │     │              │    │
│  └──────────────┘     └──────────────┘     └──────────────┘     └──────────────┘    │
│         │                    │                    │                    │           │
│         ↓                    ↓                    ↓                    ↓           │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐     ┌──────────────┐    │
│  │  GPU内存     │     │  mFrameData  │     │   rawQueue_  │     │  Writer队列  │    │
│  │  (HAL Buffer)│     │   Queue      │     │ (无锁队列)    │     │              │    │
│  │              │     │              │     │              │     │              │    │
│  │ 大小: 60个   │     │ 大小: 2      │     │ 大小: 8      │     │ 大小: 64     │    │
│  └──────────────┘     └──────────────┘     └──────────────┘     └──────────────┘    │
│         ↑                    ↑                    ↑                    ↑           │
│         │                    │                    │                    │           │
│  ┌──────┴──────┐     ┌──────┴──────┐     ┌──────┴──────┐     ┌──────┴──────┐        │
│  │  数据流向    │     │  数据流向    │     │  数据流向    │     │  数据流向    │        │
│  │  GPU→CPU    │     │  Copy→Move  │     │  Batch→SDK  │     │  Memory→Disk│        │
│  └─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘        │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
模型组件 你的代码实现
缓冲区 mFrameDataQueue、rawQueue_(无锁队列)
生产者线程 HAL回调线程 (deliverFrame_1_1)
消费者线程 捕获线程 (captureLoop)
同步机制 std::mutex + std::condition_variable
阻塞条件(满) 队列满时 forcePush 丢帧(你的策略是丢新帧)
阻塞条件(空) getFrameData() 中 wait() 阻塞

本地显示线程

1.整体架构图

c++ 复制代码
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                           车机本地显示架构                                           │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                      │
│  ToFPipelineManager (数据源)                                                         │
│         │                                                                            │
│         │  ProcessedFrameData                                                        │
│         ↓                                                                            │
│  ┌─────────────────────────────────────────────────────────────────────────────┐    │
│  │                      DisplayController                                       │    │
│  │  • 管理显示线程                                                              │    │
│  │  • 深度图转伪彩色 (LUT)                                                      │    │
│  │  • 调用 Renderer 渲染                                                        │    │
│  └─────────────────────────────────────────────────────────────────────────────┘    │
│         │                                                                            │
│         ↓                                                                            │
│  ┌─────────────────────────────────────────────────────────────────────────────┐    │
│  │                      SimpleRenderer                                          │    │
│  │  • EGL/Surface 管理                                                         │    │
│  │  • OpenGL ES 渲染                                                           │    │
│  │  • Shader 编译                                                              │    │
│  └─────────────────────────────────────────────────────────────────────────────┘    │
│         │                                                                            │
│         ↓                                                                            │
│  ┌─────────────────────────────────────────────────────────────────────────────┐    │
│  │                      SurfaceFlinger (Android 系统)                          │    │
│  │  • 合成多个窗口                                                              │    │
│  │  • 输出到物理显示屏                                                          │    │
│  └─────────────────────────────────────────────────────────────────────────────┘    │
│         │                                                                            │
│         ↓                                                                            │
│  ┌─────────────────────────────────────────────────────────────────────────────┐    │
│  │                      车机物理显示屏                                           │    │
│  │  ┌─────────────────────────┐  ┌─────────────────────────┐                   │    │
│  │  │      深度图窗口          │  │       红外图窗口         │                   │    │
│  │  │  (伪彩色显示)            │  │  (灰度显示)              │                   │    │
│  │  └─────────────────────────┘  └─────────────────────────┘                   │    │
│  └─────────────────────────────────────────────────────────────────────────────┘    │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘

三个文件的功能分工

文件 职责 核心功能 一句话功能
SimpleRenderer.cpp OpenGL 渲染 创建 EGL 环境、编译 Shader、渲染纹理 使用 OpenGL ES 把图像画到车机屏幕上
DisplayController.cpp 显示控制 管理显示线程、深度图转伪彩色、调用渲染器 管理显示线程,将深度数据转成彩色图像
createNativeWindow.cpp 窗口创建 创建 Surface、管理窗口位置/大小 创建 Android 窗口,设置位置和大小

SimpleRenderer.cpp - OpenGL 渲染器

核心职责

将深度图/红外图数据渲染到车机屏幕上。

关键函数
c++ 复制代码
// 1. 初始化 EGL 环境和 OpenGL 资源
bool SimpleRenderer::init(int depthWidth, int depthHeight,
                          int irWidth, int irHeight) {

// 2. 渲染一帧
void SimpleRenderer::renderFrame(EGLSurface surface, GLuint textureId,
                                 const uint8_t* data, int width, int height,
                                 GLenum format) {
着色器原理
scss 复制代码
// 顶点着色器:处理顶点坐标和纹理坐标
顶点坐标 (-1,-1) 到 (1,1) → 屏幕坐标
纹理坐标 (0,0) 到 (1,1) → 图像像素

// 片段着色器:每个像素的颜色
颜色 = 纹理采样(纹理坐标)  // 直接把纹理像素输出到屏幕

DisplayController.cpp - 显示控制器

核心职责

管理显示线程,将深度数据转换为彩色图像。

关键函数
javascript 复制代码
// 1. 初始化色彩查找表(将深度值映射为彩色)
void DisplayController::initColorLUT() {

// 2. 显示线程主循环
void DisplayController::displayThreadFunc() {
深度图伪彩色映射
scss 复制代码
距离近 (0mm)      → 蓝色
距离中等 (2.5m)   → 绿色
距离远 (5m+)      → 红色

车机屏幕上:近处物体显示蓝色,远处显示红色,直观易懂

createNativeWindow.cpp - 窗口管理

核心职责

创建 Android Surface 窗口,管理窗口位置和大小。

arduino 复制代码
 // 创建本地窗口EGLNativeWindowType 
createNativeWindow(int width, int height, int x, int y) {

完整数据流

c++ 复制代码
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                           完整数据流                                                 │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                      │
│  ToFPipelineManager::processLoop()                                                   │
│         │                                                                            │
│         │  ProcessedFrameData {                                                      │
│         │    depthData: uint16_t[307200]  (0-5000mm)                                │
│         │    irData: uint8_t[307200]      (0-255灰度)                               │
│         │  }                                                                         │
│         ↓                                                                            │
│  DisplayController::getLatestFrame()                                                 │
│         │                                                                            │
│         ↓                                                                            │
│  DisplayController::displayThreadFunc()                                              │
│         │                                                                            │
│         │  深度图处理:                                                              │
│         │  uint16_t depth[307200] → 伪彩色 → uint8_t rgb[921600]                     │
│         │                                                                           │
│         │  红外图处理:                                                              │
│         │  uint8_t ir[307200] → 直接使用                                             │
│         ↓                                                                            │
│  SimpleRenderer::showDepth(rgb, 640, 480)                                            │
│         │                                                                            │
│         ↓                                                                            │
│  SimpleRenderer::renderFrame()                                                       │
│         │                                                                            │
│         │  ① eglMakeCurrent (绑定 Surface)                                          │
│         │  ② glTexImage2D (上传 RGB 数据到纹理)                                     │
│         │  ③ glDrawArrays (绘制全屏矩形)                                            │
│         │  ④ eglSwapBuffers (显示到屏幕)                                            │
│         ↓                                                                            │
│  SurfaceFlinger (Android 系统合成器)                                                  │
│         │                                                                            │
│         ↓                                                                            │
│  车机显示屏                                                                          │
│  ┌─────────────────────┐  ┌─────────────────────┐                                   │
│  │   深度图 (伪彩色)    │  │   红外图 (灰度)     │                                   │
│  │   640x480           │  │   640x480           │                                   │
│  │   位置: (0, 0)      │  │   位置: (650, 0)    │                                   │
│  └─────────────────────┘  └─────────────────────┘                                   │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
相关推荐
程序员打怪兽5 小时前
跨平台相机适配方案总结
嵌入式
华清远见IT开放实验室6 小时前
硬核根基,智能载体:华清远见嵌入式“硬件+仿真+课程+师资”产教融合与实践教学方案
linux·人工智能·stm32·物联网·嵌入式·虚拟仿真
用户805533698039 小时前
嵌入式Linux驱动开发——Pinctrl 子系统架构深度解析
linux·嵌入式
ziyi程序员10 小时前
[嵌入式]嵌入式在线仿真平台 —— Wokwi 入门指南
嵌入式
Jason_zhao_MR11 小时前
RK3506工业网关:如何打通现场采集、无线传输与行业规约接入?
linux·嵌入式硬件·物联网·系统架构·嵌入式
济61711 小时前
ROS开发专栏---话题 Topic + 消息 Message 完整实战---适配Ubuntu 22.04
嵌入式·ros2·机器人开发
济61711 小时前
ROS开发专栏---机器人运动控制 ---适配Ubuntu 22.04
嵌入式·ros2·机器人开发
tom02181 天前
软考中级《嵌入式系统设计师》全套备考资料(真题 + 教材 + 笔记)
笔记·嵌入式·软考·自学·电子技术·电子资料·变成
番茄灭世神1 天前
PN学堂GD32教程第21篇——WiFiIOT
c语言·stm32·单片机·嵌入式·gd32