投放点垃圾分类 — 部署指南

套餐

摄像头 + reComputer J30 / J40(Orin)

Jetson Orin 用 TensorRT 运行分类器,提供网页、触发接口和 MQTT 输出。首次部署在设备上构建 TensorRT engine。可选开放词汇分类(步骤 4),在基线跑通后再切换。

  • 摄像头: USB 或 IP 摄像头,俯视投放区,一次拍一件。
  • 可选外设: 实体按钮作为触发源;翻盖、继电器或指示灯由 actuator 回调驱动。接线与 GPIO 读取需自行集成。

使用限制:

  • 中国四分类映射由本项目维护,各城市口径不同,不要作为收费、处罚或合规判定的唯一依据。
  • 一帧只分类一件物品,放两件时只输出一个结果。
  • textile(布料)没有训练数据,不会被识别;hazardous(有害垃圾)不会输出。
  • 训练数据是干净的单件物品照片,湿的、压扁的、堆叠或装袋的垃圾上精度会下降,请用现场数据验证。
1

部署垃圾分类

填写设备、摄像头和分类器选项,部署步骤会下载模型、构建 engine,并启动分类器和本地 MQTT broker。首次启动需要等待 engine 构建完成。

前置条件
  • JetPack 6.x 的 Jetson Orin,已配置 NVIDIA container runtime。
  • 至少 10 GB 可用磁盘。
  • 使用 USB 摄像头时,在 assets/jetson/docker-compose.yml 里取消注释对应的 /dev/videoN 行,不要挂载整个 /dev
  • 容器镜像需在设备上从上游仓库构建,retag 成 compose 文件里的名字,或把 WASTE_IMAGE 设为本地 tag。
  • 分类器选项:默认 baselineopen_vocab 见步骤 4。
故障排查
现象处理
This target is not a NVIDIA Jetson目标机器不是 Jetson,换成 Jetson Orin。
trtexec not found执行 sudo apt install tensorrt
WARNING: nvidia runtime missing执行 sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker,然后重新部署。
Engine 构建报 Static model does not take explicit shapes去掉 --minShapes/--optShapes/--maxShapes 参数。
ONNX 的 sha256 对不上文件不是发布版本,不要继续。删除设备上的 ONNX 文件后重新部署,会重新下载。
找不到 docker compose手动安装 docker-compose-plugin
Compose 解析 ._docker-compose.yml 失败在设备上执行 find … -name '._*' -delete 删除 Mac 带来的附属文件。
容器起来了但没有相机取消注释 compose 文件里的 /dev/videoN 行。
edge-waste-mosquitto 一直重启,报 Address in use1883 被其他 broker 占用。把 config/mosquitto.confconfig/config.json 里的 mqtt.port 改成空闲端口(如 18831),再执行 docker compose up -d --force-recreate mosquitto
部署方式

从本机通过 SSH 部署到 Orin,填写设备 IP、SSH 凭据、摄像头地址和分类器选项。

部署时需要填写(在 SenseCraft Solution 应用里填)
  • Jetson IP*
  • SSH 用户名*默认 recomputer

    常见默认值:recomputer(Seeed reComputer J30 / J40 系列)、nvidia 或 ubuntu。

  • SSH 密码*
  • 设备名称*默认 orin-nx

    MQTT 主题的第一段——按垃圾桶或投放点命名,多台设备在同一个 broker 上就不会混

  • 流编号*默认 bin1-cam1

    会拼在 MQTT 主题里,用于区分同一台设备上的多路相机。它同时也在 payload 里——消费者必须从 payload 读,不要解析 topic。

  • 相机类型*默认 usb
    3 项可选
    • · USB 摄像头(/dev/videoN)
    • · IP 摄像头(RTSP 地址)
    • · 视频文件——没有相机时的台面测试
  • 相机地址*

    用 USB 相机时还要在 assets/jetson/docker-compose.yml 里把对应的 /dev/videoN 那行取消注释——否则容器里看不到视频节点。不要整个挂载 /dev。

  • 分类器 Track*默认 baseline
    2 项可选
    • · 基线 EfficientNet-Lite0(m1c)——top-1 更高,13.5 MB,默认项
    • · 开放词汇 SigLIP 2——校准更好、不重训就能加类,372 MB,任何板卡上都无实测耗时

    baseline 是闭集 EfficientNet-Lite0 头(m1c):在这套分类法上 top-1 比 open_vocab 更高(同一份 val 上 0.8877 对 0.8501),CPU 上大约快 4-5 倍(两条 track 都没有 Jetson TensorRT 实测数字)。原基线 MobileNetV3-Small(m1b)已被取代,因为它在每一条测过的边缘链路 (Hailo、RK3576、RK3588)上都出现 INT8 量化塌缩——详见方案页。 open_vocab 是 SigLIP 2 视觉塔对文本原型打分:校准更好、能给开放集 拒识分数、中英文都能回答、不重训就能加类——但 372 MB,从未在任何 板卡上构建过 engine,也没有任何 Jetson 时延数字。两条 track 都没有 上板实测过。

  • 触发源*默认 http
    4 项可选
    • · 仅 HTTP——POST /trigger
    • · HTTP + 实体按钮
    • · HTTP + 画面移动侦测
    • · 三者全开

    哪些来源会触发一次拍摄。HTTP 是 8080 端口上的 POST /trigger,也是本次部署唯一能自己验证的一个;按钮与移动侦测需要接线或布好画面。

  • 触发去抖(毫秒)*默认 800

    在上一次触发之后这个时间窗内到达的触发会被合并进正在处理的请求,而不是排队。低于约 300 ms 时,抖动的按钮一次按压会触发两次。

  • 最低置信度*默认 0.5

    低于这个值时事件带 rules.fallback_category(residual)而不是 argmax。基线 track 在 val 上有 4.5% 的图低于 0.5;调高它意味着更多东西被送进其他垃圾档,而不是去猜。

