vision-anti-pinch-0923

以 Orin ~/vision-anti-pinch 的 V4L2 MMAP 摄像头及 USBCANFD 模块为基线,接入 CMS v9.10 run_risk.py 的完整风险流程。模型及 GPU 推理代码来自远端 ~/vision-anti-pinch-0920。

基线 V4L2 MMAP → NPP UYVY 转 BGR → 最新帧槽位
 → YOLO11s TensorRT → RTMW-L TensorRT(含小人遮挡隔离推理)
 → Python 原版轨迹 / 手臂手部融合 / 全局手关联
 → 骨骼主导的前后门归属 → 固定 mask 风险 → 逐帧 CAN 活跃门过滤
 → 内存中的最新只读结果(可选 JSONL / 浏览器预览)

仓库分支与 Orin 直接运行包

  • main:原 CMS v9.10 Python/ONNX 部署包。
  • inference-only:保留原 main 的纯 C++ TensorRT 推理版本。
  • use-can(本分支):0923 CAN 集成版本。

本分支已提供可单独复制的 orin/ 运行包,包含预编译库和 TensorRT 模型:

git clone --branch use-can https://git.kendeji.fun/zmv/vision-anti-pinch.git
cd vision-anti-pinch/orin
./scripts/initialize_camera.sh  # 开机后需要初始化摄像头时执行
./run.sh                      # 默认无可视化,结果保存在内存
./run.sh --visualize           # 可选预览

orin/run.sh 通过相对目录定位运行库、模型与标定,使用系统 Python,无需现场编译。目标环境及下游调用说明见 orin/README.md。 源码变更后,在目标 Orin 构建并执行 scripts/package_orin.sh 重新生成运行包;ENGINE_DIR 和 DRIVER_DIR 可指定模型/驱动目录。

两端目录和环境

  • 本机:/home/thor/文档/vision-anti-pinch-0923
  • Orin:/home/orin/vision-anti-pinch-0923
  • 目标推理环境:Orin,JetPack R35.4.1、CUDA 11.4、系统 Python 3.8、原生 TensorRT。
  • 本机保存完整源码、测试报告、模型副本;本机是 Thor / R38,未发现 /dev/video*。Orin 的序列化 engine 不能直接作为 Thor 的本机模型使用,需在 Thor 上另行转换。本次实时采集/推理在 Orin,浏览器可在本机查看。
  • 远端 models/engines 软链接到 ../../vision-anti-pinch-0920/orin/engines,避免重复占用有限磁盘。不要删除被引用的 0920 模型。
  • 原有远端 vision-anti-pinch / vision-anti-pinch-0920 源码均未修改。测试时已停止占用 /dev/video5 的旧 0920 演示进程,0923 可独立启动。

编译和运行(在 Orin)

cd ~/vision-anti-pinch-0923
./scripts/build.sh
# 开机后如 /dev/video5 不存在,加载已部署的 0920 摄像头驱动:
./scripts/initialize_camera.sh
# 编译也可使用 make
./run_camera.sh --active-door can --can-side left
# 等价入口:python3 run_risk.py --active-door can --can-side left

默认只在内存中发布结果,不创建结果目录,不绘制或编码图像,不启动 HTTP 服务。

后台启动、停止:

./scripts/start.sh                 # 无可视化
./scripts/stop.sh
./scripts/start.sh --visualize     # 可选浏览器预览

默认从 /dev/video5 采集 UYVY 1920×1536,驱动当前为 30 FPS。驱动不支持 VIDIOC_G_PARM,程序会打印警告并继续采集,命令行 fps 不代表驱动一定支持修改。

  • 开启 --visualize 后,预览:http://192.168.195.100:8765
  • 开启 --visualize 后,最新结果:http://192.168.195.100:8765/result.json
  • 开启 --visualize 后,最新图像:http://192.168.195.100:8765/frame.jpg
  • 后台进程日志:logs/live.log,PID:logs/camera.pid;加保存开关后,后台结果写入 outputs/live/。
  • --visualize 开启预览;仅设置 --preview-port 不会开启预览。监听地址默认 0.0.0.0:8765,可用 --preview-host 127.0.0.1 限制为本机。可视化在独立观察线程中绘制和编码,只保留最新待绘制帧。端口占用或绘制失败不会终止推理;关闭浏览器也不会停止推理。

