2025 电赛 C 题「发挥 3」
带编号正方形识别 & 边长测量
香橙派 + 自训 PyTorch 模型 + C++ 零拷贝调用
目录
赛题需求与官方指标
技术路线全景图
硬件平台 & 接线
软件架构(C++ → Python → 自训模型)
模型训练回顾
端到端算法流程
6.1 图像预处理 & ROI 提取
6.2 Python 推理脚本(best_epoch_weights.pth
)
6.3 C++ 零拷贝调用 Python
6.4 编号-边长映射
6.5 结果回显 & 串口输出
关键代码深度剖析
编译 & 部署指南
现场调试手册
性能 Benchmark
FAQ & 常见坑
开源仓库 & 后续拓展
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 模型训练回顾
6 端到端算法流程
6.1 图像预处理 & ROI 提取
Rect roi = detectOuterShapes ( frame, imgContour) ;
Mat roiImg = frame ( roi) ;
vector< RotatedRect> squares = findSquares ( roiImg) ;
for ( const auto & sq : squares) {
Mat square = cropSquare ( roiImg, sq) ;
resize ( square, square, Size ( 64 , 64 ) ) ;
sendToPython ( square) ;
}
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 编号-边长映射
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
./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 开源仓库 & 后续拓展
总结 : 本文在 香橙派 上实现了 “C++ 主程序 → Python 零拷贝 → 自训 PyTorch 模型” 的完整链路,现场实测 1.5 s 完成编号识别 + 边长测量,误差 ≤ 0.05 cm 。欢迎 Star、Issue、PR!