# chassis-tune 超参数预设 # # 一次整定的结果不只取决于被控对象与种子,还取决于"搜什么区间、用哪个场景、 # 什么算好、什么算达标、搜多细"。这份文件把这些**实验条件**固化下来: # 评审一次、长期复用、任何机器上都能复现同一组默认行为。 # # 用法: # chassis-tune presets # 列出本文件全部预设 # chassis-tune sweep --preset config/tune.toml # 用 default_preset # chassis-tune sweep --preset config/tune.toml --preset-name bench_loaded # chassis-tune sweep --preset-name bench_loaded # 缺省路径即本文件 # chassis-tune sweep --preset config/tune.toml --samples 128 # CLI 覆盖预设的单项 # # 优先级:内置默认 < 本文件的预设 < 显式命令行参数(逐字段覆盖)。 # 本文件里没写的项一律沿用内置默认,因此预设只需写"与默认不同"的部分—— # 但为了让评审者一眼看全工况,下面每个预设都写全了关键项。 # # **安全边界**:current_limit / direction / gear_ratio / 电调 ID / 串口设备名 # 不属于超参数,本文件**不接受**这些键(写了会直接报错)。它们只来自 # chassis.toml 或显式 CLI——否则预设文件就成了绕过限流的口子。 # # schema:见 crates/chassis-tune/src/preset.rs(version = 1,未知键报错)。 version = 1 default_preset = "sim_bench_model" # ---------------------------------------------------------------- 离线:台架量级模型 # 默认预设。评审、CI 冒烟、改完代码回归都用它:不碰硬件,结果可复现。 [preset.sim_bench_model] description = "离线 · 台架量级模型(静/库仑摩擦 + 2 拍延迟):全量搜索,评审与回归的基准" [preset.sim_bench_model.space] # kp 覆盖手调基线 2 与仿真最优 8 两侧;对数刻度保证小增益也被采样到。 kp = { min = 0.5, max = 30.0, scale = "log" } # ki 允许精确取 0(纯比例在某些工况已经够用)。 ki = { min = 0.1, max = 100.0, scale = "log0" } # kd 半概率取 0:速度环加微分对量化噪声敏感,先让搜索自己决定要不要。 kd = { min = 0.01, max = 3.0, scale = "log0" } # 前馈上限 2.5 覆盖带载所需的 ≈1.77(raw/rpm)。 feedforward = { min = 0.01, max = 2.5, scale = "log0" } [preset.sim_bench_model.scenario] profile = "ramp-and-hold" target_rpm = 1500.0 ramp_ms = 200.0 hold_ms = 1800.0 dt_us = 1000.0 [preset.sim_bench_model.cost] # 跟得上、不过冲最重要;电流只作次要惩罚(省电流不是目的,避免烧电调才是)。 settle = 1.0 overshoot = 1.0 iae = 0.5 effort = 0.1 [preset.sim_bench_model.acceptance] max_overshoot_pct = 20.0 max_ss_error_pct = 5.0 [preset.sim_bench_model.search] seed = 20260101 samples = 512 top_k = 8 solver = "nelder-mead" max_iters = 200 particles = 24 max_evals = 4000 threads = 0 [preset.sim_bench_model.sim] plant = "gm3508-bench" # 域随机化:单点模型调出来的增益对增益/时间常数/延迟高度敏感, # 因此默认在 8 个成员的集成上做鲁棒优化,而不是对着一条理想曲线最优化。 ensemble = 8 robust_weight = 0.5 gain_pct = 0.2 tau_pct = 0.3 delay_ticks_max = 3 friction_pct = 0.5 [preset.sim_bench_model.bench] # 离线预设不用台架,但安全包络仍写全,方便临时切 --backend bench 预演。 max_rpm = 3000.0 max_temp_c = 70 trial_timeout_ms = 6000 between_trials_ms = 500 startup_timeout_ms = 5000 continue_on_abort = false validate_runs = 3 # ---------------------------------------------------------------- 离线:理想模型 # 无摩擦、无延迟的一阶模型:跑得快,适合改完控制逻辑后的秒级回归, # **不要**拿它的结果直接上车(模型里没有摩擦与延迟,最优解会偏激进)。 [preset.sim_ideal] description = "离线 · 理想一阶模型(无摩擦无延迟):秒级回归用,结果不可直接上车" [preset.sim_ideal.space] kp = { min = 0.5, max = 30.0, scale = "log" } ki = { min = 0.1, max = 100.0, scale = "log0" } kd = { min = 0.01, max = 3.0, scale = "log0" } feedforward = { min = 0.01, max = 2.5, scale = "log0" } [preset.sim_ideal.scenario] profile = "ramp-and-hold" target_rpm = 1500.0 ramp_ms = 200.0 hold_ms = 1800.0 dt_us = 1000.0 [preset.sim_ideal.cost] settle = 1.0 overshoot = 1.0 iae = 0.5 effort = 0.1 [preset.sim_ideal.acceptance] max_overshoot_pct = 20.0 max_ss_error_pct = 5.0 [preset.sim_ideal.search] seed = 20260101 samples = 128 top_k = 4 solver = "nelder-mead" max_iters = 100 particles = 16 max_evals = 1000 threads = 0 [preset.sim_ideal.sim] plant = "gm3508" # 理想模型本身就是确定的,单成员即可。 ensemble = 1 robust_weight = 0.0 gain_pct = 0.0 tau_pct = 0.0 delay_ticks_max = 0 friction_pct = 0.0 [preset.sim_ideal.bench] max_rpm = 3000.0 max_temp_c = 70 trial_timeout_ms = 6000 between_trials_ms = 500 startup_timeout_ms = 5000 continue_on_abort = false validate_runs = 3 # ---------------------------------------------------------------- 台架:空载 # 轮子架空(无地面载荷)。摩擦小、静差靠积分压制,前馈**必须为 0**: # 空载加前馈会把轮子直接推到最高转速。目标转速也压低,便于观察与急停。 [preset.bench_noload] description = "真机台架 · 空载(轮子架空):小样本局部搜索,禁用前馈,目标 800 rpm" # 真机后端:仍然需要 --i-know-the-wheel-spins 才会真的驱动电机。 backend = "bench" [preset.bench_noload.space] # 空载好解在手调基线(kp≈2, ki≈10)附近,区间收紧以提高样本密度。 kp = { min = 0.2, max = 12.0, scale = "log" } ki = { min = 0.1, max = 50.0, scale = "log0" } kd = { min = 0.01, max = 1.0, scale = "log0" } # 固定为 0:单点区间 + 线性刻度 = 搜索时该轴恒定。 feedforward = { min = 0.0, max = 0.0, scale = "linear" } [preset.bench_noload.scenario] profile = "ramp-and-hold" target_rpm = 800.0 ramp_ms = 300.0 hold_ms = 1700.0 dt_us = 1000.0 [preset.bench_noload.cost] settle = 1.0 overshoot = 1.0 iae = 0.5 effort = 0.1 [preset.bench_noload.acceptance] # 空载量化噪声相对目标更大,放宽到 25%,避免"为了 0.5% 的稳态误差过冲"。 max_overshoot_pct = 25.0 max_ss_error_pct = 8.0 [preset.bench_noload.search] seed = 20260101 # 只有一条总线,每次试验 2s,24 次 ≈ 1 分钟:够做局部精修,不够铺满空间。 samples = 24 top_k = 3 solver = "nelder-mead" max_iters = 40 particles = 12 max_evals = 40 threads = 1 [preset.bench_noload.sim] # 真机后端不用这些,但保留"最接近台架的模型",方便先离线预演再上车。 plant = "gm3508-bench" ensemble = 8 robust_weight = 0.5 gain_pct = 0.2 tau_pct = 0.3 delay_ticks_max = 3 friction_pct = 0.5 [preset.bench_noload.bench] # 目标是 800,过速保护留足裕量但远低于电调上限。 max_rpm = 2000.0 max_temp_c = 60 trial_timeout_ms = 6000 between_trials_ms = 500 startup_timeout_ms = 5000 continue_on_abort = false # 同一组增益跑 3 次看重复性;离散度大说明参数贴着噪声,不该上车。 validate_runs = 3 # ---------------------------------------------------------------- 台架:带载 # 整车落地 / 配重(有地面摩擦与惯量)。静差明显,前馈开始有意义(覆盖 ≈1.77)。 [preset.bench_loaded] description = "真机台架 · 带载(整车落地/配重):前馈区间上调,目标 1200 rpm,超调收紧" backend = "bench" [preset.bench_loaded.space] kp = { min = 0.5, max = 30.0, scale = "log" } ki = { min = 0.5, max = 100.0, scale = "log0" } kd = { min = 0.01, max = 3.0, scale = "log0" } # 带载需要前馈抵消摩擦:下限不为 0,让搜索必须考虑它。 feedforward = { min = 0.5, max = 2.5, scale = "log" } [preset.bench_loaded.scenario] profile = "ramp-and-hold" target_rpm = 1200.0 ramp_ms = 300.0 hold_ms = 2200.0 dt_us = 1000.0 [preset.bench_loaded.cost] settle = 1.0 overshoot = 1.0 iae = 0.5 effort = 0.1 [preset.bench_loaded.acceptance] # 带载下超调意味着实际位移过冲,收紧到 15%。 max_overshoot_pct = 15.0 max_ss_error_pct = 5.0 [preset.bench_loaded.search] seed = 20260101 samples = 32 top_k = 4 solver = "nelder-mead" max_iters = 60 particles = 16 max_evals = 80 threads = 1 [preset.bench_loaded.sim] plant = "gm3508-bench" ensemble = 8 robust_weight = 0.5 gain_pct = 0.2 tau_pct = 0.3 delay_ticks_max = 3 friction_pct = 0.5 [preset.bench_loaded.bench] max_rpm = 2500.0 max_temp_c = 70 trial_timeout_ms = 8000 between_trials_ms = 800 startup_timeout_ms = 5000 continue_on_abort = false validate_runs = 3 # ---------------------------------------------------------------- 实战验收:双向 # 上车前的最后一道关:升速 → 保持 → 降速 → 保持(回零),验的是双向跟随与 # 回零能力,比单向斜坡更接近实战。样本很少(围绕现有增益做局部复算), # 因为此时**不该**再大改增益,只想确认现有一组在两个方向上都不出问题。 [preset.field_updown] description = "真机实战验收 · 升降速双向(回零):小样本复验,超调与稳态误差收紧" backend = "bench" [preset.field_updown.space] kp = { min = 0.5, max = 30.0, scale = "log" } ki = { min = 0.5, max = 100.0, scale = "log0" } kd = { min = 0.01, max = 3.0, scale = "log0" } feedforward = { min = 0.5, max = 2.5, scale = "log" } [preset.field_updown.scenario] profile = "up-down" target_rpm = 1200.0 ramp_ms = 400.0 hold_ms = 800.0 dt_us = 1000.0 [preset.field_updown.cost] # 双向场景里稳态误差(尤其回零)比调节时间更重要。 settle = 1.0 overshoot = 1.0 iae = 1.0 effort = 0.1 [preset.field_updown.acceptance] max_overshoot_pct = 12.0 max_ss_error_pct = 3.0 [preset.field_updown.search] seed = 20260101 samples = 16 top_k = 2 solver = "nelder-mead" max_iters = 30 particles = 12 max_evals = 40 threads = 1 [preset.field_updown.sim] plant = "gm3508-bench" ensemble = 8 robust_weight = 0.5 gain_pct = 0.2 tau_pct = 0.3 delay_ticks_max = 3 friction_pct = 0.5 [preset.field_updown.bench] max_rpm = 2500.0 max_temp_c = 70 # 双向场景总时长翻倍,超时也翻倍。 trial_timeout_ms = 10000 between_trials_ms = 1000 startup_timeout_ms = 5000 continue_on_abort = false # 验收阶段多跑几次,重复性不过关就不签字。 validate_runs = 5