Files
Cognitive_Agent/docs/workflows/linux-zlg-vehicle-state.md
T

206 lines
9.7 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.
# Linux ZLG 车辆状态接入
当前工程可以通过 ZLG Linux VCI SDK 被动读取 USBCANFD-200U 的 CH1,并把
车辆状态送入 `/api/hardware/vehicle-state` 和前端整车孪生。状态轮询使用独立
`monitor-only` 桥,桥进程会拒绝所有发送请求。服务启动后由一个常驻监听线程
持续缓存最新的已验证状态,HTTP 接口不会为每次前端请求反复开关 USB 设备。
Linux 只读状态模式的前端刷新周期为 500 ms;缓存只保留 3 秒内的新鲜反馈,
断线后会自动关闭旧桥并重试连接。监听通道只接受本文列出的 12 个标准帧 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`。收到其他帧不能替代这些已验证
反馈。车身设备还会按帧独立更新:`0x388` 四窗、`0x404` 空调、`0x41A`
遮阳帘、`0x42E/0x3F3/0x3FF` 车内灯光,以及 `0x38A` 前部灯光。
## 前端有线 / 无线选择
前端“车辆连接方式”提供两个相互独立的传输选项:
- 有线控制:状态和控制都走后端 ZLG CH1。
- 无线控制:状态订阅 `n7/n7-headunit/can/state`,控制发布到
`n7/n7-headunit/can/command`;默认 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-can-signals/v1` 状态包由顶层 `signals` 承载同样的原始帧。
`n7/n7-headunit/can/state` 可以发送单帧对象,也可以发送
`{"frames":[...]}` 批次。接收侧兼容 `can_id`、`canId`、`frame_id` 和
`dataHex` 等常见字段名,但仍强制 CAN0/CAN1、11 位 ID、1–64 字节长度和
逐通道反馈 ID 白名单。
自然语言指令仍先由后端编译、检查白名单和车辆安全条件。每个已批准的 CAN 帧
再转换为车机 App `CanCommand` 格式并发布到
`n7/n7-headunit/can/command`:
```json
{
"requestId": "cognitive-agent-0e77879b83fd47a9bf12efb3d20dfae7",
"channel": "can1",
"canId": "0x431",
"frameType": "fd",
"brs": true,
"extended": false,
"remote": false,
"data": "10 00 00 00 00 00 00 00",
"repeat": 1,
"intervalMs": 0,
"responseTimeoutMs": 1500
}
```
每次发布生成不同的 `requestId`。MQTT 外层固定 `repeat=1`,原有执行器继续负责
矩阵要求的重复次数、周期和动作后的安全停止,不会在车机端再次叠加重复发送。
CAN0 输出 `classic` 且禁用 BRS;CAN1 按编译结果输出 Classic CAN 或合法长度的
FD+BRS。车机按通道、控制 ID 和帧类型推断预期反馈 ID。
无线通道边界为:
- 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` 有效,保留值不会显示为
真实颜色。`0x388` 的四窗关好位按矩阵解析;byte 3–6 的四窗百分比另有
`project-code/canfd/window_native.txt` 实车变化轨迹和车端原始帧证据,
因此只有数值落在 `0–100` 时才显示为开度。
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`,在无线模式应返回
`source=wireless_mqtt_ch1`,并包含实际收到的 `observed_frame_ids` 和缺失反馈
列表。前端“整车设备状态”会把这两个来源都作为权威实时状态,并明确显示
“有线 CAN”或“无线 CAN”。发送成功、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
```