pub struct Piper { /* private fields */ }Expand description
Piper 机械臂驱动(对外 API)
支持单线程和双线程两种模式
- 单线程模式:使用
io_thread(向后兼容) - 双线程模式:使用
rx_thread和tx_thread(物理隔离)
Implementations§
Source§impl Piper
impl Piper
Sourcepub const MAX_REALTIME_PACKAGE_SIZE: usize = 10
pub const MAX_REALTIME_PACKAGE_SIZE: usize = 10
最大允许的实时帧包大小
允许调用者在客户端进行预检查,避免跨层调用后的运行时错误。
§示例
let frame1 = PiperFrame::new_standard(0x100, &[]);
let frame2 = PiperFrame::new_standard(0x101, &[]);
let frame3 = PiperFrame::new_standard(0x102, &[]);
let frames = [frame1, frame2, frame3];
if frames.len() > Piper::MAX_REALTIME_PACKAGE_SIZE {
return Err("Package too large".into());
}
piper.send_realtime_package(frames)?;Sourcepub fn new(
can: impl CanAdapter + Send + 'static,
config: Option<PipelineConfig>,
) -> Result<Self, CanError>
pub fn new( can: impl CanAdapter + Send + 'static, config: Option<PipelineConfig>, ) -> Result<Self, CanError>
Sourcepub fn new_dual_thread<C>(
can: C,
config: Option<PipelineConfig>,
) -> Result<Self, CanError>
pub fn new_dual_thread<C>( can: C, config: Option<PipelineConfig>, ) -> Result<Self, CanError>
创建双线程模式的 Piper 实例
将 CAN 适配器分离为独立的 RX 和 TX 适配器,实现物理隔离。 RX 线程专门负责接收反馈帧,TX 线程专门负责发送控制命令。
§参数
can: 可分离的 CAN 适配器(必须已启动)config: Pipeline 配置(可选)
§错误
CanError::NotStarted: 适配器未启动CanError::Device: 分离适配器失败
§使用场景
- 实时控制:需要 RX 不受 TX 阻塞影响
- 高频控制:500Hz-1kHz 控制循环
§注意
- 适配器必须已启动(调用
configure()或start()) - 分离后,原适配器不再可用(消费
can)
Sourcepub fn check_health(&self) -> (bool, bool)
pub fn check_health(&self) -> (bool, bool)
Sourcepub fn is_healthy(&self) -> bool
pub fn is_healthy(&self) -> bool
检查是否健康
如果所有线程都存活,返回 true。
Sourcepub fn get_metrics(&self) -> MetricsSnapshot
pub fn get_metrics(&self) -> MetricsSnapshot
获取性能指标快照
返回当前所有计数器的快照,用于监控 IO 链路健康状态。
Sourcepub fn get_joint_dynamic(&self) -> JointDynamicState
pub fn get_joint_dynamic(&self) -> JointDynamicState
获取关节动态状态(无锁,纳秒级返回)
包含关节速度和电流(独立帧 + Buffered Commit)。
§性能
- 无锁读取(ArcSwap::load)
- 返回快照副本(Clone 开销低,< 150 字节)
- 适合 500Hz 控制循环
Sourcepub fn get_joint_position(&self) -> JointPositionState
pub fn get_joint_position(&self) -> JointPositionState
Sourcepub fn get_end_pose(&self) -> EndPoseState
pub fn get_end_pose(&self) -> EndPoseState
Sourcepub fn capture_motion_snapshot(&self) -> MotionSnapshot
pub fn capture_motion_snapshot(&self) -> MotionSnapshot
Sourcepub fn get_robot_control(&self) -> RobotControlState
pub fn get_robot_control(&self) -> RobotControlState
Sourcepub fn get_gripper(&self) -> GripperState
pub fn get_gripper(&self) -> GripperState
Sourcepub fn get_joint_driver_low_speed(&self) -> JointDriverLowSpeedState
pub fn get_joint_driver_low_speed(&self) -> JointDriverLowSpeedState
Sourcepub fn get_firmware_version(&self) -> Option<String>
pub fn get_firmware_version(&self) -> Option<String>
获取固件版本字符串
从累积的固件数据中解析版本字符串。
如果固件数据未完整或未找到版本字符串,返回 None。
§性能
- 需要获取 RwLock 读锁
- 如果已解析,直接返回缓存的版本字符串
- 如果未解析,尝试从累积数据中解析
Sourcepub fn query_firmware_version(&self) -> Result<(), DriverError>
pub fn query_firmware_version(&self) -> Result<(), DriverError>
查询固件版本
发送固件版本查询指令到机械臂,并清空之前的固件数据缓存。 查询和反馈使用相同的 CAN ID (0x4AF)。
注意:
- 发送查询命令后会自动清空固件数据缓存(与 Python SDK 一致)
- 需要等待一段时间(推荐 30-50ms)让机械臂返回反馈数据
- 之后可以调用
get_firmware_version()获取解析后的版本字符串
§错误
DriverError::ChannelFull: 命令通道已满(单线程模式)DriverError::ChannelClosed: 命令通道已关闭DriverError::NotDualThread: 双线程模式下使用错误的方法
§示例
Sourcepub fn get_master_slave_control_mode(&self) -> MasterSlaveControlModeState
pub fn get_master_slave_control_mode(&self) -> MasterSlaveControlModeState
Sourcepub fn get_master_slave_joint_control(&self) -> MasterSlaveJointControlState
pub fn get_master_slave_joint_control(&self) -> MasterSlaveJointControlState
Sourcepub fn get_master_slave_gripper_control(&self) -> MasterSlaveGripperControlState
pub fn get_master_slave_gripper_control(&self) -> MasterSlaveGripperControlState
Sourcepub fn get_collision_protection(
&self,
) -> Result<CollisionProtectionState, DriverError>
pub fn get_collision_protection( &self, ) -> Result<CollisionProtectionState, DriverError>
Sourcepub fn get_joint_limit_config(
&self,
) -> Result<JointLimitConfigState, DriverError>
pub fn get_joint_limit_config( &self, ) -> Result<JointLimitConfigState, DriverError>
Sourcepub fn get_joint_accel_config(
&self,
) -> Result<JointAccelConfigState, DriverError>
pub fn get_joint_accel_config( &self, ) -> Result<JointAccelConfigState, DriverError>
Sourcepub fn get_end_limit_config(&self) -> Result<EndLimitConfigState, DriverError>
pub fn get_end_limit_config(&self) -> Result<EndLimitConfigState, DriverError>
Sourcepub fn get_motion_state(&self) -> CombinedMotionState
pub fn get_motion_state(&self) -> CombinedMotionState
获取组合运动状态(所有热数据)
注意:不同子状态的时间戳可能不同步(差异通常在毫秒级)。
如果需要时间对齐的状态,请使用 get_aligned_motion()。
Sourcepub fn get_aligned_motion(&self, max_time_diff_us: u64) -> AlignmentResult
pub fn get_aligned_motion(&self, max_time_diff_us: u64) -> AlignmentResult
Sourcepub fn wait_for_feedback(&self, timeout: Duration) -> Result<(), DriverError>
pub fn wait_for_feedback(&self, timeout: Duration) -> Result<(), DriverError>
Sourcepub fn get_fps_counts(&self) -> FpsCounts
pub fn get_fps_counts(&self) -> FpsCounts
Sourcepub fn reset_fps_stats(&self)
pub fn reset_fps_stats(&self)
重置 FPS 统计窗口(清空计数器并重新开始计时)
这是一个轻量级、无锁的重置:通过 ArcSwap 将内部 FpsStatistics 原子替换为新实例。
适合在监控工具中做固定窗口统计(例如每 5 秒 reset 一次)。
Sourcepub fn is_connected(&self) -> bool
pub fn is_connected(&self) -> bool
Sourcepub fn connection_age(&self) -> Duration
pub fn connection_age(&self) -> Duration
获取自上次反馈以来的时间
返回自上次成功处理 CAN 帧以来的时间。 可用于连接质量监控或诊断。
Sourcepub fn send_frame(&self, frame: PiperFrame) -> Result<(), DriverError>
pub fn send_frame(&self, frame: PiperFrame) -> Result<(), DriverError>
Sourcepub fn hooks(&self) -> Arc<RwLock<HookManager>>
pub fn hooks(&self) -> Arc<RwLock<HookManager>>
获取钩子管理器的引用(用于高级诊断)
§设计理念
这是一个逃生舱(Escape Hatch),用于高级诊断场景:
- 注册自定义 CAN 帧回调
- 实现录制功能
- 性能分析和调试
§使用场景
- 自定义诊断工具
- 高级抓包和调试
- 性能分析和优化
- 后台监控线程
§示例
// 获取 hooks 访问
let hooks = robot.hooks();
// 创建录制钩子
let (hook, _rx) = AsyncRecordingHook::new();
let callback = Arc::new(hook) as Arc<dyn FrameCallback>;
// 注册回调(忽略错误以简化示例)
if let Ok(mut hooks_guard) = hooks.write() {
hooks_guard.add_callback(callback);
}§安全注意事项
- 性能要求:回调必须在 <1μs 内完成
- 线程安全:返回
Arc<RwLock<HookManager>>,需手动加锁 - 不要阻塞:禁止在回调中使用 Mutex、I/O、分配等阻塞操作
§返回值
Arc<RwLock<HookManager>>: 钩子管理器的共享引用
§参考
HookManager- 钩子管理器FrameCallback- 回调 trait- 架构分析报告 - 方案 B 设计
Sourcepub fn mode(&self) -> DriverMode
pub fn mode(&self) -> DriverMode
Sourcepub fn set_mode(&self, mode: DriverMode)
pub fn set_mode(&self, mode: DriverMode)
设置 Driver 模式
§参数
mode: 新的 Driver 模式
§模式说明
- Normal: 正常模式,TX 线程按周期发送控制指令
- Replay: 回放模式,TX 线程暂停周期性发送
§使用场景
Replay 模式用于安全地回放预先录制的 CAN 帧:
- 暂停 TX 线程的周期性发送
- 避免双控制流冲突
- 允许精确控制帧发送时机
§⚠️ 安全警告
- 切换到 Replay 模式前,应确保机器人处于 Standby 状态
- 在 Replay 模式下发送控制指令时,应遵守安全速度限制
§示例
// 切换到回放模式
robot.set_mode(DriverMode::Replay);
// ... 执行回放 ...
// 恢复正常模式
robot.set_mode(DriverMode::Normal);Sourcepub fn send_frame_blocking(
&self,
frame: PiperFrame,
timeout: Duration,
) -> Result<(), DriverError>
pub fn send_frame_blocking( &self, frame: PiperFrame, timeout: Duration, ) -> Result<(), DriverError>
Sourcepub fn send_realtime(&self, frame: PiperFrame) -> Result<(), DriverError>
pub fn send_realtime(&self, frame: PiperFrame) -> Result<(), DriverError>
发送实时控制命令(邮箱模式,覆盖策略)
实时命令使用邮箱模式(Mailbox),直接覆盖旧命令,确保最新命令被发送。 这对于力控/高频控制场景很重要,只保留最新的控制指令。
§参数
frame: 控制帧(已构建的PiperFrame)
§错误
DriverError::NotDualThread: 未使用双线程模式DriverError::PoisonedLock: 锁中毒(极少见,通常意味着 TX 线程 panic)
§实现细节
- 获取 Mutex 锁并直接覆盖插槽内容(Last Write Wins)
- 锁持有时间极短(< 50ns),仅为内存拷贝
- 永不阻塞:无论 TX 线程是否消费,都能立即写入
- 如果插槽已有数据,会被覆盖(更新
metrics.tx_realtime_overwrites)
§性能
- 典型延迟:20-50ns(无竞争情况下)
- 最坏延迟:200ns(与 TX 线程锁竞争时)
- 相比 Channel 重试策略,延迟降低 10-100 倍
发送单个实时帧(向后兼容,API 不变)
Sourcepub fn send_realtime_package(
&self,
frames: impl IntoIterator<Item = PiperFrame>,
) -> Result<(), DriverError>
pub fn send_realtime_package( &self, frames: impl IntoIterator<Item = PiperFrame>, ) -> Result<(), DriverError>
发送实时帧包(新 API)
§参数
frames: 要发送的帧迭代器,必须非空
接口优化:接受 impl IntoIterator,允许用户传入:
- 数组:
[frame1, frame2, frame3](栈上,零堆分配) - 切片:
&[frame1, frame2, frame3] - Vec:
vec![frame1, frame2, frame3]
§错误
DriverError::NotDualThread: 未使用双线程模式DriverError::InvalidInput: 帧列表为空或过大DriverError::PoisonedLock: 锁中毒
§原子性保证
Package 内的所有帧要么全部发送成功,要么都不发送。 如果发送过程中出现错误,已发送的帧不会被回滚(CAN 总线特性), 但未发送的帧不会继续发送。
§性能特性
- 如果帧数量 ≤ 4,完全在栈上分配,零堆内存分配
- 如果帧数量 > 4,SmallVec 会自动溢出到堆,但仍保持高效
Sourcepub fn send_reliable(&self, frame: PiperFrame) -> Result<(), DriverError>
pub fn send_reliable(&self, frame: PiperFrame) -> Result<(), DriverError>
Sourcepub fn send_command(&self, command: PiperCommand) -> Result<(), DriverError>
pub fn send_command(&self, command: PiperCommand) -> Result<(), DriverError>
Sourcepub fn send_reliable_timeout(
&self,
frame: PiperFrame,
timeout: Duration,
) -> Result<(), DriverError>
pub fn send_reliable_timeout( &self, frame: PiperFrame, timeout: Duration, ) -> Result<(), DriverError>
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Piper
impl !RefUnwindSafe for Piper
impl Send for Piper
impl Sync for Piper
impl Unpin for Piper
impl !UnwindSafe for Piper
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