pub struct Piper<State = Disconnected> { /* private fields */ }Expand description
Implementations§
Source§impl Piper<Disconnected>
impl Piper<Disconnected>
Sourcepub fn reconnect<C>(
self,
can_adapter: C,
config: ConnectionConfig,
) -> Result<Piper<Standby>>
pub fn reconnect<C>( self, can_adapter: C, config: ConnectionConfig, ) -> Result<Piper<Standby>>
重新连接到机械臂(用于连接丢失后重新建立连接)
§参数
can_adapter: 新的 CAN 适配器(或重用现有的)config: 连接配置
§返回
Ok(Piper<Standby>): 成功重新连接Err(RobotError): 重新连接失败
§示例
let robot = Piper::connect(can_adapter, config)?;
// ... 连接丢失 ...
// 在某些情况下,你可能需要手动重新连接
// 注意:这需要一个处于 Disconnected 状态的 Piper 实例注意: 由于 Disconnected 是 ZST,self 参数本质上只是类型标记。
此方法与 connect() 功能相同,但语义上表示“重新连接“操作。
Source§impl Piper<Standby>
impl Piper<Standby>
Sourcepub fn enable_mit_mode(
self,
config: MitModeConfig,
) -> Result<Piper<Active<MitMode>>>
pub fn enable_mit_mode( self, config: MitModeConfig, ) -> Result<Piper<Active<MitMode>>>
Sourcepub fn enable_position_mode(
self,
config: PositionModeConfig,
) -> Result<Piper<Active<PositionMode>>>
pub fn enable_position_mode( self, config: PositionModeConfig, ) -> Result<Piper<Active<PositionMode>>>
Sourcepub fn enable_all(self) -> Result<Piper<Active<MitMode>>>
pub fn enable_all(self) -> Result<Piper<Active<MitMode>>>
使能全部关节并切换到 MIT 模式
这是 enable_mit_mode 的便捷方法,使用默认配置。
Sourcepub fn disable_all(self) -> Result<()>
pub fn disable_all(self) -> Result<()>
Sourcepub fn disable_joints(self, joints: &[Joint]) -> Result<()>
pub fn disable_joints(self, joints: &[Joint]) -> Result<()>
Sourcepub fn start_recording(
self,
config: RecordingConfig,
) -> Result<(Self, RecordingHandle)>
pub fn start_recording( self, config: RecordingConfig, ) -> Result<(Self, RecordingHandle)>
启动录制(Standby 状态)
§参数
config: 录制配置
§返回
返回 (Piper<Standby>, RecordingHandle)
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
// 启动录制
let (standby, handle) = standby.start_recording(RecordingConfig {
output_path: "demo.bin".into(),
stop_condition: StopCondition::Duration(10),
metadata: RecordingMetadata {
notes: "Test recording".to_string(),
operator: "Alice".to_string(),
},
})?;
// 执行操作(会被录制)
// ...
// 停止录制并保存
let _standby = standby.stop_recording(handle)?;Sourcepub fn stop_recording(
self,
handle: RecordingHandle,
) -> Result<(Self, RecordingStats)>
pub fn stop_recording( self, handle: RecordingHandle, ) -> Result<(Self, RecordingStats)>
停止录制并保存文件
§参数
handle: 录制句柄
§返回
返回 (Piper<Standby>, 录制统计)
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
let (standby, handle) = standby.start_recording(config)?;
// ... 等待一段时间 ...
// 停止录制并保存
let (standby, stats) = standby.stop_recording(handle)?;
println!("录制完成: {} 帧", stats.frame_count);Sourcepub fn enter_replay_mode(self) -> Result<Piper<ReplayMode>>
pub fn enter_replay_mode(self) -> Result<Piper<ReplayMode>>
进入回放模式
§功能
将 Driver 切换到 Replay 模式,暂停 TX 线程的周期性发送, 准备回放预先录制的 CAN 帧。
§安全保证
- Driver 进入 Replay 模式后,TX 线程暂停周期性发送
- 避免双控制流冲突
- 只能通过
replay_recording()发送预先录制的帧
§⚠️ 安全警告
- 进入 Replay 模式前,应确保机器人处于 Standby 状态
- 回放时应遵守安全速度限制(建议 ≤ 2.0x)
- 回放过程中应有人工急停准备
§返回
返回 Piper<ReplayMode> 实例
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
// 进入回放模式
let replay = standby.enter_replay_mode()?;
// 回放录制(1.0x 速度,原始速度)
let standby = replay.replay_recording("recording.bin", 1.0)?;Sourcepub fn set_collision_protection(&self, levels: [u8; 6]) -> Result<()>
pub fn set_collision_protection(&self, levels: [u8; 6]) -> Result<()>
设置碰撞保护级别
设置6个关节的碰撞防护等级(0~8,等级0代表不检测碰撞)。
§参数
levels: 6个关节的碰撞防护等级数组,每个值范围 0~8
§示例
let standby = PiperBuilder::new().interface("can0").build()?;
// 所有关节设置为等级 5(中等保护)
standby.set_collision_protection([5, 5, 5, 5, 5, 5])?;
// 为不同关节设置不同等级
// J1-J3 基座关节使用较高保护,J4-J6 末端关节使用较低保护
standby.set_collision_protection([6, 6, 6, 4, 4, 4])?;
// 禁用碰撞保护(谨慎使用)
standby.set_collision_protection([0, 0, 0, 0, 0, 0])?;Sourcepub fn set_joint_zero_positions(&self, joints: &[usize]) -> Result<()>
pub fn set_joint_zero_positions(&self, joints: &[usize]) -> Result<()>
设置关节零位
设置指定关节的当前位置为零点。
⚠️ 安全警告:
- 设置零位前,确保关节已移动到预期的零点位置
- 建议在机械臂安装或重新校准时使用
- 设置后应验证关节位置是否正确
§参数
joints: 要设置零位的关节数组(0-based,0-5 对应 J1-J6)
§示例
let standby = PiperBuilder::new().interface("can0").build()?;
// 设置 J1 的当前位置为零点
// 注意:确保 J1 已移动到预期的零点位置
standby.set_joint_zero_positions(&[0])?;
// 设置多个关节的零位
standby.set_joint_zero_positions(&[0, 1, 2])?;
// 设置所有关节的零位
standby.set_joint_zero_positions(&[0, 1, 2, 3, 4, 5])?;Source§impl<State> Piper<State>
impl<State> Piper<State>
Sourcepub fn quirks(&self) -> DeviceQuirks
pub fn quirks(&self) -> DeviceQuirks
获取固件特性(DeviceQuirks)
§返回
返回当前机械臂的固件特性,包括:
firmware_version: 固件版本号joint_flip_map: 关节 flip 标志torque_scaling: 力矩缩放因子
§示例
let robot = PiperBuilder::new().interface("can0").build()?;
// 获取固件特性
let quirks = robot.quirks();
println!("Firmware version: {}", quirks.firmware_version);
// 在控制循环中使用 quirks
for joint in [Joint::J1, Joint::J2, Joint::J3, Joint::J4, Joint::J5, Joint::J6] {
let needs_flip = quirks.needs_flip(joint);
let scaling = quirks.torque_scaling_factor(joint);
println!("Joint {:?}: flip={}, scaling={}", joint, needs_flip, scaling);
}Sourcepub fn emergency_stop(self) -> Result<Piper<ErrorState>>
pub fn emergency_stop(self) -> Result<Piper<ErrorState>>
急停:发送急停指令,并转换到 ErrorState(之后不允许继续 command_*)
§设计说明
- 急停属于“立即禁止后续指令“的软状态,若依赖硬件反馈会有窗口期
- Type State 能在编译期/所有权层面强制禁止继续使用旧实例
- 通过消耗
self并返回Piper<ErrorState>,确保无法继续发送控制命令
§参数
self: 消耗当前状态实例
§返回
Ok(Piper<ErrorState>): 成功发送急停指令,返回错误状态Err(RobotError): 发送急停指令失败
§示例
let robot = robot.enable_all()?;
// 发生紧急情况,立即急停
let robot = robot.emergency_stop()?;
// robot 现在是 Piper<ErrorState>,无法调用 command_torques 等方法
// robot.command_torques(...); // ❌ 编译错误Source§impl<M> Piper<Active<M>>
impl<M> Piper<Active<M>>
Sourcepub fn diagnostics(&self) -> PiperDiagnostics
pub fn diagnostics(&self) -> PiperDiagnostics
获取诊断接口(逃生舱)
§返回值
返回的 PiperDiagnostics 持有 Arc<piper_driver::Piper>:
- ✅ 独立于当前
Piper实例的生命周期 - ✅ 可以安全地移动到其他线程
- ✅ 可以在后台线程中长期持有
§使用场景
- 自定义诊断工具
- 高级抓包和调试
- 性能分析和优化
- 后台监控线程
§示例
let robot = PiperBuilder::new()
.interface("can0")
.build()?;
let active = robot.enable_position_mode(Default::default())?;
// 获取诊断接口
let diag = active.diagnostics();
// diag 可以安全地移动到其他线程
std::thread::spawn(move || {
// 在这里使用 diag...
});
// active 仍然可以正常使用§安全注意事项
诊断接口提供了底层访问能力,使用时需注意:
- 不要在 Active 状态下发送控制指令帧(会导致双控制流冲突)
- 确保回调执行时间 <1μs(否则会影响实时性能)
- 注意生命周期:即使持有
Arc,也要确保关联的Piper实例未被销毁
§参考
PiperDiagnostics- 诊断接口文档- 架构分析报告 - 方案 B 设计
Sourcepub fn start_recording(
self,
config: RecordingConfig,
) -> Result<(Self, RecordingHandle)>
pub fn start_recording( self, config: RecordingConfig, ) -> Result<(Self, RecordingHandle)>
启动录制(Active 状态)
§参数
config: 录制配置
§返回
返回 (Piper<Active<M>>, RecordingHandle)
§注意
Active 状态下的录制会包含控制指令帧(0x1A1-0x1FF)。
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
let active = standby.enable_mit_mode(Default::default())?;
// 启动录制(Active 状态)
let (active, handle) = active.start_recording(RecordingConfig {
output_path: "demo.bin".into(),
stop_condition: StopCondition::Duration(10),
metadata: RecordingMetadata {
notes: "Test recording".to_string(),
operator: "Alice".to_string(),
},
})?;
// 执行操作(会被录制,包含控制指令帧)
active.command_torques(...)?;
// 停止录制并保存
let (active, _stats) = active.stop_recording(handle)?;Sourcepub fn stop_recording(
self,
handle: RecordingHandle,
) -> Result<(Self, RecordingStats)>
pub fn stop_recording( self, handle: RecordingHandle, ) -> Result<(Self, RecordingStats)>
停止录制并保存文件
§参数
handle: 录制句柄
§返回
返回 (Piper<Active<M>>, 录制统计)
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
let active = standby.enable_mit_mode(Default::default())?;
let (active, handle) = active.start_recording(config)?;
// ... 执行操作 ...
// 停止录制并保存
let (active, stats) = active.stop_recording(handle)?;
println!("录制完成: {} 帧", stats.frame_count);Sourcepub fn set_collision_protection(&self, levels: [u8; 6]) -> Result<()>
pub fn set_collision_protection(&self, levels: [u8; 6]) -> Result<()>
设置碰撞保护级别
设置6个关节的碰撞防护等级(0~8,等级0代表不检测碰撞)。
注意:此方法在 Active 状态下也可调用,允许运行时调整碰撞保护级别。
§参数
levels: 6个关节的碰撞防护等级数组,每个值范围 0~8
§示例
let standby = PiperBuilder::new().interface("can0").build()?;
let active = standby.enable_position_mode(Default::default())?;
// 运行时提高碰撞保护级别(例如进入精密操作区域)
active.set_collision_protection([7, 7, 7, 7, 7, 7])?;
// 运行时降低碰撞保护级别(例如需要更大的力矩)
active.set_collision_protection([3, 3, 3, 3, 3, 3])?;Source§impl Piper<Active<MitMode>>
impl Piper<Active<MitMode>>
Sourcepub fn command_torques(
&self,
positions: &JointArray<Rad>,
velocities: &JointArray<f64>,
kp: &JointArray<f64>,
kd: &JointArray<f64>,
torques: &JointArray<NewtonMeter>,
) -> Result<()>
pub fn command_torques( &self, positions: &JointArray<Rad>, velocities: &JointArray<f64>, kp: &JointArray<f64>, kd: &JointArray<f64>, torques: &JointArray<NewtonMeter>, ) -> Result<()>
发送 MIT 模式控制指令
对所有关节发送位置、速度、力矩的混合控制指令。
§参数
positions: 各关节目标位置(Rad)velocities: 各关节目标速度(rad/s)kp: 位置增益(每个关节独立)kd: 速度增益(每个关节独立)torques: 各关节前馈力矩(NewtonMeter)
§示例
let positions = JointArray::from([
Rad(1.0), Rad(0.5), Rad(0.0), Rad(0.0), Rad(0.0), Rad(0.0)
]);
let velocities = JointArray::from([0.5, 0.0, 0.0, 0.0, 0.0, 0.0]);
let kp = JointArray::from([10.0; 6]); // 每个关节独立的 kp
let kd = JointArray::from([2.0; 6]); // 每个关节独立的 kd
let torques = JointArray::from([
NewtonMeter(5.0), NewtonMeter(0.0), NewtonMeter(0.0),
NewtonMeter(0.0), NewtonMeter(0.0), NewtonMeter(0.0)
]);
robot.command_torques(&positions, &velocities, &kp, &kd, &torques)?;Sourcepub fn open_gripper(&self) -> Result<()>
pub fn open_gripper(&self) -> Result<()>
打开夹爪
便捷方法,相当于 set_gripper(1.0, 0.3)
Sourcepub fn close_gripper(&self, effort: f64) -> Result<()>
pub fn close_gripper(&self, effort: f64) -> Result<()>
关闭夹爪
便捷方法,相当于 set_gripper(0.0, effort)
Source§impl Piper<Active<PositionMode>>
impl Piper<Active<PositionMode>>
Sourcepub fn send_position_command(&self, positions: &JointArray<Rad>) -> Result<()>
pub fn send_position_command(&self, positions: &JointArray<Rad>) -> Result<()>
Sourcepub fn command_cartesian_pose(
&self,
position: Position3D,
orientation: EulerAngles,
) -> Result<()>
pub fn command_cartesian_pose( &self, position: Position3D, orientation: EulerAngles, ) -> Result<()>
发送末端位姿命令(笛卡尔空间控制)
前提条件:必须使用 MotionType::Cartesian 或 MotionType::Linear 配置。
§参数
position: 末端位置(米)orientation: 末端姿态(欧拉角,度)
§示例
let config = PositionModeConfig {
motion_type: MotionType::Cartesian,
..Default::default()
};
let robot = robot.enable_position_mode(config)?;
// 发送末端位姿
robot.command_cartesian_pose(
Position3D::new(0.3, 0.0, 0.2), // x, y, z (米)
EulerAngles::new(0.0, 180.0, 0.0), // roll, pitch, yaw (度)
)?;Sourcepub fn move_linear(
&self,
position: Position3D,
orientation: EulerAngles,
) -> Result<()>
pub fn move_linear( &self, position: Position3D, orientation: EulerAngles, ) -> Result<()>
发送直线运动命令
末端沿直线轨迹运动到目标位姿。
前提条件:必须使用 MotionType::Linear 配置。
§参数
position: 目标位置(米)orientation: 目标姿态(欧拉角,度)
§示例
let config = PositionModeConfig {
motion_type: MotionType::Linear,
..Default::default()
};
let robot = robot.enable_position_mode(config)?;
// 发送直线运动
robot.move_linear(
Position3D::new(0.3, 0.0, 0.2), // x, y, z (米)
EulerAngles::new(0.0, 180.0, 0.0), // roll, pitch, yaw (度)
)?;Sourcepub fn move_circular(
&self,
via_position: Position3D,
via_orientation: EulerAngles,
target_position: Position3D,
target_orientation: EulerAngles,
) -> Result<()>
pub fn move_circular( &self, via_position: Position3D, via_orientation: EulerAngles, target_position: Position3D, target_orientation: EulerAngles, ) -> Result<()>
发送圆弧运动命令
末端沿圆弧轨迹运动,需要指定中间点和终点。
前提条件:必须使用 MotionType::Circular 配置。
§参数
via_position: 中间点位置(米)via_orientation: 中间点姿态(欧拉角,度)target_position: 终点位置(米)target_orientation: 终点姿态(欧拉角,度)
§示例
let config = PositionModeConfig {
motion_type: MotionType::Circular,
..Default::default()
};
let robot = robot.enable_position_mode(config)?;
// 发送圆弧运动
robot.move_circular(
Position3D::new(0.2, 0.1, 0.2), // via: 中间点
EulerAngles::new(0.0, 90.0, 0.0),
Position3D::new(0.3, 0.0, 0.2), // target: 终点
EulerAngles::new(0.0, 180.0, 0.0),
)?;Sourcepub fn command_position(&self, joint: Joint, position: Rad) -> Result<()>
pub fn command_position(&self, joint: Joint, position: Rad) -> Result<()>
更新单个关节位置(保持其他关节不变)
注意:此方法会先读取当前所有关节位置,然后只更新目标关节。
如果需要更新多个关节,请使用 motion_commander().send_position_command_batch() 方法。
为什么需要读取当前位置?
- 每个 CAN 帧(0x155, 0x156, 0x157)包含两个关节的角度
- 如果只发送单个关节,另一个关节会被错误地设置为 0.0
- 因此必须先读取当前位置,然后更新目标关节,最后批量发送
§参数
joint: 目标关节position: 目标位置(Rad)
§示例
// 只更新 J1,保持其他关节不变
robot.command_position(Joint::J1, Rad(1.57))?;
// 更新多个关节,使用批量方法
let mut positions = robot.observer().joint_positions();
positions[Joint::J1] = Rad(1.0);
positions[Joint::J2] = Rad(0.5);
robot.motion_commander().send_position_command(&positions)?;Sourcepub fn open_gripper(&self) -> Result<()>
pub fn open_gripper(&self) -> Result<()>
打开夹爪
便捷方法,相当于 set_gripper(1.0, 0.3)
Sourcepub fn close_gripper(&self, effort: f64) -> Result<()>
pub fn close_gripper(&self, effort: f64) -> Result<()>
关闭夹爪
便捷方法,相当于 set_gripper(0.0, effort)
Source§impl Piper<ReplayMode>
impl Piper<ReplayMode>
Sourcepub fn replay_recording(
self,
recording_path: impl AsRef<Path>,
speed_factor: f64,
) -> Result<Piper<Standby>>
pub fn replay_recording( self, recording_path: impl AsRef<Path>, speed_factor: f64, ) -> Result<Piper<Standby>>
回放预先录制的 CAN 帧
§参数
recording_path: 录制文件路径speed_factor: 速度倍数(1.0 = 原始速度,建议范围 0.1 ~ 2.0)
§功能
从录制文件中读取 CAN 帧序列,并按照原始时间间隔发送。 支持变速回放,但建议速度 ≤ 2.0x 以确保安全。
§安全保证
- Driver 处于 Replay 模式,TX 线程暂停周期性发送
- 按照录制的时间戳顺序发送帧
- 速度限制:建议 ≤ 2.0x,最大值 5.0x
§⚠️ 安全警告
- 速度限制: 建议使用 1.0x(原始速度),最高不超过 2.0x
- 人工监控: 回放过程中应有人工急停准备
- 环境确认: 确保回放环境安全,无人员/障碍物
- 文件验证: 只回放可信来源的录制文件
§返回
返回 Piper<Standby>,自动退出 Replay 模式
§示例
let builder = PiperBuilder::new()
.interface("can0");
let standby = Piper::connect(builder)?;
let replay = standby.enter_replay_mode()?;
// 回放录制(1.0x 速度,原始速度)
let standby = replay.replay_recording("recording.bin", 1.0)?;
// 回放完成后自动返回 Standby 状态Sourcepub fn replay_recording_with_cancel(
self,
recording_path: impl AsRef<Path>,
speed_factor: f64,
cancel_signal: &AtomicBool,
) -> Result<Piper<Standby>>
pub fn replay_recording_with_cancel( self, recording_path: impl AsRef<Path>, speed_factor: f64, cancel_signal: &AtomicBool, ) -> Result<Piper<Standby>>
回放录制(带取消支持)
§功能
回放预先录制的 CAN 帧序列,支持协作式取消。
§参数
recording_path- 录制文件路径speed_factor- 回放速度倍数(1.0 = 原始速度)cancel_signal- 停止信号(AtomicBool),检查是否需要取消
§返回
Ok(Piper<Standby>)- 回放完成或被取消后返回 Standby 状态Err(RobotError)- 回放失败
§取消机制
此方法支持协作式取消:
- 每一帧都会检查
cancel_signal - 如果
cancel_signal为false,立即停止回放 - 停止后会安全退出回放模式(恢复 Driver 到 Normal 模式)
§示例
let robot = PiperBuilder::new()
.interface("can0")
.build()?;
let replay = robot.enter_replay_mode()?;
// 创建停止信号
let running = Arc::new(AtomicBool::new(true));
// 在另一个线程中设置停止信号(例如 Ctrl-C 处理器)
// running.store(false, Ordering::SeqCst);
// 回放(可被取消)
let standby = replay.replay_recording_with_cancel(
"recording.bin",
1.0,
&running
)?;Source§impl Piper<ErrorState>
impl Piper<ErrorState>
Trait Implementations§
Auto Trait Implementations§
impl<State> Freeze for Piper<State>where
State: Freeze,
impl<State = Disconnected> !RefUnwindSafe for Piper<State>
impl<State> Send for Piper<State>where
State: Send,
impl<State> Sync for Piper<State>where
State: Sync,
impl<State> Unpin for Piper<State>where
State: Unpin,
impl<State = Disconnected> !UnwindSafe for Piper<State>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more