
从标题就开始说大实话5分钟不是从拆快递开始算也不是从给香橙派5刷系统开始算而是指你已经把环境和模型文件准备妥当之后从落地代码到摄像头前第一次成功识别真实时间就是几分钟的量级。这篇文章拿香橙派5RK3588跑一套最简人脸识别系统检测用RetinaFace识别用insightface工具包里的rec模块两个角色明确、分工干脆解决的问题只有一个“摄像头前出现一个人他是谁是不是我库里的人”。这套方案不用训练自己的模型不用搭客户端服务端代码量也压得足够低适合刚拿到RK3588开发板、想把“人脸识别”从PPT变成真实可用功能的同学参考也适合已经跑通过Demo、想搞清楚RetinaFace和rec各自在系统里承担什么任务的读者。我会把从安装依赖到注册人脸、再到现场比对识别、最后调优的几个层次全部走一遍所有代码贴在下面直接能抄。1. 项目概述与系统拆解1.1 为什么选香橙派5 RK3588 跑人脸识别香橙派5这个系列最近在嵌入式圈子里讨论度很高核心原因是它把RK3588这颗SoC的价格打了下来。RK3588的CPU部分是4核Cortex-A76加4核Cortex-A55大核主频能到2.4GHz左右这个算力跑传统的人脸检测识别链路完全够用。更关键的是它自带一个6 TOPS算力的NPU虽然本文的“5分钟快速方案”先用CPU顶着跑但后续如果要上NPU加速同一个模型文件还有明确的升级路径这点后文会专门讲。相比之下用树莓派4B跑同类方案CPU推理一帧640分辨率的人脸检测经常需要等上一两秒而RK3588的A76大核跑onnxruntime体感会流畅很多。实测在默认的640x640检测尺寸下RetinaFace加rec特征提取的完整链路大约在2到4帧每秒这已经足够做门禁验证、会员识别这类低频交互场景了。如果你只是想在开发板上拉起一个“能用的”人脸识别系统RK3588是当前性价比和扩展性都比较均衡的选择。1.2 RetinaFace和rec在系统里的分工很多人看人脸识别项目会混淆检测和识别其实这是两个完全不同的子任务在工程上通常拆成两步。RetinaFace负责的是“检测”它在图像里把所有可能的人脸框出来输出人脸边界框、关键点坐标、置信度这些信息。它不关心这个人是谁只负责回答“哪里有人脸”。如果把整套系统比作一个安检流程RetinaFace就是门口那位负责叫住每个来访者的保安先确定有人进来了而且把位置指出来。rec模块负责的是“识别”它接收RetinaFace裁出来的人脸区域通过卷积骨干网络把这张脸压缩成一个固定维度的特征向量也就是embedding。在insightface的模型包结构里rec是recognition的缩写buffalo_l这个预训练包里面实际包含det、rec、genderage、2d106、3d68几个子模块rec就是专门负责特征提取的那一个。识别环节做的事情是提取特征向量再和你注册时保存的特征向量做相似度比对相似度超过阈值就判定是同一个人。这两个模块合在一起才算一个完整的人脸识别系统。单独用RetinaFace只能“找到脸”单独用rec没有检测器给输入也跑不起来。这也是我选择直接基于insightface的FaceAnalysis接口来做原因——它把检测和识别包成了一个统一对象调用方不用关心内部先跑谁后跑谁只要拿一张图进去就能拿到人脸框、关键点、特征向量一套结果。1.3 系统整体架构与“5分钟”的时间构成整套系统的数据流非常简单摄像头采集视频帧送进FaceAnalysis对象得到当前画面中的人脸列表对每一张人脸取出normed_embedding特征向量和本地预存的JSON数据库做余弦相似度比对得分最高的记录超过设定阈值就认定身份匹配成功在画面里画框标注姓名。“5分钟”这个时间承诺我拆成三块第一块是代码编写核心实现其实只有几十行在我下面的示例里直接复制粘贴即可第二块是初始化模型首次运行FaceAnalysis时程序会在用户目录下自动下载或加载buffalo_l模型包这个过程取决于网络状况一般33MB左右网络好的话半分钟搞定第三块是现场调试摄像头编号和检测阈值这部分看运气很多板子默认摄像头索引不是0试一次就知道。这三块加起来确实就是五分钟上下的体感时间。系统镜像烧录和模型手动下载这种一次性工作不计入这五分钟。2. 环境准备系统、依赖、模型文件2.1 系统镜像烧录与基础设置在香橙派5上建议直接使用官方提供的Ubuntu镜像官方有桌面版和服务器版两种我自己用的是服务器版因为没有图形界面的牵绊每次开机内存占用能省下不少。RK3588比较新的镜像已经适配到了较新内核对USB摄像头、HDMI输出、NPU驱动的支持都比较完整尽量别用那些来路不明的第三方精简镜像省下后面排查驱动的时间。烧录工具方面Windows下用balenaEtcher或者官方配套的烧录软件Linux下用dd命令直接写卡就行。需要注意一下不要使用TF卡 读卡器组合时反复插拔导致的写入碎片问题。SD卡质量会影响系统稳定性建议选至少A2速度等级的卡实测同样环境下垃圾卡和好卡在跑人脸识别时帧率差距并不大但系统日志里I/O错误出现的概率会明显不同。烧录完开机正常会看到串口或者通过HDMI接显示器能进到控制台Ubuntu镜像默认账号一般会在官方文档里注明登录后第一件事是执行apt update与apt upgrade。如果想要无线连接或者查看局域网地址连上网络之后用ip addr确认板子的IP然后ssh进去操作。后续所有安装步骤都在ssh终端里进行这是开发板最省事的模式。2.2 Python环境与依赖安装香橙派5官方Ubuntu镜像自带Python3一般版本在3.8到3.10之间够用。但在安装pip包之前我建议先把基础编译链路补齐防止后面装某些带C扩展的依赖包时直接编译报错sudo apt update sudo apt upgrade -y sudo apt install -y python3-pip python3-dev build-essential cmake libopencv-dev然后升级pip并安装核心依赖pip install --upgrade pip setuptools wheel pip install numpy opencv-python onnxruntime这里有一个容易踩的坑在ARM架构下onnxruntime的安装会自己拉取对应的aarch64版本绝大多数情况直接pip install就能成功。如果遇到pip死活找不到匹配版本的情况可以先卸载重新装再试试。opencv-python装好后在终端里执行python3 -c import cv2; print(cv2.version)验证一下只要不报错这一步就算过了。接下来是重头戏安装insightfacepip install insightfaceinsightface在安装时会自动拉取onnx、albumentations、prettytable、requests等依赖。albumentations这个包偶尔会要求比较新的numpy版本如果你前面已经装了numpy并且版本偏低可以先单独升级一下numpy再回来装insightface能省下不少报错时间。2.3 模型文件准备buffalo_l 包说明insightface的FaceAnalysis在初始化时需要一个模型包名常见的有buffalo_l、buffalo_s、buffalo_sc等。buffalo_l是综合表现最好的一个包含检测、识别、还有两个关键点模型和性别年龄分类器。我们项目只需要detection和recognition所以初始化时可以用allowed_modules参数限制加载范围减少首次运行时的加载时间。模型文件的保存位置默认在用户目录下的.insightface/models/文件夹。如果你所在网络环境访问GitHub不稳定或者首次运行时模型自动下载失败可以直接手动下载buffalo_l的zip包解压后放到该目录下。建议直接把这个步骤当成标准操作别依赖自动下载。mkdir -p ~/.insightface/models cd ~/.insightface/models # 将buffalo_l.zip上传到此处然后解压 unzip buffalo_l.zip目录结构大致如下~/.insightface/models/buffalo_l/ ├── det_10g.onnx ├── genderage.onnx ├── rec.w600k_r50.onnx ├── 2d106det.onnx └── 3d68.onnx上面文件里的det_10g就是RetinaFace检测模型rec.w600k_r50是识别用的backbone。这两个文件名在你后续做NPU转换时也会用到我先在这里标记一下。验证模型是否放对位置的代码很简单import insightface from insightface.app import FaceAnalysis app FaceAnalysis(namebuffalo_l, allowed_modules[detection, recognition], providers[CPUExecutionProvider]) app.prepare(ctx_id0, det_size(640, 640)) print(模型初始化成功)能打印出“模型初始化成功”环境准备就算彻底完成了。3. 核心代码实现检测、识别、注册一条龙3.1 人脸检测模块RetinaFace的使用要点我们把检测和识别封装在同一个FaceAnalysis对象里实际使用中只需要调用app.get(img)这一个方法。传入的图像要求是BGR格式的numpy数组OpenCV读出来的图像就是BGR顺序直接传即可不需要转成RGB。这一步经常有人搞错转成RGB之后虽然图还是能出结果但在关键点坐标映射的时候容易出偏差。get方法返回的是一个Face对象的列表每个Face对象里最常用的属性是bbox、kps、det_score和normed_embedding。bbox是四个人脸框坐标kps是五个关键点坐标det_score是检测置信度normed_embedding是L2归一化后的特征向量后面做相似度比对直接用这个就行。官方FaceAnalysis默认的参数已经经过大量调优直接使用通常可以获得很好的效果。如果你想针对RK3588的CPU性能做一些权衡可以在prepare时调整det_size。更大的det_size会提高对小脸的检测能力但推理耗时也会上升更小的det_size比如320x320检测速度快不少但稍微远一点的人脸就可能检测不到。门禁闸机类场景det_size640是个比较中庸的选择。3.2 人脸特征提取与相似度计算recrec模块在本项目里表现为Face对象里的normed_embedding属性。它是一串长度512的浮点型数组代表一张人脸的“数学指纹”。两段特征向量是否来自同一个人最常用的判断标准是余弦相似度也就是计算两个向量的点积因为normed_embedding已经做过归一化处理点积结果直接就是余弦相似度范围在-1到1之间。在识别的工程实践中阈值设定直接影响误识率和拒识率。常见做法是先设0.5作为初始阈值然后在真实场景里采集几十个正样本和负样本调优。如果误识别情况多把阈值调到0.55甚至0.6如果经常把同一个人拒之门外就适当降低到0.45。我用buffalo_l模型在室内光照环境下测下来0.5到0.55这个区间相对均衡你可以把它当起点往下调。下面给出一段通用的人脸特征提取与比对代码import numpy as np import json import cv2 def extract_embedding(app, image_bgr): faces app.get(image_bgr) if len(faces) 0: return None return faces[0].normed_embedding def match_face(query_emb, db_embeddings, threshold0.5): best_name None best_score -1.0 for name, emb in db_embeddings.items(): score float(np.dot(query_emb, np.array(emb))) if score best_score: best_score score best_name name if best_score threshold: return best_name, best_score return None, best_score注意这里查询时只取了faces[0]也就是画面里检测到的第一张人脸。如果场景里有同时出现多人的需求要改成一个for循环遍历faces列表。3.3 人脸数据库的注册与保存识别系统需要一个“熟人库”这个库在我们项目里就是一份Json文件。每张人脸注册的时候把名字和512维特征向量存进去。Json文件本身具备可读性调试的时候打开看一眼就知道数据格式对不对比传统的二进制的存储方式更适合快速验证。下面是注册人脸入库的完整代码可以从摄像头拍照注册也可以从图片文件注册import os import json import cv2 import numpy as np FACE_DB face_db.json def load_db(): if os.path.exists(FACE_DB): with open(FACE_DB, r) as f: return json.load(f) return {} def save_db(db): with open(FACE_DB, w) as f: json.dump(db, f) def register_from_image(app, name, image_path): img cv2.imread(image_path) if img is None: print(读取图片失败:, image_path) return False face app.get(img) if len(face) 0: print(图像中未检测到人脸) return False emb face[0].normed_embedding.tolist() db load_db() db[name] emb save_db(db) print(已注册用户:, name, 特征维度:, len(emb)) return True def register_from_camera(app, name, camera_id0): cap cv2.VideoCapture(camera_id) if not cap.isOpened(): print(无法打开摄像头) return False ret, frame cap.read() cap.release() if not ret: print(摄像头采集失败) return False face app.get(frame) if len(face) 0: print(画面中未检测到人脸请调整位置后重试) return False emb face[0].normed_embedding.tolist() db load_db() db[name] emb save_db(db) print(已注册用户:, name) return True注册的时候最好保证人脸在画面中央并且正对摄像头不要有大的侧脸或者遮挡。单张照片注册的特征向量鲁棒性一般条件允许的话可以拍多张不同角度的照片对同一个名字存多个特征向量识别的时候分别比对取最高得分这个技巧在实际部署里非常管用。3.4 主识别循环与完整示例当注册库准备好之后启动识别系统的主循环就非常简单了。从OpenCV读取摄像头画面每帧调用app.get然后遍历检测到的人脸每张脸取特征向量和fa_db比对将结果画到画面里。完整代码如下import cv2 import numpy as np from insightface.app import FaceAnalysis FACE_DB face_db.json def load_db(): import os, json if os.path.exists(FACE_DB): with open(FACE_DB, r) as f: return json.load(f) return {} def main(): app FaceAnalysis( namebuffalo_l, allowed_modules[detection, recognition], providers[CPUExecutionProvider] ) app.prepare(ctx_id0, det_size(640, 640)) db load_db() print(已加载人脸库人数:, len(db)) cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) if not cap.isOpened(): print(无法打开摄像头) return while True: ret, frame cap.read() if not ret: break faces app.get(frame) for face in faces: emb face.normed_embedding best_name 陌生人 best_score -1.0 for name, saved_emb in db.items(): score float(np.dot(emb, np.array(saved_emb))) if score best_score: best_score score best_name name if best_score 0.5: best_name 陌生人 bbox face.bbox.astype(int) x1, y1, x2, y2 bbox cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) label f{best_name} ({best_score:.2f}) cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(Face Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() if __name__ __main__: main()这段代码已经是一套“最小可用”的人脸识别系统。启动后摄像头画面会实时显示每张人脸的框和名字按q键退出。新手建议先跑通这个循环再加日志、数据库、远程控制等额外功能避免一开始就把复杂度堆起来。4. 部署优化NPU加速、摄像头接入与RTSP推流4.1 RK3588 NPU加速从ONNX到RKNN前面主流程是基于CPU推理的虽然能够正常工作但在多路视频或者高帧率场景下CPU占用会偏高。RK3588最大的卖点是NPU如果想要把人脸识别链路压到NPU上需要把ONNX模型转成RKNN格式。这个过程需要额外一台x86主机安装RKNN-Toolkit2工具包然后在主机上完成模型转换再把生成的rknn文件放到香橙派5上通过RKNN Runtime加载。实际转换时只需要转换det_10g.onnx和rec.w600k_r50.onnx这两个文件因为其他模块在这个项目里根本用不到。转换脚本的核心逻辑大致是初始化RKNN配置对象指定平台为rk3588加载ONNX模型设置输入尺寸然后导出为rknn文件。这里有几个容易出问题的点首先RetinaFace模型的输入是有动态batch维度的转换前最好固定batch为1其次模型里如果包含一些不支持的算子比如某些自定义上采样操作需要先简化onnx结构或者改用RKNN-Toolkit2自带的一些预编译模型。NPU加速的收益相当明显。CPU上跑一帧640x640检测加识别大约需要300到500毫秒而NPU跑INT8量化后的模型单纯推理时间能压缩到几十毫秒量级整体帧率可以提升到十几帧每秒。不过量化的代价是精度会有一点损失如果识别阈值卡得比较紧建议在量化后重新标定一下阈值参数。4.2 USB摄像头适配与RTSP推流摄像头接入这件事看似简单在开发板上却很容易卡十分钟。香橙派5的USB口识别常规UVC摄像头基本没压力插入后执行ls /dev/video*通常会出现video0有的复合设备还会有video1或video2。如果OpenCV打开默认索引0失败可以试着把VideoCapture参数改成1或者2。从设备节点到RTSP流是另一个常见的需求。RK3588平台经常有人想把摄像头画面转成RTSP给其他设备拉流。要高效编码需要使用rk3588平台适配过的ffmpeg它自带rkmpp硬件编码器能用NPU旁边的VPU硬件单元做H.264编码CPU占用极低。执行ffmpeg -encoders | grep rkmpp即可确认自己的ffmpeg是否支持rkmpp编码器如果不支持需要安装瑞芯微维护的ffmpeg分支。基础的推流命令如下ffmpeg -f v4l2 -i /dev/video0 -c:v h264_rkmpp -b:v 2M -f rtsp rtsp://0.0.0.0:8554/live这样就把本地USB摄像头转成了RTSP流其他设备用VLC或者ffmpeg拉流就能看到实时画面。更进一步可以把识别结果画面叠加上人脸框后再推流相当于一个带AI分析的监控摄像头流不过这条路需要自己写编码pipeline复杂度会高不少。4.3 性能实测与参数调节我在香橙派5上跑这套代码几组实测参考数据如下室内光照、单目USB摄像头、640x480输入配置CPU占用帧率备注buffulo_l默认640 det_size约70%2-3 FPS稳定识别无压力det_size降到320约45%4-5 FPS小脸检测率下降仅检测不识别约30%8-10 FPS特征提取比较耗时NPU量化后双模型约20%10-15 FPS需要额外转换工作量从数据能看出来rec特征提取环节在CPU上的耗时占比不小。如果只是需要一个“检测到有人”的提醒功能可以把识别部分关掉只跑RetinaFace帧率提升明显。如果必须要实时人脸识别最省事的优化方向是降低输入分辨率比如把摄像头采集和det_size都调到320而不是硬上NPU。对于现阶段大部分场景2到3帧的识别速度已经足够用一帧大约花300多毫秒给人感觉是轻微延迟但可接受。5. 常见问题与排错记录5.1 依赖安装与import报错问题Apip install insightface时报错提示编译某个依赖失败。这个问题在ARM开发板上最常见的原因是缺少编译工具链和Python头文件。解决办法就一条确保先执行了前面的apt install命令尤其是build-essential和python3-dev。另外不要在conda环境里折腾直接用系统的Python3环境最省事。问题Bimport insightface后调用FaceAnalysis时提示No match found for buffalo_l。这种情况绝大多数是模型文件没放到正确路径。确认~/.insightface/models/buffalo_l目录存在并且里面至少有det_10g.onnx和rec.w600k_r50.onnx两个文件。如果是从Windows上传的压缩包要注意解压时不要多套一层buffalo_l目录。问题C代码报错提示onnxruntime版本和onnx版本不兼容。insightface对onnxruntime的版本要求不算苛刻但如果之前手动装过较老版本的onnx冲突概率会增加。处理方式是统一升级pip install --upgrade onnx onnxruntime5.2 摄像头不出图打开摄像头失败是开发板上演出率最高的坑。先排查设备节点执行ls /dev/video*如果没有任何输出说明系统没有识别到摄像头检查USB线或者换个USB口试试。如果video设备存在但cv2.VideoCapture依然失败要么是权限问题要么是索引不对。把代码里的摄像头索引从0依次换成1、2测试或者直接写个小循环打印所有可用的video设备。还可以用v4l2-ctl工具查看设备能力sudo apt install v4l-utils v4l2-ctl --list-devices这个命令会列出每个摄像头的详细信息包括支持的格式和分辨率方便确认摄像头是否处于可用状态。5.3 识别率低与误判如果你发现系统把自己人脸都识别成了“陌生人”或者经常把两个人搞混优先检查几个环节。第一注册照片的质量模糊、背光、侧脸的照片注册进库识别效果会非常差。第二摄像头分辨率是否太低如果画面里的人脸只占几十个像素检测都费劲更别说提取有效特征了。第三阈值是否合理可以先把阈值降到0.3打印出实际比对得分看看正常匹配时的得分大概是多少再据此调阈值。误匹配的问题也值得留意有些场景下两个人的特征相似度天然偏高比如亲兄弟。这种问题单靠调阈值很难彻底解决更实用的办法是给同一个人注册多个角度的特征比对时以最高得分为准相当于用多张样本来刻画一个人的特征分布。6. 踩坑心得与扩展建议整套系统跑下来我个人最大的体会是在RK3588平台上做AI应用最难的不是算法而是工程链路。模型选型、环境依赖、摄像头适配、权限配置任何一环出问题都会让体验变得支离破碎。强烈建议在动手前先把系统镜像、模型文件这些静态准备一次性做好然后把软件安装和代码调试当成一条独立流水线来处理这样后面真正调试代码的时候才会顺畅。从扩展角度说这套基于insightface的人脸识别方案还有几个升级方向值得尝试。第一个方向是接入RKNN加速把RetinaFace和rec模型都转成RKNN格式帧率提升会非常明显第二个方向是给系统加一个简单的Web管理界面用Flask提供注册接口和识别记录查询这样手机浏览器就能直接管理门禁设备第三个方向是结合RK3588的硬件编解码能力把识别结果画面推成RTSP流做成一个真正的边缘AI摄像头。另外同一块板子如果后续想跑YOLOv8目标检测利用的也是当前这套RKNN转换和NPU部署思路等于这次先把人脸识别链路走通后面迁移到其他模型就水到渠成了。如果你在照着步骤操作的过程中卡在某个环节不妨把报错信息原样贴到搜索引擎里RK3588现在用户群体很大大多数问题都能找到前人踩坑后的解决办法。对于想快速在嵌入式设备上落地人脸识别的朋友希望这篇文章能帮你把从零到一的路程缩短一些。