refactor: 重构架构并整理仓库
This commit is contained in:
+49
-94
@@ -32,12 +32,11 @@
|
||||
|
||||
| 文档 | 回答的问题 | 读者 |
|
||||
| --- | --- | --- |
|
||||
| [`ARCHITECTURE.md`](../ARCHITECTURE.md) | 整套 RoboMaster CAN 控制系统的**全系统分层规划**(含云台、发射机构、delta10 IMU、T153 实时性、9 电机) | 架构决策者 |
|
||||
| [`docs/architecture.md`](architecture.md) | 本仓库**已实现**部分的解耦点:`CanLink` seam、被控对象模型、自动整定两阶段搜索、测试策略 | 改代码的人 |
|
||||
| **本文** | **底盘这一块**怎么开发、怎么标定、怎么整定、怎么上车、出事怎么办 | 底盘开发 / 调试 / 上车的人 |
|
||||
| [`ARCHITECTURE.md`](../ARCHITECTURE.md) | 职责型 workspace、允许依赖、std/unsafe 策略、实现边界 | 架构决策者 |
|
||||
| [`docs/architecture.md`](architecture.md) | 架构文档入口,不复制规范 | 改代码的人 |
|
||||
| **本文** | **底盘这一块**怎么开发、标定、整定、上车与排障 | 底盘开发 / 调试 / 上车的人 |
|
||||
|
||||
`ARCHITECTURE.md` 里的 `rm-*` crate 划分是**规划**;本仓库实际落地为下面这套 crate 命名
|
||||
(功能对应关系见第 5 节)。本文只覆盖底盘切片,云台 / 发射 / IMU 不在范围内。
|
||||
本文只覆盖四轮底盘切片;云台、发射机构与 DETA10 真实载荷布局不在当前范围内。
|
||||
|
||||
---
|
||||
|
||||
@@ -46,7 +45,7 @@
|
||||
### 2.1 无硬件(macOS / Linux / CI 均可)
|
||||
|
||||
```sh
|
||||
cargo test --workspace # 单测 + 闭环仿真 + 整定,全部无需硬件
|
||||
cargo test --workspace --all-targets # 单测 + 闭环仿真 + 整定,全部无需硬件
|
||||
cargo run -p chassisd -- --mock # 进程内电机模型的全链路演示
|
||||
cargo run -p chassis-tune -- presets # 看有哪些整定超参数预设
|
||||
just tune-smoke # 确定性整定冒烟(CI 同款)
|
||||
@@ -57,10 +56,10 @@ just lint # fmt + clippy(与 CI 同标准)
|
||||
|
||||
```sh
|
||||
# 1) 只验链路与反馈,绝不发非零电流(接线排查第一步)
|
||||
cargo run --release -p chassis-bench --bin chassis-spin -- --device /dev/ttyACM0 --dry-run
|
||||
cargo run --release -p spin -- chassis --device /dev/ttyACM0 --dry-run
|
||||
|
||||
# 2) 四轮定性旋转(轮子必须架空或整车固定,急停可及)
|
||||
cargo run --release -p chassis-bench --bin chassis-spin -- --device /dev/ttyACM0 --rpm 500
|
||||
cargo run --release -p spin -- chassis --device /dev/ttyACM0 --rpm 500
|
||||
|
||||
# 3) 先离线整定,再上台架复验(用版本化的超参数预设)
|
||||
cargo run --release -p chassis-tune -- sweep --preset config/tune.toml --preset-name sim_bench_model
|
||||
@@ -89,8 +88,8 @@ T153 (Linux) Gm3508 × 4 + C620
|
||||
|
||||
| 路径 | 打开方式 | 适用 |
|
||||
| --- | --- | --- |
|
||||
| **slcan**(`dji-esc-slcan`) | 用户态直接读写 `/dev/ttyACM0`(Lawicel ASCII 协议) | 免 `ip link`、绕开 `gs_usb` 驱动;台架与初始联调首选 |
|
||||
| **SocketCAN**(`dji-esc-socketcan`) | `ip link set can0 up type can bitrate 1000000 restart-ms 100` | 板载原生 CAN;生产部署 |
|
||||
| **slcan**(`slcan`) | 用户态直接读写 `/dev/ttyACM0`(Lawicel ASCII 协议) | 免 `ip link`、绕开 `gs_usb` 驱动;台架与初始联调首选 |
|
||||
| **SocketCAN**(`socketcan`) | `ip link set can0 up type can bitrate 1000000 restart-ms 100` | 板载原生 CAN;生产部署 |
|
||||
|
||||
### 3.2 设备与协议常量(C620 手册)
|
||||
|
||||
@@ -105,11 +104,11 @@ T153 (Linux) Gm3508 × 4 + C620
|
||||
| 反馈频率 | 每电调 1 kHz |
|
||||
| 减速比 | GM3508 = 3591 / 187 ≈ **19.203**(转子 : 输出轴) |
|
||||
|
||||
C620 **内部只闭电流环**:转速环 / 位置环由主控侧实现(`motor-core::Pid`,挂在 `EscBus` 的 1 kHz 节拍上)。
|
||||
C620 **内部只闭电流环**:转速环 / 位置环由主控侧实现(`motor-control::Pid`,挂在 `EscBus` 的 1 kHz 节拍上)。
|
||||
|
||||
### 3.3 电调 ID 与安装方向
|
||||
|
||||
默认约定(`config/chassis.toml.example`、`chassis-spin` 默认值一致):
|
||||
默认约定(`config/chassis.toml.example`、`spin chassis` 默认值一致):
|
||||
|
||||
| 轮位 | 轮序下标 | 电调 ID | 安装方向 |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -125,7 +124,7 @@ C620 **内部只闭电流环**:转速环 / 位置环由主控侧实现(`moto
|
||||
|
||||
| 场景 | 建议限幅(raw) | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 空载台架(`chassis-spin` 默认) | **1500**(≈1.8 A) | 需越过静摩擦(实测 400~600 raw)并留整定余量 |
|
||||
| 空载台架(`spin chassis` 默认) | **1500**(≈1.8 A) | 需越过静摩擦(实测 400~600 raw)并留整定余量 |
|
||||
| 整车实车 | 6000 ~ 12000 | 视供电与机械而定;同时是 PID 抗饱和的钳位点 |
|
||||
|
||||
> **限流不是"越大越安全"**:它同时是 PID 输出的钳位点。设得远大于实际所需,
|
||||
@@ -135,7 +134,7 @@ C620 **内部只闭电流环**:转速环 / 位置环由主控侧实现(`moto
|
||||
|
||||
- 电调供电与主控**共地**;CAN_H / CAN_L 不接反,两端 120Ω 终端电阻齐全。
|
||||
- 轮子**架空**或整车固定;急停开关在手可及处。
|
||||
- 首次上电**先 `--dry-run`**,确认四轮反馈都在刷(`chassis-spin` 会打印每个电调的
|
||||
- 首次上电**先 `--dry-run`**,确认四轮反馈都在刷(`spin chassis` 会打印每个电调的
|
||||
转速 / 温度 / 反馈年龄)。
|
||||
|
||||
---
|
||||
@@ -161,7 +160,7 @@ v_RR = vx - vy + a·ω
|
||||
```
|
||||
|
||||
正解是逆解的左伪逆(JᵀJ 对角,可解析求平均)。实现见
|
||||
[`crates/mecanum`](../crates/mecanum):`Mecanum::wheel_speeds` / `twist` /
|
||||
[`mecanum`](../crates/mecanum):`Mecanum::wheel_speeds` / `twist` /
|
||||
`wheel_rpms` / `twist_from_wheel_rpms`。
|
||||
|
||||
### 4.3 单位与层级(最容易出错的地方)
|
||||
@@ -175,7 +174,7 @@ v_RR = vx - vy + a·ω
|
||||
| 控制量 effort | 驱动 / PID | 协议 **raw**(ESC = 电流,C620 ±16384) |
|
||||
| `kp/ki/kd/feedforward` | 速度环 | raw/rpm、raw/(rpm·s)、raw/(rpm/s)、raw/rpm |
|
||||
|
||||
- **减速比换算只发生在一个地方**:`motor-core::OutputShaft` 包装(`转子 = 输出轴 × gear_ratio`)。
|
||||
- **减速比换算只发生在一个地方**:`motor / esc-bus::OutputShaft` 包装(`转子 = 输出轴 × gear_ratio`)。
|
||||
`EscBus` / `EscMotor` 只认转子,绝不碰减速比;`EscConfig::gear_ratio` 仅作配置元数据。
|
||||
- PID 的接线顺序:`ChassisController` 算出**轮端** rpm → `OutputShaft` 换算成转子 rpm →
|
||||
`EscMotor` 的速度环以转子 rpm 闭环。
|
||||
@@ -184,77 +183,34 @@ v_RR = vx - vy + a·ω
|
||||
|
||||
## 5. 软件架构
|
||||
|
||||
### 5.1 Crate 分层与职责
|
||||
### 5.1 职责边界
|
||||
|
||||
**依赖只能向下**(上层可依赖下层,反之不行):
|
||||
完整 crate 清单和允许依赖见 [`ARCHITECTURE.md`](../ARCHITECTURE.md)。底盘路径上的关键职责是:
|
||||
|
||||
```
|
||||
第 4 层 应用 / 工具 chassisd chassis-tune chassis-bench
|
||||
(含两个 bin)
|
||||
第 3 层 领域 chassis-config mecanum dji-esc-sim
|
||||
(schema / 运动学 / 被控对象 + 离线 harness)
|
||||
第 2 层 协议 / 设备 dji-esc(C620 编解码 + EscBus 驱动 + 命令租约)
|
||||
第 1 层 抽象 can-link(CanFrame + 通道对) motor-core(能力 trait + PID)
|
||||
第 0 层 传输 dji-esc-slcan(串口) / dji-esc-socketcan(内核 CAN)
|
||||
```
|
||||
| 职责 | crate |
|
||||
| --- | --- |
|
||||
| CAN 帧、总线接口、链路与 Mock | `can` |
|
||||
| 电调协议 | `motor-protocol` |
|
||||
| 设备契约、命令/状态/减速比/注册表 | `motor` |
|
||||
| 帧泵、租约、反馈新鲜度、零电流 | `esc-bus` |
|
||||
| PID、速度/位置环、组合策略 | `pid`、`motor-control` |
|
||||
| 麦轮运动学 | `mecanum` |
|
||||
| 配置 schema | `chassis-config` |
|
||||
| 台架安全与采样 | `bench` |
|
||||
| 应用入口 | `chassisd`、`spin`、`chassis-tune` |
|
||||
|
||||
`dji-esc-slcan` / `dji-esc-socketcan` 只实现 `can-link` 的通道语义,**不含任何应用逻辑**;
|
||||
它们被第 4 层的工具与守护进程选用,而不是被协议层依赖。
|
||||
crate 内源码保持扁平,不建立 `src/<module>/` 子目录。
|
||||
|
||||
| Crate | 职责 | 平台 |
|
||||
| --- | --- | --- |
|
||||
| [`motor-core`](../crates/motor-core) | 能力 trait(`VelocityDrive` 等)、`MotorState`、`Pid`、`VelocityController`、`OutputShaft` | 纯逻辑,全平台 |
|
||||
| [`can-link`](../crates/can-link) | `CanFrame` + `CanLink` 通道对(**无协议知识**) | 纯逻辑,全平台 |
|
||||
| [`dji-esc`](../crates/dji-esc) | C620/C610 编解码、多圈累计、`EscBus` 异步驱动、命令租约 | 纯逻辑,全平台 |
|
||||
| [`dji-esc-sim`](../crates/dji-esc-sim) | 被控对象模型、场景、**离线 harness**、异步假电调 | 纯逻辑,全平台 |
|
||||
| [`dji-esc-slcan`](../crates/dji-esc-slcan) | slcan(Lawicel)串口 ↔ `CanLink` 桥接 | 全平台 |
|
||||
| [`dji-esc-socketcan`](../crates/dji-esc-socketcan) | SocketCAN ↔ `CanLink` 桥接 | Linux |
|
||||
| [`mecanum`](../crates/mecanum) | 麦轮正 / 逆运动学(纯数学) | 全平台 |
|
||||
| [`chassis-config`](../crates/chassis-config) | `chassis.toml` schema:解析 / 校验 / 写回 | 全平台 |
|
||||
| [`chassis-bench`](../crates/chassis-bench) | 台架:安全包络、采样、`chassis-spin` / `socketcan-spin` | 全平台(SocketCAN 工具需 Linux) |
|
||||
| [`chassis-tune`](../crates/chassis-tune) | 速度环 PID 自动整定 + **超参数预设** | 全平台 |
|
||||
| [`chassisd`](../crates/chassisd) | 底盘守护进程:1 kHz 循环、安全逻辑、指令源 seam | 全平台(实车需 Linux) |
|
||||
### 5.2 依赖与组合
|
||||
|
||||
### 5.2 依赖方向(硬约束)
|
||||
`esc-bus` 不依赖 PID 或 `motor-control`。`motor::EffortPolicy` 是两者的组合点:
|
||||
应用构造 `motor-control::MotorPolicy`,并把它传给 `EscBus::spawn`。
|
||||
|
||||
```
|
||||
chassisd ─┬─► chassis-config ─► motor-core, mecanum
|
||||
├─► dji-esc ──► can-link, motor-core
|
||||
├─► dji-esc-sim ─► dji-esc(协议类型), can-link
|
||||
└─► dji-esc-socketcan ─► can-link
|
||||
总线仍然在 1 kHz 节拍上运行策略,因此控制时序不变;变化的是控制律不再被封在总线驱动里。
|
||||
反馈离线、命令租约到期和模式切换会把对应策略状态复位并强制零输出。
|
||||
|
||||
chassis-tune ─┬─► dji-esc-sim(离线 harness)
|
||||
├─► chassis-bench(安全包络 + 采样)
|
||||
├─► chassis-config(共用 schema)
|
||||
└─► dji-esc ─► can-link, motor-core
|
||||
|
||||
chassis-bench ─┬─► dji-esc ─► can-link, motor-core
|
||||
├─► dji-esc-sim(场景 / 轨迹类型)
|
||||
├─► dji-esc-slcan / dji-esc-socketcan
|
||||
└─► chassis-config
|
||||
```
|
||||
|
||||
**传输后端 crate 只做桥接,不含应用逻辑**:`chassis-spin` 曾住在 `dji-esc-slcan` 的
|
||||
example 里,现在统一收归 `chassis-bench`;整定逻辑住在 `chassis-tune`。要换总线介质
|
||||
(串口转 CAN、MCU 转发),只需**新增一个桥接 crate**,协议 / 控制 / 整定全部不动。
|
||||
|
||||
### 5.3 一个 1 ms 拍里发生了什么
|
||||
|
||||
```
|
||||
tick (1ms)
|
||||
├─ 读取 watch<Twist>(有新值则更新,记录时间戳)
|
||||
├─ 指令超时判定(默认 200ms)→ 超时则 Twist::ZERO
|
||||
├─ 幅值限幅 Twist::clamped(max_vx, max_vy, max_omega)(按比例收缩,保方向)
|
||||
├─ 上电门控:四轮是否都收到过反馈?否则全部 disable 并告警
|
||||
├─ 麦轮逆解 → 四轮轮端 rpm
|
||||
├─ OutputShaft 换算 → 转子 rpm
|
||||
└─ set_velocity(续租命令)→ EscBus 在自身 1 kHz 节拍上组帧、发送
|
||||
```
|
||||
|
||||
同时 `EscBus` 侧持续:接收反馈帧 → 分发到各 `EscMotor` → 更新 `MotorState`
|
||||
→ 检查反馈离线(默认 100 ms)→ 检查命令租约(默认 50 ms)。
|
||||
|
||||
---
|
||||
`bench` 只产生试验轨迹和安全中止原因;指标、成本、搜索和报告适配由
|
||||
`tuning` 与 `chassis-tune` 完成。
|
||||
|
||||
## 6. 关键机制(实车踩过的坑)
|
||||
|
||||
@@ -292,7 +248,7 @@ tick (1ms)
|
||||
|
||||
## 7. 控制设计
|
||||
|
||||
### 7.1 PID(`motor-core::Pid`)
|
||||
### 7.1 PID(`motor-control::Pid`)
|
||||
|
||||
并联式 PID,显式时间步 `dt`(秒),特性:
|
||||
|
||||
@@ -302,7 +258,7 @@ tick (1ms)
|
||||
4. 可选**微分一阶低通**(`with_derivative_tau`),抑制编码器量化噪声;
|
||||
5. `dt <= 0` 或非有限输入 → 返回上次输出且不更新内部状态(防御跳帧 / 首帧)。
|
||||
|
||||
### 7.2 速度环(`motor-core::VelocityController`)
|
||||
### 7.2 速度环(`motor-control::VelocityController`)
|
||||
|
||||
```
|
||||
effort_raw = clamp( PID(target_rpm, measured_rpm, dt) + feedforward × target_rpm, ±limit )
|
||||
@@ -324,7 +280,7 @@ effort_raw = clamp( PID(target_rpm, measured_rpm, dt) + feedforward × target_rp
|
||||
| 一起动就飞 | 前馈过大 or 限流远大于实际所需 | ff → 0、限流贴近实际 |
|
||||
| 稳态有静差 | 摩擦未被积分吃掉 | 检查 `ki` 是否被抗饱和长期冻结 |
|
||||
|
||||
### 7.4 被控对象模型(`dji-esc-sim`)
|
||||
### 7.4 被控对象模型(`sim-harness::motor_model`)
|
||||
|
||||
逐拍离散(`dt` 秒,电调坐标系:正电流 → 正转速):
|
||||
|
||||
@@ -394,9 +350,9 @@ schema 由 [`chassis-config`](../crates/chassis-config) 定义,**守护进程
|
||||
|
||||
| # | 步骤 | 命令 | 通过判据 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | **链路与反馈** | `chassis-spin --dry-run` | 四个电调都在刷反馈(转速 ≈ 0、温度正常、反馈年龄 < 100 ms)。缺一个就查线 / 电调 ID |
|
||||
| 1 | **链路与反馈** | `spin chassis --dry-run` | 四个电调都在刷反馈(转速 ≈ 0、温度正常、反馈年龄 < 100 ms)。缺一个就查线 / 电调 ID |
|
||||
| 2 | **电调 ID 对应** | 同 1,逐个拔插 | 拔掉哪个电调,哪个 ID 的反馈消失 → ID 映射正确 |
|
||||
| 3 | **方向** | `chassis-spin --rpm 200`(低速) | 四个轮子都朝"底盘前进"方向转;反了就改 `direction` |
|
||||
| 3 | **方向** | `spin chassis --rpm 200`(低速) | 四个轮子都朝"底盘前进"方向转;反了就改 `direction` |
|
||||
| 4 | **减速比 / 轮径** | 手转一圈 + 读反馈角度 | 反馈角度 × 减速比 ≈ 轮子转过角度;不符则改 `gear_ratio` |
|
||||
| 5 | **几何参数** | 卷尺量 `half_length` / `half_width` / `wheel_radius` | 填入 `chassis.toml`(影响运动学精度,不影响稳定性) |
|
||||
| 6 | **限流** | 从 1500 raw 起,逐步加到"能可靠起动且不冲" | 越小越好;实车再按需上调 |
|
||||
@@ -485,7 +441,7 @@ chassis-tune sweep --preset config/tune.toml --samples 128 # CLI 覆盖预
|
||||
| `field_updown` | bench | **up-down** 1200 rpm | 实战验收:升 / 降 / 回零双向,阈值收紧到 12% / 3% |
|
||||
|
||||
**预设 schema**(`version = 1`,未知键报错,见
|
||||
[`crates/chassis-tune/src/preset.rs`](../crates/chassis-tune/src/preset.rs)):
|
||||
[`crates/tuning/src/lib.rs`](../crates/tuning/src/lib.rs)):
|
||||
|
||||
```toml
|
||||
version = 1
|
||||
@@ -556,7 +512,7 @@ feedforward = { min = 0.0, max = 0.0, scale = "linear" } # 单点区间 = 搜
|
||||
|
||||
## 11. 台架工具
|
||||
|
||||
### 11.1 `chassis-spin`(`chassis-bench`,slcan,全平台)
|
||||
### 11.1 `spin chassis`(`spin`,slcan,全平台)
|
||||
|
||||
四轮同时以固定**转子**转速旋转。关键参数:
|
||||
|
||||
@@ -586,13 +542,13 @@ feedforward = { min = 0.0, max = 0.0, scale = "linear" } # 单点区间 = 搜
|
||||
- 上电门控:四个电调都上报新鲜反馈前绝不下发速度;
|
||||
- 空载的合适整定与带载**完全不同**,空载**不要**加前馈。
|
||||
|
||||
### 11.2 `socketcan-spin`(`chassis-bench`,仅 Linux)
|
||||
### 11.2 `spin motor`(`spin`,仅 Linux)
|
||||
|
||||
单电机链路验证,走 SocketCAN。用于板载原生 CAN 的接线排查:
|
||||
|
||||
```sh
|
||||
ip link set can0 up type can bitrate 1000000 restart-ms 100
|
||||
cargo run --release -p chassis-bench --bin socketcan-spin -- --interface can0 --dry-run
|
||||
cargo run --release -p spin -- motor -- --interface can0 --dry-run
|
||||
```
|
||||
|
||||
---
|
||||
@@ -632,7 +588,7 @@ cargo run --release -p chassis-bench --bin socketcan-spin -- --interface can0 --
|
||||
| 整定 | 指标(合成轨迹解析解)、参数空间(界内 / 确定性 / 对数分布 / 分层覆盖)、成本(硬约束罚值)、搜索(同 seed 逐位复现、优于手调基线)、报告(CSV/TOML/JSON 往返) | 全平台(CI 跑 `just tune-smoke`) |
|
||||
| **预设** | schema 往返、逐字段覆盖、优先级(CLI > 预设 > 内置)、未知键 / 越界 / 缺失报错、**`config/tune.toml` 全部预设可解算**、命名与后端一致、安全参数不可注入 | 全平台 |
|
||||
| vcan 集成 | vcan0 双 socket 收发,真实内核 CAN 栈 | Linux(`#[ignore]`,CI 显式跑) |
|
||||
| 实车台架 | `chassis-spin` 四轮定速:链路/反馈、方向、转速稳态、过速保护、安全停机;`chassis-tune --backend bench` 逐次试验 + 重复性复验 | armv7 + slcan/SocketCAN(人工) |
|
||||
| 实车台架 | `spin chassis` 四轮定速:链路/反馈、方向、转速稳态、过速保护、安全停机;`chassis-tune --backend bench` 逐次试验 + 重复性复验 | armv7 + slcan/SocketCAN(人工) |
|
||||
| 交叉检查 | `aarch64-unknown-linux-gnu` 全 workspace check;armv7 静态构建 `chassisd` | CI |
|
||||
|
||||
### 13.2 每个阶段完成后的门禁(`just lint` + 以下)
|
||||
@@ -660,7 +616,7 @@ just build-armv7 # cargo build --release -p chassisd --target armv7-unknown-
|
||||
just deploy # 编译 + 推送 + mock 试运行(默认 root@192.168.2.2)
|
||||
```
|
||||
|
||||
目标板实测:创龙 T153 / Ubuntu 22.04(armv7)。二进制静态链接,不依赖板端 libc。
|
||||
目标板实测:创龙 T153 / Ubuntu 22.04(armv7)。发布二进制使用 musl 静态链接。
|
||||
|
||||
### 14.2 板端拉起 CAN
|
||||
|
||||
@@ -692,9 +648,8 @@ RestartSec=1
|
||||
> (`ip -d link show can0` 可见),`chassisd` 侧表现为四电机反馈离线、强制零电流——
|
||||
> **这正是安全设计的预期行为**,不是故障。
|
||||
>
|
||||
> 调度策略:`ARCHITECTURE.md` 规划了 `SCHED_FIFO` + `mlockall`(对应 `rm-app::realtime`),
|
||||
> 本仓库目前**未实现**实时优先级提升,当前只依赖 1 kHz tokio 定时器 +
|
||||
> `MissedTickBehavior::Skip`。板端抖动实测与是否需要加固见第 15 节的抖动条目。
|
||||
> 调度策略:应用不调用 libc FFI。
|
||||
> 应用不调用 libc FFI。`SCHED_FIFO` 与资源限制由 systemd 服务单元设置;std 程序不调用 `mlockall`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user