基于STM32MP157与OpenCV的嵌入式Linux人脸识别系统从零到一实战指南

张开发
2026/4/15 0:12:15 15 分钟阅读

分享文章

基于STM32MP157与OpenCV的嵌入式Linux人脸识别系统从零到一实战指南
1. 从零搭建STM32MP157开发环境第一次拿到STM32MP157开发板时我对着这个巴掌大的小玩意儿发了半天呆。作为嵌入式Linux的新手最头疼的就是如何让这块开发板活起来。下面我就用最直白的方式带你完成从开箱到系统启动的全过程。开发板刚到手需要准备以下硬件STM32MP157开发板建议选择带屏幕的套件5V/3A的Type-C电源8GB以上的MicroSD卡USB转串口调试模块网线用于网络调试软件准备有个小技巧建议直接在Ubuntu 20.04物理机上操作避免虚拟机可能遇到的USB权限问题。我试过在Windows下用虚拟机开发结果在烧录系统时各种报错最后不得不重装双系统。烧写系统镜像时要注意# 查看SD卡设备名千万小心别选错磁盘 lsblk # 使用dd命令烧录以/dev/sdb为例 sudo dd ifstm32mp157-image-qt.img of/dev/sdb bs4M statusprogress这个等待过程大概需要5-10分钟期间千万别拔卡。我第一次操作时没耐心等进度条走完结果系统启动时卡在uboot阶段排查了半天才发现是镜像烧写不完整。2. 构建嵌入式Linux系统Yocto项目是构建定制化Linux系统的利器但它的学习曲线确实陡峭。我花了整整三天才搞明白bitbake的工作原理这里把关键步骤总结给你。首先安装必要的依赖sudo apt-get install gawk wget git-core diffstat unzip \ texinfo gcc-multilib build-essential chrpath socat \ libsdl1.2-dev xterm然后获取ST官方提供的layersrepo init -u https://github.com/STMicroelectronics/oe-manifest.git \ -b dunfell -m stm32mp1-20-10-20.xml repo sync配置构建环境时有个坑要注意DISTROopenstlinux-weston MACHINEstm32mp1 source layers/meta-st/scripts/envsetup.sh这个命令必须在项目根目录执行我第一次在子目录运行导致后续构建失败。构建核心命令很简单bitbake st-image-weston但实际执行时会下载约15GB的源码建议挂代理我的MacBook Pro 16寸跑了近6小时。期间如果网络中断可以用以下命令恢复bitbake -c cleanall 中断的包名 bitbake st-image-weston3. OpenCV交叉编译实战在x86平台用apt安装OpenCV只要一行命令但在ARM平台交叉编译就是另一回事了。我参考了官方文档和十几个博客最终总结出这个可靠方案。首先在宿主机安装工具链sudo apt install gcc-arm-linux-gnueabihf g-arm-linux-gnueabihf然后下载OpenCV源码建议4.5.5稳定版wget -O opencv.zip https://github.com/opencv/opencv/archive/4.5.5.zip unzip opencv.zip cd opencv-4.5.5配置时特别注意这两个参数mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE../platforms/linux/arm-gnueabi.toolchain.cmake \ -DCMAKE_INSTALL_PREFIX/usr/local/opencv-arm \ -DWITH_GTKOFF \ -DWITH_JPEGON \ -DBUILD_JPEGON \ -DWITH_OPENGLON ..WITH_GTK必须关闭因为嵌入式环境通常没有GTK库。我第一次编译时没注意结果链接阶段报了一堆错。编译安装命令make -j$(nproc) sudo make install完成后把/usr/local/opencv-arm目录打包拷贝到开发板记得设置环境变量export LD_LIBRARY_PATH/usr/local/opencv-arm/lib:$LD_LIBRARY_PATH4. 人脸识别系统集成有了前面的基础现在可以着手实现人脸识别系统了。我采用模块化开发方式把系统分为四个核心组件。首先是摄像头驱动层使用V4L2接口#include linux/videodev2.h #include fcntl.h int init_camera(const char* dev) { int fd open(dev, O_RDWR); if (fd -1) { perror(打开摄像头失败); return -1; } struct v4l2_capability cap; if (ioctl(fd, VIDIOC_QUERYCAP, cap) -1) { perror(查询设备能力失败); close(fd); return -1; } // 设置采集格式 struct v4l2_format fmt {0}; fmt.type V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width 640; fmt.fmt.pix.height 480; fmt.fmt.pix.pixelformat V4L2_PIX_FMT_MJPEG; if (ioctl(fd, VIDIOC_S_FMT, fmt) -1) { perror(设置格式失败); close(fd); return -1; } return fd; }人脸检测模块我对比了Haar和LBP两种特征// Haar特征检测器初始化 CascadeClassifier haar_detector; haar_detector.load(haarcascade_frontalface_default.xml); // LBP特征检测器初始化 CascadeClassifier lbp_detector; lbp_detector.load(lbpcascade_frontalface.xml); // 检测效果对比 double t (double)getTickCount(); haar_detector.detectMultiScale(frame, haar_faces, 1.1, 3); t ((double)getTickCount() - t)/getTickFrequency(); cout Haar检测耗时: t 秒 endl; t (double)getTickCount(); lbp_detector.detectMultiScale(frame, lbp_faces, 1.1, 3); t ((double)getTickCount() - t)/getTickFrequency(); cout LBP检测耗时: t 秒 endl;实测发现LBP速度比Haar快3倍左右但准确率稍低。在STM32MP157上建议使用LBP毕竟嵌入式场景更看重实时性。5. 性能优化技巧在资源受限的嵌入式设备上跑OpenCV不优化根本没法用。下面这几个技巧是我踩过无数坑总结出来的。首先是编译选项优化# 在CMake中加上这些参数 -DENABLE_NEONON \ -DENABLE_VFPV3ON \ -DCMAKE_BUILD_TYPERELEASE \ -DWITH_OPENMPON开启NEON指令集后矩阵运算速度提升明显。我在人脸检测模块实测有2.8倍的加速。内存管理也很关键// 好的做法 cv::Mat frame; while(1) { camera frame; cv::Mat gray; cvtColor(frame, gray, COLOR_BGR2GRAY); // 处理... } // 坏的做法 while(1) { cv::Mat frame; camera frame; cv::Mat gray cv::Mat::zeros(frame.size(), CV_8UC1); cvtColor(frame, gray, COLOR_BGR2GRAY); // 处理... }避免在循环内频繁申请释放内存这会导致内存碎片化。我在早期版本没注意这点系统运行半小时后就开始卡顿。最后是算法层面的优化// 原始版本 for(int i0; ifaces.size(); i) { rectangle(frame, faces[i], Scalar(255,0,0), 2); } // 优化版本 Mat frame_show; frame.copyTo(frame_show); for(int i0; ifaces.size(); i) { rectangle(frame_show, faces[i], Scalar(255,0,0), 2); } imshow(Result, frame_show);将显示用的Mat对象与处理用的分开避免GUI操作影响处理流水线。这个改动让我的帧率从15fps提升到22fps。6. 系统部署与调试开发完成后如何把程序部署到目标板又是新的挑战。我推荐使用rsync进行文件同步rsync -avz --progress ./arm-app root192.168.1.100:/home/root比scp好的地方在于它支持增量同步调试时特别方便。遇到程序崩溃时gdb远程调试是救命稻草# 目标板运行 gdbserver :1234 ./face-detection # 宿主机连接 arm-linux-gnueabihf-gdb ./face-detection target remote 192.168.1.100:1234记得编译时要加-g选项保留调试信息。我遇到过最诡异的一个bug是OpenCV的Mat对象在ARM平台默认4字节对齐而x86是16字节导致内存访问越界。系统启动优化也很重要# 在/etc/rc.local添加 echo performance /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor export LD_LIBRARY_PATH/usr/local/opencv-arm/lib:$LD_LIBRARY_PATH /home/root/face-detection 把CPU频率策略设为performance模式人脸识别延迟直接从120ms降到80ms。不过要注意散热长时间运行可能需要加散热片。7. 进阶功能扩展基础功能跑通后可以尝试些进阶功能。比如用Qt做图形界面#include QApplication #include QLabel #include opencv2/opencv.hpp int main(int argc, char *argv[]) { QApplication a(argc, argv); QLabel label; label.setWindowTitle(人脸识别); VideoCapture cap(0); Mat frame; while(1) { cap frame; cvtColor(frame, frame, COLOR_BGR2RGB); QImage img(frame.data, frame.cols, frame.rows, QImage::Format_RGB888); label.setPixmap(QPixmap::fromImage(img)); label.show(); qApp-processEvents(); } return a.exec(); }交叉编译Qt程序需要特别注意qmake make # 部署时要带上这些库 libQt5Core.so.5 libQt5Gui.so.5 libQt5Widgets.so.5还可以集成TensorFlow Lite做更精准的人脸识别# 模型转换命令 tflite_convert \ --saved_model_dir./facenet \ --output_file./facenet.tflite \ --optimize1在C中加载模型#include tensorflow/lite/interpreter.h #include tensorflow/lite/model.h std::unique_ptrtflite::FlatBufferModel model tflite::FlatBufferModel::BuildFromFile(facenet.tflite);不过要注意STM32MP157的NPU加速需要特定版本的TFLite建议参考ST官方提供的SDK。

更多文章