use nalgebra as na;
use std::{f64::consts::PI, sync::Arc, time::Duration};
use libjaka::JakaMini2;
use robot_behavior::{MotionType, Pose, RobotResult, behavior::*};
use std::thread::sleep;
fn main() -> RobotResult<()> {
let mut robot = JakaMini2::new("10.5.5.100");
robot.enable()?;
robot
.with_cartesian_velocity(100.0)
.move_cartesian_async(&Pose::Euler([300.0, 0.0, 30.0], [-180.0, 0.0, 180.0]))?;
sleep(Duration::from_secs(2));
let (d, curve) = cone_spiral_curve([300.0, 0.0, 30.0], 60.0, 3, 0.3, 0.3);
let (t_min, f_t) = simple_4th_curve(1., 10. / d, 8. / d);
let mut t = Duration::from_secs(0);
let t_min = Duration::from_secs_f64(t_min);
let closure = move |_, period| {
t += period;
(curve(f_t(t)), t > t_min)
};
robot.move_with_closure(closure)?;
Ok(())
}
fn cone_spiral_curve(
vertex: [f64; 3],
h: f64,
loops: usize,
theta: f64,
alpha: f64,
) -> (
f64,
Arc<dyn Fn(f64) -> MotionType<{ JakaMini2::N }> + Send + Sync>,
) {
let r_base = h * theta.tan();
let n = loops as f64;
let sin_theta = theta.sin();
let k = 4.0 * PI.powi(2) * n.powi(2) * sin_theta.powi(2);
let total_length = if k < 1e-6 {
h / theta.cos() } else {
let sqrt_k = k.sqrt();
let sqrt_1_plus_k = (1.0 + k).sqrt();
h / theta.cos() * (0.5 * sqrt_1_plus_k + 0.5 / sqrt_k * (sqrt_k + sqrt_1_plus_k).ln())
};
let closure = Arc::new(move |s: f64| {
let t = s.clamp(0.0, 1.0);
compute_point(t, vertex, h, loops, r_base, alpha)
});
(total_length, closure)
}
fn compute_point(
t: f64,
vertex: [f64; 3],
h: f64,
loops: usize,
r_base: f64,
alpha: f64,
) -> MotionType<{ JakaMini2::N }> {
let radius = r_base * t;
let angle = 2.0 * PI * loops as f64 * t;
let x = vertex[0] + radius * angle.cos();
let y = vertex[1] + radius * angle.sin();
let z = vertex[2] + h * t;
let radial = na::Vector3::new(vertex[0] - x, vertex[1] - y, 0.0);
let radial = radial.try_normalize(1e-6).unwrap_or(na::Vector3::zeros());
let z_tool = (na::Vector3::new(0.0, 0.0, -1.0) * alpha.cos()) + (radial * alpha.sin());
let z_tool = z_tool.normalize();
let up = if z_tool.z.abs() < 0.99 {
na::Vector3::y()
} else {
na::Vector3::x()
};
let x_dir = up.cross(&z_tool).normalize();
let y_dir = z_tool.cross(&x_dir).normalize();
let rot =
na::Rotation3::from_matrix_unchecked(na::Matrix3::from_columns(&[x_dir, y_dir, z_tool]));
let (roll, pitch, yaw) = rot.euler_angles();
let euler_deg = (roll.to_degrees(), pitch.to_degrees(), yaw.to_degrees());
println!(
"{},{},{},{},{},{}",
x,
y,
z,
roll.to_degrees(),
pitch.to_degrees(),
yaw.to_degrees()
);
MotionType::Cartesian(Pose::Euler([x, y, z], euler_deg.into()))
}
fn simple_4th_curve(
delta: f64,
v_max: f64,
a_max: f64,
) -> (f64, Arc<dyn Fn(Duration) -> f64 + Send + Sync>) {
if delta < 1e-6 {
return (0., Arc::new(|_| 0.));
}
let mut v_max = v_max;
if delta < 1.5 * v_max.powi(2) / a_max {
v_max = (2. / 3. * delta * a_max).sqrt();
}
let t1 = 1.5 * v_max / a_max;
let t_min = t1 + delta / v_max;
let f = move |t: Duration| {
let t = t.as_secs_f64();
if t < t1 {
(t / t1).powi(3) * (t1 - 0.5 * t) * v_max
} else if t < t_min - t1 {
t1 * v_max / 2. + (t - t1) * v_max
} else if t < t_min {
delta - ((t_min - t) / t1).powi(3) * (t1 - 0.5 * (t_min - t)) * v_max
} else {
delta
}
};
(t_min, Arc::new(f))
}