HonestQiaoQiao 发表于 7 天前

【瑞萨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创建工程截图**
![](assets/17735899831747.jpg)

## 第二步: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配置截图**
![](assets/17735900411998.jpg)

### 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配置对比图**
![](assets/17735901764243.jpg)

### 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】最终运行效果**
![.jpg](https://www.eefocus.com/forum/data/attachment/forum/202603/16/022144nq5aabbjaqubzqba.jpg)



## 总结

这次移植最大的收获是理解了 **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]
查看完整版本: 【瑞萨AI挑战赛】基于RA8P1 NPU的人脸检测模型部署(NPU + CUE摄像头 + MIPI LCD)