边缘AI与3D可视化全链路技术清单:从RKNN部署到Qt/OpenGL集成
第一阶段 集成
第一部分:边缘AI(TinyML + YOLO轻量化模型部署于RKNN/NPU)
1.1 官方开源代码目录清单
1.1.1 核心SDK与工具链
核心仓库:
| 仓库名称 | 官方地址 | 版本要求 | 核心用途 |
|---|---|---|---|
| rknn-toolkit2 | https://github.com/airockchip/rknn-toolkit2 | v2.3.0+ | 模型转换工具包(PC端) |
| rknn_model_zoo | https://github.com/airockchip/rknn_model_zoo | v2.3.0+ | 预优化模型仓库与示例代码 |
| ultralytics_yolov8 | https://github.com/airockchip/ultralytics_yolov8 | 最新 | 瑞芯微优化版YOLOv8/YOLO11导出工具 |
| rknpu2 | https://github.com/rockchip-linux/rknpu2 | v1.7.0+ | NPU运行时库与驱动(板端) |
1.1.2 开源社区项目
| 项目名称 | GitHub地址 | 特点 |
|---|---|---|
| rknn-cpp-yolo | https://github.com/yuunnn-w/rknn-cpp-yolo | YOLOv11 C++完整实现,含后处理 |
| rk3568-yolov5s-go | https://github.com/ai-benchmark/rk3568-yolov5s-go | Go语言NPU绑定,轻量化部署 |
| rknn_model_zoo | https://gitcode.com/gh_mirrors/rk/rknn_model_zoo | 镜像仓库,国内加速访问 |
1.1.3 完整目录结构(rknn_model_zoo)
根据rknn_model_zoo源码分析,目录结构如下:
rknn_model_zoo/ ├── 3rdparty/ # 第三方依赖 │ ├── rknpu2/ # NPU运行时 │ │ └── include/ │ │ └── rknn_api.h # 核心API头文件 │ ├── opencv/ # 图像处理库 │ └── json/ # JSON解析 ├── examples/ # 各模型示例 │ ├── yolov5/ # YOLOv5示例 │ │ ├── cpp/ # C++实现 │ │ │ ├── yolov5.cc # 核心推理逻辑 │ │ │ ├── postprocess.cc # NMS后处理 │ │ │ └── CMakeLists.txt │ │ └── python/ # Python实现 │ │ ├── yolov5.py │ │ └── convert.py # 模型转换脚本 │ ├── yolov8/ # YOLOv8示例 │ │ ├── cpp/ │ │ │ ├── yolov8_demo.cc │ │ │ └── postprocess.cc │ │ └── python/ │ │ └── yolov8.py │ ├── yolo11/ # YOLO11示例(Ultralytics最新) │ │ ├── cpp/ │ │ └── model_comparison/ # 性能对比数据 │ ├── yolov8_seg/ # 实例分割 │ ├── clip/ # 多模态识别 │ └── mms_tts/ # 语音合成 ├── utils/ # 工具函数 │ ├── image_utils.c # 图像读取(JPEG/PNG/RAW) │ ├── file_utils.c # 文件操作 │ └── image_drawing.c # 画框/标注 ├── docs/ # 文档 │ ├── Compilation_Environment_Setup_Guide.md │ └── requirements_cp38.txt # Python依赖 └── datasets/ # 数据集 └── COCO/ # COCO子集及下载脚本
1.2 关键函数树形分析清单
1.2.1 RKNN API核心函数(rknn_api.h)
根据rknn_api.h源码分析,核心函数调用链如下:
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ RKNN API 核心函数调用链 │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 初始化阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_init(ctx, model_data, model_size, flag, ext) │ │ │ │ ├── 加载RKNN模型到NPU内存 │ │ │ │ ├── 返回rknn_context句柄(32位ARM/64位非ARM) │ │ │ │ └── 分配NPU内部资源 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 查询阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_query(ctx, cmd, info, size) │ │ │ │ ├── RKNN_QUERY_IN_OUT_NUM → 获取输入/输出数量 │ │ │ │ ├── RKNN_QUERY_INPUT_ATTR → 获取输入张量属性(维度/类型/量化参数) │ │ │ │ ├── RKNN_QUERY_OUTPUT_ATTR → 获取输出张量属性 │ │ │ │ └── RKNN_QUERY_PERF_DETAIL → 获取性能统计(需perf_mode=1) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 输入设置阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_inputs_set(ctx, n_inputs, inputs) │ │ │ │ ├── inputs[].buf = 图像数据指针 │ │ │ │ ├── inputs[].size = 数据大小 │ │ │ │ ├── inputs[].pass_through = 0/1(是否跳过预处理) │ │ │ │ └── inputs[].fmt = RKNN_TENSOR_NCHW / RKNN_TENSOR_NHWC │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 推理执行阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_run(ctx, nullptr) │ │ │ │ ├── NPU开始计算 │ │ │ │ ├── 阻塞直到推理完成 │ │ │ │ └── DMA零拷贝通路(需配置perf_mode=1启用硬件计数器) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 输出获取阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_outputs_get(ctx, n_outputs, outputs, wait) │ │ │ │ ├── 获取推理结果 │ │ │ │ ├── outputs[].buf = 输出数据指针(含检测框+置信度+类别) │ │ │ │ └── 如需反量化:scale和zp参数用于INT8→FP32转换 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 释放阶段 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_destroy(ctx) │ │ │ │ ├── 释放NPU内存 │ │ │ │ └── 销毁上下文句柄 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────────┘
1.2.2 YOLOv5推理完整代码结构(yolov5.cc)
根据rknn_model_zoo中的yolov5.cc源码分析,核心数据结构与函数如下:
// ============================================================================
// 核心数据结构
// ============================================================================
// 应用上下文结构体
typedef struct {
rknn_context rknn_ctx; // RKNN模型句柄
rknn_input_output_num io_num; // 输入/输出数量(YOLOv5: 1输入/3输出)
rknn_tensor_attr* input_attrs; // 输入张量属性
rknn_tensor_attr* output_attrs; // 输出张量属性
int model_channel; // 模型输入通道数(3)
int model_width; // 模型输入宽度(640)
int model_height; // 模型输入高度(640)
bool is_quant; // 是否为量化模型(INT8)
#if defined(RV1106_1103)
rknn_tensor_mem* input_mems[1]; // DMA缓冲区(RV1106专用)
rknn_tensor_mem* output_mems[3];
#endif
} rknn_app_context_t;
// 检测结果结构体
typedef struct {
float box[4]; // [x1, y1, x2, y2] 归一化坐标
float prop; // 置信度
int id; // 类别ID
} object_detect_result;
// ============================================================================
// 核心函数树形分析
// ============================================================================
/**
* 函数1: init_yolov5_model - 模型初始化
* 位置: examples/yolov5/cpp/yolov5.cc
* 调用链: main() → init_yolov5_model()
*/
int init_yolov5_model(const char* model_path, rknn_app_context_t* app_ctx)
{
// 1. 读取RKNN模型文件到内存
FILE* fp = fopen(model_path, "rb");
fseek(fp, 0, SEEK_END);
size_t model_size = ftell(fp);
uint8_t* model_data = (uint8_t*)malloc(model_size);
// 2. 初始化RKNN上下文
rknn_init(&app_ctx->rknn_ctx, model_data, model_size, 0, NULL);
free(model_data);
// 3. 查询输入/输出数量
rknn_query(app_ctx->rknn_ctx, RKNN_QUERY_IN_OUT_NUM,
&app_ctx->io_num, sizeof(app_ctx->io_num));
// 4. 分配输入/输出属性数组
app_ctx->input_attrs = (rknn_tensor_attr*)malloc(app_ctx->io_num.n_input *
sizeof(rknn_tensor_attr));
app_ctx->output_attrs = (rknn_tensor_attr*)malloc(app_ctx->io_num.n_output *
sizeof(rknn_tensor_attr));
// 5. 查询每个输入张量属性
for (uint32_t i = 0; i < app_ctx->io_num.n_input; i++) {
app_ctx->input_attrs[i].index = i;
rknn_query(app_ctx->rknn_ctx, RKNN_QUERY_INPUT_ATTR,
&app_ctx->input_attrs[i], sizeof(rknn_tensor_attr));
}
// 6. 查询每个输出张量属性
for (uint32_t i = 0; i < app_ctx->io_num.n_output; i++) {
app_ctx->output_attrs[i].index = i;
rknn_query(app_ctx->rknn_ctx, RKNN_QUERY_OUTPUT_ATTR,
&app_ctx->output_attrs[i], sizeof(rknn_tensor_attr));
}
// 7. 提取模型输入尺寸(支持NCHW/NHWC)
if (app_ctx->input_attrs[0].fmt == RKNN_TENSOR_NCHW) {
app_ctx->model_channel = app_ctx->input_attrs[0].dims[1];
app_ctx->model_height = app_ctx->input_attrs[0].dims[2];
app_ctx->model_width = app_ctx->input_attrs[0].dims[3];
} else {
app_ctx->model_height = app_ctx->input_attrs[0].dims[1];
app_ctx->model_width = app_ctx->input_attrs[0].dims[2];
app_ctx->model_channel = app_ctx->input_attrs[0].dims[3];
}
// 8. 判断是否为量化模型
app_ctx->is_quant = (app_ctx->output_attrs[0].type == RKNN_TENSOR_UINT8);
return 0;
}
/**
* 函数2: inference_yolov5_model - 执行推理
* 位置: examples/yolov5/cpp/yolov5.cc
* 调用链: main() → inference_yolov5_model()
*/
int inference_yolov5_model(rknn_app_context_t* app_ctx,
image_buffer_t* src_img,
object_detect_result_list* od_results)
{
// 1. 图像预处理(缩放+填充)
image_buffer_t dst_img;
preprocess_image(src_img, &dst_img,
app_ctx->model_width,
app_ctx->model_height);
// 2. 设置RKNN输入
rknn_input inputs[1];
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = dst_img.width * dst_img.height * dst_img.channels;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].pass_through = 0; // 需要预处理(量化)
inputs[0].buf = dst_img.virt_addr;
rknn_inputs_set(app_ctx->rknn_ctx, 1, inputs);
// 3. 执行NPU推理
rknn_run(app_ctx->rknn_ctx, NULL);
// 4. 获取输出
rknn_output outputs[3];
for (int i = 0; i < app_ctx->io_num.n_output; i++) {
outputs[i].want_float = !app_ctx->is_quant; // INT8需反量化
}
rknn_outputs_get(app_ctx->rknn_ctx, app_ctx->io_num.n_output,
outputs, NULL);
// 5. 后处理(NMS + 坐标转换)
post_process(outputs, app_ctx, dst_img, src_img, od_results);
// 6. 释放输出
rknn_outputs_put(app_ctx->rknn_ctx, app_ctx->io_num.n_output, outputs);
return 0;
}
/**
* 函数3: post_process - NMS后处理
* 位置: examples/yolov5/cpp/postprocess.cc
*/
void post_process(rknn_output* outputs,
rknn_app_context_t* app_ctx,
image_buffer_t dst_img,
image_buffer_t src_img,
object_detect_result_list* od_results)
{
// 1. 解析YOLO输出(3个输出层)
// - 输出层0: 80x80网格,检测小物体
// - 输出层1: 40x40网格,检测中物体
// - 输出层2: 20x20网格,检测大物体
for (int i = 0; i < 3; i++) {
parse_yolo_output(outputs[i].buf, i, &detections);
}
// 2. 置信度过滤
for (auto& det : detections) {
if (det.prop > CONFIDENCE_THRESHOLD) {
filtered.push_back(det);
}
}
// 3. NMS(非极大值抑制)
std::sort(filtered.begin(), filtered.end(),
[](auto& a, auto& b) { return a.prop > b.prop; });
for (auto& det : filtered) {
if (std::none_of(keep.begin(), keep.end(),
[&](auto& k) { return iou(det.box, k.box) > NMS_THRESHOLD; })) {
keep.push_back(det);
}
}
// 4. 坐标反算(从填充后图像映射回原图)
for (auto& det : keep) {
map_coordinate(det.box, dst_img, src_img);
}
// 5. 填充结果
od_results->count = keep.size();
memcpy(od_results->results, keep.data(), sizeof(od_results->results));
}
1.3 YOLO模型性能基准数据
| 模型 | 输入尺寸 | RK3588推理速度 | RK3566推理速度 | 精度(COCO mAP) |
|---|---|---|---|---|
| YOLO11n | 640×640 | 82 FPS (12.2ms) | 约25 FPS | 39.0 mAP |
| YOLO11s | 640×640 | 约60 FPS | 约12 FPS | 46.0 mAP |
| YOLOv8n | 640×640 | 62 FPS | 16–22 FPS | 37.3 mAP |
| YOLOv8s | 640×640 | 约40 FPS | 8–12 FPS | 44.9 mAP |
| YOLOv10n | 640×640 | 75 FPS | - | 38.5 mAP |
| YOLOv5s | 640×640 | 约40-48ms | 约70ms | 37.0 mAP |
关键优化要点:
-
INT8量化:速度提升3-4倍,精度下降约1-2%
-
CPU后处理优化:使用向量化NumPy或C++实现,避免Python开销
-
预处理对齐:确保
mean_values/std_values与训练时一致,否则置信度漂移
第二部分:3D可视化(Qt在嵌入式平台的集成与优化)
2.1 开源代码目录清单
2.1.1 Qt 3D核心框架
| 项目名称 | 官方地址 | 核心用途 |
|---|---|---|
| Qt 3D | https://github.com/qt/qt3d | Qt官方3D框架,集成OpenGL/Vulkan |
| Qt3D Examples | https://doc.qt.io/qt-6/qt3d-examples.html | 官方示例集(场景图、动画、PBR) |
| qwtplot3d | 源码包(219KB) | 基于Qt+OpenGL的3D绘图控件库 |
2.1.2 qwtplot3d源码目录结构
根据qwtplot3d源码分析,目录结构如下:
qwtplot3d/ ├── include/ │ ├── qwt3d_plot.h # 3D绘图主控件 │ ├── qwt3d_surfaceplot.h # 曲面图 │ ├── qwt3d_spectrogram.h # 光谱图 │ ├── qwt3d_enrichment.h # 数据点增强 │ └── qwt3d_global.h # 全局定义 ├── src/ │ ├── qwt3d_plot.cpp # 核心实现 │ ├── qwt3d_surfaceplot.cpp # 曲面绘制 │ ├── qwt3d_engine.cpp # OpenGL引擎 │ ├── qwt3d_io.cpp # 数据输入输出 │ └── qwt3d_helpers.cpp # 辅助函数 ├── examples/ # 示例程序 │ ├── simpleplot/ # 简单3D绘图示例 │ ├── surfaceplot/ # 曲面图示例 │ └── spectrogram/ # 光谱图示例 ├── CMakeLists.txt # CMake构建配置 └── qwtplot3d.pro # Qt工程文件
2.2 关键函数树形分析清单
2.2.1 Qt 3D框架核心类图
根据Qt官方文档,Qt 3D采用场景图+帧图双架构:
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ Qt 3D 核心类树形结构 │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ Qt3DCore::QEntity (实体) │ │ │ ├── 场景中的基本构建块 │ │ │ ├── 包含多个QComponent │ │ │ └── 子实体形成树形结构 │ │ │ │ │ Qt3DRender::QCamera (相机) │ │ │ ├── viewMatrix: 观察矩阵 │ │ │ ├── projectionMatrix: 投影矩阵(透视/正交) │ │ │ └── 支持鼠标交互旋转/缩放 │ │ │ │ │ Qt3DRender::QMesh (网格) │ │ │ ├── source: 3D模型文件(OBJ/STL/GLTF) │ │ │ └── 几何数据(顶点、法线、纹理坐标) │ │ │ │ │ Qt3DExtras::QPhongMaterial (材质) │ │ │ ├── ambient: 环境光反射 │ │ │ ├── diffuse: 漫反射 │ │ │ ├── specular: 镜面反射 │ │ │ └── shininess: 光泽度 │ │ │ │ │ Qt3DRender::QRenderSettings (渲染设置) │ │ │ ├── activeFrameGraph: 帧图配置 │ │ │ ├── QForwardRenderer: 前向渲染器 │ │ │ └── QRenderAspect: 渲染方面 │ │ │ │ │ Qt3DLogic::QFrameAction (帧动作) │ │ │ ├── 每帧触发回调(用于动画更新) │ │ │ └── 连接AI检测结果实时更新显示 │ └─────────────────────────────────────────────────────────────────────────────────────┘
2.2.2 qwtplot3d核心函数分析
根据qwtplot3d源码,核心渲染流程如下:
// ============================================================================
// 类: Qwt3D::Plot3D - 3D绘图主控件
// 位置: qwtplot3d/include/qwt3d_plot.h
// ============================================================================
class QWT3D_EXPORT Plot3D : public QWidget
{
Q_OBJECT
public:
Plot3D(QWidget* parent = nullptr);
~Plot3D();
// 数据设置
void setData(const Qwt3D::TripleField& data); // 设置散点数据
void setData(const Qwt3D::Matrix& data); // 设置网格数据
void setTitle(const QString& title); // 设置标题
void setAxisTitle(Qwt3D::AXIS axis, const QString& title); // 轴标签
// 坐标系统
void setCoordinateSystem(Qwt3D::COORD system); // 笛卡尔/极坐标
void setScale(Qwt3D::AXIS axis, double min, double max); // 轴范围
// 渲染控制
void updateData(); // 更新数据(触发重绘)
void setResolution(int x, int y); // 网格分辨率
protected:
// OpenGL绘制函数(重载QGLWidget)
void paintGL() override;
void initializeGL() override;
void resizeGL(int w, int h) override;
// 鼠标交互
void mousePressEvent(QMouseEvent* e) override;
void mouseMoveEvent(QMouseEvent* e) override;
void wheelEvent(QWheelEvent* e) override;
private:
// 核心绘制函数
void drawAxes(); // 绘制坐标轴
void drawGrid(); // 绘制网格
void drawData(); // 绘制数据点/曲面
void drawTitle(); // 绘制标题
// OpenGL相关
void setupViewport(int w, int h); // 视口设置
void setupLights(); // 光照设置
void setupMaterial(); // 材质设置
};
2.3 嵌入式Qt/OpenGL优化要点
根据嵌入式3D开发经验,在RK3588等平台上优化Qt 3D的关键技术:
| 优化方向 | 技术手段 | 性能提升 |
|---|---|---|
| OpenGL ES 3.2 | 使用嵌入式OpenGL,避免桌面OpenGL特性 | 内存减少30% |
| VBO/VAO | 顶点缓冲对象减少CPU-GPU传输 | 帧率提升50% |
| 纹理压缩 | ETC2/ASTC压缩格式,减少显存占用 | 显存减少75% |
| LOD技术 | 根据距离简化模型细节 | 复杂场景帧率提升2倍 |
| 离屏渲染 | 使用QOpenGLFramebufferObject | 后处理不影响主线程 |
第三部分:边缘AI与3D可视化全链路集成架构
3.1 完整数据流架构图
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ 边缘AI + 3D可视化全链路架构 │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ [视频输入层] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ USB摄像头 / MIPI CSI / RTSP流 → V4L2 / GStreamer → 帧缓冲区 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ [AI推理层 - RKNN NPU] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_init() → rknn_inputs_set() → rknn_run() → rknn_outputs_get() │ │ │ │ ├── 模型:yolo11n.rknn (INT8量化,640x640) │ │ │ │ ├── 输出:检测框[x,y,w,h,conf,class] │ │ │ │ └── 性能:82 FPS (RK3588) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ [后处理层 - CPU] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ post_process() → NMS → 坐标映射 → 结果队列 │ │ │ │ ├── 置信度过滤 (conf > 0.5) │ │ │ │ ├── 非极大值抑制 (IoU < 0.45) │ │ │ │ └── 输出:结构化检测结果 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ [数据融合层] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 检测结果 → 坐标转换 → 3D场景映射 → 更新3D对象 │ │ │ │ ├── 2D检测框 → 3D空间位置(单目测距) │ │ │ │ ├── 类别 → 3D模型材质/颜色 │ │ │ │ └── 置信度 → 透明度/高亮效果 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ [3D可视化层 - Qt3D/OpenGL] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ QEntity(Qt3DCore) → QMesh + QPhongMaterial → QTransform → QFrameAction │ │ │ │ ├── QCamera: 视角控制(俯视/平视/跟随) │ │ │ │ ├── QFrameAction: 每帧更新检测框位置 │ │ │ │ └── QRenderSettings: 前向渲染 + 阴影 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ [显示输出层] │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Qt窗口 → HDMI/LVDS/DSI输出 → 实时3D可视化界面 │ │ │ │ ├── 鸟瞰图:车辆/行人位置 │ │ │ │ ├── 轨迹预测:历史轨迹线 │ │ │ │ └── 热力图:检测置信度分布 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────────┘
3.2 集成代码示例(Qt + RKNN)
// ============================================================================
// 完整集成示例:AI检测结果 → Qt 3D可视化
// ============================================================================
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <rknn_api.h>
class AI3DVisualizer : public QWidget
{
Q_OBJECT
public:
AI3DVisualizer(QWidget* parent = nullptr);
private slots:
void onNewFrame(const QImage& frame); // 新帧到达
void onDetectionReady(const DetectionList& dets); // 检测完成
private:
// RKNN相关
rknn_context ctx;
rknn_input_output_num io_num;
rknn_tensor_attr* input_attrs;
rknn_tensor_attr* output_attrs;
// Qt 3D相关
Qt3DCore::QEntity* rootEntity;
Qt3DCore::QEntity* vehicleEntity;
Qt3DCore::QEntity* pedestrianEntity;
Qt3DCore::QEntity* bicycleEntity;
// 场景管理
void setup3DScene();
void update3DObjects(const DetectionList& dets);
void addObjectAtPosition(const QString& modelPath, const QVector3D& pos, int classId);
};
void AI3DVisualizer::onDetectionReady(const DetectionList& dets)
{
// 更新3D场景中的物体位置
update3DObjects(dets);
// 触发重新渲染
update();
}
void AI3DVisualizer::update3DObjects(const DetectionList& dets)
{
// 清除现有物体
clear3DObjects();
for (const auto& det : dets) {
// 2D坐标 → 3D空间位置(单目测距)
QVector3D pos = imageToWorld(det.x, det.y, det.w, det.h);
// 根据类别选择模型
QString modelPath;
switch (det.classId) {
case 0: // person
modelPath = ":/models/person.obj";
break;
case 2: // car
modelPath = ":/models/car.obj";
break;
case 1: // bicycle
modelPath = ":/models/bicycle.obj";
break;
}
// 添加到场景
addObjectAtPosition(modelPath, pos, det.classId);
}
}
第四部分:快速上手指南
4.1 RKNN环境搭建
# 1. 克隆RKNN Model Zoo git clone https://github.com/airockchip/rknn_model_zoo.git -b v2.3.0 cd rknn_model_zoo # 2. 安装Python依赖 pip install -r docs/requirements_cp38.txt # 3. 转换YOLO模型(以YOLOv8为例) cd examples/yolov8 python python/convert.py --model_path model/yolov8n.onnx --output model/yolov8n.rknn # 4. 板端运行 cd cpp && mkdir build && cd build cmake .. && make ./yolov8_demo ../../model/yolov8n.rknn ../../model/bus.jpg
4.2 Qt 3D环境配置(CMake)
# CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(AI3DVisualizer) find_package(Qt6 REQUIRED COMPONENTS Core Widgets) find_package(Qt6 REQUIRED COMPONENTS 3DCore 3DRender 3DExtras) target_link_libraries(AI3DVisualizer Qt6::Core Qt6::Widgets Qt6::3DCore Qt6::3DRender Qt6::3DExtras rknnrt # RKNN运行时库 )
第五部分:思路框架
1. 边缘AI部署
"RKNN Model Zoo提供了YOLOv5/v8/v11的全套示例。核心代码在examples/yolov5/cpp/目录,rknn_api.h定义了rknnu_init/run/outputs_get等关键函数。实测YOLO11n在RK3588上达82FPS,INT8量化精度损失仅1-2%。常见坑是预处理mean/std与训练不一致导致置信度漂移,以及后处理NMS在Python端成为瓶颈需用C++重写。"
2. Qt 3D集成
"Qt 3D采用场景图架构,核心类包括QEntity、QCamera、QMesh。优化要点是使用OpenGL ES 3.2替代桌面OpenGL,VBO/VAO减少CPU-GPU传输,纹理压缩采用ETC2格式。qwtplot3d是轻量级3D绘图控件,核心绘制函数paintGL()中调用drawAxes()/drawGrid()/drawData()。"
3. 全链路架构
"数据流是:摄像头→V4L2采集→RKNN推理(NPU加速)→后处理(NMS)→坐标转换→Qt 3D场景更新。帧率受限于NPU推理(约12ms)和后处理(约5ms),总延迟约20ms,满足实时性要求。"
第二部分 边缘AI + 3D可视化在ADAS/DMS/BSD场景的完整落地实战
一、场景一:ADAS(前向碰撞预警FCW + 车道偏离预警LDW)
1.1 完整数据流:从摄像头到仪表盘预警
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ ADAS场景完整数据流(每帧33ms) │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 【摄像头采集】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 前视摄像头 → MIPI CSI → V4L2 → 1920×1080@30fps → RGB帧 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【预处理】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 缩放(1920×1080 → 640×640) + 归一化(mean=[0,0,0], std=[255,255,255]) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【NPU推理 - YOLOv5s车辆检测】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ rknn_run() → 输出3个特征层 (80×80, 40×40, 20×20) │ │ │ │ ├── 检测到前车:坐标[1234,567,1560,789],类别"car",置信度0.94 │ │ │ │ ├── 检测到前车:坐标[890,432,1120,654],类别"car",置信度0.89 │ │ │ │ └── 检测到行人:坐标[1450,600,1520,720],类别"person",置信度0.76 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【车道线检测 - 传统CV + 深度学习混合】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 边缘检测(Canny) + 霍夫变换(Hough) → 车道线拟合 │ │ │ │ ├── 左车道线:y = 0.0003x² + 0.12x + 320 │ │ │ │ ├── 右车道线:y = 0.00025x² + 0.11x + 960 │ │ │ │ └── 车道中心线:y = 0.000275x² + 0.115x + 640 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【场景理解 - 将车辆与车道关联】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ for 每辆检测到的车: │ │ │ │ 1. 单目测距:Z = 焦距 × 车宽(1.8m) / 检测框宽度(像素) │ │ │ │ → 本车距离: 15.2米,车速: 72km/h │ │ │ │ 2. 计算车道偏移:检测框底部中心点(x=1400) vs 车道中心线(x=640) │ │ │ │ → 车辆在右车道,横向偏移+2.3米 │ │ │ │ 3. 卡尔曼滤波跟踪:预测下一帧位置,平滑轨迹 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【风险评估 - 决定是否预警】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ FCW前向碰撞预警: │ │ │ │ TTC = 15.2米 / 20m/s(72km/h) = 0.76秒 │ │ │ │ if (TTC < 1.2秒) → 紧急制动! 触发AEB │ │ │ │ if (TTC < 2.7秒) → 声音告警"前方碰撞风险" │ │ │ │ → 输出预警等级: 紧急(红色) │ │ │ │ │ │ │ │ LDW车道偏离预警: │ │ │ │ if (横向偏移 > 0.5米 AND 未打转向灯) → 方向盘震动 + 声音"车道偏离" │ │ │ │ → 输出: 左/右偏离警告 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【3D可视化 - Qt仪表盘渲染】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Qt3D场景更新: │ │ │ │ ├── 本车模型:位于场景中心,前向视角 │ │ │ │ ├── 前车模型:动态更新位置(距离15.2米,右车道),红色高亮(预警状态) │ │ │ │ ├── 车道线模型:根据拟合曲线动态生成3D曲线 │ │ │ │ ├── TTC指示器:显示0.76秒,红色闪烁 │ │ │ │ └── 仪表盘:72km/h速度表,紧急制动图标闪烁 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────────┘
1.2 ADAS核心代码实现
// ============================================================================
// ADAS场景:车辆检测 + 车道线拟合 + TTC计算
// ============================================================================
typedef struct {
float x1, y1, x2, y2; // 检测框坐标
float confidence; // 置信度
int class_id; // 类别:0=person, 2=car, 1=bicycle
float distance_m; // 距离本车距离(米)
float lateral_offset_m; // 横向偏移(米,正=右)
int lane_id; // 0=本车道, 1=左车道, 2=右车道
float velocity_ms; // 相对速度(米/秒)
float ttc_sec; // 碰撞时间(秒)
int risk_level; // 0=无风险, 1=警告, 2=紧急
} VehicleTarget;
// 🔥 ADAS核心处理函数
typedef struct {
VehicleTarget vehicles[16]; // 最多16辆目标车
int vehicle_count;
// 车道线参数
float left_lane_a, left_lane_b, left_lane_c; // 左车道二次曲线
float right_lane_a, right_lane_b, right_lane_c; // 右车道二次曲线
float lane_center_x; // 当前帧车道中心x坐标
// 车辆状态
float ego_speed_kmh; // 本车车速(km/h)
float ego_yaw_rate; // 本车横摆角速度
} ADASContext;
void adas_process_frame(ADASContext* ctx, YOLODetection* detections, int det_count)
{
// 1. 车道线拟合
fit_lane_lines(ctx);
// 2. 目标车辆关联与风险评估
for (int i = 0; i < det_count; i++) {
if (detections[i].class_id != 2) continue; // 只处理车辆
VehicleTarget* target = &ctx->vehicles[ctx->vehicle_count];
// 单目测距:假设标准车宽1.8米
float bbox_width = detections[i].x2 - detections[i].x1;
target->distance_m = (1.8 * CAMERA_FOCAL_LENGTH) / bbox_width;
// 横向偏移:检测框底部中心相对于车道中心线
float center_x = (detections[i].x1 + detections[i].x2) / 2;
target->lateral_offset_m = (center_x - ctx->lane_center_x) * METER_PER_PIXEL;
// 判断在哪个车道
if (target->lateral_offset_m < -LANE_WIDTH/2) {
target->lane_id = 1; // 左车道
} else if (target->lateral_offset_m > LANE_WIDTH/2) {
target->lane_id = 2; // 右车道
} else {
target->lane_id = 0; // 本车道
}
// 卡尔曼滤波更新相对速度
target->velocity_ms = kalman_update(target->distance_m);
// TTC计算
if (target->velocity_ms > 0.1) {
target->ttc_sec = target->distance_m / target->velocity_ms;
} else {
target->ttc_sec = 999.0; // 无穷大
}
// 风险评估
if (target->lane_id == 0 && target->ttc_sec < 1.2) {
target->risk_level = 2; // 紧急:触发AEB
// 发送CAN指令紧急制动
can_send_brake(100); // 100%制动力
qt_show_alert("⚠️ 紧急制动!", QColor(255,0,0));
} else if (target->lane_id == 0 && target->ttc_sec < 2.7) {
target->risk_level = 1; // 警告:FCW预警
qt_show_alert("⚠️ 前方碰撞风险", QColor(255,165,0));
}
ctx->vehicle_count++;
}
// 3. 车道偏离检测
float lateral_offset = ctx->lane_center_x - CAMERA_CENTER_X;
if (fabs(lateral_offset) > LANE_DEVIATION_THRESHOLD && !turn_signal_active()) {
// 触发LDW
qt_haptic_feedback(100); // 方向盘震动
qt_show_alert("⚠️ 车道偏离", QColor(255,165,0));
}
}
1.3 ADAS Qt 3D可视化代码
// ADAS仪表盘Qt 3D场景更新
class ADASDashboard : public Qt3DCore::QEntity
{
public:
void updateScene(const ADASContext& ctx)
{
// 1. 更新本车位置和速度显示
m_speedometer->setSpeed(ctx.ego_speed_kmh);
// 2. 更新前车模型
for (int i = 0; i < ctx.vehicle_count; i++) {
const VehicleTarget& v = ctx.vehicles[i];
// 计算3D世界坐标
QVector3D pos(
v.lateral_offset_m, // X轴:横向偏移
0.0, // Y轴:高度
-v.distance_m // Z轴:前方距离(负值)
);
// 更新车辆模型位置
m_vehicleModels[i]->setPosition(pos);
// 根据风险等级改变材质颜色
if (v.risk_level == 2) {
m_vehicleModels[i]->setMaterial(m_redMaterial); // 红色:紧急
} else if (v.risk_level == 1) {
m_vehicleModels[i]->setMaterial(m_orangeMaterial); // 橙色:警告
} else {
m_vehicleModels[i]->setMaterial(m_greenMaterial); // 绿色:安全
}
}
// 3. 更新车道线(动态生成3D曲线)
updateLaneCurves(ctx.left_lane_a, ctx.left_lane_b, ctx.left_lane_c,
ctx.right_lane_a, ctx.right_lane_b, ctx.right_lane_c);
// 4. 更新TTC指示器
float min_ttc = find_min_ttc(ctx);
if (min_ttc < 2.7) {
m_ttcIndicator->setText(QString("TTC: %1秒").arg(min_ttc, 0, 'f', 1));
m_ttcIndicator->setColor(Qt::red);
m_ttcIndicator->setBlinking(true); // 闪烁
}
}
};
二、场景二:DMS(驾驶员疲劳/分心监控)
2.1 DMS完整数据流
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ DMS场景完整数据流(驾驶员状态监控) │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 【近红外摄像头采集】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 红外摄像头(940nm) → MIPI CSI → 850nm带通滤波 → 驾驶员面部 │ │ │ │ ├── 光线适应性:白天/夜间/隧道均有效 │ │ │ │ └── 1440×1080@30fps → 640×640@30fps │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【NPU推理 - 多任务模型】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ YOLOv5s-人脸关键点模型 (修改输出层: 1×84×8400) │ │ │ │ ├── 人脸检测框: [x,y,w,h,conf] │ │ │ │ ├── 68个关键点: 眼睛、眉毛、鼻子、嘴巴轮廓 │ │ │ │ └── 头部姿态: pitch(俯仰), yaw(偏航), roll(翻滚) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【状态检测 - 关键点分析】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 眼睛状态: │ │ │ │ ├── EAR(眼睛纵横比) = (||p2-p6|| + ||p3-p5||) / (2×||p1-p4||) │ │ │ │ ├── 闭眼检测: EAR < 0.2 → 闭眼 │ │ │ │ └── PERCLOS(闭眼时间占比): 统计1分钟内闭眼帧数 > 30% → 疲劳 │ │ │ │ │ │ │ │ 头部姿态: │ │ │ │ ├── 低头检测: pitch > 30° → 低头看手机/导航 │ │ │ │ ├── 转头检测: yaw > 45° → 看侧窗/后排 │ │ │ │ └── 姿态变化率: 持续偏离超过2秒 → 分心 │ │ │ │ │ │ │ │ 视线估计: │ │ │ │ ├── 瞳孔中心定位: 基于68个关键点 + 虹膜分割 │ │ │ │ ├── 视线方向: 左/右/前/下 │ │ │ │ └── 视线偏离 > 2秒 → 分心告警 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【风险评估与预警】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 疲劳检测: │ │ │ │ if (PERCLOS > 0.3 OR 连续闭眼>3秒) → 声音告警"请注意休息" + 座椅震动 │ │ │ │ │ │ │ │ 分心检测: │ │ │ │ if (yaw > 45°持续2秒 OR 低头>30°持续2秒 OR 视线偏离>2秒) → │ │ │ │ 声音告警"请专注驾驶" + 方向盘震动 + 中控屏显示红色提示 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【3D可视化 - 驾驶员状态面板】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Qt3D场景: │ │ │ │ ├── 驾驶员3D头部模型:实时显示当前头部姿态(旋转) │ │ │ │ ├── 眼睛区域放大视图:显示瞳孔位置 │ │ │ │ ├── 疲劳度仪表盘:PERCLOS百分比指示器,红色区域 │ │ │ │ └── 分心热力图:显示视线分布区域 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────────┘
2.2 DMS核心代码实现
// ============================================================================
// DMS场景:疲劳检测 + 分心检测
// ============================================================================
typedef struct {
// 面部关键点 (68个)
float landmarks[68][2];
// 头部姿态
float pitch; // 俯仰角(低头为正)
float yaw; // 偏航角(右转为正)
float roll; // 翻滚角
// 眼睛状态
float ear_left; // 左眼纵横比
float ear_right; // 右眼纵横比
int is_blink; // 是否闭眼
// 视线
float gaze_x, gaze_y; // 视线方向(像素坐标)
// 统计
int blink_count_1min; // 1分钟内眨眼次数
float perclos; // 闭眼时间占比
int distracted_seconds; // 分心持续时间(秒)
} DriverState;
// 🔥 眼睛纵横比计算
float compute_ear(float landmarks[68][2], int eye_indices[6])
{
// 计算垂直距离
float v1 = distance(landmarks[eye_indices[1]], landmarks[eye_indices[5]]);
float v2 = distance(landmarks[eye_indices[2]], landmarks[eye_indices[4]]);
// 计算水平距离
float h = distance(landmarks[eye_indices[0]], landmarks[eye_indices[3]]);
// EAR公式
return (v1 + v2) / (2.0 * h);
}
// 🔥 DMS核心处理
void dms_process_frame(DriverState* state, YOLODetection* detections, int det_count)
{
// 1. 提取68个关键点
extract_landmarks(detections, state->landmarks);
// 2. 计算眼睛EAR
int left_eye[] = {36, 37, 38, 39, 40, 41};
int right_eye[] = {42, 43, 44, 45, 46, 47};
state->ear_left = compute_ear(state->landmarks, left_eye);
state->ear_right = compute_ear(state->landmarks, right_eye);
// 3. 闭眼检测
float ear_avg = (state->ear_left + state->ear_right) / 2;
state->is_blink = (ear_avg < 0.2);
// 4. 统计PERCLOS(1分钟滑动窗口)
update_perclos(state, state->is_blink);
// 5. 头部姿态估计 (PnP求解)
solve_pnp(state->landmarks, &state->pitch, &state->yaw, &state->roll);
// 6. 疲劳检测
if (state->perclos > 0.3) {
// 严重疲劳
qt_show_alert("⚠️ 严重疲劳!请立即休息!", QColor(255,0,0));
can_send_haptic(200); // 座椅震动
// 触发AEB辅助(降低车速)
can_send_speed_limit(60);
} else if (state->perclos > 0.15) {
// 轻度疲劳
qt_show_alert("⚠️ 检测到疲劳,请注意休息", QColor(255,165,0));
qt_audio_alert("请注意休息");
}
// 7. 分心检测
if (fabs(state->yaw) > 45 || state->pitch > 30) {
state->distracted_seconds += 0.033; // 每帧33ms
if (state->distracted_seconds > 2) {
qt_show_alert("⚠️ 请专注驾驶!", QColor(255,0,0));
qt_haptic_feedback(100); // 方向盘震动
qt_audio_alert("请专注驾驶");
}
} else {
state->distracted_seconds = 0;
}
}
三、场景三:BSD(盲区检测 + 变道辅助)
3.1 BSD完整数据流
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ BSD场景完整数据流(盲区检测 + 变道辅助) │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 【侧后视摄像头采集】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 左/右后视镜广角摄像头 → MIPI CSI → V4L2 → 盲区图像(1280×720) │ │ │ │ ├── 覆盖范围: 车后3-15米,侧向0-3.5米 │ │ │ │ └── 校准: 鸟瞰图变换(BEV) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【NPU推理 - YOLOv5s车辆+行人检测】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 盲区目标检测: │ │ │ │ ├── 左盲区: 检测到摩托车,坐标[200,450,280,520],距离4.2米 │ │ │ │ ├── 右盲区: 检测到轿车,坐标[980,420,1100,510],距离3.8米 │ │ │ │ └── 后方远处: 检测到卡车,坐标[640,380,800,480],距离12.5米 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【盲区判断 + 变道意图识别】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ 盲区定义: │ │ │ │ ├── 左盲区: X < 0.3倍图像宽度? 且 距离<5米 → 摩托车在左盲区 │ │ │ │ └── 右盲区: X > 0.7倍图像宽度? 且 距离<5米 → 轿车在右盲区 │ │ │ │ │ │ │ │ 变道意图识别: │ │ │ │ ├── 左转向灯信号(CAN) OR 方向盘转角>30°向左 → 意图左变道 │ │ │ │ └── 右转向灯信号(CAN) OR 方向盘转角>30°向右 → 意图右变道 │ │ │ │ │ │ │ │ BSD预警逻辑: │ │ │ │ if (意图左变道 AND 左盲区有目标) → 左侧后视镜LED闪烁 + 声音"左侧有车" │ │ │ │ if (意图右变道 AND 右盲区有目标) → 右侧后视镜LED闪烁 + 声音"右侧有车" │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 【3D可视化 - 360°环视+盲区预警】 │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Qt3D场景: │ │ │ │ ├── 车辆模型: 位于场景中心 │ │ │ │ ├── 盲区区域: 半透明红色立方体(左/右后侧) │ │ │ │ ├── 检测目标: 3D立方体显示位置(距离+角度) │ │ │ │ ├── 目标轨迹: 历史轨迹线(后车接近趋势) │ │ │ │ └── 变道辅助: 绿色/红色箭头指示是否可以变道 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────────┘
3.2 BSD核心代码实现
// ============================================================================
// BSD场景:盲区检测 + 变道辅助
// ============================================================================
typedef struct {
int side; // 0=左, 1=右
float distance_m; // 距离本车(米)
float angle_deg; // 相对于本车的角度(度)
float relative_velocity_ms; // 相对速度(米/秒)
int class_id; // 车辆/行人/摩托车
int in_blind_zone; // 是否在盲区内
} BSDTarget;
typedef struct {
BSDTarget targets[16];
int target_count;
// 变道意图
int left_turn_signal; // 左转向灯(0/1)
int right_turn_signal; // 右转向灯(0/1)
float steering_angle; // 方向盘转角(度)
int intention_left; // 意图左变道
int intention_right; // 意图右变道
// 盲区状态
int left_blind_occupied; // 左盲区有目标
int right_blind_occupied; // 右盲区有目标
// 预警输出
int warn_left; // 左侧预警
int warn_right; // 右侧预警
} BSDContext;
// 🔥 BSD核心处理
void bsd_process_frame(BSDContext* ctx, YOLODetection* detections, int det_count)
{
// 1. 读取CAN信号获取变道意图
ctx->left_turn_signal = can_read(MSG_TURN_SIGNAL) == LEFT;
ctx->right_turn_signal = can_read(MSG_TURN_SIGNAL) == RIGHT;
ctx->steering_angle = can_read(MSG_STEERING_ANGLE);
// 2. 变道意图判断
ctx->intention_left = (ctx->left_turn_signal || ctx->steering_angle > 30);
ctx->intention_right = (ctx->right_turn_signal || ctx->steering_angle < -30);
// 3. 解析检测结果,判断盲区
ctx->left_blind_occupied = 0;
ctx->right_blind_occupied = 0;
ctx->target_count = 0;
for (int i = 0; i < det_count; i++) {
BSDTarget* target = &ctx->targets[ctx->target_count];
// 判断在左盲区还是右盲区
float center_x = (detections[i].x1 + detections[i].x2) / 2;
if (center_x < BLIND_ZONE_LEFT_THRESHOLD) {
target->side = 0; // 左
// 测距: 基于检测框底部位置计算实际距离
float bottom_y = detections[i].y2;
target->distance_m = distance_from_bottom_y(bottom_y);
if (target->distance_m < BLIND_ZONE_DISTANCE) {
ctx->left_blind_occupied = 1;
target->in_blind_zone = 1;
}
} else if (center_x > BLIND_ZONE_RIGHT_THRESHOLD) {
target->side = 1; // 右
target->distance_m = distance_from_bottom_y(detections[i].y2);
if (target->distance_m < BLIND_ZONE_DISTANCE) {
ctx->right_blind_occupied = 1;
target->in_blind_zone = 1;
}
}
target->class_id = detections[i].class_id;
ctx->target_count++;
}
// 4. 预警逻辑
ctx->warn_left = 0;
ctx->warn_right = 0;
if (ctx->intention_left && ctx->left_blind_occupied) {
ctx->warn_left = 1;
// 触发左侧后视镜LED闪烁
gpio_set(LED_LEFT_MIRROR, 1);
// 声音提示
qt_audio_alert("左侧有车辆,请注意");
// 方向盘震动
qt_haptic_feedback(100);
}
if (ctx->intention_right && ctx->right_blind_occupied) {
ctx->warn_right = 1;
gpio_set(LED_RIGHT_MIRROR, 1);
qt_audio_alert("右侧有车辆,请注意");
qt_haptic_feedback(100);
}
// 5. 3D场景更新
update_3d_blind_zone(ctx);
}
四、总结:
| 场景 | YOLO做了什么 | 后处理做了什么 | 3D可视化做了什么 | 最终给驾驶员什么 |
|---|---|---|---|---|
| ADAS | 检测前车、行人 | 单目测距+TTC+车道关联 | 仪表盘显示前车距离+预警 | 声音+仪表盘红色闪烁+自动刹车 |
| DMS | 检测人脸+68个关键点 | EAR+头部姿态+视线估计 | 3D头部模型+疲劳仪表盘 | 声音告警+座椅震动+自动降速 |
| BSD | 检测盲区车辆/行人 | 盲区判断+变道意图识别 | 360°环视+盲区高亮 | 后视镜LED+声音+方向盘震动 |
总结:YOLOv5s只是把图像里的物体找出来了,但真正让ADAS/DMS/BSD工作的,是后续的场景理解、风险评估和决策执行,最后通过Qt 3D可视化让驾驶员直观看到危险在哪里。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)