use crate::arm_controller;
use crate::arm_controller::{ArmController, EndEffectorPose};
use crate::collision_handler::CollisionHandler;
use anyhow::{Context, Result};
use nalgebra as na;
use std::f32;
use std::sync::mpsc;
use std::time;
use tokio::task;
pub struct LinearMotion {
target: na::Vector3<f32>,
current: na::Vector3<f32>,
max_step: f32,
}
impl LinearMotion {
pub fn new(start: na::Vector3<f32>, target: na::Vector3<f32>, max_step: f32) -> Self {
LinearMotion {
target,
current: start,
max_step,
}
}
}
impl Iterator for LinearMotion {
type Item = na::Vector3<f32>;
fn next(&mut self) -> Option<Self::Item> {
match self.current.move_towards(&self.target, self.max_step) {
Some(next) => {
self.current = next;
Some(next)
}
None => None,
}
}
}
trait MoveTowards: Sized {
fn move_towards(&self, target: &Self, max_step: f32) -> Option<Self>;
}
impl MoveTowards for na::Vector3<f32> {
fn move_towards(&self, target: &Self, max_step: f32) -> Option<Self> {
if self == target {
return None;
}
let distance = na::distance(&na::Point3::from(*self), &na::Point3::from(*target));
if distance <= max_step {
return Some(*target);
}
let translation = target - self;
let next = self + (translation.normalize() * max_step);
Some(next)
}
}
impl MoveTowards for f32 {
fn move_towards(&self, target: &Self, max_step: f32) -> Option<Self> {
#[allow(clippy::float_cmp)]
if self == target {
return None;
}
let distance = (target - self).abs();
if distance <= max_step {
return Some(*target);
}
let next = self + max_step.copysign(target - self);
Some(next)
}
}
trait MovePoseTowards: Sized {
fn move_towards(
&self,
target: &Self,
max_translation: f32,
max_angle_move: f32,
) -> Option<Self>;
}
impl MovePoseTowards for EndEffectorPose {
fn move_towards(
&self,
target: &Self,
max_translation: f32,
max_angle_move: f32,
) -> Option<Self> {
let pose = (
self.position
.move_towards(&target.position, max_translation),
self.end_effector_angle
.move_towards(&target.end_effector_angle, max_angle_move),
);
match pose {
(None, None) => None,
(Some(position), None) => {
Some(EndEffectorPose::new(position, target.end_effector_angle))
}
(None, Some(angle)) => Some(EndEffectorPose::new(target.position, angle)),
(Some(position), Some(angle)) => Some(EndEffectorPose::new(position, angle)),
}
}
}
enum MotionCommand {
MoveTo(EndEffectorPose),
}
pub struct ContinuousMotionController {
sender: mpsc::Sender<MotionCommand>,
_join_handle: task::JoinHandle<()>,
}
impl ContinuousMotionController {
pub async fn new(
arm_controller: Box<dyn ArmController>,
collision_handler: CollisionHandler,
translation_speed: f32,
rotational_speed: f32,
) -> Result<Self> {
let (sender, receiver) = mpsc::channel();
let internal = MotionControllerInternal::new(
arm_controller,
collision_handler,
translation_speed,
rotational_speed,
receiver,
)
.await?;
let join_handle = internal.start();
Ok(Self {
sender,
_join_handle: join_handle,
})
}
pub fn set_target(&mut self, target: EndEffectorPose) {
self.sender
.send(MotionCommand::MoveTo(target))
.context("Failed to send motion command")
.unwrap();
}
}
struct MotionControllerInternal {
arm_controller: Box<dyn ArmController>,
collision_handler: CollisionHandler,
translation_speed: f32,
rotational_speed: f32,
last_command_ts: time::Instant,
last_pose: arm_controller::EndEffectorPose,
desired_position: Option<arm_controller::EndEffectorPose>,
receiver: mpsc::Receiver<MotionCommand>,
}
impl MotionControllerInternal {
async fn new(
mut arm_controller: Box<dyn ArmController>,
collision_handler: CollisionHandler,
translation_speed: f32,
rotational_speed: f32,
receiver: mpsc::Receiver<MotionCommand>,
) -> Result<Self> {
let last_pose = arm_controller
.read_position()
.await?
.get_end_effector_pose();
Ok(Self {
arm_controller,
collision_handler,
translation_speed,
rotational_speed,
last_command_ts: time::Instant::now(),
last_pose,
desired_position: None,
receiver,
})
}
fn start(mut self) -> task::JoinHandle<()> {
tokio::spawn(async move {
self.last_command_ts = time::Instant::now();
loop {
tokio::time::sleep(time::Duration::from_millis(20)).await;
if self.check_messaged().is_err() {
return;
}
self.tick().await.unwrap();
self.last_command_ts = time::Instant::now();
}
})
}
fn check_messaged(&mut self) -> Result<()> {
loop {
match self.receiver.try_recv() {
Ok(message) => match message {
MotionCommand::MoveTo(pose) => self.desired_position = Some(pose),
},
Err(mpsc::TryRecvError::Empty) => break,
Err(mpsc::TryRecvError::Disconnected) => {
return Err(anyhow::anyhow!("Sender closed"))
}
}
}
Ok(())
}
async fn tick(&mut self) -> Result<()> {
let elapsed = self.last_command_ts.elapsed();
if let Some(desired_pose) = &self.desired_position {
let next = self.last_pose.move_towards(
desired_pose,
(self.translation_speed * elapsed.as_secs_f32()).abs(),
(self.rotational_speed * elapsed.as_secs_f32()).abs(),
);
if let Some(next) = next {
let (positions, joints) = self.arm_controller.calculate_full_poses(next.clone())?;
if self.collision_handler.pose_collision_free(&positions) {
self.last_pose = next;
self.arm_controller
.set_color(lss_driver::LedColor::Cyan)
.await?;
self.arm_controller.move_joints_to(joints).await?;
} else {
self.arm_controller
.set_color(lss_driver::LedColor::Red)
.await?;
self.desired_position = None;
}
}
} else {
self.arm_controller
.set_color(lss_driver::LedColor::Blue)
.await?;
self.desired_position = None;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn linear_motion_end_is_identical_to_target() {
let end = na::Vector3::new(1.0, 0.0, 0.0);
let motion = LinearMotion::new(na::Vector3::new(0.0, 0.0, 0.0), end, 0.1);
assert_relative_eq!(end, motion.last().unwrap());
}
#[test]
fn linear_motion_is_long_axis() {
let start = na::Vector3::new(0.0, 0.0, 0.0);
let target = na::Vector3::new(1.0, 0.0, 0.0);
let motion = LinearMotion::new(start, target, 0.1);
for point in motion {
assert_relative_eq!(point.yz(), na::Vector2::new(0.0, 0.0));
}
}
#[test]
fn test_move_towards_float() {
let start = 1.0;
let mut current = start;
let target = 2.0;
let mut counter = 0;
while let Some(next) = current.move_towards(&target, 0.1) {
assert!(next > start);
assert!(next <= target);
current = next;
counter += 1;
}
assert_eq!(counter, 10);
assert_relative_eq!(target, current);
}
#[test]
fn test_move_towards_float_target_negative() {
let start = 1.0;
let mut current = start;
let target = -2.0;
while let Some(next) = current.move_towards(&target, 0.1) {
current = next;
}
assert_relative_eq!(target, current);
}
#[test]
fn test_move_towards_float_neg_to_neg() {
let start = -1.0;
let mut current = start;
let target = -2.0;
while let Some(next) = current.move_towards(&target, 0.1) {
current = next;
}
assert_relative_eq!(target, current);
}
#[test]
fn move_end_effector() {
let start = EndEffectorPose::new(na::Vector3::new(0.0, 0.0, 0.0), 0.0);
let target = EndEffectorPose::new(na::Vector3::new(1.0, 0.0, 0.0), 10.0);
let mut current = start;
let mut counter = 0;
while let Some(pose) = current.move_towards(&target, 0.1, 1.0) {
current = pose;
counter += 1;
}
assert_eq!(counter, 10);
assert_relative_eq!(current.position, target.position);
assert_relative_eq!(current.end_effector_angle, target.end_effector_angle);
}
}