Skip to main content

Piper

Struct Piper 

Source
pub struct Piper { /* private fields */ }
Expand description

Piper 机械臂驱动(对外 API)

支持单线程和双线程两种模式

  • 单线程模式:使用 io_thread(向后兼容)
  • 双线程模式:使用 rx_threadtx_thread(物理隔离)

Implementations§

Source§

impl Piper

Source

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)?;
Source

pub fn new( can: impl CanAdapter + Send + 'static, config: Option<PipelineConfig>, ) -> Result<Piper, CanError>

创建新的 Piper 实例

§参数
  • can: CAN 适配器(会被移动到 IO 线程)
  • config: Pipeline 配置(可选)
§错误
  • CanError: CAN 设备初始化失败(注意:这里返回 CanError,因为 DriverError 尚未完全实现 From<CanError>
Source

pub fn new_dual_thread<C>( can: C, config: Option<PipelineConfig>, ) -> Result<Piper, CanError>
where C: SplittableAdapter + Send + 'static, <C as SplittableAdapter>::RxAdapter: Send + 'static, <C as SplittableAdapter>::TxAdapter: Send + 'static,

创建双线程模式的 Piper 实例

将 CAN 适配器分离为独立的 RX 和 TX 适配器,实现物理隔离。 RX 线程专门负责接收反馈帧,TX 线程专门负责发送控制命令。

§参数
  • can: 可分离的 CAN 适配器(必须已启动)
  • config: Pipeline 配置(可选)
§错误
  • CanError::NotStarted: 适配器未启动
  • CanError::Device: 分离适配器失败
§使用场景
  • 实时控制:需要 RX 不受 TX 阻塞影响
  • 高频控制:500Hz-1kHz 控制循环
§注意
  • 适配器必须已启动(调用 configure()start()
  • 分离后,原适配器不再可用(消费 can
Source

pub fn check_health(&self) -> (bool, bool)

检查线程健康状态

返回 RX 和 TX 线程的存活状态。

§返回
  • (rx_alive, tx_alive): 两个布尔值,表示线程是否还在运行
Source

pub fn is_healthy(&self) -> bool

检查是否健康

如果所有线程都存活,返回 true

Source

pub fn get_metrics(&self) -> MetricsSnapshot

获取性能指标快照

返回当前所有计数器的快照,用于监控 IO 链路健康状态。

Source

pub fn get_joint_dynamic(&self) -> JointDynamicState

获取关节动态状态(无锁,纳秒级返回)

包含关节速度和电流(独立帧 + Buffered Commit)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本(Clone 开销低,< 150 字节)
  • 适合 500Hz 控制循环
Source

pub fn get_joint_position(&self) -> JointPositionState

获取关节位置状态(无锁,纳秒级返回)

包含6个关节的位置信息(500Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本(Clone 开销低)
  • 适合 500Hz 控制循环
§注意
  • 此状态与 EndPoseState 不是原子更新的,如需同时获取,请使用 capture_motion_snapshot()
Source

pub fn get_end_pose(&self) -> EndPoseState

获取末端位姿状态(无锁,纳秒级返回)

包含末端执行器的位置和姿态信息(500Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本(Clone 开销低)
  • 适合 500Hz 控制循环
§注意
  • 此状态与 JointPositionState 不是原子更新的,如需同时获取,请使用 capture_motion_snapshot()
Source

pub fn capture_motion_snapshot(&self) -> MotionSnapshot

获取运动快照(无锁,纳秒级返回)

原子性地获取 JointPositionStateEndPoseState 的最新快照。 虽然这两个状态在硬件上不是同时更新的,但此方法保证逻辑上的原子性。

§性能
  • 无锁读取(两次 ArcSwap::load)
  • 返回快照副本
  • 适合需要同时使用关节位置和末端位姿的场景
§示例
Source

pub fn get_robot_control(&self) -> RobotControlState

获取机器人控制状态(无锁)

包含控制模式、机器人状态、故障码等(100Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本
Source

pub fn get_gripper(&self) -> GripperState

获取夹爪状态(无锁)

包含夹爪行程、扭矩、状态码等(100Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本
Source

pub fn get_joint_driver_low_speed(&self) -> JointDriverLowSpeedState

获取关节驱动器低速反馈状态(无锁)

包含温度、电压、电流、驱动器状态等(40Hz更新)。

§性能
  • 无锁读取(ArcSwap::load,Wait-Free)
  • 返回快照副本
Source

pub fn get_firmware_version(&self) -> Option<String>

获取固件版本字符串

从累积的固件数据中解析版本字符串。 如果固件数据未完整或未找到版本字符串,返回 None

§性能
  • 需要获取 RwLock 读锁
  • 如果已解析,直接返回缓存的版本字符串
  • 如果未解析,尝试从累积数据中解析
Source

pub fn query_firmware_version(&self) -> Result<(), DriverError>

查询固件版本

发送固件版本查询指令到机械臂,并清空之前的固件数据缓存。 查询和反馈使用相同的 CAN ID (0x4AF)。

注意

  • 发送查询命令后会自动清空固件数据缓存(与 Python SDK 一致)
  • 需要等待一段时间(推荐 30-50ms)让机械臂返回反馈数据
  • 之后可以调用 get_firmware_version() 获取解析后的版本字符串
§错误
  • DriverError::ChannelFull: 命令通道已满(单线程模式)
  • DriverError::ChannelClosed: 命令通道已关闭
  • DriverError::NotDualThread: 双线程模式下使用错误的方法
§示例
Source

pub fn get_master_slave_control_mode(&self) -> MasterSlaveControlModeState

获取主从模式控制模式指令状态(无锁)

包含控制模式、运动模式、速度等(主从模式下,~200Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本
Source

pub fn get_master_slave_joint_control(&self) -> MasterSlaveJointControlState

获取主从模式关节控制指令状态(无锁)

包含6个关节的目标角度(主从模式下,~500Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本
  • 帧组同步,保证6个关节数据的逻辑一致性
Source

pub fn get_master_slave_gripper_control(&self) -> MasterSlaveGripperControlState

获取主从模式夹爪控制指令状态(无锁)

包含夹爪目标行程、扭矩等(主从模式下,~200Hz更新)。

§性能
  • 无锁读取(ArcSwap::load)
  • 返回快照副本
Source

pub fn get_collision_protection( &self, ) -> Result<CollisionProtectionState, DriverError>

获取碰撞保护状态(读锁)

包含各关节的碰撞保护等级(按需查询)。

§性能
  • 读锁(RwLock::read)
  • 返回快照副本
Source

pub fn get_joint_limit_config( &self, ) -> Result<JointLimitConfigState, DriverError>

获取关节限制配置状态(读锁)

包含关节角度限制和速度限制(按需查询)。

§性能
  • 读锁(RwLock::read)
  • 返回快照副本
Source

pub fn get_joint_accel_config( &self, ) -> Result<JointAccelConfigState, DriverError>

获取关节加速度限制配置状态(读锁)

包含关节加速度限制(按需查询)。

§性能
  • 读锁(RwLock::read)
  • 返回快照副本
Source

pub fn get_end_limit_config(&self) -> Result<EndLimitConfigState, DriverError>

获取末端限制配置状态(读锁)

包含末端执行器的速度和加速度限制(按需查询)。

§性能
  • 读锁(RwLock::read)
  • 返回快照副本
Source

pub fn get_motion_state(&self) -> CombinedMotionState

获取组合运动状态(所有热数据)

注意:不同子状态的时间戳可能不同步(差异通常在毫秒级)。 如果需要时间对齐的状态,请使用 get_aligned_motion()

Source

pub fn get_aligned_motion(&self, max_time_diff_us: u64) -> AlignmentResult

获取时间对齐的运动状态(推荐用于力控算法)

joint_position.hardware_timestamp_us 为基准时间,检查时间戳差异。 即使时间戳差异超过阈值,也返回状态数据(让用户有选择权)。

§参数
  • max_time_diff_us: 允许的最大时间戳差异(微秒),推荐值:5000(5ms)
§返回值
  • AlignmentResult::Ok(state): 时间戳差异在可接受范围内
  • AlignmentResult::Misaligned { state, diff_us }: 时间戳差异过大,但仍返回状态数据
Source

pub fn wait_for_feedback(&self, timeout: Duration) -> Result<(), DriverError>

等待接收到第一个有效反馈(用于初始化)

Piper::new() 后调用,确保在控制循环开始前已收到有效数据。 避免使用全零的初始状态导致错误的控制指令。

§参数
  • timeout: 超时时间
§返回值
  • Ok(()): 成功接收到有效反馈(timestamp_us > 0
  • Err(DriverError::Timeout): 超时未收到反馈
Source

pub fn get_fps(&self) -> FpsResult

获取 FPS 统计结果

返回最近一次统计窗口内的更新频率(FPS)。 建议定期调用(如每秒一次)或按需调用。

§性能
  • 无锁读取(仅原子读取)
  • 开销:~100ns(5 次原子读取 + 浮点计算)
§Example
Source

pub fn get_fps_counts(&self) -> FpsCounts

获取 FPS 计数器原始值

返回当前计数器的原始值,可以配合自定义时间窗口计算 FPS。

§性能
  • 无锁读取(仅原子读取)
  • 开销:~50ns(5 次原子读取)
§Example
Source

pub fn reset_fps_stats(&self)

重置 FPS 统计窗口(清空计数器并重新开始计时)

这是一个轻量级、无锁的重置:通过 ArcSwap 将内部 FpsStatistics 原子替换为新实例。 适合在监控工具中做固定窗口统计(例如每 5 秒 reset 一次)。

Source

pub fn is_connected(&self) -> bool

检查机器人是否仍在响应

如果在超时窗口内收到反馈,返回 true。 这可用于检测机器人是否断电、CAN 线缆断开或固件崩溃。

§性能
  • 无锁读取(AtomicU64::load)
  • O(1) 时间复杂度
Source

pub fn connection_age(&self) -> Duration

获取自上次反馈以来的时间

返回自上次成功处理 CAN 帧以来的时间。 可用于连接质量监控或诊断。

Source

pub fn send_frame(&self, frame: PiperFrame) -> Result<(), DriverError>

发送控制帧(非阻塞)

§参数
  • frame: 控制帧(已构建的 PiperFrame
§错误
  • DriverError::ChannelClosed: 命令通道已关闭(IO 线程退出)
  • DriverError::ChannelFull: 命令队列已满(缓冲区容量 10)
Source

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>>: 钩子管理器的共享引用

§参考
Source

pub fn interface(&self) -> String

获取 CAN 接口名称

§返回值

CAN 接口名称,例如 “can0”, “vcan0” 等

Source

pub fn bus_speed(&self) -> u32

获取 CAN 总线速度

§返回值

CAN 总线速度(bps),例如 1000000 (1Mbps)

Source

pub fn mode(&self) -> DriverMode

获取当前 Driver 模式

§返回值

当前 Driver 模式(Normal 或 Replay)

§示例
let mode = robot.mode();
println!("Current mode: {:?}", mode);
Source

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);
Source

pub fn send_frame_blocking( &self, frame: PiperFrame, timeout: Duration, ) -> Result<(), DriverError>

发送控制帧(阻塞,带超时)

如果命令通道已满,阻塞等待直到有空闲位置或超时。

§参数
  • frame: 控制帧(已构建的 PiperFrame
  • timeout: 超时时间
§错误
  • DriverError::ChannelClosed: 命令通道已关闭(IO 线程退出)
  • DriverError::Timeout: 超时未发送成功
Source

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 不变)

Source

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 会自动溢出到堆,但仍保持高效
Source

pub fn send_reliable(&self, frame: PiperFrame) -> Result<(), DriverError>

发送可靠命令(FIFO 策略)

可靠命令使用容量为 10 的队列,按 FIFO 顺序发送,不会覆盖。 这对于配置帧、状态机切换帧等关键命令很重要。

§参数
  • frame: 控制帧(已构建的 PiperFrame
§错误
  • DriverError::NotDualThread: 未使用双线程模式
  • DriverError::ChannelClosed: 命令通道已关闭(TX 线程退出)
  • DriverError::ChannelFull: 队列满(非阻塞)
Source

pub fn send_command(&self, command: PiperCommand) -> Result<(), DriverError>

发送命令(根据优先级自动选择队列)

根据命令的优先级自动选择实时队列或可靠队列。

§参数
  • command: 带优先级的命令
§错误
  • DriverError::NotDualThread: 未使用双线程模式
  • DriverError::ChannelClosed: 命令通道已关闭(TX 线程退出)
  • DriverError::ChannelFull: 队列满(仅可靠命令)
Source

pub fn send_reliable_timeout( &self, frame: PiperFrame, timeout: Duration, ) -> Result<(), DriverError>

发送可靠命令(阻塞,带超时)

如果队列满,阻塞等待直到有空闲位置或超时。

§参数
  • frame: 控制帧(已构建的 PiperFrame
  • timeout: 超时时间
§错误
  • DriverError::NotDualThread: 未使用双线程模式
  • DriverError::ChannelClosed: 命令通道已关闭(TX 线程退出)
  • DriverError::Timeout: 超时未发送成功

Trait Implementations§

Source§

impl Drop for Piper

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more