use std::f64::consts::PI;
use crate::{generate_servo, servo::conversion::Conversion};
generate_servo!(
MX, v1,
reg: (model_number, r, 0, u16, None),
reg: (firmware_version, r, 2, u8, None),
reg: (id, rw, 3, u8, None),
reg: (baudrate, rw, 4, u8, None),
reg: (return_delay_time, rw, 5, u8, None),
reg: (cw_angle_limit, rw, 6, i16, AnglePosition),
reg: (ccw_angle_limit, rw, 8, i16, AnglePosition),
reg: (temperature_limit, rw, 11, u8, None),
reg: (min_voltage_limit, rw, 12, u8, None),
reg: (max_voltage_limit, rw, 13, u8, None),
reg: (max_torque, rw, 14, u16, None),
reg: (status_return_level, rw, 16, u8, None),
reg: (alarm_led, rw, 17, u8, None),
reg: (shutdown, rw, 18, u8, None),
reg: (multi_turn_offset, rw, 20, i16, None),
reg: (resolution_divider, rw, 22, u8, None),
reg: (torque_enable, rw, 24, u8, None),
reg: (led, rw, 25, u8, None),
reg: (d_gain, rw, 26, u8, None),
reg: (i_gain, rw, 27, u8, None),
reg: (p_gain, rw, 28, u8, None),
reg: (goal_position, rw, 30, i16, AnglePosition),
reg: (moving_speed, rw, 32, u16, None),
reg: (torque_limit, rw, 34, u16, None),
reg: (present_position, r, 36, i16, AnglePosition),
reg: (present_speed, r, 38, u16, None),
reg: (present_load, r, 40, u16, None),
reg: (present_voltage, r, 42, u8, None),
reg: (present_temperature, r, 43, u8, None),
reg: (registered, r, 44, u8, None),
reg: (moving, r, 46, u8, None),
reg: (lock, rw, 47, u8, None),
reg: (punch, rw, 48, u16, None),
reg: (realtime_tick, r, 50, u16, None),
reg: (goal_acceleration, rw, 73, u8, None),
);
pub fn sync_read_present_position_speed_load(
dph: &crate::DynamixelProtocolHandler,
serial_port: &mut dyn serialport::SerialPort,
ids: &[u8],
) -> crate::Result<Vec<(i16, u16, u16)>> {
let val = dph.sync_read(serial_port, ids, 36, 2 + 2 + 2)?;
let val = val
.iter()
.map(|v| {
(
i16::from_le_bytes(v[0..2].try_into().unwrap()),
u16::from_le_bytes(v[2..4].try_into().unwrap()),
u16::from_le_bytes(v[4..6].try_into().unwrap()),
)
})
.collect();
Ok(val)
}
pub struct AnglePosition;
impl Conversion for AnglePosition {
type RegisterType = i16;
type UsiType = f64;
fn from_raw(raw: i16) -> f64 {
(2.0 * PI * (raw as f64) / 4096.0) - PI
}
fn to_raw(value: f64) -> i16 {
(4096.0 * (PI + value) / (2.0 * PI)) as i16
}
}
pub mod conv {
pub fn dxl_abs_speed_to_rad_per_sec(speed: u16) -> f64 {
let rpm = speed as f64 * 0.114;
rpm * 0.10472
}
pub fn rad_per_sec_to_dxl_abs_speed(speed: f64) -> u16 {
let rpm = speed / 0.10472;
(rpm / 0.114) as u16
}
pub fn dxl_oriented_speed_to_rad_per_sec(speed: u16) -> f64 {
let cw = (speed >> 11) == 1;
let rad_per_sec = dxl_abs_speed_to_rad_per_sec(speed % 1024);
match cw {
true => rad_per_sec,
false => -rad_per_sec,
}
}
pub fn rad_per_sec_to_dxl_oriented_speed(speed: f64) -> u16 {
let raw = rad_per_sec_to_dxl_abs_speed(speed.abs());
match speed < 0.0 {
true => raw,
false => raw + 2048,
}
}
pub fn dxl_load_to_abs_torque(load: u16) -> f64 {
load as f64 / 1023.0 * 100.0
}
pub fn torque_to_dxl_abs_load(torque: f64) -> u16 {
assert!((0.0..=100.0).contains(&torque));
(torque * 1023.0 / 100.0) as u16
}
pub fn dxl_load_to_oriented_torque(load: u16) -> f64 {
let cw = (load >> 10) == 1;
let torque = dxl_load_to_abs_torque(load % 1024);
match cw {
true => torque,
false => -torque,
}
}
pub fn oriented_torque_to_dxl_load(torque: f64) -> u16 {
let load = torque_to_dxl_abs_load(torque.abs());
match torque < 0.0 {
true => load,
false => load + 1024,
}
}
}
#[cfg(test)]
mod tests {
use std::f64::consts::PI;
use crate::servo::{conversion::Conversion, dynamixel::mx::AnglePosition};
use super::conv::*;
#[test]
fn position_conversions() {
assert_eq!(AnglePosition::to_raw(0.0), 2048);
assert_eq!(AnglePosition::to_raw(-PI / 2.0), 1024);
assert_eq!(AnglePosition::to_raw(PI / 2.0), 3072);
assert_eq!(AnglePosition::from_raw(2048), 0.0);
}
#[test]
fn abs_speed_conversions() {
assert_eq!(rad_per_sec_to_dxl_abs_speed(0.0), 0);
assert_eq!(rad_per_sec_to_dxl_abs_speed(0.5), 41);
}
#[test]
fn speed_conversions() {
assert_eq!(dxl_oriented_speed_to_rad_per_sec(300), -3.581424);
assert_eq!(dxl_oriented_speed_to_rad_per_sec(2048 + 300), 3.581424);
assert_eq!(rad_per_sec_to_dxl_oriented_speed(-3.581424), 300);
assert_eq!(rad_per_sec_to_dxl_oriented_speed(3.581424), 2048 + 300);
}
#[test]
fn torque_conversions() {
assert_eq!(torque_to_dxl_abs_load(0.0), 0);
assert_eq!(torque_to_dxl_abs_load(50.0), 511);
assert_eq!(torque_to_dxl_abs_load(100.0), 1023);
assert_eq!(dxl_load_to_abs_torque(0), 0.0);
assert!((dxl_load_to_abs_torque(511) - 50.0).abs() < 1e-1);
assert_eq!(dxl_load_to_abs_torque(1023), 100.0);
}
#[test]
fn load_conversions() {
assert!((dxl_load_to_oriented_torque(511) + 50.0).abs() < 1e-1);
assert!((dxl_load_to_oriented_torque(1024 + 512) - 50.0).abs() < 1e-1);
assert_eq!(oriented_torque_to_dxl_load(-50.0), 511);
assert_eq!(oriented_torque_to_dxl_load(50.0), 1024 + 511);
}
}