Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0192fccbf0 | |||
| c1e2ea6577 |
+3
-6
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CMAKE_CUDA_ARCHITECTURES 87)
|
||||
set(CMAKE_CUDA_ARCHITECTURES native)
|
||||
endif()
|
||||
project(camera_risk LANGUAGES CXX CUDA)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
@@ -13,14 +13,11 @@ find_path(TENSORRT_INCLUDE_DIR NvInfer.h PATH_SUFFIXES aarch64-linux-gnu x86_64-
|
||||
find_library(TENSORRT_LIBRARY nvinfer REQUIRED)
|
||||
add_library(camera_risk_core
|
||||
src/trt_engine.cpp src/pose_engine.cu src/gpu_preprocess.cu src/uyvy_converter.cu
|
||||
src/detector.cpp src/door_risk.cpp src/camera.cpp src/camera_pipeline.cpp src/serialization.cpp src/output_writer.cpp src/result_renderer.cpp src/config.cpp src/lazy_gate.cpp src/can_input.cpp src/can_comm.cpp)
|
||||
src/detector.cpp src/door_risk.cpp src/camera.cpp src/camera_pipeline.cpp src/serialization.cpp src/output_writer.cpp src/result_renderer.cpp src/config.cpp)
|
||||
target_include_directories(camera_risk_core PUBLIC include third_party ${TENSORRT_INCLUDE_DIR} ${OpenCV_INCLUDE_DIRS})
|
||||
target_link_libraries(camera_risk_core PUBLIC ${TENSORRT_LIBRARY} ${OpenCV_LIBS} Threads::Threads CUDA::cudart CUDA::nppc CUDA::nppig CUDA::nppidei CUDA::nppicc
|
||||
"${CMAKE_SOURCE_DIR}/orin/lib/libusbcanfd.so" "${CMAKE_SOURCE_DIR}/orin/lib/libusb-1.0.so")
|
||||
target_link_libraries(camera_risk_core PUBLIC ${TENSORRT_LIBRARY} ${OpenCV_LIBS} Threads::Threads CUDA::cudart CUDA::nppc CUDA::nppig CUDA::nppidei CUDA::nppicc)
|
||||
target_compile_options(camera_risk_core PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wall;-Wextra;-Wpedantic>)
|
||||
add_library(camera_risk_visualizer src/visualizer.cpp)
|
||||
target_link_libraries(camera_risk_visualizer PUBLIC camera_risk_core)
|
||||
add_executable(camera_risk src/main.cpp)
|
||||
target_link_libraries(camera_risk PRIVATE camera_risk_visualizer)
|
||||
|
||||
set_target_properties(camera_risk PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH "$ORIGIN/../lib;$ORIGIN/../orin/lib;/usr/local/cuda/lib64")
|
||||
|
||||
@@ -1,81 +1,78 @@
|
||||
# Vision Anti-Pinch — lazy-inference
|
||||
# Vision Anti-Pinch
|
||||
|
||||
从 `inference-only` 分支派生的纯 C++ 惰性推理工程。保留该分支的 YOLO / RTMW / 时序融合 / mask 风险流程,在执行推理前加入 CAN 门状态控制。模型启动时加载、预热一次,暂停期间保持 TensorRT 引擎、上下文及显存,不反复加载。
|
||||
C++ / TensorRT 实时摄像头风险推理,算法来自最新 run_risk.py。
|
||||
持续取图、推理并更新内存中的 FrameResult;可视化可选。
|
||||
|
||||
## 触发规则
|
||||
相机每次取帧默认等待 **2000 ms**,连续超时 **6 次**才停止流水线(纯超时约 **12 秒**);成功取得有效帧后清零超时计数。单次等待时间由 `config/camera.json` 的 `camera.timeout_ms` 配置。运行中的坏帧、取流错误共用该重试期限;设备失效时尝试重新打开相机,恢复有效帧后继续推理。GPU 错误仍按原有异常处理退出。
|
||||
|
||||
接收 CAN / CAN FD `0x391`,`data[0..3]` 按百分比解释,顺序为左前、右前、左后、右后。当前仅使用左前和左后,任一满足即可检测。
|
||||
## Orin 直接运行
|
||||
|
||||
| 状态 | 推理行为 |
|
||||
|---|---|
|
||||
| 左前或左后开度 **<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+。
|
||||
适用环境:Jetson AGX Orin、JetPack 5.1.2 / L4T R35.4.1、CUDA 11.4、TensorRT 8.5.2、OpenCV 4.5.4。
|
||||
仓库已包含该板端编译的程序和重新构建的 TensorRT 引擎。
|
||||
|
||||
```bash
|
||||
./scripts/build.sh
|
||||
./scripts/initialize_camera.sh # 摄像头尚未初始化时
|
||||
./run_camera.sh
|
||||
./run_camera.sh --visualize # 可选本地 OpenCV 窗口
|
||||
./run_camera.sh --duration 30 # 运行 30 秒后正常退出
|
||||
git clone https://git.kendeji.fun/zmv/vision-anti-pinch.git
|
||||
cd vision-anti-pinch/orin
|
||||
./scripts/initialize_camera.sh # 开机后初始化 /dev/video5 摄像头链路
|
||||
./run.sh # 持续推理,结果只保存在内存
|
||||
```
|
||||
|
||||
Orin 独立包可单独复制运行:
|
||||
可选板端本地可视化:
|
||||
|
||||
```bash
|
||||
cd orin
|
||||
./scripts/initialize_camera.sh
|
||||
./run.sh
|
||||
./run.sh --visualize
|
||||
```
|
||||
|
||||
`orin/build/camera_risk` 是 ARM64 C++ 可执行程序。运行不依赖 Python。修改源码后,在 Orin 编译并运行 `./scripts/package_orin.sh` 更新独立包。`cd orin && sha256sum -c SHA256SUMS` 检查文件完整性。
|
||||
Ctrl+C / SIGTERM 停止程序。
|
||||
关闭预览窗口或按 q/Esc 只关闭预览,推理继续。
|
||||
|
||||
## 流水线和下游结果
|
||||
摄像头为 /dev/video5,1920×1536 UYVY,30 FPS。
|
||||
当前 door mask 是指定的 constant_door_mask.json 副本,标定状态为 false;装车后应更换与实际画面匹配的门区并更新标定状态。
|
||||
|
||||
摄像头持续采集、CAN 持续接收。启用时运行采集、YOLO、RTMW/融合/风险三阶段流水线;暂停时继续取帧以保持画面新鲜,但不执行 YOLO、RTMW 或风险计算。暂停前已提交的 GPU 工作可以完成,其结果会被丢弃。状态切换会清空待处理帧,恢复时重置跟踪与时序融合,不使用暂停前的历史人物状态。
|
||||
## 下游获取结果
|
||||
|
||||
main.cpp 注释中也包含以下示例。下游通过自己的调用位置或线程读取:
|
||||
|
||||
```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<const FrameResult>
|
||||
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 个关键点
|
||||
}
|
||||
// 启动、暂停或等待恢复后的首帧时,result 为空;不能解释为风险 clear。
|
||||
// 退出:pipeline.requestStop(); pipeline.join();
|
||||
```
|
||||
|
||||
`latest()` 不返回旧运行周期的风险;调用者已持有的旧快照不会被修改,应重新读取状态和结果。`FrameResult::inference` 保存生成结果时的 CAN 状态和 generation。状态变化后的下一次读取会反映新的周期。`running()` 表示整个服务在运行,暂停推理时仍为 true。
|
||||
latest() 返回 shared_ptr<const FrameResult>;首帧尚未完成时为空。
|
||||
公共定义位于 include/camera_pipeline.hpp 和 include/frame_result.hpp。
|
||||
|
||||
默认结果仅保存在内存变量,不保存视频或逐帧文件。标准输出记录启停原因和累计推理次数;`stats()` 提供 detector_runs / pose_runs / skipped_idle 等计数。可视化暂停时显示 IDLE,不继续显示旧风险。
|
||||
## 从源码构建
|
||||
|
||||
```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/`:C++ / CUDA 源码及公共接口。
|
||||
- `config/`:门 mask、摄像头及惰性推理配置。
|
||||
- `orin/`:模型、可执行程序、CAN 库、相机驱动及配置。
|
||||
- `scripts/`:编译、初始化、打包;`third_party/`:JSON 依赖。
|
||||
- src/、include/:采集、推理、结果变量及可选绘制。
|
||||
- config/:源码构建程序的配置和固定门区。
|
||||
- scripts/:构建和摄像头初始化脚本。
|
||||
- orin/:已编译程序、TensorRT 引擎、配置、当前 GMSL 驱动及启动脚本。
|
||||
- third_party/:nlohmann JSON 及其许可证。
|
||||
|
||||
模型和推理算法沿用纯推理基线。当前门 mask 为研发机位配置,装车需按实际机位标定。
|
||||
NPP 用于 UYVY 转换、YOLO 缩放填充、RTMW 仿射裁剪;CUDA 用于通道转换与归一化。
|
||||
风险几何计算仍包含 CPU/OpenCV 操作;算法内部的临时 JSON 对象只在内存中使用。
|
||||
|
||||
当前摄像头驱动启动时偶发首帧 ERROR,应用会丢弃并计数;持续错误、采集超时或推理错误会明确报错并退出。
|
||||
现有 GMSL 驱动停止采集时可产生 videobuf2 缓冲区告警,v4l2-ctl 同样复现;需在驱动侧完成量产修复和验证。
|
||||
|
||||
## 可选保存(默认全部关闭)
|
||||
|
||||
|
||||
@@ -18,10 +18,5 @@
|
||||
"warning_relative": 0.3,
|
||||
"release_relative": 0.45,
|
||||
"calibration_verified": false
|
||||
},
|
||||
"lazy_inference": {
|
||||
"threshold_percent": 15,
|
||||
"can_timeout_ms": 1000,
|
||||
"motion_timeout_ms": 300
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,6 @@ public:
|
||||
if(closed_)return false;
|
||||
items_.push_back(std::move(item));changed_.notify_all();return true;
|
||||
}
|
||||
bool pushLatest(T item) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if(closed_)return false;
|
||||
if(items_.size()==capacity_)items_.pop_front();
|
||||
items_.push_back(std::move(item));changed_.notify_all();return true;
|
||||
}
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);items_.clear();changed_.notify_all();
|
||||
}
|
||||
std::optional<T> pop() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
changed_.wait(lock,[&]{return closed_||!items_.empty();});
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "camera.hpp"
|
||||
#include "frame_result.hpp"
|
||||
#include "output_writer.hpp"
|
||||
#include "lazy_gate.hpp"
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
@@ -10,7 +9,6 @@ namespace cms {
|
||||
struct PipelineConfig {
|
||||
CameraConfig camera;
|
||||
OutputOptions output;
|
||||
LazyGateConfig lazy_gate;
|
||||
std::filesystem::path engines,door_mask;
|
||||
double detector_confidence{0.30};
|
||||
int max_people{3};
|
||||
@@ -21,10 +19,9 @@ struct PipelineConfig {
|
||||
struct PipelineStats {
|
||||
std::uint64_t captured{},published{},driver_sequence_gaps{},discarded_error_frames{};
|
||||
double mean_capture_to_result_ms{};
|
||||
std::uint64_t detector_runs{},pose_runs{},skipped_idle{};
|
||||
};
|
||||
// One lifecycle per instance. start() warms engines before starting capture.
|
||||
// Captures and receives CAN continuously. Engines stay warm while inference is idle.
|
||||
// Runs continuously until requestStop() or a worker failure.
|
||||
// latest() reads the immutable result variable without removing or acknowledging it.
|
||||
// No result queue, consumer registration or frame-count termination.
|
||||
// Stop is safe from any thread;
|
||||
@@ -38,7 +35,6 @@ public:
|
||||
void start();
|
||||
FrameResultPtr latest() const;
|
||||
bool running() const noexcept;
|
||||
InferenceStatus inferenceStatus() const;
|
||||
void requestStop() noexcept;
|
||||
void join();
|
||||
PipelineStats stats() const;
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// can_comm.h - USBCANFD CAN/CANFD 收发封装
|
||||
//
|
||||
// 整合自 vision-anti-pinch_bak/main.cpp 的 CAN 模块, 提供:
|
||||
// can_start() - 打开设备, 初始化并启动双通道接收线程
|
||||
// can_stop() - 停止接收并关闭设备
|
||||
// can_send() - 发送一帧 CAN (8 字节)
|
||||
// canfd_send() - 发送一帧 CANFD (64 字节)
|
||||
//
|
||||
// 依赖 zcan.h (include/) 和 libusbcanfd.so + libusb-1.0.so (lib/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace CanComm {
|
||||
|
||||
// 初始化并启动 CAN 收发(双通道, 仲裁域 500kbps / 数据域 2000kbps)。
|
||||
// 返回 true 成功; false 失败(主流程可继续仅取图)。
|
||||
bool can_start();
|
||||
|
||||
// 停止 CAN 接收线程并关闭设备。
|
||||
void can_stop();
|
||||
|
||||
// 注册报文解析回调。收到每帧 CAN/CANFD 数据帧时调用。
|
||||
// time: 时间戳(us)
|
||||
// msg_id: CAN-ID
|
||||
// data: 数据指针(回调内有效, 不要保存)
|
||||
// len: 数据长度
|
||||
// 传 nullptr 取消注册。
|
||||
using MsgCallback = void (*)(int64_t time, uint32_t msg_id, const uint8_t *data, uint32_t len);
|
||||
void set_msg_callback(MsgCallback cb);
|
||||
|
||||
// 发送一帧 CAN2.0 数据帧(8 字节)。
|
||||
// chn_idx: 通道号 0/1
|
||||
// id: CAN-ID
|
||||
// data: 8 字节数据(不足补 0, 超出截断)
|
||||
// 返回实际发送帧数(0 失败)。
|
||||
uint32_t can_send(int chn_idx, uint32_t id, const uint8_t data[8]);
|
||||
|
||||
// 发送一帧 CANFD 数据帧(64 字节, 扩展帧+加速)。
|
||||
// chn_idx: 通道号 0/1
|
||||
// id: CAN-ID
|
||||
// data: 64 字节数据(不足补 0, 超出截断)
|
||||
// 返回实际发送帧数(0 失败)。
|
||||
uint32_t canfd_send(int chn_idx, uint32_t id, const uint8_t data[64]);
|
||||
|
||||
// 是否已打开并运行
|
||||
bool is_open();
|
||||
|
||||
// 累计接收帧数(线程安全)
|
||||
int receive_count();
|
||||
|
||||
} // namespace CanComm
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include "lazy_gate.hpp"
|
||||
#include <memory>
|
||||
namespace cms {
|
||||
// Receives continuously, including while inference is idle; retries missing USB CAN.
|
||||
class CanInput {
|
||||
public:
|
||||
explicit CanInput(LazyGateConfig config);
|
||||
~CanInput();
|
||||
CanInput(CanInput const&)=delete;
|
||||
CanInput& operator=(CanInput const&)=delete;
|
||||
InferenceStatus snapshot();
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
// Public downstream contract. Owns its data; no GPU, JSON or network handles.
|
||||
#include "image.hpp"
|
||||
#include "lazy_gate.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -64,7 +63,6 @@ struct StageTiming {
|
||||
double detector_ms{}, pose_ms{}, fusion_risk_ms{}, capture_to_result_ms{};
|
||||
};
|
||||
struct FrameResult {
|
||||
InferenceStatus inference;
|
||||
int frame_index{};
|
||||
std::vector<int> track_ids;
|
||||
std::vector<std::array<double,4>> boxes;
|
||||
|
||||
@@ -11,7 +11,6 @@ public:
|
||||
if(!result)throw std::invalid_argument("Cannot publish a null result");
|
||||
std::atomic_store_explicit(&latest_,std::move(result),std::memory_order_release);
|
||||
}
|
||||
void clear() { std::atomic_store_explicit(&latest_,FrameResultPtr{},std::memory_order_release); }
|
||||
FrameResultPtr load() const {
|
||||
return std::atomic_load_explicit(&latest_,std::memory_order_acquire);
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace cms {
|
||||
struct LazyGateConfig {
|
||||
int threshold_percent{15};
|
||||
int can_timeout_ms{1000};
|
||||
int motion_timeout_ms{300};
|
||||
};
|
||||
struct InferenceStatus {
|
||||
bool models_ready{};
|
||||
bool enabled{true};
|
||||
bool can_valid{};
|
||||
bool left_front_trigger{},left_rear_trigger{};
|
||||
std::array<int,4> positions{{-1,-1,-1,-1}}; // LF, RF, LR, RR; percent
|
||||
std::array<int,2> direction{}; // LF, LR: -1 closing, 0 stationary, +1 opening
|
||||
std::uint64_t generation{1},sample_monotonic_ns{};
|
||||
double can_age_ms{-1};
|
||||
std::string reason{"CAN_UNAVAILABLE"};
|
||||
};
|
||||
// Pure state machine. Caller serializes access. Time is monotonic nanoseconds.
|
||||
class LazyGate {
|
||||
public:
|
||||
explicit LazyGate(LazyGateConfig config={});
|
||||
void receive(std::uint32_t id,std::uint8_t const* data,std::size_t size,std::uint64_t now);
|
||||
InferenceStatus snapshot(std::uint64_t now);
|
||||
private:
|
||||
LazyGateConfig config_;
|
||||
bool have_sample_{};
|
||||
std::uint64_t last_sample_{};
|
||||
std::array<std::uint64_t,2> last_change_{};
|
||||
InferenceStatus state_;
|
||||
};
|
||||
}
|
||||
-443
@@ -1,443 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN_C extern "C"
|
||||
#else
|
||||
#define EXTERN_C
|
||||
#endif
|
||||
|
||||
#define ZCAN_API
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)(-1))
|
||||
|
||||
#define CMD_CAN_FILTER 0x14 // 滤波
|
||||
#define CMD_CAN_TTX 0x16 // 定时发送
|
||||
#define CMD_CAN_TTX_CTL 0x17 // 使能定时发送
|
||||
#define CMD_CAN_TRES 0x18 // CAN终端电阻
|
||||
|
||||
#define ZCAN_CMD_SET_CHNL_RECV_MERGE 0x32 // 设置合并接收 0:不合并接收;1:合并接收
|
||||
#define ZCAN_CMD_GET_CHNL_RECV_MERGE 0x33 // 获取是否开启合并接收 0:不合并接收;1:合并接收
|
||||
|
||||
#define CMD_SET_SN 0x42 // 获取SN号
|
||||
#define CMD_GET_SN 0x43 // 设置SN号
|
||||
#define CMD_CAN_TX_TIMEOUT 0x44 // 发送超时
|
||||
|
||||
#define ZCAN_CMD_GET_SEND_QUEUE_SIZE 0x100 // 获取队列大小,uint32_t
|
||||
#define ZCAN_CMD_GET_SEND_QUEUE_SPACE 0x101 // 获取队列剩余空间, uint32_t
|
||||
#define ZCAN_CMD_SET_SEND_QUEUE_CLR 0x102 // 清空发送队列,1:清空
|
||||
#define ZCAN_CMD_SET_SEND_QUEUE_EN 0x103 // 开启发送队列,1:使能
|
||||
|
||||
typedef unsigned char U8;
|
||||
typedef unsigned short U16;
|
||||
typedef unsigned int U32;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
enum eZCANDataDEF
|
||||
{
|
||||
//数据类型
|
||||
ZCAN_DT_ZCAN_CAN_DATA = 1, // CAN/CANFD数据
|
||||
ZCAN_DT_ZCAN_CANFD_DATA = 2, // CAN/CANFD数据
|
||||
ZCAN_DT_ZCAN_ERROR_DATA = 3, // CAN/CANFD错误数据
|
||||
ZCAN_DT_ZCAN_LIN_DATA = 4, // LIN数据
|
||||
ZCAN_DT_ZCAN_LIN_ERROR_DATA = 5, // LIN错误数据
|
||||
};
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef enum {
|
||||
ZCAN_TX_NORM = 0, /**< normal transmission */
|
||||
ZCAN_TX_ONCE = 1, /**< single-shot transmission */
|
||||
ZCAN_SR_NORM = 2, /**< self reception */
|
||||
ZCAN_SR_ONCE = 3, /**< single-shot transmission & self reception */
|
||||
} ZCAN_TX_MODE;
|
||||
|
||||
/** CAN filter configuration */
|
||||
typedef struct {
|
||||
U8 type; /**< 0-std_frame, 1-ext_frame */
|
||||
U8 pad[3];
|
||||
U32 sid; /**< start-id */
|
||||
U32 eid; /**< end-id */
|
||||
} ZCAN_FILTER;
|
||||
|
||||
typedef struct {
|
||||
U32 size;
|
||||
ZCAN_FILTER table[64];
|
||||
} ZCAN_FILTER_TABLE;
|
||||
|
||||
/** controller initialization */
|
||||
typedef struct {
|
||||
U32 clk; // 时钟频率
|
||||
U32 mode; /**< bit0-normal/listen_only, bit1-ISO/BOSCH */
|
||||
struct {
|
||||
U8 tseg1;
|
||||
U8 tseg2;
|
||||
U8 sjw;
|
||||
U8 smp;
|
||||
U16 brp;
|
||||
} aset;
|
||||
struct {
|
||||
U8 tseg1;
|
||||
U8 tseg2;
|
||||
U8 sjw;
|
||||
U8 smp;
|
||||
U16 brp;
|
||||
} dset;
|
||||
} ZCAN_INIT;
|
||||
|
||||
/** CAN message info */
|
||||
typedef struct {
|
||||
U32 txm : 4; /**< TX-mode, @see ZCAN_TX_MODE */
|
||||
U32 fmt : 4; /**< 0-CAN2.0, 1-CANFD */
|
||||
U32 sdf : 1; /**< 0-data_frame, 1-remote_frame */
|
||||
U32 sef : 1; /**< 0-std_frame, 1-ext_frame */
|
||||
U32 err : 1; /**< error flag */
|
||||
U32 brs : 1; /**< bit-rate switch */
|
||||
U32 est : 1; /**< error state */
|
||||
U32 tx : 1; /**< received valid, tx frame */
|
||||
U32 echo : 1; /**< tx valid, echo frame */
|
||||
U32 qsend_100us:1; /**< queue send delay unit, 1-100us, 0-ms */
|
||||
U32 qsend:1; /**< send valid, queue send frame */
|
||||
U32 pad : 15;
|
||||
} ZCAN_MSG_INF;
|
||||
|
||||
/** CAN message header */
|
||||
typedef struct {
|
||||
U32 ts; /**< timestamp */
|
||||
U32 id; /**< CAN-ID */
|
||||
ZCAN_MSG_INF inf; /**< @see ZCAN_MSG_INF */
|
||||
U16 pad; /**< send queue delay */
|
||||
U8 chn; /**< channel */
|
||||
U8 len; /**< data length */
|
||||
} ZCAN_MSG_HDR;
|
||||
|
||||
/** CAN2.0-frame */
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[8];
|
||||
} ZCAN_20_MSG;
|
||||
|
||||
/** CANFD-frame */
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[64];
|
||||
} ZCAN_FD_MSG;
|
||||
|
||||
// 错误帧
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[8];
|
||||
} ZCAN_ERR_MSG;
|
||||
|
||||
// 定时发送结构体
|
||||
typedef struct {
|
||||
U32 interval; // 定时发送周期,单位 100us
|
||||
U16 repeat; // 发送次数,0等于循环发
|
||||
U8 index; // 定时发送列表的帧索引号,也就是第几条定时发送报文
|
||||
U8 flags; // 0-此帧禁用定时发送,1-此帧使能定时发送
|
||||
ZCAN_FD_MSG msg; // CANFD帧结构体
|
||||
} ZCAN_TTX;
|
||||
|
||||
typedef struct {
|
||||
U32 size; // 实际生效的数组的长度(用来控制实际生效多少条定时发送帧结构体)
|
||||
ZCAN_TTX table[8]; // 最大设置8条
|
||||
} ZCAN_TTX_CFG;
|
||||
|
||||
// 设备信息
|
||||
typedef struct {
|
||||
U16 hwv; /**< hardware version */
|
||||
U16 fwv; /**< firmware version */
|
||||
U16 drv; /**< driver version */
|
||||
U16 api; /**< API version */
|
||||
U16 irq; /**< IRQ */
|
||||
U8 chn; /**< channels */
|
||||
U8 sn[20]; /**< serial number */
|
||||
U8 id[40]; /**< card id */
|
||||
U16 pad[4];
|
||||
} ZCAN_DEV_INF;
|
||||
|
||||
/** controller status */
|
||||
typedef struct {
|
||||
U8 IR; /**< not used(for backward compatibility) */
|
||||
U8 MOD; /**< not used */
|
||||
U8 SR; /**< not used */
|
||||
U8 ALC; /**< not used */
|
||||
U8 ECC; /**< not used */
|
||||
U8 EWL; /**< not used */
|
||||
U8 RXE; /**< RX errors */
|
||||
U8 TXE; /**< TX errors */
|
||||
U32 PAD;
|
||||
} ZCAN_STAT;
|
||||
|
||||
//lin begin
|
||||
typedef struct _VCI_LIN_INIT_CONFIG
|
||||
{
|
||||
U8 linMode; // 是否作为主机,0-从机,1-主机
|
||||
U8 chkSumMode; // 校验方式,1-经典校验 2-增强校验 3-自动(对应eZLINChkSumMode的模式)
|
||||
U16 reserved; // 保留
|
||||
U32 linBaud; // 波特率,取值1000~20000
|
||||
}ZCAN_LIN_INIT_CONFIG, *PZCAN_LIN_INIT_CONFIG;
|
||||
|
||||
typedef struct tagZCANLINData
|
||||
{
|
||||
union{
|
||||
struct {
|
||||
U8 ID : 6; // 帧ID
|
||||
U8 Parity : 2; // 帧ID校验
|
||||
}unionVal;
|
||||
U8 rawVal; // 受保护的ID原始值
|
||||
} PID; // 受保护的ID
|
||||
struct
|
||||
{
|
||||
U64 timeStamp; // 时间戳,单位微秒(us)
|
||||
U8 dataLen; // 数据长度
|
||||
U8 dir; // 传输方向,0-接收 1-发送
|
||||
U8 chkSum; // 数据校验, 部分设备不支持校验数据的获取
|
||||
U8 reserved[13]; // 保留
|
||||
U8 data[8]; // 数据
|
||||
}RxData; // 仅接收数据时有效
|
||||
U8 reserved[7]; // 保留
|
||||
}ZCANLINData;
|
||||
|
||||
typedef struct tagZCANLINErrData
|
||||
{
|
||||
U64 timeStamp; // 时间戳, 单位微秒(us)
|
||||
union {
|
||||
struct {
|
||||
U8 ID : 6; // 帧ID
|
||||
U8 Parity : 2; // 帧ID校验
|
||||
}unionVal;
|
||||
U8 rawVal; // 受保护的ID原始值
|
||||
} PID; // 受保护的ID
|
||||
U8 dataLen;
|
||||
U8 data[8];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
U16 errStage : 4; // 错误阶段
|
||||
U16 errReason : 4; // 错误原因
|
||||
U16 reserved : 8; // 保留
|
||||
};
|
||||
U16 unionErrData;
|
||||
}errData;
|
||||
U8 dir; // 传输方向
|
||||
U8 chkSum; // 数据校验, 部分设备不支持校验数据的获取
|
||||
U8 reserved[10]; // 保留
|
||||
}ZCANLINErrData;
|
||||
|
||||
typedef struct _VCI_LIN_MSG{
|
||||
U8 chnl; // 数据通道
|
||||
U8 dataType; // 数据类型, 0-LIN数据 1-LIN错误数据
|
||||
union
|
||||
{
|
||||
ZCANLINData zcanLINData; // LIN数据
|
||||
ZCANLINErrData zcanLINErrData; // LIN错误数据
|
||||
U8 raw[46]; // RAW数据
|
||||
} data; // 实际数据, 联合体,有效成员根据 dataType 字段而定
|
||||
}ZCAN_LIN_MSG, *PZCAN_LIN_MSG;
|
||||
|
||||
enum eZLINChkSumMode
|
||||
{
|
||||
DEFAULT = 0, // 默认,启动时配置
|
||||
CLASSIC_CHKSUM, // 经典校验
|
||||
ENHANCE_CHKSUM, // 增强校验
|
||||
AUTOMATIC, // 自动,设备自动识别校验方式(仅ZCAN_SetLINSubscribe时有效)
|
||||
};
|
||||
enum
|
||||
{
|
||||
MAX_LIN_ID_COUNT = 63,
|
||||
MIN_LIN_DLC = 1,
|
||||
MAX_LIN_DLC = 8,
|
||||
AUTO_LIN_DLC = 255,
|
||||
};
|
||||
typedef struct _VCI_LIN_PUBLISH_CFG
|
||||
{
|
||||
U8 ID; // 受保护的ID(ID取值范围为0-63)
|
||||
U8 dataLen; // dataLen范围为1-8
|
||||
U8 data[8];
|
||||
U8 chkSumMode; // 校验方式,0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式)
|
||||
U8 reserved[5]; // 保留
|
||||
}ZCAN_LIN_PUBLISH_CFG, *PZCAN_LIN_PUBLISH_CFG;
|
||||
|
||||
typedef struct _VCI_LIN_SUBSCIBE_CFG
|
||||
{
|
||||
U8 ID; // 受保护的ID(ID取值范围为0-63)
|
||||
U8 dataLen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度
|
||||
U8 chkSumMode; // 校验方式,0-默认,启动时配置 1-经典校验 2-增强校验 3-自动(对应eZLINChkSumMode的模式)
|
||||
U8 reserved[5]; // 保留
|
||||
}ZCAN_LIN_SUBSCIBE_CFG, *PZCAN_LIN_SUBSCIBE_CFG;
|
||||
|
||||
// 合并接收数据数据结构,支持CAN/CANFD/LIN/错误等不同类型数据
|
||||
typedef struct tagZCANDataObj
|
||||
{
|
||||
U8 dataType; // 数据类型, 参考eZCANDataDEF中 数据类型 部分定义
|
||||
U8 chnl; // 数据通道
|
||||
union{
|
||||
struct{
|
||||
U16 reserved : 16; // 保留
|
||||
}unionVal;
|
||||
U16 rawVal;
|
||||
}flag; // 标志信息, 暂未使用
|
||||
U8 extraData[4]; // 额外数据, 暂未使用
|
||||
union
|
||||
{
|
||||
ZCAN_20_MSG zcanCANData; // CAN数据
|
||||
ZCAN_FD_MSG zcanCANFDData; // CANFD数据
|
||||
ZCAN_ERR_MSG zcanErrData; // 错误数据
|
||||
ZCANLINData zcanLINData; // LIN数据
|
||||
ZCANLINErrData zcanLINErrData; // LIN错误数据
|
||||
U8 raw[92]; // RAW数据
|
||||
} data; // 实际数据, 联合体,有效成员根据 dataType 字段而定
|
||||
}ZCANDataObj;
|
||||
|
||||
|
||||
// UDS传输协议版本
|
||||
typedef U8 ZCAN_UDS_TRANS_VER;
|
||||
#define ZCAN_UDS_TRANS_VER_0 0 // ISO15765-2(2004版本)
|
||||
#define ZCAN_UDS_TRANS_VER_1 1 // ISO15765-2(2016版本)
|
||||
|
||||
// 帧类型
|
||||
typedef U8 ZCAN_UDS_FRAME_TYPE;
|
||||
#define ZCAN_UDS_FRAME_CAN 0 // CAN帧
|
||||
#define ZCAN_UDS_FRAME_CANFD 1 // CANFD帧
|
||||
#define ZCAN_UDS_FRAME_CANFD_BRS 2 // CANFD加速帧
|
||||
|
||||
// UDS填充模式
|
||||
#define FILL_MODE_SHORT 0 // 小于8字节填充至8字节,大于8字节时按DLC就近填充
|
||||
#define FILL_MODE_NONE 1 // 不填充
|
||||
#define FILL_MODE_MAX 2 // 填充至最大数据长度 (不建议)
|
||||
|
||||
// CAN UDS请求数据
|
||||
typedef struct _ZCAN_UDS_REQUEST
|
||||
{
|
||||
U32 req_id; // 请求事务ID,范围0~65535,本次请求的唯一标识
|
||||
U8 channel; // 设备通道索引 0~255
|
||||
ZCAN_UDS_FRAME_TYPE frame_type; // 帧类型
|
||||
U8 reserved0[2]; // 保留
|
||||
U32 src_addr; // 请求地址
|
||||
U32 dst_addr; // 响应地址
|
||||
U8 suppress_response; // 1:抑制响应
|
||||
U8 sid; // 请求服务id
|
||||
U8 reserved1[6]; // 保留
|
||||
struct {
|
||||
U32 timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms
|
||||
U32 enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms
|
||||
U8 check_any_negative_response:1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误
|
||||
U8 wait_if_suppress_response:1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间
|
||||
U8 flag:6; // 保留
|
||||
U8 reserved0[7]; // 保留
|
||||
} session_param; // 会话层参数
|
||||
struct {
|
||||
ZCAN_UDS_TRANS_VER version; // 传输协议版本, VERSION_0, VERSION_1
|
||||
U8 max_data_len; // 单帧最大数据长度, can:8, canfd:64
|
||||
U8 local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔, 0x00-0x7F(0ms~127ms), 0xF1-0xF9(100us~900us)
|
||||
U8 block_size; // 流控帧的块大小
|
||||
U8 fill_byte; // 无效字节的填充数据
|
||||
U8 ext_frame; // 0:标准帧 1:扩展帧
|
||||
U8 is_modify_ecu_st_min; // 是否忽略ECU返回流控的STmin,强制使用本程序设置的 remote_st_min
|
||||
U8 remote_st_min; // 发送多帧时用, is_ignore_ecu_st_min = 1 时有效, 0x00-0x7F(0ms~127ms), 0xF1-0xF9(100us~900us)
|
||||
U32 fc_timeout; // 接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧
|
||||
U8 fill_mode; // 数据长度填充模式, FILL_MODE_SHORT, FILL_MODE_NONE, FILL_MODE_MAX
|
||||
U8 reserved0[3]; // 保留
|
||||
} trans_param; // 传输层参数
|
||||
U8 *data; // 数据数组(不包含SID)
|
||||
U32 data_len; // 数据数组的长度
|
||||
U32 reserved2; // 保留
|
||||
} ZCAN_UDS_REQUEST;
|
||||
|
||||
// UDS错误码
|
||||
typedef U8 ZCAN_UDS_ERROR;
|
||||
#define ZCAN_UDS_ERROR_OK 0 // 没错误
|
||||
#define ZCAN_UDS_ERROR_TIMEOUT 1 // 响应超时
|
||||
#define ZCAN_UDS_ERROR_TRANSPORT 2 // 发送数据失败
|
||||
#define ZCAN_UDS_ERROR_CANCEL 3 // 取消请求
|
||||
#define ZCAN_UDS_ERROR_SUPPRESS_RESPONSE 4 // 抑制响应
|
||||
#define ZCAN_UDS_ERROR_BUSY 5 // 忙碌中
|
||||
#define ZCAN_UDS_ERROR_REQ_PARAM 6 // 请求参数错误
|
||||
#define ZCAN_UDS_ERROR_OTHTER 100
|
||||
|
||||
typedef U8 ZCAN_UDS_RESPONSE_TYPE;
|
||||
#define ZCAN_UDS_RT_NEGATIVE 0 // 消极响应
|
||||
#define ZCAN_UDS_RT_POSITIVE 1 // 积极响应
|
||||
|
||||
// UDS响应数据
|
||||
typedef struct _ZCAN_UDS_RESPONSE
|
||||
{
|
||||
ZCAN_UDS_ERROR status; // 响应状态
|
||||
U8 reserved[6]; // 保留
|
||||
ZCAN_UDS_RESPONSE_TYPE type; // 响应类型
|
||||
union {
|
||||
struct {
|
||||
U8 sid; // 响应服务id
|
||||
U32 data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中
|
||||
} positive;
|
||||
struct {
|
||||
U8 neg_code; // 固定为0x7F
|
||||
U8 sid; // 请求服务id
|
||||
U8 error_code; // 错误码
|
||||
} negative;
|
||||
U8 raw[8];
|
||||
};
|
||||
} ZCAN_UDS_RESPONSE;
|
||||
|
||||
// UDS控制类型
|
||||
typedef U32 ZCAN_UDS_CTRL_CODE;
|
||||
#define ZCAN_UDS_CTRL_STOP_REQ 0 // 停止UDS请求
|
||||
|
||||
// UDS控制请求
|
||||
typedef struct _ZCAN_UDS_CTRL_REQ
|
||||
{
|
||||
U32 reqID; // 请求事务ID,指明要操作哪一条请求
|
||||
ZCAN_UDS_CTRL_CODE cmd; // 控制类型
|
||||
U8 reserved[8]; // 保留
|
||||
} ZCAN_UDS_CTRL_REQ;
|
||||
|
||||
// UDS控制结果
|
||||
typedef U32 ZCAN_UDS_CTRL_RESULT;
|
||||
#define ZCAN_UDS_CTRL_RESULT_OK 0 // 成功
|
||||
#define ZCAN_UDS_CTRL_RESULT_ERR 1 // 失败
|
||||
|
||||
// UDS控制响应数据
|
||||
typedef struct _ZCAN_UDS_CTRL_RESP
|
||||
{
|
||||
ZCAN_UDS_CTRL_RESULT result; // 操作结果
|
||||
U8 reserved[8]; // 保留
|
||||
} ZCAN_UDS_CTRL_RESP;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
//
|
||||
EXTERN_C U32 ZCAN_API VCI_OpenDevice(U32 Type, U32 Card, U32 Reserved);
|
||||
EXTERN_C U32 ZCAN_API VCI_CloseDevice(U32 Type, U32 Card);
|
||||
EXTERN_C U32 ZCAN_API VCI_InitCAN(U32 Type, U32 Card, U32 Port, ZCAN_INIT *pInit);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadBoardInfo(U32 Type, U32 Card, ZCAN_DEV_INF *pInfo);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadErrInfo(U32 Type, U32 Card, U32 Port, ZCAN_ERR_MSG *pErr);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadCANStatus(U32 Type, U32 Card, U32 Port, ZCAN_STAT *pStat);
|
||||
EXTERN_C U32 ZCAN_API VCI_GetReference(U32 Type, U32 Card, U32 Port, U32 Ref, void *pData);
|
||||
EXTERN_C U32 ZCAN_API VCI_SetReference(U32 Type, U32 Card, U32 Port, U32 Ref, void *pData);
|
||||
EXTERN_C U32 ZCAN_API VCI_GetReceiveNum(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_ClearBuffer(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_StartCAN(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_ResetCAN(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_Transmit(U32 Type, U32 Card, U32 Port, ZCAN_20_MSG *pData, U32 Count);
|
||||
EXTERN_C U32 ZCAN_API VCI_TransmitFD(U32 Type, U32 Card, U32 Port, ZCAN_FD_MSG *pData, U32 Count);
|
||||
EXTERN_C U32 ZCAN_API VCI_Receive(U32 Type, U32 Card, U32 Port, ZCAN_20_MSG *pData, U32 Count, U32 Time);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReceiveFD(U32 Type, U32 Card, U32 Port, ZCAN_FD_MSG *pData, U32 Count, U32 Time);
|
||||
EXTERN_C U32 ZCAN_API VCI_Debug(U32 Debug);
|
||||
|
||||
// LIN
|
||||
EXTERN_C U32 VCI_InitLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_INIT_CONFIG pLINInitConfig);
|
||||
EXTERN_C U32 VCI_StartLIN(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ResetLIN(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_TransmitLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_MSG pSend, U32 Len);
|
||||
EXTERN_C U32 VCI_GetLINReceiveNum(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ClearLINBuffer(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ReceiveLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_MSG pReceive, U32 Len,int WaitTime);
|
||||
EXTERN_C U32 VCI_SetLINSubscribe(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_SUBSCIBE_CFG pSend, U32 nSubscribeCount);
|
||||
EXTERN_C U32 VCI_SetLINPublish(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_PUBLISH_CFG pSend, U32 nPublishCount);
|
||||
|
||||
// 合并接收
|
||||
EXTERN_C U32 VCI_TransmitData(unsigned Type, unsigned Card, unsigned Port, ZCANDataObj *pData, unsigned Count);
|
||||
EXTERN_C U32 VCI_ReceiveData(unsigned Type, unsigned Card, unsigned Port, ZCANDataObj *pData, unsigned Count, unsigned Time);
|
||||
|
||||
// UDS
|
||||
EXTERN_C U32 VCI_UDS_Request(unsigned Type, unsigned Card, const ZCAN_UDS_REQUEST *req, ZCAN_UDS_RESPONSE *resp, U8 *dataBuf, U32 dataBufSize);
|
||||
EXTERN_C U32 VCI_UDS_Control(unsigned Type, unsigned Card, const ZCAN_UDS_CTRL_REQ *ctrl, ZCAN_UDS_CTRL_RESP *resp);
|
||||
+14
-9
@@ -1,23 +1,28 @@
|
||||
# Orin 惰性推理运行包(C++)
|
||||
# Orin 可直接运行目录
|
||||
|
||||
相机每次等待默认 2 秒,连续 6 次超时才退出(约 12 秒);有效帧恢复后重新计数。Ctrl+C / SIGTERM 主动停止不需要等待 6 次超时。
|
||||
|
||||
|
||||
在 JetPack 5.1.2 / TensorRT 8.5 / CUDA 11.4 / OpenCV 4.5 环境执行:
|
||||
本目录包含实际在 Jetson AGX Orin 上编译的 aarch64 程序和 TensorRT FP16 引擎。
|
||||
目标环境:L4T R35.4.1 / JetPack 5.1.2,CUDA 11.4,TensorRT 8.5.2,OpenCV 4.5.4。
|
||||
使用系统安装的 CUDA、TensorRT、OpenCV 共享库;不适用于任意 JetPack 版本或 x86 主机。
|
||||
|
||||
```bash
|
||||
./scripts/initialize_camera.sh # 摄像头未初始化时
|
||||
cd orin
|
||||
sha256sum -c SHA256SUMS
|
||||
./scripts/initialize_camera.sh
|
||||
./run.sh
|
||||
./run.sh --visualize # 可选窗口
|
||||
```
|
||||
|
||||
`build/camera_risk` 为 ARM64 ELF 程序,无 Python 运行依赖。
|
||||
可视化可选:`./run.sh --visualize`,需要板端本地桌面 DISPLAY。
|
||||
默认持续推理直到 Ctrl+C / SIGTERM;正常退出没有统计输出。
|
||||
预览窗口关闭后推理继续。结果通过 C++ CameraPipeline::latest() 获取,不通过端口或结果文件传递。
|
||||
|
||||
CAN 0x391 的 data[0..3] 为左前、右前、左后、右后百分比。左前或左后开度 <15% 且正在增加或减小时推理;其余有效 CAN 状态暂停。CAN 缺失或超时 1000 ms 时推理,恢复后重新判断。
|
||||
程序为 build/camera_risk,配置为 config/camera.json,引擎为 engines/*.engine。
|
||||
./run.sh 从所在目录解析路径,也可以在其他工作目录通过绝对路径调用。
|
||||
|
||||
模型只加载预热一次并常驻;相机和 CAN 始终运行。重复相同开度保留运动方向 300 ms,之后视为静止。参数见 config/camera.json 的 lazy_inference。默认结果保存在 C++ 内存,不写逐帧文件;暂停时 latest() 为空。标准输出记录启停状态与累计模型运行次数。
|
||||
|
||||
文件完整性:`sha256sum -c SHA256SUMS`。
|
||||
bin/drivers 中 GMSL 模块对应内核 5.10.120-tegra。初始化脚本检查内核版本;加载模块需要 root 或现有 sudo 权限。
|
||||
门区保留当前室内联调设置,door.calibration_verified=false。
|
||||
|
||||
## 可选保存(默认全部关闭)
|
||||
|
||||
|
||||
+5
-7
@@ -1,13 +1,11 @@
|
||||
b0110852effcec51d5e6b74f32a37fbb22997d6fa4f9f1e2a5dbc7cade00ab7b ./README.md
|
||||
49a272525082edf5ef4619efc57bc2509c06653af262fc8a8d257d138f9206fb ./README.md
|
||||
6d5e874fd27fcfd75d303d7e35d9672028af71041f52dcc82a1e83676faefa65 ./bin/drivers/max9295.ko
|
||||
8a78d04ef8be44e9b62a5e4ee315d3d6d679e34aedff02a234bbf4d40e222933 ./bin/drivers/max9296.ko
|
||||
3da628587477cc35d5e3f8aee86e718e2ddf16808e893e00ba6e164b2307e843 ./bin/drivers/sgx-yuv-gmsl2.ko
|
||||
0d77d17626644fb50b663c33edbb1a09884b34b5410a1109fc56b4d3781651b1 ./build/camera_risk
|
||||
951c514ee76e10233a84746f25a884e0a1e8cbed84df57845a5aff66af9aecbd ./config/camera.json
|
||||
3a984e1e4c0fa12285967085e3359a5b7c698d744e9c2deb2480a312eb637c1c ./build/camera_risk
|
||||
8a572f661e056e3916758a442e73212842bfe89361dd1b07b87de8f6ae523cf8 ./config/camera.json
|
||||
9ba768d559fd3b64e833b50c62c70f2adfd6c169620aea5fba32cfb1b4c5b0e9 ./config/door_mask.json
|
||||
ebcd09e8c2e35008adbb0c1d222e580b17e0df1c8093df3481ba3374cd9a8174 ./engines/rtmw_l_384.engine
|
||||
f6a65b926ac1144484f4aa93cd26d7997d32d62310ab93fe867f2bd4412222e9 ./engines/yolo11s.engine
|
||||
8dae8c557deadbe6bd7241c0dcd7025cb007077ee39a79b115e953a2b22cd9c1 ./lib/libusb-1.0.so
|
||||
ab8b7f7249d5fb73d1d33d14fa7532a522088ffb6c25f37ba334daf4c6b4d313 ./lib/libusbcanfd.so
|
||||
a0b083ad029fd6430941f5aff472150c441f7d5c4b6d7f201ed1999f0f86095a ./run.sh
|
||||
9122cd4ed17fb26da1cb3f9456f923508430a518eb346c5038919d4475d8d2e8 ./scripts/initialize_camera.sh
|
||||
7f42b0e7f9396e0d82313a892fc62c153261dab1206a206a466bce6376f58a36 ./run.sh
|
||||
54e1d63e3aa72ad9b056a224d55968d4947ca6703a8d1c405570de5707a7e7ef ./scripts/initialize_camera.sh
|
||||
|
||||
Binary file not shown.
@@ -18,10 +18,5 @@
|
||||
"warning_relative": 0.3,
|
||||
"release_relative": 0.45,
|
||||
"calibration_verified": false
|
||||
},
|
||||
"lazy_inference": {
|
||||
"threshold_percent": 15,
|
||||
"can_timeout_ms": 1000,
|
||||
"motion_timeout_ms": 300
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
libusb-1.0.so
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
libusbcanfd.so
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$root/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
exec "$root/build/camera_risk" --config "$root/config/camera.json" "$@"
|
||||
|
||||
@@ -11,9 +11,7 @@ fi
|
||||
for driver in max9295 max9296 sgx-yuv-gmsl2; do
|
||||
file="$root/bin/drivers/$driver.ko"
|
||||
[[ -f "$file" ]] || { echo "Missing driver: $file" >&2; exit 1; }
|
||||
vermagic="$(modinfo -F vermagic "$file")"
|
||||
# Some supplied drivers omit this metadata; normal insmod still validates them.
|
||||
[[ -z "$vermagic" || "$vermagic" == "$(uname -r) "* ]] || { echo "Kernel mismatch: $file" >&2; exit 1; }
|
||||
[[ "$(modinfo -F vermagic "$file")" == "$(uname -r) "* ]] || { echo "Kernel mismatch: $file" >&2; exit 1; }
|
||||
done
|
||||
loaded max9295 || root_command insmod "$root/bin/drivers/max9295.ko"
|
||||
loaded max9296 || root_command insmod "$root/bin/drivers/max9296.ko"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$root/orin/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
exec "$root/build/camera_risk" --config "$root/config/camera.json" "$@"
|
||||
|
||||
+10
-46
@@ -5,7 +5,6 @@
|
||||
#include "door_risk.hpp"
|
||||
#include "records.hpp"
|
||||
#include "result_conversion.hpp"
|
||||
#include "can_input.hpp"
|
||||
#include <opencv2/core.hpp>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
@@ -46,10 +45,9 @@ void attachPoses(FrameResult& result,fangjia::PoseFrameResult const& fused) {
|
||||
}
|
||||
}
|
||||
struct CameraPipeline::Impl {
|
||||
struct Pending {CapturedFrame frame;std::uint64_t generation;};
|
||||
struct Detected {CapturedFrame frame;std::vector<fangjia::Box> boxes;double elapsed_ms{};std::uint64_t generation;};
|
||||
struct Detected {CapturedFrame frame;std::vector<fangjia::Box> boxes;double elapsed_ms{};};
|
||||
PipelineConfig config;
|
||||
BoundedQueue<Pending> captures;
|
||||
BoundedQueue<CapturedFrame> captures;
|
||||
BoundedQueue<Detected> detections;
|
||||
LatestResult results;
|
||||
std::unique_ptr<OutputWriter> outputWriter;
|
||||
@@ -59,9 +57,6 @@ struct CameraPipeline::Impl {
|
||||
std::unique_ptr<DoorRisk> risk;
|
||||
std::shared_ptr<const BinaryMask> doorPixels;
|
||||
std::unique_ptr<Camera> camera;
|
||||
std::unique_ptr<CanInput> can;
|
||||
std::atomic<bool> modelsReady{false};
|
||||
std::atomic<std::uint64_t> detectorRuns{0},poseRuns{0},skippedIdle{0};
|
||||
std::thread captureThread,detectorThread,inferenceThread;
|
||||
mutable std::mutex stateMutex;
|
||||
std::exception_ptr failure;
|
||||
@@ -71,7 +66,6 @@ struct CameraPipeline::Impl {
|
||||
double totalLatency{};
|
||||
|
||||
explicit Impl(PipelineConfig c):config(std::move(c)),captures(config.queue_capacity),detections(config.queue_capacity) {
|
||||
LazyGate validateGate(config.lazy_gate);
|
||||
if(config.queue_capacity>8)throw std::invalid_argument("Queue capacity must be between 1 and 8");
|
||||
if(config.max_people<1||config.max_people>16||!std::isfinite(config.detector_confidence)||config.detector_confidence<=0||config.detector_confidence>=1)
|
||||
throw std::invalid_argument("Invalid detector configuration");
|
||||
@@ -84,7 +78,7 @@ struct CameraPipeline::Impl {
|
||||
if(inferenceThread.joinable())inferenceThread.join();
|
||||
}
|
||||
void stop() noexcept {
|
||||
stopped=true;active=false;results.clear();
|
||||
stopped=true;active=false;
|
||||
// Camera stays alive until all threads have joined. start()/destruction
|
||||
// are owner-thread operations; requestStop() only interrupts existing work.
|
||||
{std::lock_guard<std::mutex> lock(stateMutex);if(camera)camera->requestStop();}
|
||||
@@ -114,71 +108,48 @@ struct CameraPipeline::Impl {
|
||||
pose->warmup(warmup.value);
|
||||
detector->infer(cv::Mat(config.camera.height,config.camera.width,CV_8UC3,cv::Scalar()),warmup.value);
|
||||
{std::lock_guard<std::mutex> lock(stateMutex);camera=std::make_unique<Camera>(config.camera);if(stopped)camera->requestStop();}
|
||||
{std::lock_guard<std::mutex> lock(stateMutex);can=std::make_unique<CanInput>(config.lazy_gate);}
|
||||
modelsReady=true;
|
||||
active=!stopped.load();
|
||||
captureThread=std::thread([this]{try{captureLoop();}catch(...){fail();}});
|
||||
detectorThread=std::thread([this]{try{detectorLoop();}catch(...){fail();}});
|
||||
inferenceThread=std::thread([this]{try{inferenceLoop();}catch(...){fail();}});
|
||||
}catch(...){stop();joinWorkers();throw;}
|
||||
}
|
||||
InferenceStatus status() const {
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
auto status=can?can->snapshot():InferenceStatus{};
|
||||
status.models_ready=modelsReady;
|
||||
if(!active||stopped){status.enabled=false;status.reason=stopped?"STOPPED":"STARTING";}
|
||||
return status;
|
||||
}
|
||||
bool eligible(std::uint64_t generation) const {auto s=status();return s.enabled&&s.generation==generation;}
|
||||
void captureLoop() {
|
||||
std::uint64_t generation=0;
|
||||
while(!stopped) {
|
||||
auto frame=camera->read();if(!frame)break;
|
||||
gaps+=frame->metadata.skipped_driver_frames;
|
||||
errorFrames+=frame->metadata.discarded_error_frames;
|
||||
if(!captures.push(std::move(*frame)))break;
|
||||
++captured;
|
||||
auto gate=status();
|
||||
if(gate.generation!=generation){captures.clear();detections.clear();results.clear();generation=gate.generation;}
|
||||
if(!gate.enabled){++skippedIdle;continue;}
|
||||
if(!captures.pushLatest({std::move(*frame),gate.generation}))break;
|
||||
}
|
||||
captures.close();
|
||||
}
|
||||
void detectorLoop() {
|
||||
Stream stream;
|
||||
while(!stopped) {
|
||||
auto pending=captures.pop();if(!pending)break;
|
||||
if(!eligible(pending->generation))continue;
|
||||
auto startedAt=monotonicNs();++detectorRuns;
|
||||
auto boxes=detector->infer(imageView(*pending->frame.image),stream.value);
|
||||
if(!eligible(pending->generation))continue;
|
||||
if(!detections.pushLatest({std::move(pending->frame),std::move(boxes),elapsedMs(startedAt),pending->generation}))break;
|
||||
auto frame=captures.pop();if(!frame)break;
|
||||
auto startedAt=monotonicNs();auto boxes=detector->infer(imageView(*frame->image),stream.value);
|
||||
if(!detections.push({std::move(*frame),std::move(boxes),elapsedMs(startedAt)}))break;
|
||||
}
|
||||
detections.close();
|
||||
}
|
||||
void inferenceLoop() {
|
||||
Stream stream;fangjia::BoxTracker boxesTracker;fangjia::PoseFusionTracker fusion;
|
||||
std::uint64_t generation=0;
|
||||
while(!stopped) {
|
||||
auto detected=detections.pop();if(!detected)break;
|
||||
if(!eligible(detected->generation))continue;
|
||||
if(generation!=detected->generation){boxesTracker=fangjia::BoxTracker{};fusion=fangjia::PoseFusionTracker{};generation=detected->generation;}
|
||||
if(detected->frame.metadata.frame_index>static_cast<std::uint64_t>(std::numeric_limits<int>::max()))throw std::runtime_error("Frame index limit reached; restart pipeline");
|
||||
auto index=static_cast<int>(detected->frame.metadata.frame_index);
|
||||
auto& boxes=detected->boxes;
|
||||
auto frame=imageView(*detected->frame.image);
|
||||
boxesTracker.update(boxes,index,frame.cols,frame.rows);
|
||||
auto poseStart=monotonicNs();
|
||||
++poseRuns;
|
||||
auto raw=pose->infer(frame,boxes,stream.value);auto isolated=raw;
|
||||
for(std::size_t p=0;p<boxes.size();++p) {
|
||||
auto const& box=boxes[p];double area=std::max(double(box.x2-box.x1)*(box.y2-box.y1),1.0);
|
||||
bool needsIsolation=false;
|
||||
for(std::size_t q=0;q<boxes.size();++q)if(q!=p&&std::max(double(boxes[q].x2-boxes[q].x1)*(boxes[q].y2-boxes[q].y1),1.0)<0.5*area)needsIsolation=true;
|
||||
if(!eligible(generation))break;
|
||||
if(needsIsolation){auto masked=maskOtherPeople(frame,boxes,p);isolated[p]=pose->infer(masked,{box},stream.value).at(0);}
|
||||
}
|
||||
if(!eligible(generation))continue;
|
||||
auto poseElapsed=elapsedMs(poseStart);auto fusionStart=monotonicNs();
|
||||
auto fused=fusion.update(raw,isolated,index);
|
||||
auto record=makeRecord(index,raw,isolated,fused,false,false);
|
||||
@@ -188,9 +159,6 @@ struct CameraPipeline::Impl {
|
||||
result->image=std::move(detected->frame.image);result->door_mask_pixels=doorPixels;
|
||||
result->door_mask_calibration_verified=config.door_mask_calibration_verified;
|
||||
result->timing={detected->elapsed_ms,poseElapsed,elapsedMs(fusionStart),elapsedMs(result->capture.capture_monotonic_ns)};
|
||||
auto gate=status();
|
||||
if(!gate.enabled||gate.generation!=generation)continue;
|
||||
result->inference=gate;
|
||||
FrameResultPtr immutable=result;
|
||||
results.publish(std::move(immutable));
|
||||
{std::lock_guard<std::mutex> lock(stateMutex);totalLatency+=result->timing.capture_to_result_ms;++published;}
|
||||
@@ -202,16 +170,12 @@ struct CameraPipeline::Impl {
|
||||
CameraPipeline::CameraPipeline(PipelineConfig config):impl_(std::make_unique<Impl>(std::move(config))){}
|
||||
CameraPipeline::~CameraPipeline()=default;
|
||||
void CameraPipeline::start(){impl_->start();}
|
||||
FrameResultPtr CameraPipeline::latest()const{
|
||||
auto result=impl_->results.load();auto gate=impl_->status();
|
||||
return result&&gate.enabled&&result->inference.generation==gate.generation?result:FrameResultPtr{};
|
||||
}
|
||||
InferenceStatus CameraPipeline::inferenceStatus()const{return impl_->status();}
|
||||
FrameResultPtr CameraPipeline::latest()const{return impl_->results.load();}
|
||||
bool CameraPipeline::running()const noexcept{return impl_->active.load();}
|
||||
void CameraPipeline::requestStop()noexcept{impl_->stop();}
|
||||
void CameraPipeline::join(){impl_->joinWorkers();{std::lock_guard<std::mutex> lock(impl_->stateMutex);impl_->camera.reset();impl_->can.reset();}impl_->rethrowFailure();}
|
||||
void CameraPipeline::join(){impl_->joinWorkers();{std::lock_guard<std::mutex> lock(impl_->stateMutex);impl_->camera.reset();}impl_->rethrowFailure();}
|
||||
PipelineStats CameraPipeline::stats()const {
|
||||
std::lock_guard<std::mutex> lock(impl_->stateMutex);auto count=impl_->published.load();
|
||||
return {impl_->captured.load(),count,impl_->gaps.load(),impl_->errorFrames.load(),count?impl_->totalLatency/count:0,impl_->detectorRuns.load(),impl_->poseRuns.load(),impl_->skippedIdle.load()};
|
||||
return {impl_->captured.load(),count,impl_->gaps.load(),impl_->errorFrames.load(),count?impl_->totalLatency/count:0};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
// can_comm.cpp - USBCANFD CAN/CANFD 收发封装实现
|
||||
//
|
||||
// 代码迁移自 vision-anti-pinch_bak/main.cpp 的 CAN 模块部分。
|
||||
|
||||
#include "can_comm.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <pthread.h>
|
||||
#include <atomic>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zcan.h"
|
||||
|
||||
/* =====================================================================
|
||||
* 类型/宏/全局变量
|
||||
* ===================================================================== */
|
||||
|
||||
typedef uint32_t UINT;
|
||||
typedef uint32_t U32;
|
||||
#define msleep(ms) usleep((ms) * 1000)
|
||||
|
||||
#define USBCANFD 33
|
||||
#define MAX_CHANNELS 2 // 最大通道数量
|
||||
#define RX_WAIT_TIME 100
|
||||
#define RX_BUFF_SIZE 1000
|
||||
|
||||
// 接收线程上下文
|
||||
typedef struct
|
||||
{
|
||||
int dev_type; // 设备类型
|
||||
int dev_idx; // 设备索引
|
||||
int chn_idx; // 通道号
|
||||
int total; // 接收总数
|
||||
std::atomic<int> stop{0}; // 线程结束标志
|
||||
} THREAD_CTX;
|
||||
|
||||
/* CAN 运行标志, 由信号处理函数(Ctrl+C)或外部置位 */
|
||||
static volatile bool g_can_running = true;
|
||||
|
||||
static std::atomic<int> g_receive_count{0};
|
||||
|
||||
/* ===== 仅供 can_start/can_stop 共享的文件内静态变量 ===== */
|
||||
static int s_DevType = USBCANFD;
|
||||
static int s_DevIdx = 0;
|
||||
static THREAD_CTX s_rx_ctx[MAX_CHANNELS];
|
||||
static pthread_t s_rx_threads[MAX_CHANNELS];
|
||||
static bool s_thread_started[MAX_CHANNELS]{};
|
||||
static bool s_can_opened = false;
|
||||
|
||||
namespace CanComm {
|
||||
|
||||
bool is_open() { return s_can_opened; }
|
||||
|
||||
int receive_count() { return g_receive_count.load(); }
|
||||
|
||||
/* ===== 报文解析回调 (由 main.cpp 通过 set_msg_callback 注册) ===== */
|
||||
static MsgCallback s_msg_cb = nullptr;
|
||||
|
||||
void set_msg_callback(MsgCallback cb) { s_msg_cb = cb; }
|
||||
|
||||
} // namespace CanComm
|
||||
|
||||
/* =====================================================================
|
||||
* 帧构建与收发
|
||||
* ===================================================================== */
|
||||
|
||||
namespace CanComm {
|
||||
|
||||
// 构建 CAN 帧
|
||||
static void construct_can_frame(ZCAN_20_MSG *can_msg, UINT id, int chn_idx, int pad)
|
||||
{
|
||||
memset(can_msg, 0, sizeof(ZCAN_20_MSG));
|
||||
can_msg->hdr.inf.txm = 0; // 0-正常发送
|
||||
can_msg->hdr.inf.fmt = 0; // 0-CAN
|
||||
can_msg->hdr.inf.sdf = 0; // 0-数据帧, 1-远程帧
|
||||
can_msg->hdr.inf.sef = 0; // 0-标准帧, 1-扩展帧
|
||||
|
||||
can_msg->hdr.id = id; // ID
|
||||
can_msg->hdr.chn = chn_idx; // 通道
|
||||
can_msg->hdr.len = 8; // 数据长度
|
||||
|
||||
if (pad > 0)
|
||||
{
|
||||
can_msg->hdr.pad = pad; // 发送后延迟 pad ms
|
||||
can_msg->hdr.inf.qsend = 1; // 队列发送帧,仅判断首帧
|
||||
can_msg->hdr.inf.qsend_100us = 0; // 队列发送单位,0-ms,1-100us
|
||||
}
|
||||
|
||||
for (int i = 0; i < can_msg->hdr.len; i++)
|
||||
can_msg->dat[i] = i;
|
||||
}
|
||||
|
||||
// 构建 CANFD 帧
|
||||
static void construct_canfd_frame(ZCAN_FD_MSG *canfd_msg, UINT id, int chn_idx, int pad)
|
||||
{
|
||||
memset(canfd_msg, 0, sizeof(ZCAN_FD_MSG));
|
||||
canfd_msg->hdr.inf.txm = 0; // 0-正常发送
|
||||
canfd_msg->hdr.inf.fmt = 1; // 0-CAN, 1-CANFD
|
||||
canfd_msg->hdr.inf.sdf = 0; // 0-数据帧 CANFD只有数据帧!
|
||||
canfd_msg->hdr.inf.sef = 1; // 0-标准帧, 1-扩展帧
|
||||
canfd_msg->hdr.inf.brs = 1; // canfd 加速
|
||||
|
||||
canfd_msg->hdr.id = id; // ID
|
||||
canfd_msg->hdr.chn = chn_idx; // 通道
|
||||
canfd_msg->hdr.len = 64; // 数据长度
|
||||
|
||||
if (pad > 0)
|
||||
{
|
||||
canfd_msg->hdr.pad = 100; // 发送后延迟100ms
|
||||
canfd_msg->hdr.inf.qsend = 1; // 队列发送帧,仅判断首帧
|
||||
canfd_msg->hdr.inf.qsend_100us = 0; // 队列发送单位,0-ms,1-100us
|
||||
}
|
||||
|
||||
for (int i = 0; i < canfd_msg->hdr.len; i++)
|
||||
canfd_msg->dat[i] = i;
|
||||
}
|
||||
|
||||
// 分发报文到 main.cpp 注册的解析回调
|
||||
static inline void dispatch_msg(uint32_t ts, uint32_t msg_id, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (s_msg_cb)
|
||||
s_msg_cb(ts, msg_id, data, len);
|
||||
}
|
||||
|
||||
// 接收线程
|
||||
static void *rx_thread(void *data)
|
||||
{
|
||||
THREAD_CTX *ctx = (THREAD_CTX *)data;
|
||||
int DevType = ctx->dev_type;
|
||||
int DevIdx = ctx->dev_idx;
|
||||
int chn_idx = ctx->chn_idx;
|
||||
|
||||
ZCAN_20_MSG can_data[RX_BUFF_SIZE];
|
||||
ZCAN_FD_MSG canfd_data[RX_BUFF_SIZE];
|
||||
|
||||
while (!ctx->stop)
|
||||
{
|
||||
memset(can_data, 0, sizeof(can_data));
|
||||
memset(canfd_data, 0, sizeof(canfd_data));
|
||||
|
||||
int rcount = VCI_Receive(DevType, DevIdx, chn_idx, can_data, RX_BUFF_SIZE, RX_WAIT_TIME); // CAN
|
||||
for (int i = 0; i < rcount; ++i)
|
||||
{
|
||||
if (can_data[i].hdr.inf.sdf == 0)
|
||||
{ // 数据帧
|
||||
dispatch_msg(can_data->hdr.ts, can_data[i].hdr.id, can_data[i].dat, can_data[i].hdr.len);
|
||||
}
|
||||
}
|
||||
ctx->total += rcount;
|
||||
|
||||
rcount = VCI_ReceiveFD(DevType, DevIdx, chn_idx, canfd_data, RX_BUFF_SIZE, RX_WAIT_TIME); // CANFD
|
||||
for (int i = 0; i < rcount; ++i)
|
||||
{
|
||||
dispatch_msg(canfd_data->hdr.ts, canfd_data[i].hdr.id, canfd_data[i].dat, canfd_data[i].hdr.len);
|
||||
}
|
||||
ctx->total += rcount;
|
||||
msleep(10);
|
||||
}
|
||||
g_receive_count = ctx->total;
|
||||
printf("chn: %d receive %d\n", chn_idx, ctx->total);
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
// 合并接收线程
|
||||
static void *rx_merge_thread(void *data)
|
||||
{
|
||||
THREAD_CTX *ctx = (THREAD_CTX *)data;
|
||||
int DevType = ctx->dev_type;
|
||||
int DevIdx = ctx->dev_idx;
|
||||
|
||||
ZCANDataObj obj_data[RX_BUFF_SIZE];
|
||||
|
||||
while (!ctx->stop)
|
||||
{
|
||||
memset(obj_data, 0, sizeof(obj_data));
|
||||
int rcount = VCI_ReceiveData(DevType, DevIdx, 0, obj_data, RX_BUFF_SIZE, RX_WAIT_TIME); // 第三个参数固定为0就好
|
||||
for (int i = 0; i < rcount; ++i)
|
||||
{
|
||||
if (obj_data[i].dataType == ZCAN_DT_ZCAN_CAN_DATA)
|
||||
{ // CAN
|
||||
ZCAN_20_MSG *can_data = &obj_data[i].data.zcanCANData;
|
||||
if (can_data->hdr.inf.sdf == 0)
|
||||
{ // 数据帧
|
||||
dispatch_msg(can_data->hdr.ts, can_data->hdr.id, can_data->dat, can_data->hdr.len);
|
||||
}
|
||||
}
|
||||
else if (obj_data[i].dataType == ZCAN_DT_ZCAN_CANFD_DATA)
|
||||
{ // CANFD
|
||||
ZCAN_FD_MSG *canfd_data = &obj_data[i].data.zcanCANFDData;
|
||||
dispatch_msg(canfd_data->hdr.ts, canfd_data->hdr.id, canfd_data->dat, canfd_data->hdr.len);
|
||||
}
|
||||
}
|
||||
ctx->total += rcount;
|
||||
msleep(10);
|
||||
}
|
||||
g_receive_count = ctx->total;
|
||||
printf("merge receive %d\n", ctx->total);
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化并启动 CAN 收发。
|
||||
* 返回 true 成功; false 失败(主流程可继续仅取图)。
|
||||
*/
|
||||
bool can_start()
|
||||
{
|
||||
g_can_running = true;
|
||||
int IsMerge = 1; // 是否合并接收
|
||||
|
||||
// 打开设备
|
||||
if (!VCI_OpenDevice(s_DevType, s_DevIdx, 0))
|
||||
{
|
||||
printf("Open device fail\n");
|
||||
return false;
|
||||
}
|
||||
s_can_opened = true;
|
||||
printf("Open device success\n");
|
||||
|
||||
// 初始化,启动通道
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
{
|
||||
ZCAN_INIT init{}; // 波特率结构体,数据根据zcanpro的波特率计算器得出
|
||||
init.clk = 60000000; // clock: 60M(V1.01) 80M(V1.03即以上)
|
||||
init.mode = 0; // 0-正常
|
||||
|
||||
init.aset.tseg1 = 14; // 仲裁域 500kbps
|
||||
init.aset.tseg2 = 3;
|
||||
init.aset.sjw = 2;
|
||||
init.aset.smp = 0;
|
||||
init.aset.brp = 5;
|
||||
|
||||
init.dset.tseg1 = 10; // 数据域 2000kbps
|
||||
init.dset.tseg2 = 2;
|
||||
init.dset.sjw = 2;
|
||||
init.dset.smp = 0;
|
||||
init.dset.brp = 1;
|
||||
|
||||
if (!VCI_InitCAN(s_DevType, s_DevIdx, i, &init)) // 初始化通道
|
||||
{
|
||||
printf("InitCAN(%d) fail\n", i);
|
||||
can_stop();return false;
|
||||
}
|
||||
printf("InitCAN(%d) success\n", i);
|
||||
|
||||
U32 on = 1;
|
||||
if (!VCI_SetReference(s_DevType, s_DevIdx, i, CMD_CAN_TRES, &on)) // 终端电阻
|
||||
{
|
||||
printf("CMD_CAN_TRES fail\n");
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
if (!VCI_SetReference(s_DevType, s_DevIdx, i, ZCAN_CMD_SET_CHNL_RECV_MERGE, &IsMerge)) // 合并接收
|
||||
{
|
||||
printf("ZCAN_CMD_SET_CHNL_RECV_MERGE fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!VCI_StartCAN(s_DevType, s_DevIdx, i)) // 启动通道
|
||||
{
|
||||
printf("StartCAN(%d) fail\n", i);
|
||||
can_stop();return false;
|
||||
}
|
||||
printf("StartCAN(%d) success\n", i);
|
||||
|
||||
s_rx_ctx[i].dev_type = s_DevType;
|
||||
s_rx_ctx[i].dev_idx = s_DevIdx;
|
||||
s_rx_ctx[i].chn_idx = i;
|
||||
s_rx_ctx[i].total = 0;
|
||||
s_rx_ctx[i].stop = 0;
|
||||
|
||||
if (IsMerge && i != 0)continue;
|
||||
if (IsMerge && i == 0)
|
||||
{
|
||||
s_thread_started[i]=pthread_create(&s_rx_threads[i], NULL, rx_merge_thread, &s_rx_ctx[i])==0; // 合并接收线程
|
||||
}
|
||||
else
|
||||
{
|
||||
s_thread_started[i]=pthread_create(&s_rx_threads[i], NULL, rx_thread, &s_rx_ctx[i])==0; // 创建接收线程
|
||||
}
|
||||
if(!s_thread_started[i]){can_stop();return false;}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 停止 CAN 接收并关闭设备。
|
||||
*/
|
||||
void can_stop()
|
||||
{
|
||||
if (!s_can_opened)
|
||||
return;
|
||||
|
||||
g_can_running = false;
|
||||
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
{
|
||||
s_rx_ctx[i].stop = 1;
|
||||
if(s_thread_started[i]){pthread_join(s_rx_threads[i], NULL);s_thread_started[i]=false;} // 等待接收线程退出
|
||||
|
||||
if (!VCI_ResetCAN(s_DevType, s_DevIdx, i)) // 复位通道
|
||||
printf("ResetCAN(%d) fail\n", i);
|
||||
else
|
||||
printf("ResetCAN(%d) success!\n", i);
|
||||
}
|
||||
|
||||
if (!VCI_CloseDevice(s_DevType, s_DevIdx))
|
||||
printf("CloseDevice fail\n");
|
||||
else
|
||||
printf("CloseDevice success\n");
|
||||
|
||||
s_can_opened = false;
|
||||
}
|
||||
|
||||
uint32_t can_send(int chn_idx, uint32_t id, const uint8_t data[8])
|
||||
{
|
||||
if (!s_can_opened)
|
||||
return 0;
|
||||
|
||||
ZCAN_20_MSG msg;
|
||||
construct_can_frame(&msg, id, chn_idx, 0);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
msg.dat[i] = data[i];
|
||||
|
||||
return VCI_Transmit(s_DevType, s_DevIdx, chn_idx, &msg, 1);
|
||||
}
|
||||
|
||||
uint32_t canfd_send(int chn_idx, uint32_t id, const uint8_t data[64])
|
||||
{
|
||||
if (!s_can_opened)
|
||||
return 0;
|
||||
|
||||
ZCAN_FD_MSG msg;
|
||||
construct_canfd_frame(&msg, id, chn_idx, 0);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
msg.dat[i] = data[i];
|
||||
|
||||
return VCI_TransmitFD(s_DevType, s_DevIdx, chn_idx, &msg, 1);
|
||||
}
|
||||
|
||||
} // namespace CanComm
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "can_input.hpp"
|
||||
#include "can_comm.h"
|
||||
#include "camera.hpp"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <stdexcept>
|
||||
namespace cms {
|
||||
struct CanInput::Impl {
|
||||
static std::mutex owner_mutex;
|
||||
static Impl* owner;
|
||||
std::mutex mutex;
|
||||
std::condition_variable wake;
|
||||
LazyGate gate;
|
||||
std::atomic<bool> stopped{false};
|
||||
std::thread worker;
|
||||
static void receive(int64_t,uint32_t id,uint8_t const* data,uint32_t size) {
|
||||
std::lock_guard<std::mutex> lock(owner_mutex);
|
||||
if(owner){std::lock_guard<std::mutex> stateLock(owner->mutex);owner->gate.receive(id,data,size,monotonicNs());}
|
||||
}
|
||||
explicit Impl(LazyGateConfig config):gate(config) {
|
||||
{std::lock_guard<std::mutex> lock(owner_mutex);if(owner)throw std::logic_error("Only one CAN receiver per process");owner=this;}
|
||||
CanComm::set_msg_callback(receive);
|
||||
try {worker=std::thread([this] {
|
||||
bool opened=false;
|
||||
auto attempted=monotonicNs();
|
||||
while(!stopped) {
|
||||
auto now=monotonicNs();
|
||||
auto status=snapshot();
|
||||
// Retry unplugged/stalled adapters while CAN fallback inference continues.
|
||||
if(!opened||(now-attempted>5000000000ULL&&!status.can_valid)) {
|
||||
if(opened)CanComm::can_stop();
|
||||
opened=CanComm::can_start();attempted=monotonicNs();
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
wake.wait_for(lock,std::chrono::seconds(1),[this]{return stopped.load();});
|
||||
}
|
||||
if(opened)CanComm::can_stop();
|
||||
});}catch(...){CanComm::set_msg_callback(nullptr);std::lock_guard<std::mutex> lock(owner_mutex);owner=nullptr;throw;}
|
||||
}
|
||||
~Impl() {
|
||||
stopped=true;wake.notify_all();if(worker.joinable())worker.join();
|
||||
CanComm::set_msg_callback(nullptr);
|
||||
std::lock_guard<std::mutex> lock(owner_mutex);owner=nullptr;
|
||||
}
|
||||
InferenceStatus snapshot() {std::lock_guard<std::mutex> lock(mutex);return gate.snapshot(monotonicNs());}
|
||||
};
|
||||
std::mutex CanInput::Impl::owner_mutex;
|
||||
CanInput::Impl* CanInput::Impl::owner=nullptr;
|
||||
CanInput::CanInput(LazyGateConfig config):impl_(std::make_unique<Impl>(config)){}
|
||||
CanInput::~CanInput()=default;
|
||||
InferenceStatus CanInput::snapshot(){return impl_->snapshot();}
|
||||
}
|
||||
@@ -19,12 +19,6 @@ PipelineConfig loadConfig(std::filesystem::path const& path) {
|
||||
auto const& door=j.at("door");config.door_mask=resolve(door.at("mask"));
|
||||
config.warning_relative=door.at("warning_relative");config.release_relative=door.at("release_relative");
|
||||
config.door_mask_calibration_verified=door.at("calibration_verified");
|
||||
if(j.contains("lazy_inference")) {
|
||||
auto const& gate=j.at("lazy_inference");
|
||||
config.lazy_gate.threshold_percent=gate.value("threshold_percent",15);
|
||||
config.lazy_gate.can_timeout_ms=gate.value("can_timeout_ms",1000);
|
||||
config.lazy_gate.motion_timeout_ms=gate.value("motion_timeout_ms",300);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#include "lazy_gate.hpp"
|
||||
#include <stdexcept>
|
||||
namespace cms {
|
||||
LazyGate::LazyGate(LazyGateConfig config):config_(config) {
|
||||
if(config.threshold_percent<1||config.threshold_percent>100||config.can_timeout_ms<=0||config.motion_timeout_ms<=0||config.motion_timeout_ms>config.can_timeout_ms)
|
||||
throw std::invalid_argument("Invalid lazy inference gate configuration");
|
||||
}
|
||||
void LazyGate::receive(std::uint32_t id,std::uint8_t const* data,std::size_t size,std::uint64_t now) {
|
||||
if(id!=0x391||size<4||!data)return;
|
||||
snapshot(now); // Account for a timeout before receiving a new sample.
|
||||
bool continuity=have_sample_&&now>=last_sample_&&now-last_sample_<=std::uint64_t(config_.can_timeout_ms)*1000000;
|
||||
for(int side=0;side<2;++side) {
|
||||
int index=side*2;
|
||||
if(!continuity){state_.direction[side]=0;last_change_[side]=0;}
|
||||
else if(data[index]!=state_.positions[index]) {
|
||||
state_.direction[side]=data[index]>state_.positions[index]?1:-1;
|
||||
last_change_[side]=now;
|
||||
}
|
||||
}
|
||||
for(int i=0;i<4;++i)state_.positions[i]=data[i];
|
||||
have_sample_=data[0]<=100&&data[2]<=100;
|
||||
if(!have_sample_){state_.direction={};last_change_={};}
|
||||
last_sample_=now;
|
||||
snapshot(now);
|
||||
}
|
||||
InferenceStatus LazyGate::snapshot(std::uint64_t now) {
|
||||
state_.sample_monotonic_ns=now;
|
||||
state_.can_age_ms=have_sample_&&now>=last_sample_?(now-last_sample_)*1e-6:-1;
|
||||
state_.can_valid=state_.can_age_ms>=0&&state_.can_age_ms<=config_.can_timeout_ms;
|
||||
bool triggers[2]{};
|
||||
for(int i=0;i<2;++i) {
|
||||
if(!state_.can_valid||now<last_change_[i]||now-last_change_[i]>=std::uint64_t(config_.motion_timeout_ms)*1000000)state_.direction[i]=0;
|
||||
triggers[i]=state_.can_valid&&state_.positions[i*2]<config_.threshold_percent&&state_.direction[i]!=0;
|
||||
}
|
||||
state_.left_front_trigger=triggers[0];state_.left_rear_trigger=triggers[1];
|
||||
bool enabled=!state_.can_valid||triggers[0]||triggers[1];
|
||||
if(enabled!=state_.enabled){++state_.generation;state_.enabled=enabled;}
|
||||
state_.reason=!state_.can_valid?"CAN_UNAVAILABLE":enabled?"LEFT_DOOR_MOVING_BELOW_THRESHOLD":"IDLE";
|
||||
return state_;
|
||||
}
|
||||
}
|
||||
+34
-56
@@ -2,69 +2,47 @@
|
||||
#include "config.hpp"
|
||||
#include "stop_signals.hpp"
|
||||
#include "visualizer.hpp"
|
||||
#include <opencv2/core.hpp>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
|
||||
int main(int argc,char** argv)try {
|
||||
auto root=std::filesystem::canonical("/proc/self/exe").parent_path().parent_path();
|
||||
auto configPath=root/"config/camera.json";
|
||||
cms::OutputOptions output;output.directory=root/"output";
|
||||
bool visualize=false;double duration=0;
|
||||
auto value=[&](int& i){if(++i>=argc)throw std::invalid_argument("Missing option value");return std::string(argv[i]);};
|
||||
for(int i=1;i<argc;++i) {
|
||||
std::string arg=argv[i];
|
||||
if(arg=="--config")configPath=value(i);
|
||||
else if(arg=="--save-original")output.original=true;
|
||||
else if(arg=="--save-annotated")output.annotated=true;
|
||||
else if(arg=="--save-results")output.results=true;
|
||||
else if(arg=="--visualize")visualize=true;
|
||||
else if(arg=="--duration") {duration=std::stod(value(i));if(!std::isfinite(duration)||duration<0)throw std::invalid_argument("Invalid duration");}
|
||||
else if(arg=="--help") {
|
||||
std::cout<<"camera_risk [--save-original] [--save-annotated] [--save-results] [--config FILE] [--visualize] [--duration SECONDS]\n"
|
||||
<<"C++ lazy inference; resident warm engines, continuous CAN reception.\n";return 0;
|
||||
}else throw std::invalid_argument("Unknown option: "+arg);
|
||||
}
|
||||
cv::setNumThreads(2);
|
||||
bool visualize=false;
|
||||
std::map<std::string,std::function<void(int&)>> arguments{
|
||||
{"--save-original",[&](int&){output.original=true;}},
|
||||
{"--save-annotated",[&](int&){output.annotated=true;}},
|
||||
{"--save-results",[&](int&){output.results=true;}},
|
||||
{"--visualize",[&](int&){visualize=true;}},
|
||||
{"--config",[&](int&i){if(++i>=argc)throw std::invalid_argument("--config needs a file");configPath=argv[i];}},
|
||||
{"--help",[](int&){std::cout<<"camera_risk [--save-original] [--save-annotated] [--save-results] [--config FILE] [--visualize]\nContinuous camera inference; latest FrameResult stays in memory. Stop with Ctrl+C or SIGTERM.\n";std::exit(0);}}
|
||||
};
|
||||
for(int i=1;i<argc;++i){auto handler=arguments.find(argv[i]);if(handler==arguments.end())throw std::invalid_argument("Unknown option: "+std::string(argv[i]));handler->second(i);}
|
||||
|
||||
auto config=cms::loadConfig(configPath);config.output=output;
|
||||
cms::CameraPipeline pipeline(config);
|
||||
cms::StopSignals signals(pipeline);pipeline.start();
|
||||
auto started=std::chrono::steady_clock::now();std::atomic<bool> monitorStop{false};
|
||||
std::thread monitor([&] {
|
||||
auto reported=started-std::chrono::seconds(5);std::uint64_t generation=0;
|
||||
while(!monitorStop&&pipeline.running()) {
|
||||
auto now=std::chrono::steady_clock::now();
|
||||
if(duration>0&&std::chrono::duration<double>(now-started).count()>=duration){pipeline.requestStop();break;}
|
||||
auto state=pipeline.inferenceStatus();
|
||||
if(state.generation!=generation||now-reported>=std::chrono::seconds(5)) {
|
||||
auto stats=pipeline.stats();
|
||||
std::cout<<"inference="<<(state.enabled?"RUNNING":"IDLE")<<" reason="<<state.reason
|
||||
<<" left_front="<<state.positions[0]<<" left_rear="<<state.positions[2]
|
||||
<<" generation="<<state.generation<<" detector_runs="<<stats.detector_runs
|
||||
<<" pose_runs="<<stats.pose_runs<<" results="<<stats.published<<std::endl;
|
||||
generation=state.generation;reported=now;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
});
|
||||
try {
|
||||
if(visualize) {
|
||||
if(!std::getenv("DISPLAY")&&!std::getenv("WAYLAND_DISPLAY"))std::cerr<<"No display session; running without visualization.\n";
|
||||
else try {cms::runLocalVisualization(pipeline);}
|
||||
catch(std::exception const& e){std::cerr<<"Visualization unavailable: "<<e.what()<<std::endl;}
|
||||
}
|
||||
// Downstream reads: pipeline.inferenceStatus(); auto result=pipeline.latest();
|
||||
// result is null while idle, starting, or awaiting the new generation's first frame.
|
||||
pipeline.join();
|
||||
}catch(...){monitorStop=true;pipeline.requestStop();monitor.join();throw;}
|
||||
monitorStop=true;monitor.join();auto stats=pipeline.stats();
|
||||
auto elapsed=std::chrono::duration<double>(std::chrono::steady_clock::now()-started).count();
|
||||
std::cout<<"captured="<<stats.captured<<" results="<<stats.published<<" detector_runs="<<stats.detector_runs
|
||||
<<" pose_runs="<<stats.pose_runs<<" skipped_idle="<<stats.skipped_idle<<" elapsed_seconds="<<elapsed
|
||||
<<" capture_to_result_ms="<<stats.mean_capture_to_result_ms<<std::endl;
|
||||
cms::StopSignals signals(pipeline);
|
||||
pipeline.start();
|
||||
/*
|
||||
* 下游使用示例:在下游自己的线程/调用位置读取最新结果。
|
||||
* 首帧尚未完成时 result 为空;读取不会影响取图和推理。
|
||||
*
|
||||
* 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 个关键点
|
||||
* }
|
||||
*/
|
||||
// Optional observer. Closing/failing the window never requests inference stop.
|
||||
if(visualize) {
|
||||
try {cms::runLocalVisualization(pipeline);}
|
||||
catch(std::exception const& e){std::cerr<<"Visualization unavailable; inference continues: "<<e.what()<<std::endl;}
|
||||
}
|
||||
pipeline.join();
|
||||
return 0;
|
||||
}catch(std::exception const& e){std::cerr<<"Camera pipeline failed: "<<e.what()<<std::endl;return 1;}
|
||||
|
||||
@@ -27,8 +27,6 @@ Json serializeResult(FrameResult const& r) {
|
||||
for(int s=0;s<2;++s){auto const& h=p.hands[s];metadata[s==0?"left":"right"]={{"limb_state",h.limb_consensus?"consensus":"inferred"},{"hand_source",h.source},{"hand_state",h.state},{"distance_eligible",h.distance_eligible},{"alarm_eligible",h.alarm_eligible}};}
|
||||
out["skeletons"].push_back({{"track_id",p.track_id},{"person_index",i},{"layout","coco_wholebody_133"},{"keypoints",points},{"metadata",metadata}});
|
||||
}
|
||||
auto const& state=r.inference;
|
||||
out["inference"]={{"models_ready",state.models_ready},{"enabled",state.enabled},{"can_valid",state.can_valid},{"positions",state.positions},{"direction",state.direction},{"left_front_trigger",state.left_front_trigger},{"left_rear_trigger",state.left_rear_trigger},{"generation",state.generation},{"can_age_ms",state.can_age_ms},{"reason",state.reason}};
|
||||
for(std::size_t i=0;i<r.persons.size();++i) {
|
||||
auto const& p=r.persons[i];auto& skeleton=out["skeletons"][i];
|
||||
skeleton["confidence"]=p.confidence;skeleton["keypoint_scores"]=Json::array();
|
||||
|
||||
@@ -14,13 +14,6 @@ void runLocalVisualization(CameraPipeline const& pipeline) {
|
||||
while(pipeline.running()) {
|
||||
auto result=pipeline.latest();
|
||||
if(result&&result!=shown){cv::imshow(windowName,renderResult(*result));shown=std::move(result);}
|
||||
if(!result) {
|
||||
auto state=pipeline.inferenceStatus();
|
||||
cv::Mat idle(800,1000,CV_8UC3,cv::Scalar(20,24,30));
|
||||
cv::putText(idle,state.enabled?"WAITING FOR FRESH RESULT":"INFERENCE IDLE - MODELS READY",{35,100},cv::FONT_HERSHEY_SIMPLEX,.85,{235,235,235},2);
|
||||
cv::putText(idle,state.reason,{35,150},cv::FONT_HERSHEY_SIMPLEX,.7,{0,190,255},2);
|
||||
cv::imshow(windowName,idle);shown.reset();
|
||||
}
|
||||
int key=cv::waitKey(16)&0xff;
|
||||
if(key==27||key=='q'||key=='Q'||cvGetWindowHandle(windowName)==nullptr)return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user