摄像头限定时长、模拟门状态:

./run_camera.sh --no-can --active-door both --duration 30 --save-results --output outputs/demo

有人视频回放(同一 TensorRT/融合/风险链路):

./run_camera.sh --video tests/person-test.mp4 --no-can --active-door both \
  --max-frames 150 --save-results --save-pose-records --output outputs/replay

tests/person-test.mp4 来自用户提供的 segment_009.mp4 第 10 秒起的 150 帧片段。

下游直接读取结果变量

参考 0920 的 LatestResult / CameraPipeline.latest(),0923 提供同进程 Python 接口:

from run_camera import CameraPipeline, parse_args

args = parse_args([])  # 默认 CAN 模式;不打开可视化,不保存文件
with CameraPipeline(args) as pipeline:
    result = pipeline.wait_for_result(timeout=10)  # 等待第一帧,可选
    # 在下游自己的循环/线程中读取当前完整快照:
    result = pipeline.latest_result  # 等价于 pipeline.latest()
    if result is not None:
        risk = result["risk"]
        door_risks = result["door_risks"]
        skeletons = result["skeletons"]
        can = result["can"]
        print(result["frame_index"], risk["risk_type"], risk["risk_score"])

完整持续读取示例:examples/read_latest.py,在 Orin 执行。示例会自己启动推理并占用摄像头,运行前先停止已有后台进程:

./scripts/stop.sh
python3 examples/read_latest.py
# 或开启可视化:
python3 examples/read_latest.py --visualize
  • latest_result 是单个最新结果变量,首帧前为 None。每帧完成风险计算后发布,早于任何可选绘制或文件写入。
  • 快照深度只读:字典使用只读映射,数组使用 tuple;字段和索引含义与原 JSON 一致。旧快照不会随下一帧变化,多个读取线程互不消费或修改结果。
  • 只保留最新一帧,没有结果队列;下游处理慢可跳过中间帧。需要每帧历史时显式开启 --save-results。
  • pipeline.wait_for_result(after_frame_index, timeout) 是可选等待接口,返回更新的最新结果;超时或结束且无新帧时返回 None。
  • pipeline.request_stop() 请求停止,pipeline.join() 等待退出并向调用方传播推理异常;停止后可读取最后快照及 pipeline.summary。使用 with 自动停止并回收资源。
  • 停止后的快照只是最后结果;持续控制时应同时检查 pipeline.running() 及帧序号/时间戳,避免使用旧结果。
  • 需要修改副本或 JSON 序列化时使用 from run_camera import result_to_dict,再调用 result_to_dict(result)。
  • 变量属于创建 CameraPipeline 的 Python 进程。另一个进程或 C++ 程序不能直接 import 读取后台服务的变量,需要另行使用 IPC/HTTP 接口;当前没有新增 C++ 风险结果 ABI。

可选输出开关

参数 行为
无输出参数 只发布内存结果,不绘制、不保存结果文件
--visualize 开启独立可视化线程和 HTTP 预览
--save-results 保存逐帧 JSONL 及约每 10 秒的 latest.json
--save-snapshots 约每 10 秒绘制并保存 latest.jpg,无 HTTP 服务
--save-pose-records 保存诊断姿态 JSONL
--write-video 绘制并保存叠加 MP4,无需打开预览

输出开关可组合。--output 仅指定保存目录,不会自动开启保存。未开启保存时,旧输出文件保留但不会更新。任一保存开关开启后,停止时额外保存 summary.json;默认内存模式的统计保存在 pipeline.summary 并打印到终端。

恢复此前的预览与文件输出行为:

./run_camera.sh --visualize --save-results --save-snapshots --output outputs/demo

CAN 约定

按用户确认的 0x391 数据布局:

