那段时间我正好在帮工作室做一套门禁原型手边有一块香橙派5 Plus核心就是瑞芯微RK3588芯片。一开始我想偷懒直接怼GPU服务器后来发现完全没有必要——RK3588自带的4颗A76大核跑onnxruntime已经足够再加上它内置6 TOPS NPU后续想进一步加速也有余地。最后我落地的方案是用insightface里的RetinaFace做检测用其中的rec识别模型也就是ArcFace系列Backbone做特征提取配合USB摄像头在香橙派上跑通了一套完整的人脸识别系统。这篇内容会从板卡选型、环境搭建、模型加载到完整的注册/识别代码把我踩过的坑一起写出来。如果你手里正好有香橙派RK3588系列板子或者任何一块ARM Linux板卡这套流程基本可以直接抄作业。1. 为什么选RK3588做端侧人脸识别1.1 板卡与芯片香橙派5 Plus到底合适在哪先说芯片RK3588是瑞芯微2022年推出的一颗8核SoCCPU部分是4核Cortex-A76最大2.4GHz级别 4核Cortex-A55GPU是Mali-G610内存带宽也比上一代RK3399高了一大截。跟我之前玩过的树莓派5对比树莓派5的优势在生态和社区但是纯靠CPU去跑RetinaFace ArcFace这种链路树莓派5也不是跑不动只是余量不大。RK3588的A76大核算力明显更猛跑onnxruntime这类推理引擎时效果直接反映在帧率上。香橙派5 Plus这块板子我选的是16GB内存版。做人脸识别底库时内存大一些没坏处尤其是后续想同时挂几十路摄像头或者把识别记录和图片本地缓存都放在板卡上内存太小会很难受。为什么不做云服务器方案一方面是成本另一方面是延迟和隐私。门禁、考勤、实验室人员管理这类场景人脸数据属于敏感信息全部走云端意味着网络抖动、服务成本、数据外泄风险都得考虑。端侧识别把特征提取和比对都放在本地摄像头数据不出设备妥妥离线可用。1.2 模型选型RetinaFace负责“找到脸”rec负责“认出来”如果把人脸识别拆开其实只有两件事先找到画面里有没有人脸再判断这张脸是谁。我选了RetinaFace做检测选了rec系列模型做识别。RetinaFace是InsightFace团队提出的检测模型亮点在于它同时回归人脸框、5个人脸关键点和3D信息。相比MTCNNRetinaFace在遮挡、小脸、极端姿态下的鲁棒性更好相比YOLOv5的face变体RetinaFace自带关键点输出这对后续人脸对齐很有用。rec模型这里指的是InsightFace生态里的识别模型通常是类似MobileFaceNet或ResNet的Backbone用ArcFace的损失函数训练。ArcFace的核心思想是把特征映射到超球面上通过角度间隔让同类特征更聚拢、异类特征更分散。我用到的antelopev2模型包里识别backbone就是w600k_r50输出是512维人脸特征向量。为什么不自己训练因为普通场景下没必要。预训练模型在LFW、MegaFace等公开数据集上已经验证过泛化能力对于门禁、考勤、访客识别这类需求来说完全够用。自己训练一套还涉及数据采集、清洗、标注、模型迭代这不是一两周能搞定的事。这套选型组合还有个实际好处insightface库已经把模型和后处理包好了不需要自己写anchor解码、NMS这些脏活。在RK3588上直接调用很适合快速验证。2. 板卡环境搭建让RK3588先跑起来2.1 系统镜像烧录与基础网络设置香橙派的官方系统镜像有Ubuntu、Debian、Armbian等我这边用的是Ubuntu 22.04桌面版镜像文件系统选eMMC还是TF卡都行。建议先跑TF卡方便换系统等确认没问题了再刷进eMMC。烧录工具我用的是balenaEtcher没有特殊坑选择镜像后直接写入TF卡就行。写完后插卡上电如果有HDMI显示器就直接进桌面如果是无头模式把板卡通过网线接到路由器然后去路由器后台找板卡的IP用SSH登录。默认用户一般能在官方文档里找到常见是orangepi。如果你习惯用ADB调试也可以把板卡用USB Type-C线连接到开发电脑在电脑上执行adb devices能看到设备后直接adb shell进去。不过日常开发我还是更推荐SSH传输文件、跑服务都更顺手。烧录完第一件事是看CPU频率策略。很多板卡默认的governor是schedutil跑推理时大核可能不会立刻拉满。我习惯先装linux-cpupower或者直接改sysfs把performance模式打开确认完之后再开始跑模型。2.2 Python运行环境与摄像头接入RK3588跑Ubuntu后软件源里的Python 3.10/3.11都是现成的。我用venv创建独立环境不让系统Python被污染。先安装编译工具因为insightface在ARM板上大概率需要源码构建sudo apt update sudo apt install -y build-essential cmake python3-dev python3 -m venv --system-site-packages faceenv source faceenv/bin/activate pip install --upgrade pip pip install onnxruntime opencv-python numpy insightface这里有个细节insightface在x86上有预编译wheel在ARM上不一定有所以pip install时可能会现场编译需要Cython和gcc。如果编译报错最常见的是缺libopenblas-dev先装一下再重试sudo apt install -y libopenblas-dev摄像头方面USB摄像头接入后执行ls /dev/video*正常情况下会看到/dev/video0。再用v4l2-utils确认摄像头能力sudo apt install -y v4l-utils v4l2-ctl --device/dev/video0 --list-formats-ext如果OpenCV打开摄像头时报权限错误把当前用户加进video组再重新登录sudo usermod -aG video $USER我踩过的一个挺隐蔽的坑是系统里如果有其他进程占用了摄像头比如桌面自带的相机程序或GStreamer测试命令cv2.VideoCapture(0)就会打开失败但又不报错只能拿到黑帧。排查时先看进程fuser /dev/video0能直接告诉你谁占着。3. RetinaFace人脸检测模块实战3.1 用FaceAnalysis加载RetinaFace模型insightface库提供了一个非常方便的门面类FaceAnalysis它会把检测、识别、甚至年龄性别等模块统一加载进来。我们只需要指定模型包名并调用prepare。from insightface.app import FaceAnalysis app FaceAnalysis(nameantelopev2) app.prepare(ctx_id-1, det_size(640, 640))这里逐行解释一下nameantelopev2指定使用哪个模型包。antelopev2包含RetinaFace检测模型和w600k_r50识别模型也包含一些其他辅助模型。如果机器性能一般可以换成buffalo_s它更轻量但精度略低。ctx_id-1表示使用CPU推理。RK3588虽然有GPU但我们暂时用的是onnxruntime的CPUExecutionProviderGPU加速留给后面优化时再考虑。det_size(640, 640)是传给RetinaFace的输入尺寸。检测尺寸越大小脸检测效果越好但耗时也线性增长。我在实际项目里一般先用640确认效果再根据帧率调整到320。如果加载时联网下载模型很慢甚至失败可以把模型文件提前放到~/.insightface/models/antelopev2/目录里目录结构保持官方包结构即可。模型包里主要就是det_10g.onnx和w600k_r50.onnx这几个关键文件。注意如果提示缺少模块可以传参限制只加载检测和识别模块例如FaceAnalysis(nameantelopev2, allowed_modules[detection, recognition])。但不同版本可能有兼容问题报错时去掉这个参数就好。3.2 检测结果的bbox与5点关键点加载完成后对一帧图像调用app.get(frame)就能拿到人脸列表。每个face对象里有几个关键属性face.bbox4个数左上角x、y右下角x、y类型是float画框时记得转int。face.kps5个关键点分别是左眼中心、右眼中心、鼻尖、左嘴角、右嘴角坐标也是float。face.det_score检测置信度。face.normed_embedding识别特征向量后面rec模块用。写一段简单的可视化代码import cv2 from insightface.app import FaceAnalysis app FaceAnalysis(nameantelopev2) app.prepare(ctx_id-1, det_size(640, 640)) frame cv2.imread(test.jpg) faces app.get(frame) for face in faces: bbox face.bbox.astype(int) cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2) for x, y in face.kps.astype(int): cv2.circle(frame, (x, y), 2, (0, 0, 255), 2) cv2.imwrite(result.jpg, frame) print(f检测到 {len(faces)} 张人脸)关键点为什么重要看似只是画两个眼睛、一个鼻子、两个嘴角实际上后续人脸对齐、姿态矫正都依赖这些点。当我们把人脸旋转或者侧脸角度较大时直接用整张图做识别效果会变差而RetinaFace给出的关键点可以帮助我们把人脸区域归一化到标准角度。insightface内部已经默认做了对齐所以你直接拿normed_embedding用就好不需要自己写对齐逻辑。4. rec人脸识别模块注册与比对4.1 特征向量与余弦相似度为什么阈值取0.6人脸识别的底层逻辑比很多人想象中简单把一张人脸压缩成一串数字然后比较两串数字有多接近。rec模型输出的embedding在antelopev2里是512维浮点向量。face.normed_embedding已经做过L2归一化所以向量模长是1。两个归一化向量之间的相似度可以直接用点积计算也就是余弦相似度取值范围在-1到1之间。数值越接近1代表两张脸越像。阈值怎么定没有人能给你一个万能的数值。我自己的经验是使用w600k_r50模型在普通室内光照环境下0.6是一个比较稳妥的起点。如果设太高比如0.7容易把同一个人的不同姿态判成陌生人如果设太低比如0.4会让长得像的两个人互相误认。不同摄像头、不同光线、不同注册照片质量阈值都要重新标定。我在项目里会在界面上把实时相似度显示出来收集几天数据后再微调。4.2 注册人脸到本地底库注册的本质就是把某人的特征存起来后面再遇到这个人时拿实时特征和所有已存特征做比较。底库存储方式我用了最简单的JSON文件。原因很简单单机、低并发、数据量小JSON足够可靠而且方便直接查看和调试。如果以后在多进程场景下使用或者需要并发写入可以考虑换成SQLite逻辑完全一样。import json import os import numpy as np DB_PATH face_db.json def load_db(): if not os.path.exists(DB_PATH): return {} with open(DB_PATH, r, encodingutf-8) as f: return json.load(f) def save_db(db): with open(DB_PATH, w, encodingutf-8) as f: json.dump(db, f, ensure_asciiFalse, indent2) def register_user(name, embedding): db load_db() if name in db: print(f用户 {name} 已存在) return False db[name] embedding.tolist() save_db(db) print(f用户 {name} 注册成功) return True如果你担心单张注册照片不够稳定可以在注册时连拍几帧取多个embedding的平均值再把平均值归一化后存入。缺点是特征可能会丢失一些细节但整体稳定性会提升。4.3 实时识别与阈值判断识别过程非常直接def recognize(embedding): db load_db() best_name unknown best_score -1.0 for name, saved_emb in db.items(): score float(np.dot(embedding, np.array(saved_emb))) if score best_score: best_score score best_name name if best_score SCORE_THRESHOLD: return best_name, best_score return unknown, best_score这里只做了一件事把实时特征和底库里的每一个特征算相似度取最大值。如果最大值超过阈值返回对应名字否则返回unknown。有人会问要不要用支持向量机或最近邻分类器来分类在小型人脸底库场景下纯余弦相似度已经足够。当底库膨胀到几百人甚至上千人时再考虑用Faiss或者向量数据库做ANN检索现在完全没必要。5. 完整系统集成摄像头循环与命令行交互5.1 主流程按R注册、按Q退出把检测和识别串起来后需要一个交互方式。我设计成摄像头实时画面里检测人脸检测到的人脸画框并显示名字键盘按下r时如果画面里只检测到一个人脸就把当前这个人的embedding注册到底库按下q退出。为什么不注册当前框选的人因为画面里可能有多张脸如果要选特定的一帧代码复杂度会上升。原型阶段我建议先确保画面里只有一个人再按注册键。5.2 完整可运行代码下面的代码在香橙派RK3588 Ubuntu系统上实测可以运行。前提是你已经装好了insightface、opencv-python、numpy。import os import json import cv2 import numpy as np from insightface.app import FaceAnalysis DB_PATH face_db.json SCORE_THRESHOLD 0.6 app FaceAnalysis(nameantelopev2) app.prepare(ctx_id-1, det_size(640, 640)) def load_db(): if not os.path.exists(DB_PATH): return {} with open(DB_PATH, r, encodingutf-8) as f: return json.load(f) def save_db(db): with open(DB_PATH, w, encodingutf-8) as f: json.dump(db, f, ensure_asciiFalse, indent2) def register_user(name, embedding): db load_db() if name in db: print(f用户 {name} 已存在跳过注册) return False db[name] embedding.tolist() save_db(db) print(f用户 {name} 注册成功) return True def recognize(embedding): db load_db() best_name unknown best_score -1.0 for name, saved_emb in db.items(): score float(np.dot(embedding, np.array(saved_emb))) if score best_score: best_score score best_name name if best_score SCORE_THRESHOLD: return best_name, best_score return unknown, best_score def main(): cap cv2.VideoCapture(0) if not cap.isOpened(): print(无法打开摄像头) return cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) print(按 R 注册当前画面中第一个人脸按 Q 退出) while True: ret, frame cap.read() if not ret: print(读帧失败) break faces app.get(frame) for face in faces: bbox face.bbox.astype(int) color (0, 255, 0) text f{face.det_score:.2f} if face.normed_embedding is not None: name, score recognize(face.normed_embedding) if name ! unknown: color (0, 0, 255) text f{name} ({score:.2f}) cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2) cv2.putText(frame, text, (bbox[0], bbox[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) for x, y in face.kps.astype(int): cv2.circle(frame, (x, y), 2, (0, 0, 255), 2) cv2.imshow(face system, frame) key cv2.waitKey(1) 0xFF if key ord(q): break elif key ord(r): if len(faces) 0: print(当前画面没有检测到人脸无法注册) continue if len(faces) 1: print(检测到多张人脸每次只注册一个人请保持画面只有单人) continue name input(请输入注册姓名: ).strip() if not name: print(姓名不能为空) continue emb faces[0].normed_embedding register_user(name, emb) cap.release() cv2.destroyAllWindows() if __name__ __main__: main()代码逻辑并不复杂运行后你会看到实时画面。第一次按r注册一个人再按q退出重新运行这个人再次出现在画面里时名字就会显示在框上方。有一个体验上的小问题按r之后input()会阻塞主线程摄像头画面会卡住一会儿。原型阶段可以接受但如果要做成正式产品建议把注册做成独立的HTTP接口或者用键盘输入框代替终端输入。5.3 实机性能与预期我在香橙派5 Plus上实测默认det_size(640, 640)时单帧检测加识别大概在300到400毫秒折合每秒2到3帧。这个帧率不能满足“边走边刷脸”那种动态场景但对门禁、考勤这种“人在摄像头前停一下”的场景完全够用。如果你希望更流畅有几个立竿见影的手段把det_size从640降到320检测耗时能下降一大截但小脸容易漏检。对识别做跳帧处理比如每3帧只跑一次rec模型检测每帧都跑。换用更轻量的buffalo_s模型包。把ONNX模型转成RKNN格式利用RK3588的NPU推理这个优化幅度最大也是后续需要单独研究的方向。6. 常见问题与排查技巧实录6.1 摄像头打不开或读帧失败先确认设备节点存在ls /dev/video*。如果USB摄像头插了但节点没出来拔插一次再看dmesg | tail -20有没有设备上报错误。如果节点存在但OpenCV打不开检查是不是被别的进程占用sudo fuser -v /dev/video0另外很多USB摄像头在MJPG格式下帧率更高但OpenCV默认可能用YUYV导致分辨率大了之后帧率暴跌。可以在代码里加摄像头参数cap cv2.VideoCapture(0, cv2.CAP_V4L2) cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(M, J, P, G)) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)6.2 onnxruntime或模型加载失败如果你用FaceAnalysis时报Model file not found之类的错误大概率是模型没有自动下载成功。解决办法是去已有模型文件的机器上把~/.insightface/models/antelopev2整个目录复制到板卡的同一路径下。另外注意Python版本。太老的Python会导致insightface编译失败太新的Python也可能遇到依赖兼容问题。Ubuntu 22.04自带的Python 3.10是我测下来最稳的组合。6.3 识别误判和漏检怎么办误判先别急着换模型先看两个数据检测置信度det_score和识别相似度score。如果你发现某个陌生人的相似度经常超过阈值把SCORE_THRESHOLD往上调比如调到0.65或0.7。漏检方面室内光线不足是最常见原因。人脸区域过暗时RetinaFace检测不到很正常。我的建议是不要只依赖阈值可以再加上简单的图像预处理比如转灰度后做直方图均衡化或者调高摄像头曝光补偿。注册阶段务必保证照片质量正脸、光线均匀、无遮挡、表情自然。如果注册的照片是模糊的后面实时识别再准也很难比对成功。6.4 从原型到正式部署的几条扩展建议如果这个系统要继续往下走我建议按这几个方向迭代底库管理做成Web界面方便在浏览器里注册、删除、导入人员。把识别记录写入SQLite或者MySQL保留时间、照片、相似度方便追溯。改成RTSP流输入对接已有IPC摄像头而不是只依赖USB摄像头。多路摄像头并发识别时把检测与识别放到不同线程避免单路卡顿影响到所有画面。我在实际调试中最大的体会是RK3588这颗芯片的上限比很多人想的高但前提是环境搭得干净、模型选得合适。整个流程从烧录系统到跑通识别快的话一个下午足够。代码部分看起来不长但背后的模型选型、阈值调整、摄像头调试才是真正花时间的地方。如果你手里也有这块板子按上面的步骤走一遍应该很快就能看到自己的名字出现在摄像头画面里。
企业数字化 ERP 产品动态
相关推荐
KNN情感分析实战:中文短文本分类的向量化与调优 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:28:19
自己做网站为什么出现403保姆级教程 避坑403错误:自建网站最佳实践与排查全攻略 网站做好了没人访问,最心碎的时刻莫过于打开后台发现一堆403 Forbidden错误。很多站长以为这是服务器挂了,其实90%的情况是权限配置或文件路径的小失误。别慌,咱们不整虚的,直接拆解那些让… · 2026/9/28 1:28:13
Xshell串口调试避坑指南:从COM端口到嵌入式设备通信 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:28:13
Spingboot启动预热的实现 启动预热的适用场景启动预热适合以下情况:数据主要来自第三方接口,无法直接从本地数据库读取。第三方接口响应较慢,首次访问容易超时。一个页面需要调用多个第三方接口或逐项查询。数据读取频繁,但变化不频繁。希望服务启动后&… · 2026/9/28 3:40:12
学Java别走弯路,这5个方向最吃香 学Java的人很多,但学明白的人不多。有人学了半年还在写控制台程序,有人一年就能独当一面。差别不在天赋,而在方向。Java生态太庞大了,什么都学等于什么都没学。选对方向,事半功倍。今天盘点当前最吃香的5个Java方向&am… · 2026/9/28 3:32:15
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
制作网页比较方便的软件怎么选?一文搞懂避坑指南 制作网页比较方便的软件怎么选?一文搞懂避坑指南 很多老板一上来就问:做个网站多少钱?但我反问他:你的域名买了吗?服务器租了吗?他一脸懵。这就是典型的“域名服务器搞不懂”。别急,今天咱们不聊虚的,直接 一文搞懂 那些让你头秃的技术名词。… · 2026/9/28 0:00:06
婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 找婚恋网站建站公司,最怕的就是被坑高价。很多同行跟我吐槽,报价单上写得模棱两可,功能栏里全是“高级定制”、“专属UI”,结果落地全是套壳。今天不聊虚的,直接甩几个我经手的 实战案例… · 2026/9/28 0:00:19
济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 上周济南一个做建材的老板找我,脸都绿了。他的官网首页弹出了赌博广告,后台被植入了挖矿脚本。他慌得问我:“网站被黑挂马不知道怎么办?能不能直接找之前的外包公司要源码下载,看看哪里被动了手脚?… · 2026/9/28 0:00:25