# Vision Anti-Pinch — lazy-inference 从 `inference-only` 分支派生的纯 C++ 惰性推理工程。保留该分支的 YOLO / RTMW / 时序融合 / mask 风险流程,在执行推理前加入 CAN 门状态控制。模型启动时加载、预热一次,暂停期间保持 TensorRT 引擎、上下文及显存,不反复加载。 ## 触发规则 接收 CAN / CAN FD `0x391`,`data[0..3]` 按百分比解释,顺序为左前、右前、左后、右后。当前仅使用左前和左后,任一满足即可检测。 | 状态 | 推理行为 | |---|---| | 左前或左后开度 **<15%**,且正在增加(开门) | 检测并计算风险 | | 左前或左后开度 **<15%**,且正在减小(关门) | 检测并计算风险 | | 恰好 15%、大于 15%,或两门均静止 | 暂停模型推理 | | 从未收到有效报文、报文超时或左门开度值无效 | 开启检测 | | CAN 恢复且不满足运动条件 | 停止检测 | 开度不变的重复报文不会立即抹掉运动方向。默认最后一次开度变化后 **300 ms** 仍沿用方向,超过该时间判定静止,避免整数百分比报文导致频繁启停。到达 0% 后也保留这段检测时间。首次报文及超时后的首次报文只建立基线,不将断线前后的差值当作运动。 CAN 默认 **1000 ms** 超时。上述参数可在 `config/camera.json` 的 `lazy_inference` 中调整;15% 使用严格小于。USB CAN 缺失时后台持续重试,恢复后自动接收。当前不发送执行器指令。 相机每次取帧默认等待 **2000 ms**,连续超时 **6 次**才停止流水线(纯超时约 **12 秒**);成功取得有效帧后清零超时计数。重试期间 CAN 继续接收。单次等待时间由 `camera.timeout_ms` 配置;运行中的坏帧、取流错误共用该重试期限;设备失效时尝试重新打开相机,恢复有效帧后继续推理。GPU 错误仍按原有异常处理退出。 ## 编译和运行 目标设备:Orin / JetPack 5.1.2 / CUDA 11.4 / TensorRT 8.5 / OpenCV 4.5,构建需 CMake 3.24+。 ```bash ./scripts/build.sh ./scripts/initialize_camera.sh # 摄像头尚未初始化时 ./run_camera.sh ./run_camera.sh --visualize # 可选本地 OpenCV 窗口 ./run_camera.sh --duration 30 # 运行 30 秒后正常退出 ``` Orin 独立包可单独复制运行: ```bash cd orin ./scripts/initialize_camera.sh ./run.sh ``` `orin/build/camera_risk` 是 ARM64 C++ 可执行程序。运行不依赖 Python。修改源码后,在 Orin 编译并运行 `./scripts/package_orin.sh` 更新独立包。`cd orin && sha256sum -c SHA256SUMS` 检查文件完整性。 ## 流水线和下游结果 摄像头持续采集、CAN 持续接收。启用时运行采集、YOLO、RTMW/融合/风险三阶段流水线;暂停时继续取帧以保持画面新鲜,但不执行 YOLO、RTMW 或风险计算。暂停前已提交的 GPU 工作可以完成,其结果会被丢弃。状态切换会清空待处理帧,恢复时重置跟踪与时序融合,不使用暂停前的历史人物状态。 ```cpp cms::CameraPipeline pipeline(cms::loadConfig("config/camera.json")); pipeline.start(); // 下游线程中: auto state = pipeline.inferenceStatus(); // state.models_ready / enabled / can_valid / reason // state.positions[0]:左前;state.positions[2]:左后 // state.direction[0..1]:左前/左后,-1 关门,0 静止,+1 开门 auto result = pipeline.latest(); // shared_ptr if (result) { const auto& risk = result->risk; const auto& people = result->persons; } // 启动、暂停或等待恢复后的首帧时,result 为空;不能解释为风险 clear。 // 退出:pipeline.requestStop(); pipeline.join(); ``` `latest()` 不返回旧运行周期的风险;调用者已持有的旧快照不会被修改,应重新读取状态和结果。`FrameResult::inference` 保存生成结果时的 CAN 状态和 generation。状态变化后的下一次读取会反映新的周期。`running()` 表示整个服务在运行,暂停推理时仍为 true。 默认结果仅保存在内存变量,不保存视频或逐帧文件。标准输出记录启停原因和累计推理次数;`stats()` 提供 detector_runs / pose_runs / skipped_idle 等计数。可视化暂停时显示 IDLE,不继续显示旧风险。 ## 目录 - `src/`、`include/`:C++ / CUDA 源码及公共接口。 - `config/`:门 mask、摄像头及惰性推理配置。 - `orin/`:模型、可执行程序、CAN 库、相机驱动及配置。 - `scripts/`:编译、初始化、打包;`third_party/`:JSON 依赖。 模型和推理算法沿用纯推理基线。当前门 mask 为研发机位配置,装车需按实际机位标定。 ## 可选保存(默认全部关闭) 三个独立参数可以单独使用,也可以组合: ```bash ./run_camera.sh --save-original --save-annotated --save-results # 独立包:./orin/run.sh --save-original --save-annotated --save-results ``` 开启任一项时,程序自动创建可执行文件所属工程的 `output/` 及三个子目录: - `original/`:`--save-original` 保存未叠加的原图,JPEG 质量 95。 - `annotated/`:`--save-annotated` 保存带框、关键点及风险文字的 JPEG,无需开启可视化窗口。 - `results/`:`--save-results` 每个完成推理的帧保存一个 JSON,包含帧号、检测框、跟踪 ID、关键点及置信度、风险和耗时;有 CAN 的分支还包含相应状态。 同一帧三类文件同名(扩展名不同),文件名包含运行标识和帧号,多次运行不覆盖。JSON 的 `images` 提供相对于 `output/` 的图片路径,未选择保存的图片为 null。源码入口写入工程 `output/`;独立包入口写入 `orin/output/`。默认不创建目录、不保存文件。 保存对象是完成推理的帧;惰性分支暂停时不新增文件。开启保存会增加编码和磁盘 I/O 开销。写入失败时报告错误并停止,不静默丢失文件。 相机超时回归测试:`./tests/run_camera_timeout.sh`;加 `--wall-clock` 验证默认 6 次超时约 12 秒。测试直接编译实际相机代码,使用模拟采集和转换器,无需摄像头、GPU 或 OpenCV;不覆盖视频回放路径。 板端真实摄像头 / TensorRT 恢复与停止测试记录见 [Orin 测试报告](tests/ORIN_TEST_REPORT.md)。