字节 车门
data[0] 左前
data[1] 右前
data[2] 左后
data[3] 右后
  • --can-side left 使用 data[0]/data[2];right 使用 data[1]/data[3]。默认 left,请按摄像头安装侧选择。
  • 值为 0 时对应门活跃,每帧读取最新 CAN 快照。有效报文至少需要 4 字节。
  • 未收到有效报文或超过 --can-timeout-ms(默认 1000 ms),整体输出 CAN_UNKNOWN,门级输出 FRONT_UNKNOWN/REAR_UNKNOWN,分数为 null。
  • 此时 active_door=none 仅为内部无有效活跃门输入,业务必须读取 risk 和 can.valid,不能将其解释为已确认两门关闭。
  • 显式 --active-door front/rear/both/none 是模拟模式,输出 can.source=simulation,预览显示 CAN SIMULATION。--no-can 要求显式选择模拟状态。
  • 本项目提供最终风险结果;基线未提供防夹执行器的 CAN 指令协议,因此没有自行添加执行器报文。
  • 本次 USB-CAN VCI_OpenDevice 返回失败,lsusb 中未见 USB-CAN 设备;真实 0x391 报文链路尚未验证。连接适配器后重新启动程序;字节映射及门状态组合通过软件测试。

风险行为与机位标定

风险算法、默认 warning=0.30 / release=0.45、133 点骨骼、track_id、前后门归属、transition→unknown、无有效手部时人体框兜底均沿用原版。详见 README-risk-upstream.md。

annotations/constant_door_mask.json 和 door_zone_multifeature.json 原样复制,属于原研发机位。当前室内摄像头位置与其不一致,默认输出 calibration_verified=false,预览显示 CALIBRATION UNVERIFIED。实车验证需更换标定文件;--calibration-verified 仅标记经人工验证的标定,不会自动标定。

当前实时场景没有人,约 29 FPS 是该场景的实测值,不能当作多人满负载帧率。

优化与输出

  • 重用 0920 的常驻 TensorRT engine、CUDA/NPP 预处理、显存及锁页缓存、GPU UYVY 转换。
  • 独立取帧线程只保留最新帧,主动丢弃旧帧,避免积压;不像 0920 那样单独并行检测线程,完整 Python 融合阶段按结果顺序执行。
  • 风险 mask / 距离变换按原版缓存;开启预览时默认每 3 帧提交一次待绘制图像;开启快照保存时每 10 秒一次。
  • 长期运行清理过期轨迹,计时统计仅保留最近 3000 帧。
  • 开启 --save-results 后,causal_results.jsonl 默认每 32 MiB 轮换,保留 .1 和 .2;--jsonl-max-mb 0 可禁用轮换。读取全部历史时按 .2 → .1 → 当前文件 顺序处理。
  • --save-pose-records 额外保存同样轮换的诊断姿态记录;--write-video 录制叠加 MP4(视频不轮换,建议配合 --duration)。
  • 文件输出遵循上面的独立开关;关闭可视化仍可完整读取内存结果。
  • 新增 capture、can、capture_to_result_ms、calibration_verified 字段,原版核心 JSON 字段保留。
  • 驱动原始时间戳存在时钟偏移,保存在 capture.driver_timestamp_ms 供诊断。延迟采用主机单调时钟在 DQBUF 后取样,表示“主机取帧到风险结果”,不包含曝光到驱动交付的时间。

文件关系与测试

  • main.cpp, v4l2_capture.*, can_comm.*, include/zcan.h, lib/:基线工程。新工程修正错误 V4L2 buffer 处理;旧 main 同步修正 CAN 字节解析。
  • native/native_api.cpp:采集线程、CAN 快照、TensorRT 的 ctypes C 接口。
  • accelerator/:远端 0920 源码快照,当前构建只选取 TensorRT/预处理/检测/姿态源文件。
  • pipeline/:原版 Python 算法,推理适配在 native_backend.py;ONNX/Ultralytics 的可选导入移到旧离线入口,原版风险公式不变。
  • Makefile.baseline 和 make baseline:旧基线演示程序的编译方式;默认 make 编译新的原生共享库。
  • pipeline/latest_result.py:线程安全的单结果发布槽位及不可变快照。
  • examples/read_latest.py:同进程下游读取示例。
  • tests/test_latest_result.py:结果所有权、线程生命周期、可选输出、独立可视化及异常传播测试。
  • tests/test_risk_integration.py:CAN 左右门组合、失效状态、手部/人体兜底、transition、轨迹回收、日志轮换测试。
  • TEST_REPORT.md:实测结果和限制。
python3 -m unittest discover -s tests -v

本机重新部署源码(会使用 SSH 交互认证,不保存密码):

./scripts/deploy.sh orin@192.168.195.100

重新编译共享库前应先停止 0923 运行进程,再启动新进程加载新库。

S
Description
No description provided
Readme 366 MiB
Languages
Python 100%