Files
linux_sentry_chassis/docs/architecture.md
T

12 KiB
Raw Blame History

架构设计

分层

   ┌────────────────┐   ┌──────────────────────┐   ┌─────────────────────────┐
   │ chassisd       │   │ chassis-tune         │   │ chassis-bench           │
   │ 守护进程        │   │ 自动整定(搜索/报告)  │   │ 台架(激励/包络/采样)    │
   │ 1kHz 循环 / 安全│   │  sim ↓      bench ↓  │   │ chassis-spin 等         │
   └───┬────────┬───┘   └────┬──────────┬──────┘   └────┬───────────────────┘
       │        │            │          │               │
       │        │            │          └───────┬───────┘
       ▼        ▼            ▼                  ▼
 ┌───────────┐ ┌──────────────────┐  ┌───────────────────────┐
 │ mecanum   │ │ dji-esc-sim      │  │ dji-esc-slcan         │
 │ 麦轮运动学 │ │ 被控对象 / 场景   │  │ dji-esc-socketcan     │
 └───────────┘ │ 离线 harness     │  │ 真实总线 ↔ CanLink     │
               │ 异步假电调        │  └──────────┬────────────┘
               └────────┬─────────┘             │
                        │  CanLink(tokio mpsc 通道对)
                        ▼                       ▼
               ┌────────────────────┐  ┌──────────────────┐
               │ motor-core         │◄─│ dji-esc          │
               │ 能力 trait / Pid   │  │ C620/C610 协议    │
               │ VelocityGains      │  │ EscBus 异步驱动   │
               └────────────────────┘  └──────────────────┘

               ┌────────────────────┐
               │ chassis-config     │  chassis.toml schema(chassisd + chassis-tune 共用)
               └────────────────────┘

核心解耦点:CanLink 是一对 tokio 通道(发送 / 接收 CanFrame)。dji-esc 的协议状态机 只面向通道工作,因此:

  • 闭环仿真由 dji-esc-sim 提供两条路径:异步假电调(挂在同一对通道上,chassisd --mock 与集成测试用)与同步 harness(纯浮点积分、无任务无通道,自动整定用,可并行上千次);
  • dji-esc-socketcan(板载原生 SocketCAN)与 dji-esc-slcan(CANable 等 USB 适配器, 串口 Lawicel 协议)只把真实总线桥接到通道,不含任何应用逻辑;应用住在 chassis-bench (台架)与 chassis-tune(整定);
  • 未来更换总线介质(串口转 CAN、MCU 转发)只需新增一个桥接 crate。

被控对象模型(dji-esc-sim)

逐拍离散(dt 秒),电调坐标系(正电流 → 正转速):

  1. 命令延迟:current_raw 进 FIFO,delay_ticks 拍后才作用(链路 + 采样延迟);
  2. 摩擦:运动时 net = u − sign(rpm)·coulomb − viscous·rpm;静止且 |u| > static 时 net = u − sign(u)·coulomb;静止且 |u| ≤ static 时 net = 0(粘住);
  3. 一阶转速:rpm_target = clamp(net × gain, ±max_rpm) − load_rpm, 以时间常数 tau 一阶趋近;
  4. 量化:机械角按转速积分并对 8192 取模,反馈帧是 EscFeedback, 因此角度被截断为 u16、转速为 i16——量化由协议类型自带。

PlantParams::gm3508() 是无摩擦无延迟的理想模型(chassisd --mock 与历史闭环测试依赖它, 行为逐位不变);PlantParams::gm3508_bench() 叠加静 / 库仑摩擦(400~600 raw 量级, 取自台架实测)与 2 拍延迟,是整定的默认被控对象。

单点模型整定出来的增益往往对增益 / 时间常数 / 延迟高度敏感,因此 chassis-tune 默认在 PlantEnsemble(域随机化:增益 ±20%、时间常数 ±30%、延迟 0~3 拍、摩擦 ±50%)上做鲁棒优化, 而不是针对一条理想曲线做最优化。

自动整定(chassis-tune)

两阶段搜索:

  1. 粗扫:Latin Hypercube 分层采样 + rayon 并行评估,用有限次数铺满四维空间;
  2. 精修:以成本最低的 top-k 为起点,在坐标空间(对数刻度,增益恒正)做多起点 argmin 局部搜索(Nelder-Mead 默认,可选 PSO)。

成本 = 归一化加权(调节时间 / 超调 / IAE / 电流);硬约束不进权重:过速、饱和占比过高、 不收敛直接给罚值并记录判定——否则搜索会主动挑一个会飞车的解。

可复现性:每个候选的随机性只来自 (种子, 候选下标) 派生的独立 RNG,粗扫结果排序后再取 top-k, 因此同 seed 两次运行逐位一致(CI 的 just tune-smoke 依赖这一点)。

超参数(参数空间 / 场景 / 权重 / 验收阈值 / 搜索预算 / 被控对象 / 真机安全包络)不在命令行 默认值里散着,而是版本化在 config/tune.toml 的具名预设中(见 preset.rs)。优先级为 内置默认 < 预设文件 < 显式 CLI,逐字段覆盖;current_limit / direction / gear_ratio 不是预设字段,写进预设文件会直接报错(安全参数不允许被预设绕过)。

真机后端(--backend bench)与仿真后端实现同一个 Evaluator trait,区别只是"评估一组增益" 从浮点积分换成真机试验:打开链路 → 上电门控(反馈新鲜)→ 逐次热更新增益 (EscMotor::set_velocity_gains,不重启总线)→ 逐拍重发设定值 + 安全包络 → 任何退出路径 先 0 电流 → 试验间静默。搜索强制单线程,因为只有一条总线。

