use clap::Parser;
use piper_sdk::client::state::MitModeConfig;
use piper_sdk::client::types::{JointArray, NewtonMeter, Rad};
use piper_sdk::prelude::*;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
#[derive(Parser, Debug)]
#[command(name = "multi_threaded_demo")]
#[command(about = "多线程控制演示 - 展示如何在多线程环境下安全地共享 Piper 实例")]
struct Args {
#[arg(long, default_value = "can0")]
interface: String,
#[arg(long, default_value = "1000000")]
baud_rate: u32,
#[arg(long, default_value = "100")]
frequency_hz: f64,
#[arg(long, default_value = "5")]
duration_sec: u64,
}
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
piper_sdk::init_logger!();
let args = Args::parse();
println!("🤖 Piper SDK - 多线程控制演示");
println!("=========================\n");
println!("📡 步骤 1: 连接并使能机械臂...");
let robot = PiperBuilder::new()
.interface(&args.interface)
.baud_rate(args.baud_rate)
.build()?;
let robot = robot.enable_mit_mode(MitModeConfig::default())?;
println!(" ✅ 使能成功\n");
let robot = Arc::new(Mutex::new(robot));
println!("🔒 机器人已包装在 Arc<Mutex<>> 中,可安全跨线程共享\n");
println!(
"⚙️ 步骤 2: 启动控制线程 ({} Hz,{} 秒)...",
args.frequency_hz, args.duration_sec
);
let robot_clone = Arc::clone(&robot);
let control_thread = thread::spawn(move || {
let period = Duration::from_secs_f64(1.0 / args.frequency_hz);
let start_time = Instant::now();
let mut iteration = 0;
println!(" 📝 控制线程已启动");
loop {
let elapsed = start_time.elapsed().as_secs_f64();
let amplitude = 0.2;
let frequency = 0.5;
let phase = 2.0 * std::f64::consts::PI * frequency * elapsed;
let j1_target = amplitude * phase.sin();
let positions = JointArray::from([
Rad(j1_target),
Rad(0.0),
Rad(0.0),
Rad(0.0),
Rad(0.0),
Rad(0.0),
]);
if let Ok(robot) = robot_clone.lock() {
let velocities = JointArray::from([0.0; 6]);
let kp = JointArray::from([0.0; 6]);
let kd = JointArray::from([0.0; 6]);
let torques = JointArray::from([NewtonMeter(0.0); 6]);
if let Err(e) = robot.command_torques(&positions, &velocities, &kp, &kd, &torques) {
eprintln!(" ❌ 发送命令失败: {:?}", e);
break;
}
} else {
eprintln!(" ❌ 获取锁失败");
break;
}
if elapsed >= args.duration_sec as f64 {
println!(" 📝 控制线程结束,总迭代次数: {}", iteration);
break;
}
iteration += 1;
std::thread::sleep(period);
}
});
println!("📊 步骤 3: 主线程监控机械臂状态...\n");
let monitor_start = Instant::now();
let mut sample_count = 0;
while monitor_start.elapsed() < Duration::from_secs(args.duration_sec) {
let observer = {
let robot = robot.lock().unwrap();
robot.observer().clone()
};
let positions = observer.joint_positions();
sample_count += 1;
if sample_count % (args.frequency_hz as u32) == 0 {
println!(
" 📍 J1 = {:.4} rad ({:.2} deg) - 样本 #{:04}",
positions[Joint::J1].0,
positions[Joint::J1].to_deg().0,
sample_count
);
}
std::thread::sleep(Duration::from_millis(10));
}
control_thread.join().unwrap();
println!("\n ✅ 控制线程已结束\n");
println!("🛑 步骤 4: 失能机械臂...");
let _robot = robot.lock().unwrap();
println!(" ✅ 演示完成!");
println!("\n💡 关键要点:");
println!(" 1. 使用 Arc<Mutex<Piper>> 而非提取 MotionCommander");
println!(" 2. 每次发送命令时获取锁,发送后立即释放");
println!(" 3. Observer 可以 clone 用于只读监控(不需要锁)");
println!(" 4. 这种模式保证了 Type State 安全性");
Ok(())
}