3. 图像分类(CIFAR-10)

任务目标

在STM32平台上部署一个针对CIFAR-10数据集的CNN图像分类模型,实现对32×32 RGB输入图像的实时推理,输出10个类别(飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车)中的预测标签。

环境与依赖

  • 硬件:STM32H743ZI(或同等性能Cortex-M7,建议≥1MB RAM)
  • 软件:STM32CubeIDE 1.13+,X-CUBE-AI 9.0+
  • 模型:CIFAR-10量化CNN(.tflite或.onnx格式),输入1×32×32×3,输出1×10
  • 图像源:通过UART接收32×32 RGB原始像素数据(或板载摄像头采集后缩放)

实现方案

使用X-CUBE-AI将预训练CNN模型转换为STM32可调用的C库。核心代码包含:模型初始化、输入张量填充(像素归一化至[0,1])、推理调用、输出后处理(argmax获取类别索引)。UART接收完整图像帧后触发推理,结果通过UART以文本形式回传。

关键完整代码

/* main.c 核心推理逻辑 */
#include "ai_platform.h"
#include "network.h"
#include "cifar10_weights.h"  // X-CUBE-AI生成的权重头文件

#define IMG_SIZE  (32*32*3)
#define NUM_CLASS 10

static ai_handle network = AI_HANDLE_NULL;
static ai_network_report report;
static ai_buffer ai_input[1], ai_output[1];
static ai_i8 input_data[IMG_SIZE];   // 量化输入
static ai_i8 output_data[NUM_CLASS]; // 量化输出

// CIFAR-10类别名称
static const char* class_names[NUM_CLASS] = {
    "airplane", "automobile", "bird", "cat", "deer",
    "dog", "frog", "horse", "ship", "truck"
};

// 初始化AI网络
int ai_init(void) {
    if (ai_network_create(&network, &report) != 0) return -1;
    
    ai_input[0] = (ai_buffer){AI_BUFFER_FORMAT_Q, 0, 0, {0}, {1,32,32,3}, {0}};
    ai_output[0] = (ai_buffer){AI_BUFFER_FORMAT_Q, 0, 0, {0}, {1,10}, {0}};
    
    // 绑定输入输出缓冲区
    ai_input[0].data = input_data;
    ai_output[0].data = output_data;
    
    if (ai_network_init(network, &report) != 0) return -2;
    return 0;
}

// 执行推理并返回类别索引
int ai_predict(uint8_t* rgb_pixels) {
    // 像素值从uint8 [0,255] 量化为int8 [-128,127]
    for (int i = 0; i < IMG_SIZE; i++) {
        input_data[i] = (ai_i8)((rgb_pixels[i] - 128) & 0xFF);
    }
    
    if (ai_run(network, ai_input, ai_output) != 0) return -1;
    
    // argmax获取最高置信度类别
    int max_idx = 0;
    ai_i8 max_val = output_data[0];
    for (int i = 1; i < NUM_CLASS; i++) {
        if (output_data[i] > max_val) {
            max_val = output_data[i];
            max_idx = i;
        }
    }
    return max_idx;
}

// UART接收回调(中断中置标志,主循环处理)
volatile uint8_t frame_ready = 0;
uint8_t frame_buffer[IMG_SIZE];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart == &huart1) {
        frame_ready = 1;
        HAL_UART_Receive_IT(&huart1, frame_buffer, IMG_SIZE);
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_UART1_Init();
    
    if (ai_init() != 0) {
        Error_Handler();
    }
    
    HAL_UART_Receive_IT(&huart1, frame_buffer, IMG_SIZE);
    
    while (1) {
        if (frame_ready) {
            frame_ready = 0;
            int cls = ai_predict(frame_buffer);
            if (cls >= 0) {
                char msg[64];
                int len = sprintf(msg, "Predicted: %s\r\n", class_names[cls]);
                HAL_UART_Transmit(&huart1, (uint8_t*)msg, len, 1000);
            }
        }
    }
}

运行与验证

  1. 模型转换:在CubeIDE中启用X-CUBE-AI,导入CIFAR-10量化CNN模型,生成network.c/h及权重文件
  2. 编译烧录:编译工程并烧录至STM32H743
  3. 发送测试图像:通过UART1(115200-8-N-1)发送3072字节原始RGB数据(按行优先,R,G,B顺序)
  4. 观察输出:开发板应返回Predicted: 类别名文本

通过条件:使用CIFAR-10测试集图像(如飞机、猫各1张)发送,返回的类别与图像内容一致。若使用官方测试集前10张图像,正确率应≥80%(模型量化精度损失后)。

常见问题

  • 内存不足:确认模型量化至int8,且STM32H743的RAM≥1MB;若仍不足,减小批处理至1并关闭调试打印
  • 输出全为0:检查输入量化是否正确——像素值需减去128再转为int8
  • UART接收超时:确保发送端一次性连续发送3072字节,中间无间隔;或增大DMA缓冲
UART接收 32×32×3 预处理 uint8→int8 CNN推理 X-CUBE-AI 后处理 argmax→类别 "cat"