feat: 完成四轮子独立pid调试
This commit is contained in:
+1
-1
@@ -206,7 +206,7 @@ friction_pct = 0.5
|
|||||||
max_rpm = 2000.0
|
max_rpm = 2000.0
|
||||||
max_temp_c = 60
|
max_temp_c = 60
|
||||||
trial_timeout_ms = 6000
|
trial_timeout_ms = 6000
|
||||||
between_trials_ms = 500
|
between_trials_ms = 1500
|
||||||
startup_timeout_ms = 5000
|
startup_timeout_ms = 5000
|
||||||
continue_on_abort = false
|
continue_on_abort = false
|
||||||
# 同一组增益跑 3 次看重复性;离散度大说明参数贴着噪声,不该上车。
|
# 同一组增益跑 3 次看重复性;离散度大说明参数贴着噪声,不该上车。
|
||||||
|
|||||||
@@ -786,8 +786,10 @@ pub mod hil {
|
|||||||
|
|
||||||
let mut policy = MotorPolicy::new();
|
let mut policy = MotorPolicy::new();
|
||||||
policy.configure(config.esc.esc_id, config.esc.current_limit, VelocityGains::ZERO);
|
policy.configure(config.esc.esc_id, config.esc.current_limit, VelocityGains::ZERO);
|
||||||
|
let runtime_guard = runtime.enter();
|
||||||
let bus = EscBus::spawn(link, vec![config.esc.clone()], config.bus.clone(), policy)
|
let bus = EscBus::spawn(link, vec![config.esc.clone()], config.bus.clone(), policy)
|
||||||
.map_err(|e| BenchError::Bus(e.to_string()))?;
|
.map_err(|e| BenchError::Bus(e.to_string()))?;
|
||||||
|
drop(runtime_guard);
|
||||||
let motor = bus
|
let motor = bus
|
||||||
.motor(esc_id)
|
.motor(esc_id)
|
||||||
.ok_or_else(|| BenchError::Bus(format!("esc {esc_id} missing from the bus")))?
|
.ok_or_else(|| BenchError::Bus(format!("esc {esc_id} missing from the bus")))?
|
||||||
@@ -938,6 +940,22 @@ pub mod hil {
|
|||||||
open_link("ignored", 2_000_000, crate::CanSpeed::M1, Some("can-not-real"));
|
open_link("ignored", 2_000_000, crate::CanSpeed::M1, Some("can-not-real"));
|
||||||
assert!(missing_interface.is_err(), "不存在的 CAN 接口必须报错");
|
assert!(missing_interface.is_err(), "不存在的 CAN 接口必须报错");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluator_spawns_bus_without_an_outer_tokio_runtime() {
|
||||||
|
let (link, peer) = can::CanLink::pair(8);
|
||||||
|
// 测试只关心 runtime 启动;立刻关闭对端,避免假链路堵住 1kHz 命令发送。
|
||||||
|
drop(peer);
|
||||||
|
let mut config = config();
|
||||||
|
config.startup_timeout = Duration::from_millis(1);
|
||||||
|
|
||||||
|
let result = BenchEvaluator::open(link, config);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(result, Err(BenchError::NoFeedback { esc_id: 1, .. })),
|
||||||
|
"应当在自有 runtime 中启动总线并因无反馈返回错误"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
pub mod report {
|
pub mod report {
|
||||||
//! 报告与产物写出:CSV / TOML / JSON / Markdown(可选 SVG)。
|
//! 报告与产物写出:CSV / TOML / JSON / Markdown(可选 SVG)。
|
||||||
@@ -1656,6 +1657,7 @@ struct TuningBench {
|
|||||||
continue_on_abort: bool,
|
continue_on_abort: bool,
|
||||||
best: Mutex<Option<(f64, experiment::Trace)>>,
|
best: Mutex<Option<(f64, experiment::Trace)>>,
|
||||||
aborts: Mutex<Vec<String>>,
|
aborts: Mutex<Vec<String>>,
|
||||||
|
stopped: AtomicBool,
|
||||||
describe: String,
|
describe: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1682,6 +1684,7 @@ impl TuningBench {
|
|||||||
continue_on_abort,
|
continue_on_abort,
|
||||||
best: Mutex::new(None),
|
best: Mutex::new(None),
|
||||||
aborts: Mutex::new(Vec::new()),
|
aborts: Mutex::new(Vec::new()),
|
||||||
|
stopped: AtomicBool::new(false),
|
||||||
describe,
|
describe,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1701,12 +1704,18 @@ impl TuningBench {
|
|||||||
|
|
||||||
impl Evaluator for TuningBench {
|
impl Evaluator for TuningBench {
|
||||||
fn evaluate(&self, gains: &VelocityGains) -> Score {
|
fn evaluate(&self, gains: &VelocityGains) -> Score {
|
||||||
|
if self.stopped.load(Ordering::Relaxed) {
|
||||||
|
return Score::penalized(Verdict::Rejected);
|
||||||
|
}
|
||||||
let outcome = self.runner.run_trial(gains);
|
let outcome = self.runner.run_trial(gains);
|
||||||
let score = if let Some(reason) = outcome.abort {
|
let score = if let Some(reason) = outcome.abort {
|
||||||
self.aborts
|
self.aborts
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||||
.push(format!("{}: {reason}", self.describe));
|
.push(format!("{}: {reason}", self.describe));
|
||||||
|
if !self.continue_on_abort {
|
||||||
|
self.stopped.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
Score::penalized(verdict_for(&reason))
|
Score::penalized(verdict_for(&reason))
|
||||||
} else {
|
} else {
|
||||||
match experiment::StepMetrics::from_trace(&outcome.trace, &self.scenario()) {
|
match experiment::StepMetrics::from_trace(&outcome.trace, &self.scenario()) {
|
||||||
|
|||||||
@@ -484,13 +484,26 @@ pub mod metrics {
|
|||||||
|
|
||||||
use crate::{Scenario, Trace};
|
use crate::{Scenario, Trace};
|
||||||
|
|
||||||
/// 稳态判据的相对带宽(±2% of target)。
|
/// 稳态判据的相对带宽(±5% of target)。
|
||||||
pub const SETTLING_BAND: f32 = 0.02;
|
///
|
||||||
|
/// C620 的转速反馈是整数 rpm,空载低速时测量抖动本身就会超过 ±2%;
|
||||||
|
/// 真机台架若沿用 ±2% 会把几乎每一组正常 PID 都误判为 `no-settle`。
|
||||||
|
pub const SETTLING_BAND: f32 = 0.05;
|
||||||
|
/// 判定“基本收敛”时使用的较宽相对带宽(±10% of target)。
|
||||||
|
///
|
||||||
|
/// 调节时间仍按 ±5% 计算;这里只回答“稳态阶段是否大体跟住”,避免真机反馈
|
||||||
|
/// 的少量量化尖峰把整组可用增益判成未收敛。
|
||||||
|
pub const SETTLED_BAND: f32 = 0.10;
|
||||||
/// 稳态窗口占轨迹总时长的比例(从 75% 处开始)。
|
/// 稳态窗口占轨迹总时长的比例(从 75% 处开始)。
|
||||||
///
|
///
|
||||||
/// 与场景自带的 `steady_start_s()` 取**更晚**者:阶跃场景没有显式斜坡,
|
/// 与场景自带的 `steady_start_s()` 取**更晚**者:阶跃场景没有显式斜坡,
|
||||||
/// 若不设下限就会把整条上升段也算进"稳态",任何真实响应都会被判成未收敛。
|
/// 若不设下限就会把整条上升段也算进"稳态",任何真实响应都会被判成未收敛。
|
||||||
pub const STEADY_FRACTION: f32 = 0.75;
|
pub const STEADY_FRACTION: f32 = 0.75;
|
||||||
|
/// 稳态窗口允许的离带采样占比。
|
||||||
|
///
|
||||||
|
/// 真机转速反馈存在量化与负载扰动,要求稳态窗口内**每一个采样**都位于带内
|
||||||
|
/// 会把正常的小幅抖振误判成未收敛;允许少量孤立离带样本更符合台架数据。
|
||||||
|
pub const MAX_STEADY_EXCURSION_FRACTION: f32 = 0.10;
|
||||||
/// 指标最少需要的采样数(样本太少时一切统计都无意义)。
|
/// 指标最少需要的采样数(样本太少时一切统计都无意义)。
|
||||||
pub const MIN_SAMPLES: usize = 8;
|
pub const MIN_SAMPLES: usize = 8;
|
||||||
|
|
||||||
@@ -501,9 +514,9 @@ pub mod metrics {
|
|||||||
pub rise_time_s: f32,
|
pub rise_time_s: f32,
|
||||||
/// 超调量(%,相对目标;负值截为 0)。
|
/// 超调量(%,相对目标;负值截为 0)。
|
||||||
pub overshoot_pct: f32,
|
pub overshoot_pct: f32,
|
||||||
/// 调节时间:最后一次离开 ±2% 带的时刻(秒)。
|
/// 调节时间:最后一次离开 ±5% 带的时刻(秒)。
|
||||||
pub settling_time_s: f32,
|
pub settling_time_s: f32,
|
||||||
/// 是否在稳态窗口内保持 ±2% 带(即真正收敛)。
|
/// 是否在稳态窗口内基本保持 ±10% 带(允许少量测量抖动离带)。
|
||||||
pub settled: bool,
|
pub settled: bool,
|
||||||
/// 稳态误差(%,相对目标)。
|
/// 稳态误差(%,相对目标)。
|
||||||
pub ss_error_pct: f32,
|
pub ss_error_pct: f32,
|
||||||
@@ -581,8 +594,13 @@ pub mod metrics {
|
|||||||
settling_time_s = t;
|
settling_time_s = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 收敛 = 最后一次离开带宽的时刻已经进入稳态窗口(此后一直留在带内)。
|
// 收敛 = 稳态窗口内绝大多数采样位于带内;孤立的反馈尖峰不否决整组增益。
|
||||||
let settled = settling_time_s <= steady_start && !window.is_empty();
|
let settled_band = SETTLED_BAND * magnitude;
|
||||||
|
let out_of_band =
|
||||||
|
window.rpm().iter().filter(|rpm| (**rpm - target).abs() > settled_band).count();
|
||||||
|
let excursion_fraction =
|
||||||
|
if window.is_empty() { 1.0 } else { out_of_band as f32 / window.len() as f32 };
|
||||||
|
let settled = excursion_fraction <= MAX_STEADY_EXCURSION_FRACTION;
|
||||||
|
|
||||||
// 误差积分(全过程,按 dt 累加)。
|
// 误差积分(全过程,按 dt 累加)。
|
||||||
let mut abs_error_area = 0.0f32;
|
let mut abs_error_area = 0.0f32;
|
||||||
@@ -678,11 +696,9 @@ pub mod metrics {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn steady_state_offset_shows_up() {
|
fn steady_state_offset_shows_up() {
|
||||||
let trace = synthetic(0.02, 0.0, 50.0);
|
let trace = synthetic(0.02, 0.0, 60.0);
|
||||||
let metrics = StepMetrics::from_trace(&trace, &scenario()).unwrap();
|
let metrics = StepMetrics::from_trace(&trace, &scenario()).unwrap();
|
||||||
assert!((metrics.ss_error_pct - 5.0).abs() < 1.0, "ss = {}", metrics.ss_error_pct);
|
assert!((metrics.ss_error_pct - 6.0).abs() < 1.0, "ss = {}", metrics.ss_error_pct);
|
||||||
// 5% 的末端偏差超出 ±2% 带 → 不算收敛。
|
|
||||||
assert!(!metrics.settled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -694,6 +710,18 @@ pub mod metrics {
|
|||||||
assert!(metrics.ss_error_pct > 2.0);
|
assert!(metrics.ss_error_pct > 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn isolated_feedback_excursions_do_not_reject_a_settled_response() {
|
||||||
|
let mut trace = synthetic(0.02, 0.0, 0.0);
|
||||||
|
let target = scenario().target_rpm;
|
||||||
|
let index = trace.t_s.iter().position(|t_s| *t_s > 0.75).unwrap();
|
||||||
|
// 稳态窗口内制造一个孤立测量尖峰。
|
||||||
|
trace.rpm[index] = target * 1.12;
|
||||||
|
|
||||||
|
let metrics = StepMetrics::from_trace(&trace, &scenario()).unwrap();
|
||||||
|
assert!(metrics.settled, "少量孤立反馈尖峰不应否决稳定响应");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn effort_statistics_and_saturation_are_reported() {
|
fn effort_statistics_and_saturation_are_reported() {
|
||||||
let mut trace = synthetic(0.02, 0.0, 0.0);
|
let mut trace = synthetic(0.02, 0.0, 0.0);
|
||||||
|
|||||||
+38
-10
@@ -704,10 +704,13 @@ pub mod cost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Score {
|
impl Score {
|
||||||
/// 罚分结果。
|
/// 没有可用指标时的罚分结果。
|
||||||
|
///
|
||||||
|
/// 它必须高于 [`Objective::rejected_cost`] 产生的指标罚值,否则预算耗尽、
|
||||||
|
/// 安全停机等“未评估”占位结果会反过来击败真实测得的失败候选。
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn penalized(verdict: Verdict) -> Self {
|
pub fn penalized(verdict: Verdict) -> Self {
|
||||||
Self { cost: PENALTY, verdict, metrics: None }
|
Self { cost: PENALTY * 2.0, verdict, metrics: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,20 +760,29 @@ pub mod cost {
|
|||||||
pub fn score_metrics(&self, metrics: StepMetrics) -> Score {
|
pub fn score_metrics(&self, metrics: StepMetrics) -> Score {
|
||||||
if metrics.max_rpm > self.max_rpm {
|
if metrics.max_rpm > self.max_rpm {
|
||||||
return Score {
|
return Score {
|
||||||
cost: PENALTY,
|
cost: self.rejected_cost(&metrics)
|
||||||
|
+ f64::from(metrics.max_rpm / self.max_rpm - 1.0).max(0.0) * 10.0,
|
||||||
verdict: Verdict::Overspeed,
|
verdict: Verdict::Overspeed,
|
||||||
metrics: Some(metrics),
|
metrics: Some(metrics),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if metrics.saturated_pct / 100.0 > self.max_saturated_fraction {
|
if metrics.saturated_pct / 100.0 > self.max_saturated_fraction {
|
||||||
return Score {
|
return Score {
|
||||||
cost: PENALTY,
|
cost: self.rejected_cost(&metrics)
|
||||||
|
+ (f64::from(metrics.saturated_pct) / 100.0
|
||||||
|
- f64::from(self.max_saturated_fraction))
|
||||||
|
.max(0.0)
|
||||||
|
* 10.0,
|
||||||
verdict: Verdict::Saturated,
|
verdict: Verdict::Saturated,
|
||||||
metrics: Some(metrics),
|
metrics: Some(metrics),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if !metrics.settled {
|
if !metrics.settled {
|
||||||
return Score { cost: PENALTY, verdict: Verdict::NoSettle, metrics: Some(metrics) };
|
return Score {
|
||||||
|
cost: self.rejected_cost(&metrics),
|
||||||
|
verdict: Verdict::NoSettle,
|
||||||
|
metrics: Some(metrics),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Score {
|
Score {
|
||||||
cost: self.weighted_cost(&metrics),
|
cost: self.weighted_cost(&metrics),
|
||||||
@@ -779,6 +791,14 @@ pub mod cost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 硬约束失败时的可比较罚值。
|
||||||
|
///
|
||||||
|
/// 所有失败解仍高于 [`PENALTY`],但保留指标残差,避免搜索面对一整片完全
|
||||||
|
/// 同值的“罚值平原”而无法排序、精修或选出最接近可用的候选。
|
||||||
|
fn rejected_cost(&self, metrics: &StepMetrics) -> f64 {
|
||||||
|
PENALTY + self.weighted_cost(metrics).max(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
/// 归一化加权成本。
|
/// 归一化加权成本。
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn weighted_cost(&self, metrics: &StepMetrics) -> f64 {
|
pub fn weighted_cost(&self, metrics: &StepMetrics) -> f64 {
|
||||||
@@ -844,7 +864,15 @@ pub mod cost {
|
|||||||
simulate(weak, &PlantParams::gm3508(), &scenario, LoopLimits::gm3508()).unwrap();
|
simulate(weak, &PlantParams::gm3508(), &scenario, LoopLimits::gm3508()).unwrap();
|
||||||
let score = objective(scenario).score(&trace);
|
let score = objective(scenario).score(&trace);
|
||||||
assert_eq!(score.verdict, Verdict::NoSettle);
|
assert_eq!(score.verdict, Verdict::NoSettle);
|
||||||
assert!((score.cost - PENALTY).abs() < 1.0);
|
assert!(
|
||||||
|
score.cost >= PENALTY && score.cost < PENALTY + 100.0,
|
||||||
|
"失败解应保留可比较的指标残差,cost = {}",
|
||||||
|
score.cost
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
Score::penalized(Verdict::Rejected).cost > score.cost,
|
||||||
|
"未评估占位结果不得击败真实测得的失败候选"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2841,8 +2869,8 @@ pub mod search {
|
|||||||
//!
|
//!
|
||||||
//! # 为什么是两阶段
|
//! # 为什么是两阶段
|
||||||
//!
|
//!
|
||||||
//! 成本函数有一大片"罚值平原"(不收敛 / 饱和的候选成本都等于 `PENALTY`),
|
//! 失败候选位于 `PENALTY` 以上的罚值区带,并携带连续的指标残差;纯局部搜索
|
||||||
//! 纯局部搜索会陷在平原上;而纯网格搜索在四个数量级跨度上需要天文数字的
|
//! 仍难跨过成功 / 失败边界,而纯网格搜索在四个数量级跨度上需要天文数字的
|
||||||
//! 评估次数。两阶段各取所长:
|
//! 评估次数。两阶段各取所长:
|
||||||
//!
|
//!
|
||||||
//! 1. **粗扫**:Latin Hypercube 分层采样 + `rayon` 并行评估,用有限次数把
|
//! 1. **粗扫**:Latin Hypercube 分层采样 + `rayon` 并行评估,用有限次数把
|
||||||
@@ -3133,12 +3161,12 @@ pub mod search {
|
|||||||
fn cost(&self, param: &Self::Param) -> Result<f64, ArgminError> {
|
fn cost(&self, param: &Self::Param) -> Result<f64, ArgminError> {
|
||||||
let coords: [f64; AXIS_COUNT] = match param.as_slice() {
|
let coords: [f64; AXIS_COUNT] = match param.as_slice() {
|
||||||
[a, b, c, d] => [*a, *b, *c, *d],
|
[a, b, c, d] => [*a, *b, *c, *d],
|
||||||
_ => return Ok(crate::cost::PENALTY),
|
_ => return Ok(crate::cost::PENALTY * 2.0),
|
||||||
};
|
};
|
||||||
let gains = self.space.from_coords(coords);
|
let gains = self.space.from_coords(coords);
|
||||||
let cost = match self.budget.take() {
|
let cost = match self.budget.take() {
|
||||||
Some(_) => self.evaluator.evaluate(&gains).cost,
|
Some(_) => self.evaluator.evaluate(&gains).cost,
|
||||||
None => crate::cost::PENALTY,
|
None => crate::cost::PENALTY * 2.0,
|
||||||
};
|
};
|
||||||
if cost.is_finite() {
|
if cost.is_finite() {
|
||||||
self.tracker.record(cost, coords);
|
self.tracker.record(cost, coords);
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# 空载台架 PID 整定结果
|
||||||
|
|
||||||
|
测试条件:轮子架空,CANable2 `/dev/ttyACM0`,CAN 1 Mbps,目标转子转速 800 rpm,
|
||||||
|
限流 1500 raw,`ramp-and-hold` 300 ms + 1700 ms,试验间隔 1500 ms。
|
||||||
|
|
||||||
|
| 电调 | 轮子 | direction | kp | ki | kd | ff | 最终冒烟超调 | 稳态误差 | 结果 |
|
||||||
|
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
|
||||||
|
| 1 | front_left | +1 | 0.985 | 2.136 | 0.068 | 0 | 5.1% | 0.00% | 通过 |
|
||||||
|
| 2 | front_right | -1 | 0.985 | 2.136 | 0.068 | 0 | 6.2% | 0.75% | 通过 |
|
||||||
|
| 3 | rear_left | -1 | 0.750 | 1.200 | 0.030 | 0 | 7.6% | 1.04% | 通过 |
|
||||||
|
| 4 | rear_right | -1 | 1.139 | 2.199 | 0.044 | 0 | 21.8% | 1.61% | 通过 |
|
||||||
|
|
||||||
|
## 调试结论
|
||||||
|
|
||||||
|
- ESC3 的安装方向实测应为 `-1`;使用 `+1` 时闭环方向错误,会出现大幅超调和重复性崩溃。
|
||||||
|
- 空载必须保持 `feedforward = 0`。
|
||||||
|
- 真机反馈有明显量化抖动,整定指标改为 ±5% 调节带、稳态窗口允许少量离带采样,
|
||||||
|
并以 ±10% 带判断“基本收敛”。
|
||||||
|
- 默认限流从 12000 raw 收紧到台架 1500 raw;搜索遇到过速事件后立即停止后续驱动。
|
||||||
|
- `tune-bench` 现在交叉编译 armv7 musl 产物,避免把宿主机二进制推到板端。
|
||||||
|
|
||||||
|
## 产物
|
||||||
|
|
||||||
|
- `esc1/`、`esc2/`、`esc4/`:自动搜索报告、轨迹、Top 候选和重复性复验。
|
||||||
|
- `esc3/`:方向修正与保守增益复验摘要。
|
||||||
|
- `chassis.tuned.toml`:四轮完整配置。
|
||||||
@@ -75,7 +75,8 @@ tune-write config="config/chassis.toml.example" motor="front_left" out="target/t
|
|||||||
cargo run --release -p chassis-tune -- sweep --config {{config}} --motor {{motor}} --write-config {{out}} --samples 256 --out-dir target/tune
|
cargo run --release -p chassis-tune -- sweep --config {{config}} --motor {{motor}} --write-config {{out}} --samples 256 --out-dir target/tune
|
||||||
|
|
||||||
tune-bench board="root@192.168.2.2" device="/dev/ttyACM0" esc_id="1" samples="24" preset="bench_noload" opts="":
|
tune-bench board="root@192.168.2.2" device="/dev/ttyACM0" esc_id="1" samples="24" preset="bench_noload" opts="":
|
||||||
cargo build --release -p chassis-tune
|
rustup target add armv7-unknown-linux-musleabihf
|
||||||
scp -q target/release/chassis-tune {{board}}:/tmp/chassis-tune
|
cargo build --release -p chassis-tune --target armv7-unknown-linux-musleabihf
|
||||||
|
scp -q target/armv7-unknown-linux-musleabihf/release/chassis-tune {{board}}:/tmp/chassis-tune
|
||||||
scp -q config/tune.toml {{board}}:/tmp/tune.toml
|
scp -q config/tune.toml {{board}}:/tmp/tune.toml
|
||||||
ssh {{board}} "/tmp/chassis-tune sweep --backend bench --preset /tmp/tune.toml --preset-name {{preset}} --device {{device}} --esc-id {{esc_id}} --samples {{samples}} --top-k 3 --i-know-the-wheel-spins {{opts}}"
|
ssh {{board}} "/tmp/chassis-tune sweep --backend bench --preset /tmp/tune.toml --preset-name {{preset}} --device {{device}} --esc-id {{esc_id}} --current-limit 1500 --samples {{samples}} --top-k 3 --i-know-the-wheel-spins {{opts}}"
|
||||||
|
|||||||
Reference in New Issue
Block a user