piper_client/control/mit_controller.rs
1//! MIT 模式高层控制器
2//!
3//! 简化 MIT 模式的使用,提供:
4//! - 阻塞式位置控制
5//! - 自动状态管理
6//! - 循环锚点机制(消除累积漂移,保证精确 200Hz)
7//! - 容错性增强(允许偶发丢帧)
8//!
9//! # 设计理念
10//!
11//! - **Option 模式**:使用 `Option<Piper<Active<MitMode>>>` 允许安全提取
12//! - **状态流转**:`park()` 返还 `Piper<Standby>`,支持继续使用
13//! - **循环锚点**:使用绝对时间锚点,消除累积漂移
14//! - **容错性**:允许最多连续 5 帧丢帧(25ms @ 200Hz)
15//!
16//! # 使用示例
17//!
18//! ```rust,no_run
19//! use piper_client::control::MitController;
20//! use piper_client::control::MitControllerConfig;
21//! use piper_client::state::*;
22//! use piper_client::types::*;
23//! use std::time::Duration;
24//!
25//! # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
26//! # // 注意:此示例展示了API用法,实际运行需要硬件连接
27//! # // 以下代码为伪代码,仅展示API调用方式
28//! #
29//! # // 假设已经连接并使能
30//! # // let piper = ...;
31//! #
32//! // 创建控制器配置
33//! let config = MitControllerConfig {
34//! kp_gains: [5.0; 6], // Nm/rad
35//! kd_gains: [0.8; 6], // Nm/(rad/s)
36//! rest_position: None,
37//! control_rate: 200.0,
38//! };
39//! # // let mut controller = MitController::new(piper, config);
40//!
41//! // 运动到目标位置(使用锚点机制,保证精确 200Hz)
42//! let target = [
43//! Rad(0.5), Rad(0.7), Rad(-0.4),
44//! Rad(0.2), Rad(0.3), Rad(0.5),
45//! ];
46//! # // let reached = controller.move_to_position(
47//! # // target,
48//! # // Rad(0.01),
49//! # // Duration::from_secs(5),
50//! # // )?;
51//!
52//! // 显式停车(返还 Piper<Standby>)
53//! # // let piper_standby = controller.park(DisableConfig::default())?;
54//! # Ok(())
55//! # }
56//! ```
57
58use std::time::{Duration, Instant};
59use tracing::{error, warn};
60
61use crate::observer::Observer;
62use crate::state::machine::{Active, DisableConfig, MitMode, Piper, Standby};
63use crate::types::*;
64
65/// MIT 控制器配置
66#[derive(Debug, Clone)]
67pub struct MitControllerConfig {
68 /// PD 控制器 Kp 增益(每个关节独立)
69 ///
70 /// 单位:Nm/rad
71 /// 典型值:3.0 - 10.0
72 pub kp_gains: [f64; 6],
73
74 /// PD 控制器 Kd 增益(每个关节独立)
75 ///
76 /// 单位:Nm/(rad/s)
77 /// 典型值:0.3 - 1.5
78 pub kd_gains: [f64; 6],
79
80 /// 休息位置(Drop 时自动移动到此位置)
81 ///
82 /// `None` 表示不自动移动(仅失能)
83 pub rest_position: Option<[Rad; 6]>,
84
85 /// 控制循环频率(Hz)
86 ///
87 /// 使用绝对时间锚点机制,实际频率将精确锁定在此值。
88 /// 推荐值:200.0 Hz(与固件更新频率一致)
89 pub control_rate: f64,
90}
91
92impl Default for MitControllerConfig {
93 fn default() -> Self {
94 Self {
95 kp_gains: [5.0; 6],
96 kd_gains: [0.8; 6],
97 rest_position: None,
98 control_rate: 200.0,
99 }
100 }
101}
102
103/// 控制错误
104#[derive(Debug, thiserror::Error)]
105pub enum ControlError {
106 /// Controller was already parked
107 #[error("Controller was already parked, cannot execute commands")]
108 AlreadyParked,
109
110 /// 连续错误超过阈值
111 #[error("Consecutive CAN failures: {count}, last error: {last_error}")]
112 ConsecutiveFailures {
113 count: u32,
114 #[source]
115 last_error: Box<RobotError>,
116 },
117
118 /// 其他机器人错误
119 #[error("Robot error: {0}")]
120 RobotError(#[from] RobotError),
121
122 /// 超时
123 #[error("Operation timeout: {timeout_ms}ms")]
124 Timeout { timeout_ms: u64 },
125}
126
127/// MIT 模式高层控制器
128///
129/// **核心特性**:
130/// - ✅ Option 模式:允许安全提取 Piper
131/// - ✅ 循环锚点:消除累积漂移,保证精确 200Hz
132/// - ✅ 容错性:允许最多连续 5 帧丢帧
133/// - ✅ 状态流转:`park()` 返还 `Piper<Standby>`
134pub struct MitController {
135 /// ⚠️ Option 包装,允许 park() 时安全提取
136 piper: Option<Piper<Active<MitMode>>>,
137
138 /// 状态观察器
139 observer: Observer,
140
141 /// 控制器配置
142 config: MitControllerConfig,
143}
144
145impl MitController {
146 /// 创建新的 MIT 控制器
147 ///
148 /// # 参数
149 ///
150 /// - `piper`: 已使能 MIT 模式的 Piper
151 /// - `config`: 控制器配置
152 ///
153 /// # 示例
154 ///
155 /// ```rust,no_run
156 /// # use piper_client::control::MitController;
157 /// # use piper_client::control::MitControllerConfig;
158 /// # use piper_client::state::*;
159 /// #
160 /// # // 假设已经使能 MIT 模式
161 /// # // let piper = ...;
162 /// #
163 /// let config = MitControllerConfig::default();
164 /// # // let controller = MitController::new(piper, config);
165 /// # // 使用 controller 进行控制...
166 /// ```
167 pub fn new(piper: Piper<Active<MitMode>>, config: MitControllerConfig) -> Self {
168 // 提取 observer(Clone 是轻量的,Arc 指针)
169 let observer = piper.observer().clone();
170
171 Self {
172 piper: Some(piper),
173 observer,
174 config,
175 }
176 }
177
178 /// 阻塞式运动到目标位置
179 ///
180 /// **循环锚点机制**:
181 /// - ✅ 使用绝对时间锚点,消除累积漂移
182 /// - ✅ 无论 CAN 通信耗时多少,频率都锁定在 200Hz
183 /// - ✅ 自动处理任务超时(Overrun)
184 ///
185 /// # 容错性
186 ///
187 /// 控制循环具有**容错性**:允许偶尔的 CAN 通信错误(最多连续 5 帧)。
188 ///
189 /// # 参数
190 ///
191 /// - `target`: 目标关节位置(弧度)
192 /// - `threshold`: 到达阈值(弧度)
193 /// - `timeout`: 超时时间
194 ///
195 /// # 返回
196 ///
197 /// - `Ok(true)`: 到达目标
198 /// - `Ok(false)`: 超时未到达
199 /// - `Err(ControlError::ConsecutiveFailures)`: 连续错误超过阈值
200 ///
201 /// # 示例
202 ///
203 /// ```rust,no_run
204 /// # use piper_client::control::MitController;
205 /// # use piper_client::types::*;
206 /// # use std::time::Duration;
207 /// #
208 /// # let mut controller: MitController = unsafe { std::mem::zeroed() };
209 /// // 运动到目标位置,5秒超时
210 /// # // let reached = controller.move_to_position(
211 /// # // [Rad(0.5), Rad(0.7), Rad(-0.4), Rad(0.2), Rad(0.3), Rad(0.5)],
212 /// # // Rad(0.01), // 1cm 阈值
213 /// # // Duration::from_secs(5),
214 /// # // )?;
215 /// ```
216 pub fn move_to_position(
217 &mut self,
218 target: [Rad; 6],
219 threshold: Rad,
220 timeout: Duration,
221 ) -> core::result::Result<bool, ControlError> {
222 const MAX_TOLERANCE: u32 = 5; // 允许连续丢 5 帧(25ms @ 200Hz)
223 let mut error_count = 0;
224
225 let start = Instant::now();
226
227 // 使用绝对时间锚点机制消除累积漂移,保证精确的 200Hz 控制频率
228 let period = Duration::from_secs_f64(1.0 / self.config.control_rate); // 5ms @ 200Hz
229 let mut next_tick = Instant::now();
230
231 // 提取 piper 引用(避免每次都解 Option)
232 let _piper = self.piper.as_ref().ok_or(ControlError::AlreadyParked)?;
233
234 while start.elapsed() < timeout {
235 // 1. 设定下一个锚点(绝对时间)
236 next_tick += period;
237
238 // 2. 发送命令 & 检查状态(耗时操作)
239 match self.command_joints(JointArray::from(target), None) {
240 Ok(_) => {
241 error_count = 0; // 重置计数器
242 },
243 Err(e) => {
244 error_count += 1;
245 if error_count > MAX_TOLERANCE {
246 error!(
247 "Consecutive CAN failures ({}): {:?}. Aborting motion.",
248 error_count, e
249 );
250 return Err(ControlError::ConsecutiveFailures {
251 count: error_count,
252 last_error: Box::new(e),
253 });
254 }
255 warn!(
256 "Transient CAN error ({}): {:?}, skipping frame. \
257 This is acceptable as long as errors don't occur consecutively.",
258 error_count, e
259 );
260 // 跳过本帧,继续循环
261 continue;
262 },
263 }
264
265 // 3. 检查是否到达
266 let current = self.observer.joint_positions();
267 let reached =
268 current.iter().zip(target.iter()).all(|(c, t)| (*c - *t).abs() < threshold);
269
270 if reached {
271 return Ok(true);
272 }
273
274 // 4. 睡眠到下一个锚点(自动扣除耗时操作的时间)
275 let now = Instant::now();
276 if next_tick > now {
277 // 还有剩余时间,睡眠到下一个锚点
278 spin_sleep::sleep(next_tick - now);
279 } else {
280 // ⚠️ 任务超时(Overrun):耗时操作超过了预期周期
281 warn!(
282 "Control loop overrun: operation took {:?}, \
283 but next tick was {:?} from now (expected {:?}). \
284 Skipping sleep to catch up.",
285 now.duration_since(next_tick - period),
286 next_tick.duration_since(now),
287 period
288 );
289 // 追赶锚点:不睡眠,直接进入下一帧
290 // 重置锚点到当前时间,避免后续累积延迟
291 next_tick = now;
292 }
293 }
294
295 Ok(false)
296 }
297
298 /// 发送关节命令(MIT 模式 PD 控制)
299 ///
300 /// 直接传递每个关节的 kp/kd 增益到固件,让固件进行 PD 计算,
301 /// 而不是在软件中计算 PD 输出。这样可以充分利用硬件的实时性能。
302 ///
303 /// # 参数
304 ///
305 /// - `target`: 目标位置
306 /// - `feedforward`: 前馈力矩(可选)
307 fn command_joints(
308 &self,
309 target: JointArray<Rad>,
310 feedforward: Option<JointArray<NewtonMeter>>,
311 ) -> crate::types::Result<()> {
312 // 使用配置中的每个关节独立的 kp/kd 增益
313 let kp = JointArray::from(self.config.kp_gains);
314 let kd = JointArray::from(self.config.kd_gains);
315
316 // 速度参考(目标速度,通常为 0)
317 let velocities = JointArray::from([0.0; 6]);
318
319 // 前馈力矩(可选)
320 let torques = match feedforward {
321 Some(ff) => ff,
322 None => JointArray::from([NewtonMeter(0.0); 6]),
323 };
324
325 // 直接传递 kp/kd 增益到底层,让固件进行 PD 计算
326 let piper =
327 self.piper.as_ref().ok_or(ControlError::AlreadyParked).map_err(|e| match e {
328 ControlError::AlreadyParked => crate::RobotError::InvalidTransition {
329 from: "Active<MitMode>".to_string(),
330 to: "Standby".to_string(),
331 },
332 _ => crate::RobotError::StatePoisoned {
333 reason: format!("Controller error: {:?}", e),
334 },
335 })?;
336 piper.command_torques(&target, &velocities, &kp, &kd, &torques)
337 }
338
339 /// 放松关节(发送零力矩命令)
340 ///
341 /// **注意**:此方法只发送一次零力矩命令,不会阻塞。
342 /// 如果需要让关节自然下垂,应该多次调用或在循环中调用。
343 ///
344 /// # 软降级
345 ///
346 /// 如果发送失败,只记录警告,不返回错误(软降级策略)。
347 ///
348 /// # 示例
349 ///
350 /// ```rust,no_run
351 /// # use piper_client::control::MitController;
352 /// # let mut controller: MitController = unsafe { std::mem::zeroed() };
353 /// controller.relax_joints();
354 /// ```
355 pub fn relax_joints(&mut self) {
356 let zero_pos = JointArray::from([Rad(0.0); 6]);
357 let zero_vel = JointArray::from([0.0; 6]);
358 let zero_kp = JointArray::from([0.0; 6]);
359 let zero_kd = JointArray::from([0.0; 6]);
360 let zero_torques = JointArray::from([NewtonMeter(0.0); 6]);
361
362 let piper = match self.piper.as_ref() {
363 Some(p) => p,
364 None => {
365 warn!("Cannot relax joints: Piper already consumed");
366 return;
367 },
368 };
369
370 if let Err(e) =
371 piper.command_torques(&zero_pos, &zero_vel, &zero_kp, &zero_kd, &zero_torques)
372 {
373 warn!("Failed to relax joints: {:?}. Continuing anyway.", e);
374 }
375 }
376
377 /// 停车(失能并返还 `Piper<Standby>`)
378 ///
379 /// **资源管理**:
380 /// - ✅ 返还 `Piper<Standby>`,支持继续使用
381 /// - ✅ 使用 Option 模式,安全提取 Piper
382 ///
383 /// # 安全保证
384 ///
385 /// **显式调用 park()**(推荐):
386 /// - 提取 Piper,调用 disable(),等待完成
387 /// - 返回 `Piper<Standby>` 可继续使用
388 /// - 不会触发 Piper 的 Drop
389 ///
390 /// **忘记调用 park()**(安全网):
391 /// - MitController 被 drop 时,`Piper<Active>::drop()` 自动触发
392 /// - 发送 disable 命令(不等待确认)
393 /// - 电机被安全失能
394 ///
395 /// # 示例
396 ///
397 /// ```rust,no_run
398 /// # use piper_client::control::MitController;
399 /// # use piper_client::state::*;
400 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
401 /// # let mut controller: MitController = unsafe { std::mem::zeroed() };
402 ///
403 /// // 方式 1:显式停车(推荐)
404 /// let piper_standby = controller.park(DisableConfig::default())?;
405 /// // 现在 piper_standby 可以重新使能或做其他操作
406 ///
407 /// // 方式 2:直接丢弃(触发自动安全失能)
408 /// // drop(controller); // 自动调用 Piper::drop(),发送 disable 命令
409 /// # Ok(())
410 /// # }
411 /// ```
412 pub fn park(mut self, config: DisableConfig) -> crate::types::Result<Piper<Standby>> {
413 // 安全提取 piper(Option 变为 None)
414 let piper = self.piper.take().ok_or(ControlError::AlreadyParked).map_err(|e| match e {
415 ControlError::AlreadyParked => crate::RobotError::InvalidTransition {
416 from: "Active<MitMode>".to_string(),
417 to: "Standby".to_string(),
418 },
419 _ => crate::RobotError::StatePoisoned {
420 reason: format!("Controller error: {:?}", e),
421 },
422 })?;
423
424 // 失能并返回到 Standby 状态
425 piper.disable(config)
426 }
427
428 /// 获取 Observer(只读)
429 pub fn observer(&self) -> &Observer {
430 &self.observer
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437
438 #[test]
439 fn test_config_default() {
440 let config = MitControllerConfig::default();
441 assert_eq!(config.kp_gains[0], 5.0);
442 assert_eq!(config.kd_gains[0], 0.8);
443 assert!(config.rest_position.is_none());
444 assert_eq!(config.control_rate, 200.0);
445 }
446}