2025 电赛 C 题 发挥3 带数字编号的正方形识别& 边长测量

发布于:2025-08-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

2025 电赛 C 题「发挥 3」

带编号正方形识别 & 边长测量

香橙派 + 自训 PyTorch 模型 + C++ 零拷贝调用


目录

  1. 赛题需求与官方指标
  2. 技术路线全景图
  3. 硬件平台 & 接线
  4. 软件架构(C++ → Python → 自训模型)
  5. 模型训练回顾
  6. 端到端算法流程
    • 6.1 图像预处理 & ROI 提取
    • 6.2 Python 推理脚本(best_epoch_weights.pth
    • 6.3 C++ 零拷贝调用 Python
    • 6.4 编号-边长映射
    • 6.5 结果回显 & 串口输出
  7. 关键代码深度剖析
  8. 编译 & 部署指南
  9. 现场调试手册
  10. 性能 Benchmark
  11. FAQ & 常见坑
  12. 开源仓库 & 后续拓展

1 赛题需求与官方指标

指标 要求
目标 边长 6 cm~12 cm 的带编号正方形
输出 指定编号的正方形边长,误差 ≤ 0.5 cm
输入 单张 640×480 图像
限制 5 s 内完成,禁止 PC,一键启动
额外 编号由串口屏下发

2 技术路线全景图

在这里插入图片描述

3 硬件平台 & 接线

模块 接口 引脚 供电
OV5640 MIPI-CSI CAM1 3.3 V
OLED SSD1306 I²C-1 PB8 PB9 3.3 V
串口屏 UART3 TX PA9 / RX PA10 3.3 V
按键 GPIO PC13 3.3 V

4 软件架构

2025-C-NumberSquare/
├── src/
│   ├── main.cpp          # C++ 主程序
│   ├── python_wrapper.cpp # 调 Python
│   └── python/
│       ├── infer.py      # 加载 best_epoch_weights.pth(模型不开源了,可以来xianyu:)
│       └── dataset.py    # 训练脚本(留档)
├── models/
│   └── best_epoch_weights.pth
├── build/                # CMake 输出
└── scripts/
    └── build.sh          # 一键编译 & 运行

5 模型训练回顾

  • 数据集
    • 200张 64×64 正方形截图
    • 数字 0-9,字体 Arial,字号 20-36
  • 网络:MobileNetV2-0.5×
  • 损失:CrossEntropy + Label Smoothing
  • 训练
    python train.py --epochs 50 --batch 128 --lr 1e-3
    
  • 精度
    • 训练集 99.7 %
    • 验证集 97.8 %
  • 模型大小:7.9 MB

6 端到端算法流程

6.1 图像预处理 & ROI 提取

// 1. 外轮廓提取 (同发挥 1)
Rect roi = detectOuterShapes(frame, imgContour);
Mat roiImg = frame(roi);

// 2. 正方形内 ROI
vector<RotatedRect> squares = findSquares(roiImg);
for(const auto& sq : squares){
    Mat square = cropSquare(roiImg, sq);
    resize(square, square, Size(64,64));
    sendToPython(square);   // 64×64 送入推理
}

6.2 Python 推理脚本(零拷贝)

python/infer.py

import torch
import cv2
import numpy as np

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = torch.load("models/best_epoch_weights.pth", map_location=device)
model.eval()

def infer_one(img_bgr):
    img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    img = img.astype(np.float32) / 255.0
    tensor = torch.from_numpy(img).permute(2,0,1).unsqueeze(0)
    with torch.no_grad():
        logits = model(tensor)
        pred = logits.argmax(1).item()
    return pred

6.3 C++ 零拷贝调用 Python

src/python_wrapper.cpp

#include <Python.h>
#include <opencv2/opencv.hpp>

class PyInference{
public:
    PyInference(){
        Py_Initialize();
        PyRun_SimpleString("import sys; sys.path.append('./python')");
        pModule = PyImport_ImportModule("infer");
        pFunc   = PyObject_GetAttrString(pModule, "infer_one");
    }
    int run(const cv::Mat& img){
        PyObject* pArgs = PyTuple_New(1);
        npy_intp dims[3] = {img.rows, img.cols, 3};
        PyObject* pValue = PyArray_SimpleNewFromData(3, dims, NPY_UINT8, img.data);
        PyTuple_SetItem(pArgs, 0, pValue);
        PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
        int num = PyLong_AsLong(pResult);
        Py_DECREF(pArgs); Py_DECREF(pResult);
        return num;
    }
    ~PyInference(){ Py_Finalize(); }
private:
    PyObject *pModule, *pFunc;
};

6.4 编号-边长映射

// 建立 map<编号, 边长>
unordered_map<int, double> id2len;
for(const auto& sq : squares){
    int id = pyInf.run(cropSquare(sq));
    double len = calSide(sq) * scale;
    id2len[id] = len;
}

6.5 结果回显 & 串口输出

int target_id = uart_get_target();          // 来自串口屏
double edge_cm = id2len[target_id];
printf("编号 %d 边长 %.2f cm\n", target_id, edge_cm);

7 关键代码深度剖析

7.1 正方形内截图

Mat cropSquare(const Mat& src, const RotatedRect& r){
    Mat M = getRotationMatrix2D(r.center, r.angle, 1.0);
    Mat rotated;
    warpAffine(src, rotated, M, src.size());
    Rect roi = r.boundingRect() & Rect(0,0,src.cols,src.rows);
    return rotated(roi);
}

7.2 Python 零拷贝数据传递

  • 无内存拷贝PyArray_SimpleNewFromData 直接包裹 cv::Mat 数据指针
  • 线程安全:C++ 主线程 + Python GIL 互斥

8 编译 & 部署指南

8.1 依赖安装

sudo apt update
sudo apt install build-essential cmake libopencv-dev python3 python3-pip
pip3 install torch torchvision numpy

8.2 一键脚本

git clone https://github.com/YourTeam/2025-C-NumberSquare.git
cd 2025-C-NumberSquare
chmod +x scripts/build.sh scripts/run.sh
./scripts/build.sh   # 约 40 s
./scripts/run.sh     # 自动加载模型 + 串口屏

9 现场调试手册

现象 根因 解决
“ModuleNotFoundError: torch” Python 环境缺失 pip3 install torch
数字识别 0 % 图像方向错误 cropSquare 加旋转
串口屏无回显 波特率不匹配 统一 115200
内存溢出 640×480 全图 仅处理 ROI

10 性能 Benchmark

场景 编号真值 边长真值 识别号 测量边长 误差 耗时
单正方形 7 cm 3 7.00 cm 3 7.02 cm 0.02 cm 1.4 s
两正方形并排 5 9.00 cm 5 9.05 cm 0.05 cm 1.5 s
重叠 20 % 8 6.50 cm 8 6.48 cm 0.02 cm 1.6 s

11 FAQ & 常见坑

问题 原因 解决
Python 启动慢 冷加载 预加载模型 torch.jit.script
数字 6/9 混淆 样本不足 再采集 200 张
图像灰化 BGR→RGB 顺序 统一 RGB 送入 PyTorch

12 开源仓库 & 后续拓展

  • GitHub:github.com/langhaofu/2025-C-NumberSquare
  • 数据集 & 训练脚本:python/train.py
  • 香橙派镜像:2025-C-number-ubuntu-22.04-lite.img.xz
  • 未来:
    • ONNX 导出torch.onnx.export
    • TensorRT FP16 → Jetson Nano 迁移

总结
本文在 香橙派 上实现了 “C++ 主程序 → Python 零拷贝 → 自训 PyTorch 模型” 的完整链路,现场实测 1.5 s 完成编号识别 + 边长测量,误差 ≤ 0.05 cm。欢迎 Star、Issue、PR!


网站公告

今日签到

点亮在社区的每一天
去签到