命令租约与链路深度(实车踩过的坑)

  1. 命令是租约:EscBus 在 command_timeout(默认 50ms)内没收到新命令就强制 0 输出 (防上层任务死亡后旧指令持续输出)。所以 set_velocity 不是"设一次就完事",上层必须 逐拍重发;只在开头下发一次的台架脚本会在 50ms 后自动失电。
  2. 命令队列必须浅:驱动按 1kHz 生产命令,而 slcan 一次 write 在 CDC ACM 上约 2ms, 下行只有几百帧/秒。若 CanLink 命令方向缓冲很深(早期是 512),队列会被过时命令灌满, ESC 执行的是一秒多以前算出的电流——速度环等于开了 1.4s 延迟,轮子必然跑飞。 现在 slcan 桥的 CHANNEL_BOUND = 8:send().await 立即回压,每一帧都按最新反馈现算。 反馈方向是采样流,来不及就丢(try_send),绝不阻塞泵线程。
  3. 桥的两个方向不能互相饿死:发送要有上界(TX_BURST)并在一帧写不动时立刻让位, 接收要批量排空(RX_BURST)。serial crate 的 TTYPort 读写共用同一个超时, 写超时属正常现象,不能当致命错误(否则一次写超时就会把接收方向一起带停)。

DJI 电调 CAN 协议要点(C620 手册)

项目 值
控制帧 ID 0x200(电调 1–4)、0x1FF(电调 5–8)
控制帧格式 8 字节 = 4 × i16 大端电流命令,按电调 ID 顺序
C620 电流标度 ±16384 ↔ ±20 A
C610 电流标度 ±10000 ↔ ±10 A(M2006 组合,接硬件前需核对)
反馈帧 ID 0x200 + 电调ID(0x201–0x208)
反馈帧格式 u16 角度(0..8191) + i16 转速rpm + i16 转矩电流 + i8 温度℃,均大端
反馈频率 每电调 1kHz

C620 内部只闭电流环:转速 / 位置环由主控侧 PID 实现(motor-core::Pid, 挂在 EscBus 的 1kHz 发送节拍上)。GM3508 减速比 3591 / 187 ≈ 19.203。

单位与方向约定

  • 机器人坐标系:ISO 约定——x 前、y 左、ω 逆时针为正(见 mecanum)。
  • Motor 接口呈现的是物理量(安装方向修正后): EscConfig::direction = -1 时,驱动层在写命令与读反馈时统一翻转符号, 上层永远以"轮子正转 = 底盘前进方向"的正向工作。
  • MotorState::rotor_speed_rpm 为电机转子转速;轮端转速 = 转子转速 / 减速比。
  • 电流一律以协议原始值(raw)为主,安培换算通过 EscModel 常量提供。

安全设计

  1. 上电门控:底盘循环在收到任一电机首帧反馈前不下发速度命令。
  2. 反馈离线:任一电机反馈超时(默认 100ms)→ 该电机命令强制 0 电流并告警。
  3. 命令超时:上层 twist 命令超时(默认 200ms)→ 目标速度归零。
  4. 所有超时可经 chassis.toml 配置。

测试策略

层级 内容 平台
单元测试 帧编解码字节级样例 / 饱和、多圈回绕、PID(阶跃、抗饱和、滤波)、运动学解析解 + 属性测试、slcan 帧形状过滤 全平台
闭环仿真 内存通道 + 一阶电机模型:4 电机速度环收敛、群帧组装、离线安全、命令租约到期与续租、增益热更新、守护进程全链路 全平台
模型一致性 同步 harness 与 EscBus 异步闭环在同一组增益下的稳态 / effort 对齐(防止"对着错模型调参") 全平台
整定 指标(合成轨迹解析解)、参数空间(界内 / 确定性 / 对数分布 / 分层覆盖)、成本(硬约束罚值)、搜索(同 seed 逐位复现、优于手调基线)、报告(CSV/TOML/JSON 往返) 全平台(CI 跑 just tune-smoke)
超参数预设 schema 往返、逐字段覆盖与优先级、未知键 / 越界 / 缺失报错、config/tune.toml 全部预设可解算、命名与后端一致、安全参数不可注入 全平台
vcan 集成 vcan0 双 socket 收发,真实内核 CAN 栈 Linux(#[ignore],CI 显式跑)
实车台架 chassis-spin(chassis-bench)四轮定速旋转:链路/反馈、方向、转速稳态、过速保护、安全停机;chassis-tune --backend bench 逐次试验 + 重复性复验 armv7 目标板 + slcan/SocketCAN(人工)
交叉检查 aarch64-unknown-linux-gnu 全 workspace check CI

部署

chassisd 为单二进制守护进程:--interface can0 --config /etc/chassisd/chassis.toml。 systemd 样例见 deploy/chassisd.service。板端 CAN 两条路径:

  • SocketCAN:USB 转 CAN(gs_usb / candleLight)或 SPI CAN HAT(MCP2515)驱动加载后的 can0;
  • slcan:USB 适配器(CANable2 等)在 CDC ACM 下枚举为 /dev/ttyACM0,由 dji-esc-slcan 在用户态处理(不需 ip link,也绕开了 gs_usb 驱动)。台架工具 chassis-spin(chassis-bench)与 自动整定的真机后端走这条路径。整定工具本身是宿主机 / 板端通用的纯 Rust 程序, current_limit 等安全参数由 CLI 显式给出,不参与搜索。

相关文档