Files
vision-anti-pinch/README.md
T

179 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vision Anti-Pinch
C++ / TensorRT 实时摄像头风险推理,算法来自最新 run_risk.py。
持续取图、推理并更新内存中的 FrameResult;实时推理、录制和回灌默认开启可视化。
相机每次取帧默认等待 **2000 ms**,连续超时 **6 次**才停止流水线(纯超时约 **12 秒**);成功取得有效帧后清零超时计数。单次等待时间由 `config/camera.json` 的 `camera.timeout_ms` 配置。运行中的坏帧、取流错误共用该重试期限;设备失效时尝试重新打开相机,恢复有效帧后继续推理。GPU 错误仍按原有异常处理退出。
## Orin 直接运行
适用环境:Jetson AGX Orin、JetPack 5.1.2 / L4T R35.4.1、CUDA 11.4、TensorRT 8.5.2、OpenCV 4.5.4。
仓库已包含该板端编译的程序和重新构建的 TensorRT 引擎。
```bash
git clone https://git.kendeji.fun/zmv/vision-anti-pinch.git
cd vision-anti-pinch/orin
./scripts/initialize_camera.sh # 开机后初始化 /dev/video5 摄像头链路
./run.sh # 持续推理并显示预览,结果只保存在内存
```
默认显示本地预览,叠加门区、检测框、手臂/手部关键点及风险信息。需要关闭预览时:
```bash
./run.sh --no-visualize
```
`--visualize` 仍可显式开启;没有 DISPLAY / WAYLAND_DISPLAY 时自动跳过窗口并继续推理。
Ctrl+C / SIGTERM 停止程序。
关闭预览窗口或按 q/Esc 只关闭预览,推理继续。
摄像头为 /dev/video5,1920×1536 UYVY,30 FPS。
当前 door mask 是指定的 constant_door_mask.json 副本,标定状态为 false;装车后应更换与实际画面匹配的门区并更新标定状态。
## 下游获取结果
main.cpp 注释中也包含以下示例。下游通过自己的调用位置或线程读取:
```cpp
auto result = pipeline.latest();
if (result) {
const auto& risk = result->risk;
const auto& people = result->persons;
// result->frame_index / track_ids / boxes
// result->image / door_mask_pixels / capture / timing
// people[i].selected / smoothed / temporal:各 133 个关键点
}
```
latest() 返回 shared_ptr<const FrameResult>;首帧尚未完成时为空。
公共定义位于 include/camera_pipeline.hpp 和 include/frame_result.hpp。
## 从源码构建
```bash
./scripts/build.sh
./scripts/initialize_camera.sh
./run_camera.sh
```
源码构建要求 CMake 3.24+、CUDA/NPP、TensorRT 和 OpenCV 开发依赖。
构建脚本会优先使用 Orin 上已有的 ~/miniconda3/envs/yolo/bin/cmake。
源码配置直接使用 orin/engines/ 中的板端引擎。该引擎仅适用于上述 Orin 环境;仓库不包含 ONNX 或模型转换脚本。
独立库目标为 camera_risk_core;可视化在 camera_risk_visualizer。
## 目录
- src/、include/:采集、推理、结果变量及可选绘制。
- config/:源码构建程序的配置和固定门区。
- scripts/:构建和摄像头初始化脚本。
- orin/:已编译程序、TensorRT 引擎、配置、当前 GMSL 驱动及启动脚本。
- third_party/:nlohmann JSON 及其许可证。
NPP 用于 UYVY 转换、YOLO 缩放填充、RTMW 仿射裁剪;CUDA 用于通道转换与归一化。
风险几何计算仍包含 CPU/OpenCV 操作;算法内部的临时 JSON 对象只在内存中使用。
当前摄像头驱动启动时偶发首帧 ERROR,应用会丢弃并计数;持续错误、采集超时或推理错误会明确报错并退出。
现有 GMSL 驱动停止采集时可产生 videobuf2 缓冲区告警,v4l2-ctl 同样复现;需在驱动侧完成量产修复和验证。
## 可选保存(默认全部关闭)
三个独立参数可以单独使用,也可以组合:
```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)。
## 推理过程录制与视频回灌
当前 `inference-only` 分支支持 H.264/MP4 会话;默认关闭,不再为会话逐帧保存图片。
```bash
# 录制实际完成推理的帧,Ctrl+C / SIGTERM 正常停止并封装视频
./run_camera.sh --record-session output/sessions/drive-001
# 视频解码后重新运行 YOLO、RTMW、融合与风险推理;文件结束自动退出
./run_camera.sh --replay-session output/sessions/drive-001 --save-results
# 同时保存回灌后的新视频及推理过程,便于对比
./run_camera.sh --replay-session output/sessions/drive-001 \
--record-session output/sessions/drive-001-replay
```
独立包使用同样参数,例如 `./orin/run.sh --record-session output/sessions/drive-001`。
会话路径相对于当前工作目录;录制目录必须尚不存在,避免覆盖原始记录。
会话内容:
- `frames.mp4`:H.264 有损压缩视频,按完成推理的顺序编码,尺寸与原图一致。
- `frames/*.json`:对应视频帧序号、原推理帧号、完整采集元数据、检测框、原始/隔离关键点、融合记录、最终结果与耗时。
- `frames.txt`:视频帧与 JSON 的顺序索引。
- `session.json`、`door_mask.json`:录制时的推理参数、分辨率、名义帧率、门区副本、模型路径与大小。
- `complete.json`:正常停止后写入的帧数与视频大小,用于检查会话是否完成。
回灌不打开摄像头。它恢复会话中的分辨率、检测阈值、人数上限、风险阈值和门区,使用 `--config` 指定的当前 TensorRT 引擎,按顺序尽快推理,不按墙钟时间限速。视频使用配置的固定名义帧率;真实采集间隔和断流间隔以 JSON 中的纳秒时间戳为准。原始采集时间在再次录制时保存为 `source_capture`,回灌耗时使用本次运行的单调时钟。
编码使用 OpenCV 的 FFmpeg H.264 编码器;需 OpenCV `videoio` 与可用的 H.264 编码支持,宽高必须为正偶数。不支持时明确报错。当前使用软件编解码,会增加 CPU、编码延迟和磁盘开销;保存对象是完成推理的帧,并非相机全部原始帧。H.264 有损压缩和不同引擎可能改变检测、关键点与风险结果,不承诺逐位一致。模型只记录来源和大小,不复制引擎,也不保证大小相同即模型相同;精确对比应使用同一组引擎。
Ctrl+C / SIGTERM 会封装已写入的视频,`CameraPipeline::join()` 返回前完成收尾。强制杀进程、断电或写入失败可能留下 `frames.pending.mp4`;缺少完成标记的会话拒绝回灌。编码器封装后检查帧数,回灌遇到缺帧、缺失 JSON、损坏索引或解码错误会报错,不静默跳帧。录制没有自动限时或磁盘轮转,请按可用空间控制录制时长。
编解码及会话回归:`./tests/run_session.sh`,无需摄像头或 TensorRT。真实摄像头与 TensorRT 验证脚本:`python3 tests/orin_session_test.py --binary build/camera_risk --config config/camera.json --output /tmp/new-session-test`;输出目录必须不存在,脚本不会停止已有部署进程。
本次实机结果与未覆盖范围见 [H.264 会话测试报告](tests/ORIN_SESSION_REPORT.md)。
## CAN/CANFD 收发与四门位置显示
实时运行默认打开参考工程 `/home/orin/vision-anti-pinch` 使用的 USBCANFD(设备类型 33、设备索引 0),启用 CH0/CH1:60 MHz 时钟、仲裁域 500 kbps、数据域 2 Mbps。一个合并接收线程读取两个通道,默认启用终端电阻。可在 `config/camera.json` 的 `can` 中设置 `enabled`、`device_index`、`timeout_ms`(默认 1000 ms)和 `termination`;`--no-can` 显式关闭实时 CAN。设备不可用时显示 UNAVAILABLE,摄像头推理继续;接收 API 失败显示 ERROR,需检查设备后重启程序。
首次使用 USB-CAN 时,若节点为 root:root 且普通用户不可写,可运行 `./scripts/enable_can_access.sh`(需要 sudo)。它只为 `3068:0009` 适配器安装 `plugdev` 组读写规则并刷新设备权限;运行用户须属于 `plugdev`,新加组后需重新登录。无需以 root 运行整个推理程序。
`0x391` 四个信号按参考工程注释中的四个连续字节解析:
| 字节(从 0 开始) | 预览标签 | 信号 |
|---|---|---|
| 0 | LF | 左前门实际位置反馈 |
| 1 | RF | 右前门实际位置反馈 |
| 2 | LR | 左后门实际位置反馈 |
| 3 | RR | 右后门实际位置反馈 |
显示值是无符号原始字节 0–255,尚无物理单位、缩放或无效值定义,因此不标为百分比、不把 255 擅自视为无效。接收 CAN/CANFD 数据帧,过滤远程帧、错误帧和发送回显;少于 4 字节的 `0x391` 不更新信号。修正了参考代码 `get_bit_value(0/1/2/3, 8)` 导致的位域重叠,字节起始位应为 0/8/16/24。
默认预览为两个通道分别显示四门位置、LIVE/STALE/WAIT、新鲜度及帧数,并显示总 RX/TX 和错误计数。尚未接收到值时显示 `--`,超时保留最后值但标记 STALE;两个通道互不覆盖。即使相机正在重试或暂无新推理帧,CAN 预览仍约每 100 ms 刷新。CAN 只提供状态显示和下游数据,本次没有新增基于门位置的推理启停或风险门控。
下游通过 `pipeline.canStatus()` 读取当前状态;`pipeline.latest()->can` 是该推理结果生成时的快照。按需发送使用线程安全的 `pipeline.sendCan()`:
```cpp
cms::CanFrame frame;
frame.channel = 0; // CH0 或 CH1
frame.id = 0x123; // 按实际协议设置
frame.data = {0x01, 0x02}; // 示例数据,应替换为实际报文
bool sent = pipeline.sendCan(frame);
// CANFD:frame.fd = true;可按协议设置 extended、bitrate_switch。
// CAN 数据长度 0–8;CANFD 长度 0–8、12、16、20、24、32、48、64。
// 标准 ID 最大 0x7FF,扩展 ID 最大 0x1FFFFFFF。
```
非法通道/ID/长度/标志抛出 `std::invalid_argument`;设备未就绪或 SDK 发送失败返回 false,发送计数以 SDK 返回为准。接口不自动填充/截断数据,不自动发送周期或风险控制报文。发送调用与设备关闭互斥;双通道接收保持独立线程运行。
结果 JSON 和录制会话自动包含每帧 `can` 快照。视频回灌不打开 USB-CAN,不发送报文,直接恢复录制时的状态并标记 `source=replay`;旧会话没有 CAN 字段时显示 NOT_RECORDED。回灌调用 `sendCan()` 会明确拒绝。
依赖 `third_party/zlg/zcan.h` 和 `orin/lib/` 中的参考工程 SDK 库(含 SONAME 链接),独立包已包含它们。纯模拟收发回归:`./tests/run_can.sh`;会话回归同时覆盖 CAN 快照保存。模拟测试不代表物理总线收发验证。
本次检查范围与延期项目见 [CAN 实现记录](tests/ORIN_CAN_REPORT.md);按当前要求暂不验证真实 CAN 接收。