Orin 上 DeepStream 二次推理
文章目录
- Orin 上 DeepStream 二次推理
- 1. 结论先行
- 2. 贯穿示例:四路门店 + 行人属性
- 3. 主检测与二次推理的分工
- 4. 二次推理在链路中的位置
- 5. 什么时候该上二次推理
- 6. 为二次网络准备 engine
- 7. deepstream-app 配置要点
- 8. batch-size 与路数怎么对齐
- 9. 在 probe 里读二次结果
- 10. 资源不够时的调整顺序
- 11. 多个 secondary-gie
- 12. 常见误区与排查
- 13. 与后续链路的衔接
- 14. 术语对照
- 15. 上线前检查清单
摘要:多路检测和跟踪跑通后,业务常要在主检测框上再跑一层:工服颜色、是否戴帽、车牌字符、包裹属性等。DeepStream 里这一层叫secondary GIE(二次推理),配置块常写作
[secondary-gie0]。本文用「四路门店监控 + 行人二次属性」贯穿示例,写清二次推理挂在哪、怎么只对指定类别裁剪推理、batch-size与 engine 怎么对齐、以及和 DLA 分流的关系。适合已跑通 Orin 上用 DeepStream 跑多路检测 与系列《Orin 上 DeepStream 跟踪与事件输出》的人。插件名与字段随 JetPack / DeepStream 版本可能略有差异,以板上 sample 为准。
图1. 主检测、跟踪与二次推理链路
1. 结论先行
| 阶段 | 建议做法 | 不建议做法 |
|---|---|---|
| 主检测还不稳 | 先稳住 pgie、路数与 batch | 一上来叠二次网络 |
| 只要框,不要属性 | 不要开 secondary-gie | 全类别每帧二次推理 |
| 只要「人」做属性 | 用operate-on-class-ids过滤 | 车、包也送进分类网 |
| 辅模型是规整 CNN | 评估 DLA 挂 secondary | 把 YOLO 主检测硬塞 DLA |
| 多路已经掉帧 | 先减路数或加大interval | 同时开 tracker + 二次 + 推流 |
主检测回答「框在哪」;二次推理回答「框里更细的属性是什么」。两层 engine、两层 batch 约束都要单独验收,不能共用主检测的 TensorRT 文件。
2. 贯穿示例:四路门店 + 行人属性
设定如下:
- Orin NX 16GB,4 路 720p RTSP,主检测 YOLO(人 / 车 / 包)
- 已开tracker,事件层只要「人」进入收银区
- 业务新增:行人二次网络输出
uniform/no_uniform/helmet等属性,写入事件 JSON - 封闭小壳,工作功耗档下不能把 GPU 长期打满
在这个设定里,二次推理只应对class_id=0(人)的框裁剪后推理;车和包不进入 secondary-gie。属性结果挂在NvDsObjectMeta的classifier meta(分类元数据,存放二次网络输出的标签与置信度)上,由 probe 合并进事件。
验收时建议按下面顺序走,不要跳步:
- 只开 pgie + 4 路,确认 FPS 与显存占用达标。
- 加 tracker,确认
track_id连续、事件去重逻辑仍成立。 - 只开 secondary-gie,暂时关掉 analytics 与 Agent,看 OSD 或日志里是否出现属性标签。
- 把 probe 挪到 sgie 之后,事件 JSON 里带上
attributes字段。 - 再开 analytics / Agent / GPIO,每次只加一层,方便定位掉帧来源。
3. 主检测与二次推理的分工
| 维度 | 主检测 pgie | 二次推理 sgie |
|---|---|---|
| 输入 | 整帧或 streammux 合批后的帧 | 主检测框裁剪出的 ROI |
| 典型模型 | YOLO、SSD 等检测网 | 属性分类、车牌字符、细粒度 CNN |
| 输出挂在 | obj_meta的类别与框 | classifier_meta链表 |
| batch 含义 | 视频路数 | 单帧内目标 crop 数 |
| engine 文件 | 独立一份 | 独立一份,不能复用 pgie |
很多人第一次加二次推理时,会把两份模型合成一个「大检测网」再导出 engine。这样做的问题是:改属性模型就要重训整网,路数一多显存也吃不消。DeepStream 的 secondary-gie 机制,本质是把检测与属性解耦,各自维护 engine、各自调 batch。
4. 二次推理在链路中的位置
在 pgie(主检测)之后、OSD 之前插入secondary-gie;若已开 tracker,通常顺序为:
streammux → pgie → tracker → secondary-gie0 →(可选 nvdsanalytics)→ OSD → sink| 组件 | 作用 |
|---|---|
| pgie | 主检测,输出框、类别、置信度 |
| tracker | 稳定object_id,便于事件与属性按 track 去重 |
| secondary-gie | 对每个(或过滤后的)目标框裁剪 ROI,跑第二份 engine |
| probe | 读取主类 + 二次属性,写事件队列 |
tracker 与 sgie 的顺序:行业常见写法是pgie → tracker → sgie。tracker 先稳定框与track_id,二次网络跟在后面做属性;这样属性可以按 track 做冷却与去重。若你的辅模型对框抖动极敏感,也可以评估pgie → sgie → tracker,但要实测 ID 跳变对业务的影响。
analytics 放哪:若规则是「人进收银区且未穿工服才告警」,通常sgie在nvdsanalytics之前,让 analytics 或 probe 同时读到区域状态与属性。具体顺序以你板上deepstream-app的 sample 为准,改顺序后务必重跑一轮事件回放。
官方说明见 Gst-nvinfer 中 Primary / Secondary 推理模式。
图2. 只对指定类别做二次推理
5. 什么时候该上二次推理
| 业务需求 | 是否适合 secondary-gie |
|---|---|
| 工服 / 帽檐 / 口罩等属性 | 适合,辅模型多为小 CNN |
| 车牌字符(规整检测+识别链) | 适合,常拆成检测后的 crop 分类 |
| 主检测类别已经够用 | 不需要二次层 |
| 要用大参数 VLM 看全图 | 不适合塞在 sgie,应独立进程 |
| 每帧全图二次推理 | 浪费算力,应先过滤类别与 ROI |
Orin Nano没有 DLA(深度学习加速器,Orin 上专用于卸载规整卷积网络的固定功能单元),二次网络只能和主检测一起挤 GPU,路数要更保守。NX / AGX 可把规整辅模型放到 DLA,见 Orin 上 DLA 与 GPU 的分工与使用。
各板型粗估(同一 720p、YOLO 主检 + 小属性网,仅供参考,以板上压测为准):
| 板型 | 4 路 + tracker + 1 个 sgie | 备注 |
|---|---|---|
| Orin Nano 8GB | 紧,宜 2~3 路或加interval | 无 DLA,GPU 独扛 |
| Orin NX 16GB | 较宽裕,可试 DLA0 挂 sgie | 2 颗 DLA |
| AGX Orin | 余量最大,可叠 2 个 sgie | 多模型并发 |
6. 为二次网络准备 engine
二次 engine必须在目标板上用 TensorRT 生成,规则和主检测相同:换 SKU、换 JetPack 大版本都要重建。
# 示例:属性分类 ONNX → FP16 engine(GPU)trtexec\--onnx=/home/ubuntu/models/person_attr.onnx\--saveEngine=/home/ubuntu/models/person_attr_fp16.engine\--fp16\--memPoolSize=workspace:256M若走 DLA(NX / AGX):
trtexec\--onnx=/home/ubuntu/models/person_attr.onnx\--saveEngine=/home/ubuntu/models/person_attr_dla0_fp16.engine\--fp16\--useDLACore=0\--allowGPUFallback\--memPoolSize=workspace:256M说明:
- 输入尺寸与训练时 crop 一致(例如 128×256),与主检测 640×640 无关。
- DLA 引擎要查构建日志里的fallback 比例;大半层回 GPU 时,收益有限。
- 文件命名建议带
sgie、dla0等后缀,避免 DeepStream 配置指错 engine。 - 建引擎时的batch 维必须与
[secondary-gie0]里batch-size一致;这是 secondary 启动失败的头号原因。
离线先用单张 crop 图验模型,再进 DeepStream:
# 从测试图裁一块人体 ROI,用 trtexec 或自写脚本跑一遍trtexec--loadEngine=/home/ubuntu/models/person_attr_fp16.engine\--shapes=input:1x3x256x128\--dumpOutput输出向量长度应与labelfile-path行数一致;不对就先修 ONNX,不要急着改 pipeline。
7. deepstream-app 配置要点
在已有[primary-gie]、[tracker]基础上增加[secondary-gie0](路径与字段名以本机deepstream-appsample 为准):
[secondary-gie0] enable=1 gpu-id=0 gie-unique-id=2 operate-on-gie-id=1 operate-on-class-ids=0 input-object-min-width=32 input-object-min-height=32 batch-size=16 model-engine-file=/home/ubuntu/models/person_attr_fp16.engine labelfile-path=/home/ubuntu/models/person_attr_labels.txt network-mode=2 process-mode=2 classifier-async-mode=0 classifier-threshold=0.51| 参数 | 含义 |
|---|---|
gie-unique-id | 本 secondary 的唯一 ID,probe 里用来区分 |
operate-on-gie-id | 依赖哪一路 pgie 的输出(对应 pgie 的gie-unique-id) |
operate-on-class-ids | 只对哪些主检测类别做二次推理;上例只对「人」 |
input-object-min-width/height | 太小的框不送分类,减少噪声 |
batch-size | 二次合批上限,见下一节 |
process-mode=2 | 分类器模式(以当前 DeepStream 文档为准) |
主检测里要事先对齐:
[primary-gie] enable=1 gie-unique-id=1 batch-size=4 ...labels 文件每行一个属性类名,顺序与模型输出一致。改类别名或顺序后,要同步改 probe 里解析逻辑。
deepstream-app主配置里还要声明 secondary 数量(字段名因版本而异,常见写法如下):
[application] enable-perf-measurement=1 ... [num-sources] ... [primary-gie] enable=1 ... [tracker] enable=1 ... [secondary-gie0] enable=1 ... # 若还有 secondary-gie1,继续往下写若走 DLA,在对应[secondary-gie0]段增加(以 sample 为准):
enable-dla=1 use-dla-core=0注意:use-dla-core=0的 engine 必须用--useDLACore=0构建;core 号与 engine 文件必须一一对应。
8. batch-size 与路数怎么对齐
多路场景里,主检测batch-size通常等于路数(见多路检测一文)。二次层的batch-size含义不同:它是单帧内最多合批多少个目标框,不是 RTSP 路数。
| 维度 | 主检测 pgie | 二次 sgie |
|---|---|---|
| batch 含义 | 多少路视频进一次推理 | 多少个人脸 / 人体 crop 进一次推理 |
| 常见设值 | 等于路数(如 4) | 按峰值目标数(如 8、16、32) |
| engine 构建 | 按 pgie batch 建 | 按 sgiebatch-size建 |
经验上:
- 先统计单路高峰时「人」框数量,再定 sgie
batch-size。 batch-size设太小会排队掉帧;设太大浪费显存且 engine 变大。- 改 sgie
batch-size后必须重建secondary engine。
举例:4 路门店高峰时,单路平均 3 个「人」框、峰值 6 个,则全帧最多约 24 个 crop。batch-size=16时,高峰会拆成两批推理,延迟略升但通常可接受;若峰值经常超过 batch,表现为属性更新变慢或日志里出现排队。此时优先加大 batch 并重建 engine,而不是盲目加路数。
压测时同时看业务 FPS 与tegrastats,口径见 Orin 上同一模型 FPS 不同:测量方法、功耗和前后处理拆解;壳温与降频见 Orin 上功耗档与降频排查。
图3. 路数 batch 与目标 crop batch 是两件事
9. 在 probe 里读二次结果
二次分类结果挂在对象的classifier meta链表上。probe 仍挂在 tracker(或 sgie)之后,遍历NvDsObjectMeta:
# 示意:读取二次属性并写入事件defsgie_probe(pad,info,u_data):batch_meta=pyds.gst_buffer_get_nvds_batch_meta(hash(info.get_buffer()))l_frame=batch_meta.frame_meta_listwhilel_frame:frame_meta=pyds.NvDsFrameMeta.cast(l_frame.data)l_obj=frame_meta.obj_meta_listwhilel_obj:obj=pyds.NvDsObjectMeta.cast(l_obj.data)attrs=[]l_classifier=obj.classifier_meta_listwhilel_classifier:cmeta=pyds.NvDsClassifierMeta.cast(l_classifier.data)l_label=cmeta.label_info_listwhilel_label:label=pyds.NvDsLabelInfo.cast(l_label.data)attrs.append({"name":label.result_label,"score":label.result_prob,})l_label=l_label.nextl_classifier=l_classifier.nextifattrsandshould_emit(frame_meta,obj,attrs):event_queue.put(build_event(frame_meta,obj,attrs))l_obj=l_obj.nextl_frame=l_frame.nextreturnpyds.GST_PAD_PROBE_OK与系列《Orin 上 DeepStream 跟踪与事件输出》的事件 JSON 对齐时,可增加字段:
"attributes":[{"name":"uniform","score":0.91},{"name":"helmet","score":0.87}]probe 里仍遵守:只写队列,不调 GPIO、不调大模型。属性字段建议与主事件共用同一track_id,这样 Agent 侧可以「同一 track 在冷却时间内不重复处理」。
业务规则示例(伪代码):
defshould_emit(frame_meta,obj,attrs):ifobj.class_id!=0:returnFalseuniform=next((aforainattrsifa['name']=='uniform'),None)ifnotuniformoruniform['score']<0.6:returnFalsereturnin_roi(frame_meta,obj)andnotin_cooldown(obj.object_id)10. 资源不够时的调整顺序
多路 + 跟踪 + 二次同时开时,建议按下面顺序减压:
primary-gie的interval改为 1 或 2(隔帧检测)- 缩小
operate-on-class-ids,只对必要类别二次推理 - 提高
input-object-min-width/height,过滤远小目标 - 减小 sgie
batch-size或换更小的属性模型 - 减少路数或降分辨率
- 仍不够再评估升 SKU或DLA 分流(NX / AGX)
不要第一步就关 tracker:没有track_id,带属性的告警很难去重。
11. 多个 secondary-gie
需要两套属性(例如「工服」+「工牌 OCR 小网」)时,可再加[secondary-gie1]:
| 注意点 | 说明 |
|---|---|
gie-unique-id | 每个 secondary 必须不同 |
operate-on-gie-id | 可以都指向同一个 pgie |
| DLA | NX 16GB / AGX 可尝试 sgie0→DLA0、sgie1→DLA1 |
| 时延 | 串行多个 sgie 会累加延迟,要实测 P99 |
第二颗 DLA 与 engine 绑定关系见 DLA 分工一文;同一 core 建 engine、配置里却写另一个 core,是常见启动失败原因。
12. 常见误区与排查
图4. 二次推理排查顺序
| 现象 | 多见原因 | 处理 |
|---|---|---|
| 启动报 engine 错误 | sgie batch 与建引擎时不一致 | 按配置 batch 重建 engine |
| 没有任何二次结果 | operate-on-class-ids与主类 ID 对不上 | 对照 labels 与 pgie 类别表 |
| 只有 stream-0 有属性 | 只配了单路或 gie-id 引用错 | 检查每路 meta 与 unique-id |
| 一开 sgie 就掉帧 | 高峰 crop 数超过 batch 或 GPU 已满 | 减路数、加 interval、缩小 batch |
| 属性全错 | 二次 labels 顺序与模型不一致 | 用单张 crop 图离线验证 |
| DLA 开了更慢 | fallback 过多 | 看trtexec层日志,或回 GPU |
| 事件里没属性字段 | probe 在 sgie 之前 | 把 probe 挂到 sgie 之后 |
| 属性分数一直为 0 | classifier-threshold过高 | 先降到 0.3 做联调,再拉回生产值 |
| 二次结果闪烁 | 主框抖动 + 无 track 冷却 | 开 tracker 或对属性做滑动平均 |
排查时一次只改一个变量:先关 sgie 确认主链稳,再只开 sgie 不对 analytics,最后才叠事件与 Agent。建议把每步的 FPS、GPU 占用、事件条数记进表格,方便和 Orin 上用 DeepStream 跑多路检测 里的压测口径对齐。
13. 与后续链路的衔接
| 下一步 | 说明 |
|---|---|
| 事件 + 属性进 Agent | 见 Orin 上边缘 Agent 落地 |
| 属性触发 GPIO | 见 Orin 上检测事件接 GPIO 与继电器 |
| INT8 二次模型 | 见系列《Orin 上 INT8 与量化部署》 |
| 量产固化 | 见 Orin 上开机自启跑检测docker容器(含完整脚本) |
| 总览 | 见 NVIDIA Jetson Orin 完全使用手册 |
14. 术语对照
| 术语 | 含义 |
|---|---|
| pgie | Primary GIE,主检测推理插件 |
| sgie / secondary-gie | Secondary GIE,对主检测框做二次推理 |
| operate-on-class-ids | 只对指定主检测类别做二次推理 |
| classifier meta | 二次分类结果附在对象 meta 上的链表 |
| process-mode | nvinfer 工作模式(检测 / 分类等) |
| gie-unique-id | 推理组件在 pipeline 内的唯一编号 |
| ROI | Region of Interest,感兴趣区域;此处指主检测框裁剪出的小图 |
| TensorRT engine | 在目标板上编译好的推理引擎文件,batch 与精度写死在构建参数里 |
| interval | 主检测隔帧推理间隔;设为 1 表示隔一帧检一次,可腾出 GPU 给 sgie |
15. 上线前检查清单
| 检查项 | 通过标准 |
|---|---|
| 主检测 4 路 FPS | 与加 sgie 前相比,掉幅在可接受范围(例如不超过 15%) |
| secondary engine batch | 与[secondary-gie0]的batch-size一致 |
operate-on-class-ids | 与 pgie labels 里「人」的 ID 一致 |
| probe 挂载位置 | 在 sgie 之后,事件 JSON 含attributes |
| DLA engine(若启用) | use-dla-core与trtexec --useDLACore一致 |
| 草稿箱标识 | 保存后见GEO检测与高质量 |
Orin 上 DeepStream 二次推理,核心是把辅模型从主检测里拆出来,用operate-on-class-ids控制算力,用独立的 engine 与 batch 验收。主链(多路 + 跟踪)稳了之后再加 sgie,属性结果通过 classifier meta 进事件层,后面才能稳妥接 Agent 与 GPIO。上线前请在本机跑通「关 sgie / 只开 sgie / 全链路」三档对照,并记录 FPS 与显存;具体字段与 sample 路径以当前 JetPack 自带的deepstream-app配置为准。