175 lines
8.3 KiB
Markdown
175 lines
8.3 KiB
Markdown
# Linux ZLG 车辆状态接入
|
||
|
||
当前工程可以通过 ZLG Linux VCI SDK 被动读取 USBCANFD-200U 的 CH1,并把
|
||
车辆状态送入 `/api/hardware/vehicle-state` 和前端整车孪生。状态轮询使用独立
|
||
`monitor-only` 桥,桥进程会拒绝所有发送请求。服务启动后由一个常驻监听线程
|
||
持续缓存最新的已验证状态,HTTP 接口不会为每次前端请求反复开关 USB 设备。
|
||
Linux 只读状态模式的前端刷新周期为 500 ms;缓存只保留 3 秒内的新鲜反馈,
|
||
断线后会自动关闭旧桥并重试连接。监听通道只接受本文列出的 11 个标准帧 ID,
|
||
同时在 SDK 桥内保留同样的软件过滤,避免把无关总线流量转发给服务进程。
|
||
|
||
## 硬件和总线基线
|
||
|
||
- USB:`3068:0009 ZLG USBCANFD-200U`
|
||
- Linux SDK 设备类型:`33`
|
||
- 通道:CH1
|
||
- 帧格式:11-bit ISO CAN FD,按报文保留 BRS 属性
|
||
- 控制器时钟:60 MHz
|
||
- 仲裁段:500 kbit/s,`tseg1=14, tseg2=3, sjw=2, brp=5`
|
||
- 数据段:2 Mbit/s,`tseg1=10, tseg2=2, sjw=2, brp=1`
|
||
- 终端:默认不启用适配器内部 120 欧;只能按实际拓扑调整
|
||
|
||
前端车辆概况依赖 `0x342`(档位)、`0x4EB/0x07`(车速)和 `0x407`
|
||
(门锁)。主驾座椅依赖 `0x4FA`、`0x494`。收到其他帧不能替代这些已验证
|
||
反馈。
|
||
|
||
## 前端有线 / 无线选择
|
||
|
||
前端“车辆连接方式”提供两个相互独立的传输选项:
|
||
|
||
- 有线控制:状态和控制都走后端 ZLG CH1。
|
||
- 无线控制:状态订阅 `n7/n7-headunit/can/state`,控制发布到
|
||
`n7/can_control`;默认 Broker 为 `127.0.0.1:1883`。MQTT CAN0 专用于
|
||
副驾座椅,MQTT CAN1 专用于除副驾座椅外的车辆状态和控制。
|
||
|
||
选择保存在当前浏览器,并随车辆状态请求和控制请求一起传给后端。切换传输时
|
||
会建立新的会话上下文,上一条链路缓存的状态不会冒充当前链路状态。两种方式
|
||
共用同一套编译范围检查、P 挡/零车速预检、反馈目标匹配和硬件批准令牌;MQTT
|
||
发布成功只证明 Broker 接收了消息,不能替代 ECU 状态反馈。
|
||
|
||
MQTT 主题只承载原始 CAN 帧,不接受已经解析的车辆状态 JSON。规范帧格式为:
|
||
|
||
```json
|
||
{
|
||
"schema": "n7.can.frame.v1",
|
||
"arbitration_id": 834,
|
||
"frame_id": "0x342",
|
||
"channel": 1,
|
||
"data_hex": "00 00 00 01 00 00 00 00",
|
||
"data": [0, 0, 0, 1, 0, 0, 0, 0],
|
||
"payload_size": 8,
|
||
"is_fd": true,
|
||
"bitrate_switch": true,
|
||
"timestamp": 1787029200.0
|
||
}
|
||
```
|
||
|
||
`n7/n7-headunit/can/state` 可以发送单帧对象,也可以发送
|
||
`{"frames":[...]}` 批次。接收侧兼容 `can_id`、`canId`、`frame_id` 和
|
||
`dataHex` 等常见字段名,但仍强制 CAN0/CAN1、11 位 ID、1–64 字节长度和
|
||
逐通道反馈 ID 白名单。`n7/can_control` 始终由后端输出上述规范单帧格式,
|
||
其中 `channel` 保留编译计划选择的通道。
|
||
|
||
无线通道边界为:
|
||
|
||
- CAN0:经典 CAN 副驾座椅;直接状态接收 `0x301–0x307`、`0x4FA`。当前车端
|
||
还会以只读 `0x6A0` `SEAT_INFO(0x60)` 查询和 `0x6A2` 响应上报四轴 Hall
|
||
位置、范围和标志。后端只配对 `C7/7C`、相同序号、`status=0` 的查询响应,
|
||
再转换成统一的四轴 `0x302` 状态;查询帧不会被当作普通动作执行。
|
||
无线副驾控制只允许 `0x6A0` 中固件明确提供的运行时 opcode:
|
||
`STOP(0x63)`、`MOVE(0x64)`、`HEARTBEAT(0x65)` 和
|
||
`DYNAMIC_START(0x66)`,并等待匹配序号/opcode 的 `0x6A2` 成功响应及新鲜
|
||
四轴位置反馈。刷写、进入 Bootloader、测试、校准和回零 opcode 全部拒绝;
|
||
旧 `0x29E/0x300/0x3CE` 不会在该无线 CAN0 路径发布。
|
||
- CAN1:除副驾座椅以外的整车 CAN;状态和控制继续使用项目中已有的 CH1
|
||
车辆白名单。CAN0 控制 ID 不能经 CAN1 发送,CAN1 控制 ID 也不能经 CAN0
|
||
发送。
|
||
|
||
两个通道的协议来源严格分离:
|
||
|
||
- CAN0 只以 `/home/orin/project511/seat-code-file/seat-ota` 副驾固件为准;
|
||
入站状态必须是 11 位标准帧、经典 CAN、无 BRS、DLC 8。
|
||
- CAN1 只以
|
||
`/home/orin/project511/project-code/canfd/CAN矩阵表_filled.xlsx` 为准。
|
||
例如 `0x42E` 颜色值只有 `0x01–0x1F` 有效,保留值不会显示为
|
||
真实颜色。
|
||
|
||
MQTT 消息自带的 `can0Seat.parsed` 和 `can1Vehicle.parsed` 只可用于车端
|
||
诊断,后端不使用它们更新状态或通过安全门禁。
|
||
|
||
副驾位置换算严格使用固件 `OTA_SEAT_INFO_AXIS0..3_POSITION_RANGE`:响应值低
|
||
16 位为当前位置 Hall 计数,高 16 位为该轴 Hall 范围;
|
||
`OTA_SEAT_INFO_AXIS0..3_FLAGS` 提供 `valid/moving/driver_fault/direction`。四轴
|
||
分别映射为水平、坐垫、靠背和腿托。只读缓存最多保留 3 秒,新建状态请求可
|
||
立即复用新鲜四轴快照,不需要等待下一轮车端查询。
|
||
|
||
每个 MQTT 总线视图只会收到本通道反馈。发布完成仅代表 Broker 接收消息;
|
||
座椅和整车执行器仍必须等待对应状态 ID 与目标字段匹配,才会报告动作成功。
|
||
当前 seat-ota 固件只提供四轴位置和动态模式控制,没有副驾加热/通风运行时
|
||
opcode;无线模式收到这两类副驾请求会在发布前明确失败,不会伪造反馈。
|
||
|
||
车端现有 `n7-can-signals/v1` 状态封装也可直接发布到
|
||
`n7/n7-headunit/can/state`。后端优先从顶层 `signals` 读取,避免与嵌套副本重复;
|
||
顶层缺失时会合并 `can0Seat.signals`(默认 CAN0)和
|
||
`can1Vehicle.signals`(默认 CAN1)。接收兼容 `channel: "can0"/"can1"`、
|
||
`channelIndex`、`frameType`、`brs`、`data`/`bytes` 和毫秒时间戳。若同一帧的
|
||
`channel` 与 `channelIndex` 冲突,整批消息会被拒绝。封装中的
|
||
`can0Seat.parsed` 和 `can1Vehicle.parsed` 都不作为权威状态输入;安全预检和
|
||
前端状态仍由原始 CAN 帧通过逐通道白名单解码器生成。带 Unix 时间戳的过期
|
||
CAN1 非安全帧可短暂作为 `display_only` 缓存显示,但不会进入控制执行器的
|
||
反馈队列。过期的 `0x342` 挡位和 `0x4EB` 车速帧会直接拒绝,绝不能用
|
||
旧的 P 挡或零车速结果通过当前控制预检。
|
||
|
||
无线 CAN0 和 CAN1 状态读取互不持有真实执行独占锁。CAN0 状态缺失或等待时
|
||
不会再让 CAN1 接口返回 `hardware_busy`。前端按通道分别保持 single-flight,
|
||
CAN1 以 500 ms 周期刷新;手动切换有线/无线后会立即按所选后端重新建立轮询
|
||
周期,上一传输方式的迟到响应会被丢弃。
|
||
|
||
无线副驾动作在向 CAN0 发布前,也必须通过同一条 MQTT 链路上的 CAN1 新鲜
|
||
P 挡与零车速预检。远端链路不会触发本机 IAR 自动烧录;副驾固件升级仍需走
|
||
独立、明确授权的维护流程。
|
||
|
||
## USB 权限
|
||
|
||
设备节点必须允许运行服务的用户读写。推荐使用仅授予当前桌面登录用户权限的
|
||
udev 规则:
|
||
|
||
```bash
|
||
sudo sh -c 'printf "%s\n" '\''SUBSYSTEM=="usb", ATTR{idVendor}=="3068", ATTR{idProduct}=="0009", TAG+="uaccess", MODE="0660"'\'' > /etc/udev/rules.d/50-zlg-usbcanfd.rules'
|
||
sudo udevadm control --reload-rules
|
||
sudo udevadm trigger --attr-match=idVendor=3068 --attr-match=idProduct=0009
|
||
```
|
||
|
||
若 ACL 没有立即出现,重新插拔适配器。使用以下命令核对,当前服务用户应具有
|
||
`rw`:
|
||
|
||
```bash
|
||
getfacl -p /dev/bus/usb/001/006
|
||
```
|
||
|
||
设备编号每次插拔可能变化,应以 `lsusb` 的 Bus/Device 为准。
|
||
|
||
## 服务配置
|
||
|
||
将 `config/environments/linux_vehicle_state.example.env` 中的配置复制到仓库
|
||
根目录 `.env`,并按实际 SDK 位置修改
|
||
`COGNITIVE_AGENT_ZLGCAN_LINUX_LIBRARY`。不要设置硬件批准令牌;此时
|
||
`vehicle_can.state_configured=true`,但 `vehicle_can.configured=false`,所以只
|
||
允许状态同步,前端真实执行保持锁定。
|
||
|
||
启动:
|
||
|
||
```bash
|
||
make dev
|
||
```
|
||
|
||
核对运行时:
|
||
|
||
```bash
|
||
curl -s http://127.0.0.1:8000/api/runtime
|
||
```
|
||
|
||
车辆状态接口应返回 `source=physical_can_ch1`、实际收到的
|
||
`observed_frame_ids`,以及缺失反馈列表。发送成功、CAN ACK 和状态反馈是不同
|
||
证据层级;只有接口实际收到并解码目标反馈才会更新前端。
|
||
|
||
## SDK 自检
|
||
|
||
自检只加载动态库和核对 ABI,不打开 USB:
|
||
|
||
```bash
|
||
.venv/bin/python src/cognitive_agent/infrastructure/can/zlgcan_linux_bridge.py \
|
||
--dll /home/orin/project511/project-code/canfd/libusbcanfd.so \
|
||
--self-test
|
||
```
|