【瑞萨AI挑战赛】基于RA8P1 NPU的人脸检测模型部署(NPU + CUE摄像头 + MIPI LCD)
本帖最后由 HonestQiaoQiao 于 2026-3-16 02:23 编辑> 记录一下踩坑经历,希望能帮到同样想玩这块板子的朋友。
## 前言
**RT-Thread Titan Board (RA8P1)**,这块板子最吸引我的就是内置了 **Arm Ethos-U55 NPU**,可以跑神经网络加速。官方有个 `Titan_npu_ai_face_detection` 的例程,但是是基于 **RGB LCD (800x480)** 的。而我手上只有 **MIPI LCD (480x800)**,这就尴尬了...
于是有了这篇折腾记录:**如何把官方的人脸检测例程移植到 MIPI LCD 上**。
## 官方文档参考
首先贴一下官方文档,基础配置看这里:
- (https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/start/index_zh.html)
- (https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/npu_vision/Titan_npu_ai_face_detection/README_zh.html)
YOLO-Fastest模型的详细训练部署流程,我参考了这篇瑞萨官方合作文章:
- (https://mp.weixin.qq.com/s?__biz=MzIwMzA2NzI1Ng==&mid=2655223642&idx=1&sn=2648d421bc5e1268005d04a38ba6d2d8)
## 项目结构
我新建了一个项目叫 `Titan_npu_ai_face_detection_mipi_lcd`,结构如下:
```
Titan_npu_ai_face_detection_mipi_lcd/
├── src/
│ ├── hal_entry.c # 主程序入口(重头戏)
│ ├── models/ # NPU模型文件
│ │ ├── model.c # 模型推理接口
│ │ ├── sub_0000_model_data.c# 转换后的YOLO模型数据(约2.6MB)
│ │ └── ...
│ ├── yolo/ # YOLO后处理代码
│ │ ├── yolo_rtthread.c# 检测框解码、NMS等
│ │ └── yolo_rtthread.h
│ └── SConscript # 编译脚本
├── board/ports/
│ ├── mipi_lcd/ # MIPI LCD初始化
│ └── lcd_port.h # LCD参数配置
└── configuration.xml # FSP配置(关键!)
```
## 第一步:环境准备
### 1.1 硬件清单
- **开发板**: RA8P1开发板(Titan Board)
- **显示屏**: MIPI LCD (480x800) ⚠️ 注意不是官方的RGB LCD
- **摄像头**: OV5640
**【图2】硬件连接图**
!(assets/366885094efe1ddce331e069e319e279.jpg)
### 1.2 软件工具
- **RT-Thread Studio**(建议最新版)
- **Python 3.9-3.11**(用于模型训练转换)
- **Miniconda**(管理Python环境)
### 1.3 创建RT-Thread Studio工程
1. 打开 RT-Thread Studio,导入 Titan-Board SDK
2. 创建示例工程,选择 `Titan_npu_ai_face_detection` 作为基础
3. 或者直接基于 `Titan_display_ceu_camera_mipi_lcd` 来改
**【图3】RT-Thread Studio创建工程截图**

## 第二步:FSP配置(最容易踩坑的地方)
打开 `configuration.xml`,需要配置的东西真不少:
### 2.1 HyperRAM配置(帧缓冲区)
MIPI LCD需要HyperRAM作为帧缓冲,新建 `r_ospi_b` stack:
- Name: `g_ospi1`
- Unit: `1`
- Channel: `0`
- Command Set Table: `g_hyper_ram_commands`
记得在 **Pins** 标签页配置OSPI引脚,驱动能力设为 **H**,数据线上拉。
**【图4】HyperRAM配置截图**

### 2.2 MIPI DSI + GLCDC配置
这是MIPI屏幕的核心配置:
**关键区别来了!** 官方例程是800x480横屏,我们要改成480x800竖屏:
1. 新建 `r_glcdc` stack:
- Name: `g_display0`
- Input: 480x800, RGB565
- Output: MIPI DSI接口
2. 新建 `r_mipi_dsi` stack:
- Name: `g_mipi_dsi0`
- Continuous Clock: Enable
- Number of Lanes: 2
3. 新建 `r_mipi_phy` stack(MIPI物理层)
**【图5】GLCDC配置对比图**

### 2.3 CEU摄像头配置
摄像头还是OV5640,用CEU接口:
- 新建 `r_ceu` stack,Name: `g_ceu_qvga`
- 分辨率: 640x480
- 格式: RGB565
- 数据位: 8-bit
**【图6】CEU配置截图**
!(https://www.eefocus.com/forum/data/attachment/forum/202603/16/014351q3quyqfy88zc0hq3.png)
### 2.4 NPU组件配置(重点!)
这是人脸检测的核心,需要添加以下组件:
| 组件 | 作用 |
|------|------|
| CMSIS-NN | Arm NN Library |
| Ethos-U Core Driver | NPU驱动 |
| TFLM Core Lib | TensorFlow Lite Micro |
| Flatbuffers | 模型序列化 |
| rm_ethosu | Renesas NPU Wrapper |
在 **Stacks** 标签页新建 `Google TFLM Core Lib` stack,它会自动拉取依赖。
**【图7】NPU组件配置图**
!(https://www.eefocus.com/forum/data/attachment/forum/202603/16/014440rfduozjx5llkjlzp.png)
### 2.5 D/AVE 2D配置
用来绘制检测框的硬件加速:
- 新建 `r_drw` stack
- 关联 `dave2d_driver`
配置完后,点击 **Generate Project Content**。如果看到 `g_ospi1` 还是红色的,别慌,可能是引脚配置冲突,检查一遍引脚分配。
**【图8】Generate后的ra_gen目录**
!(https://www.eefocus.com/forum/data/attachment/forum/202603/16/014516k6xxxxx4xvxmv594.png)
## 第三步:MIPI LCD适配
这是整个项目最折腾的部分。官方例程是800x480横屏,我是480x800竖屏,怎么适配?
### 3.1 方案对比
我一开始想直接拉伸,结果人脸都变形了,辣眼睛...
| 方案 | 显示尺寸 | 黑边 | 屏幕利用率 |
|------|---------|------|-----------|
| 方案A:等比例缩放 | 480x360 | 上下各220px | 45% ❌ |
| 方案B:旋转90度 | 480x640 | 上下各80px | 80% ✅ |
**果断选方案B!**
### 3.2 代码实现
在 `hal_entry.c` 里添加旋转函数:
```c
// 软件旋转90度:640x480 -> 480x640
static void rotate_image_90_cw(const uint16_t *src, uint16_t *dst, int src_w, int src_h)
{
// 顺时针90度: dst = src
for (int y = 0; y < src_h; y++) {
for (int x = 0; x < src_w; x++) {
int src_idx = y * src_w + x;
int dst_x = y;// 原y变为新x
int dst_y = src_w - 1 - x;// 原x反转后变为新y
int dst_idx = dst_y * src_h + dst_x;
dst = src;
}
}
}
```
显示参数定义:
```c
#define CAM_WIDTH 640
#define CAM_HEIGHT 480
#define DISP_WIDTH 480 // 旋转后宽度
#define DISP_HEIGHT 640 // 旋转后高度
#define DISP_Y_OFFSET 80 // 垂直居中偏移
```
主循环里先旋转再显示:
```c
while(1)
{
sensor_snapshot(&sensor, g_image_rgb565_sdram_buffer, 0);
// 软件旋转90度
rotate_image_90_cw((uint16_t*)g_image_rgb565_sdram_buffer,
g_rotated_buffer, CAM_WIDTH, CAM_HEIGHT);
// NPU推理(还是用原始图像)
rgb565_to_gray_resize_192_and_quantization(...);
RunModel(false);
// 显示旋转后的图像 + 检测框
lcd_draw_jpg_with_frame(0, 0, g_rotated_buffer, DISP_WIDTH, DISP_HEIGHT,
argb, thickness, pool, out_n);
}
```
### 3.3 检测框坐标的映射
图像旋转了,检测框坐标也要跟着变。从640x480映射到480x640:
```c
// 旋转公式:new_x = y, new_y = 640 - x
for(int i=0; i<box_num; i++){
int x1 = boxes.y1 + DISP_Y_OFFSET; // y变为x,加偏移
int y1 = CAM_WIDTH - boxes.x2; // 640-x2
int x2 = boxes.y2 + DISP_Y_OFFSET;
int y2 = CAM_WIDTH - boxes.x1; // 640-x1
// 用D/AVE 2D画框
d2_renderline(hdl, x1, y1, x2, y1, thickness, flags);
// ... 画四条边
}
```
## 第四步:YOLO-Fastest模型与部署
本项目使用的AI模型是基于YOLO-Fastest的人脸检测模型。关于模型的详细训练、转换和部署流程,请参考这篇非常详细的教程:
📎 (https://mp.weixin.qq.com/s?__biz=MzIwMzA2NzI1Ng==&mid=2655223642&idx=1&sn=2648d421bc5e1268005d04a38ba6d2d8)
该文章完整覆盖了从**数据采集**→**模型训练**→**模型转换(darknet→tflite→ruhmi)**→**嵌入式部署**的全流程,并提供了网盘资源和培训视频。
### 4.1 模型基本信息
```
模型: YOLO-Fastest (人脸检测版本)
输入: 192x192 灰度图 (INT8量化)
输出: 人脸检测框坐标(x1,y1,x2,y2) + 置信度
推理时间: 约25ms/帧 (NPU加速)
模型大小: 约2.6MB (INT8量化后)
```
### 4.2 模型文件说明
将从上述教程转换得到的模型文件拷贝到工程 `src/models` 目录:
- `model.c` - 模型推理接口
- `sub_0000_model_data.c` - 模型权重数据(约2.6MB)
- `sub_0000_tensors.c` - 张量定义
- `sub_0000_invoke.c` - 模型调用
- `sub_0000_command_stream.c` - 命令流
### 4.3 后处理代码解析
在 `yolo_rtthread.c` 里实现了:
```c
typedef struct {
int16_t x1, y1, x2, y2;// 检测框坐标
float score; // 置信度
uint8_t cls; // 类别(人脸就是0)
} det_box_t;
```
后处理流程:
1. **decode_output_layer**: 把模型输出的raw data解码成检测框坐标
2. **nms_filter**: 去除重叠的检测框(IoU阈值0.45)
3. **sort_boxes_by_score**: 按置信度排序
## 第五步:踩坑记录
### 坑1:g_ospi1红色警告
- **现象**: FSP中g_ospi1显示红色
- **原因**: OSPI引脚驱动能力没设成H
- **解决**: Pin配置里把OM_1_SIO0~OM_1_SIO7设为上拉+H驱动
**【图11】OSPI引脚配置正确截图**
!(https://www.eefocus.com/forum/data/attachment/forum/202603/16/020024y97isy0s8szv3kb4.png)
### 坑2:检测框位置偏移
- **现象**: 检测框画在人脸旁边
- **原因**: 坐标映射公式写错了,旋转后的高度是640不是480
- **解决**: `new_y = 640 - x` 别写成 `480 - x`
### 坑3:软件旋转太慢
- **现象**: 帧率掉到10fps以下
- **优化**: 用指针代替数组索引,减少乘法运算
### 坑4:MIPI屏幕不亮
- **现象**: 程序运行但屏幕黑屏
- **原因**: MIPI DSI时钟配置不对
- **解决**: 检查Continuous Clock设置,确保MIPI PHY正确初始化
## 运行效果
烧录后,摄像头对准人脸,LCD上能看到:
- 几乎满屏(480x640)
- 绿色框框实时跟踪人脸
- 串口打印检测框数量和耗时
```
detect box num: 1
Time elapsed: 35 ms
```
**性能数据:**
- 推理时间: 约25ms/帧(NPU加速)
- 整体帧率: 15-20fps(包括摄像头采集、显示、旋转)
- 模型大小: 约2.6MB(INT8量化后)
**【图18】最终运行效果**

## 总结
这次移植最大的收获是理解了 **FSP配置和硬件抽象层** 的关系。官方例程给的是RGB LCD,移植到MIPI LCD主要改三部分:
1. **FSP配置**: 把GLCDC改成MIPI DSI输出
2. **显示适配**: 横竖屏转换(旋转90度比缩放更好)
3. **坐标映射**: 检测框坐标跟着图像一起变
## 参考资料
1. (https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/start/index_zh.html)
2. (https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/npu_vision/Titan_npu_ai_face_detection/README_zh.html)
3. (https://mp.weixin.qq.com/s?__biz=MzIwMzA2NzI1Ng==&mid=2655223642&idx=1&sn=2648d421bc5e1268005d04a38ba6d2d8)
4. RT-Thread 官方社区:https://club.rt-thread.org/
页:
[1]