2

查看实时分类画面

打开设备自带的页面,查看实时画面、触发按钮和最近的分类结果。在验证前用它对准摄像头。

故障排查
现象处理
页面打不开在设备上执行 docker ps 确认 waste 容器在运行,查看 docker logs edge-waste-app
预览是黑的USB 摄像头检查 /dev/videoN 是否已挂进容器;RTSP 先用 VLC 测试地址。
预览正常但 /events 一直空还没有触发。默认模式下只在触发时分类。
物品在画面里很小调整摄像头,让物品占满大部分画面。
3

接好触发并确认一次分类

把一件物品放到摄像头下,触发一次,确认 MQTT 收到一条分类结果。

前置条件
  • 步骤 1 已完成,容器在运行。
  • 同网络的机器上有 mosquitto_sub,或使用 broker 容器:docker exec edge-waste-mosquitto mosquitto_sub …
  • 一件非布料的待分类物品。
故障排查
现象处理
一条消息都没有查看 /healthz 的触发计数;不增长时检查 config/config.json 里的 trigger.sources
按一次按钮出两条消息调高 trigger.debounce_ms,不低于约 300 ms。
类别总是 residual置信度低于 rules.min_confidence。检查光照、取景,以及物品是否属于八个类别。
玻璃与塑料判错两者都属于可回收物,四分类结果不受影响。
大多判成 organic改善取景与光照;彻底解决需要重新平衡数据后重训。
布料物品被判成别的属已知限制,布料没有训练数据。
物品不是生活垃圾基线分类器无法拒识词表外物品,需要时切换到步骤 4。
部署完成后

分类器已运行,并完成一次端到端分类。

快速验证

  1. 打开 http://<设备IP>:8080/,确认画面对准投放区,物品占满大部分画面。
  2. 订阅:mosquitto_sub -h <设备IP> -t '<设备名>/waste/+/results' -v
  3. 触发一次:curl -X POST http://<设备IP>:8080/trigger
  4. 确认收到一条消息,categorytop3[0] 一致。
  5. 在 800 ms 内触发两次,确认只收到一条消息。

MQTT 消息

每次分类发布一条 JSON,主要字段如下(stream_id 从 payload 读取,不要解析主题):

{
  "type": "waste_sorting_result",
  "stream_id": "bin1-cam1",
  "timestamp": 1757030400123,
  "trigger": "button",
  "category": {
    "class_name": "plastic",
    "china_category": "recyclable",
    "china_category_zh": "可回收物"
  },
  "confidence": 0.913,
  "top3": [
    {"rank": 0, "class_name": "plastic", "confidence": 0.913, "china_category": "recyclable"},
    {"rank": 1, "class_name": "glass", "confidence": 0.052, "china_category": "recyclable"},
    {"rank": 2, "class_name": "residual", "confidence": 0.021, "china_category": "residual"}
  ],
  "image_ref": {
    "kind": "local",
    "uri": "/var/lib/edge-waste-sorting/captures/2026-09-05/bin1-cam1-4207.jpg"
  },
  "model": {"name": "efficientnet_lite0_waste8", "accelerator": "tensorrt"}
}

下一步

  • 接翻盖或指示灯:设置 "actuator": {"enabled": true, "min_confidence": 0.5} 并编写集成代码。
  • 正式使用前把 MQTT 指向带凭据的 broker,自带 broker 允许匿名连接,只用于调试。
  • 需要识别词表外物品或不重训加类时,见步骤 4。
  • 采集现场数据验证精度。
4

切换到开放词汇分类(可选)

换成 SigLIP 2 开放词汇分类器:可以拒识词表外物品、中英文输出、不重训加类,代价是 top-1 精度下降、时延上升。

前置条件
  • 步骤 1 已用 model_track: baseline 跑通。
  • 额外约 372 MB 放视觉模型,另需 engine 空间。
  • 原型库和 meta 文件需手工拷到设备,并用 assets/models/SHA256SUMS.open_vocab 执行 sha256sum -c 校验。
  • 仅 Orin 套餐支持。
故障排查
现象处理
Engine 构建比基线久得多属正常,不要中途终止。
Engine 构建报 Static model does not take explicit shapes去掉 --minShapes/--optShapes/--maxShapes 参数。
时延明显变高属正常。无法接受时切回 baseline
置信度整体变了修改过 temperature 后需要重调 min_confidence;标定值是 0.0075。
换成中文四分类原型库后四分类精度下降改用先分八类再映射四分类的层级方式。
未知物体仍拿到高置信度材质标签residual 类上的拒识效果较弱,属已知限制。