1pub mod io;
26
27use io::Orbita3dIOConfig;
28
29#[cfg(feature = "build_ethercat")]
30use crate::io::EthercatPoulpeController;
31#[cfg(feature = "build_dynamixel")]
32use io::{
33 CachedDynamixelPoulpeController, CachedDynamixelSerialController, DynamixelPoulpeController,
34 DynamixelSerialController,
35};
36
37use motor_toolbox_rs::{FakeMotorsController, MotorsController, Result, PID};
38
39use orbita3d_kinematics::{conversion, Orbita3dKinematicsModel};
40use serde::{Deserialize, Serialize};
41use std::{thread, time::Duration};
42
43#[derive(Debug, Deserialize, Serialize)]
44pub struct Orbita3dConfig {
46 pub io: Orbita3dIOConfig,
48 pub disks: DisksConfig,
50 pub kinematics_model: Orbita3dKinematicsModel,
52 pub inverted_axes: [Option<bool>; 3],
53}
54
55#[derive(Debug, Deserialize, Serialize)]
56pub struct DisksConfig {
58 pub zeros: ZeroType,
60 pub reduction: f64,
62}
63
64#[derive(Debug, Deserialize, Serialize)]
65pub enum ZeroType {
68 ApproximateHardwareZero(ApproximateHardwareZero),
70 ZeroStartup(ZeroStartup),
72 HallZero(HallZero),
74 FirmwareZero(FirmwareZero),
76}
77
78#[derive(Debug, Deserialize, Serialize)]
79pub struct FirmwareZero;
81
82#[derive(Debug, Deserialize, Serialize)]
83pub struct ApproximateHardwareZero {
85 pub hardware_zero: [f64; 3],
87}
88
89#[derive(Debug, Deserialize, Serialize)]
90pub struct HallZero {
92 pub hardware_zero: [f64; 3],
94
95 pub hall_indice: [u8; 3],
97}
98
99#[derive(Debug, Deserialize, Serialize)]
100pub struct ZeroStartup;
102
103pub struct Orbita3dController {
105 inner: Box<dyn MotorsController<3> + Send>,
106 pub kinematics: Orbita3dKinematicsModel,
107}
108
109#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
110pub struct Orbita3dFeedback {
112 pub orientation: [f64; 4],
113}
114
115impl Orbita3dController {
116 pub fn with_config(configfile: &str) -> Result<Self> {
118 log::info!("Loading config file: {}", configfile);
119
120 let f = match std::fs::File::open(configfile) {
121 Ok(f) => f,
122 Err(e) => {
123 log::error!("Error opening config file: {}", e);
124 return Err(e.into());
125 }
126 };
127 let config: Orbita3dConfig = serde_yaml::from_reader(f)?;
128
129 let controller: Box<dyn MotorsController<3> + Send> = match config.io {
130 #[cfg(feature = "build_dynamixel")]
132 Orbita3dIOConfig::DynamixelSerial(dxl_config) => match dxl_config.use_cache {
133 true => {
134 let controller = CachedDynamixelSerialController::new(
135 &dxl_config.serial_port,
136 dxl_config.id,
137 config.disks.zeros,
138 config.disks.reduction,
139 )?;
140
141 log::info!("Using cached dynamixel controller {:?}", controller);
142
143 Box::new(controller)
144 }
145 false => {
146 let controller = DynamixelSerialController::new(
147 &dxl_config.serial_port,
148 dxl_config.id,
149 config.disks.zeros,
150 config.disks.reduction,
151 )?;
152
153 log::info!("Using dynamixel controller {:?}", controller);
154
155 Box::new(controller)
156 }
157 },
158 #[cfg(feature = "build_dynamixel")]
160 Orbita3dIOConfig::DynamixelPoulpe(dxl_config) => match dxl_config.use_cache {
161 true => {
162 let controller = CachedDynamixelPoulpeController::new(
163 &dxl_config.serial_port,
164 dxl_config.id,
165 config.disks.zeros,
166 config.disks.reduction,
167 )?;
168
169 log::info!("Using cached poulpe dynamixel controller {:?}", controller);
170
171 Box::new(controller)
172 }
173 false => {
174 let controller = DynamixelPoulpeController::new(
175 &dxl_config.serial_port,
176 dxl_config.id,
177 config.disks.zeros,
178 config.disks.reduction,
179 )?;
180
181 log::info!("Using poulpe dynamixel controller {:?}", controller);
182
183 Box::new(controller)
184 }
185 },
186 Orbita3dIOConfig::FakeMotors(_) => {
188 let mut controller = FakeMotorsController::<3>::new();
189
190 let offsets = match config.disks.zeros {
191 ZeroType::ApproximateHardwareZero(zero) => zero.hardware_zero,
192 ZeroType::ZeroStartup(_) => controller.get_current_position()?,
193 ZeroType::HallZero(zero) => zero.hardware_zero,
194 ZeroType::FirmwareZero(_) => [0.0, 0.0, 0.0],
195 };
196
197 let controller = controller
198 .with_offsets(offsets.map(Some))
199 .with_reduction([Some(config.disks.reduction); 3])
200 .with_inverted_axes(config.inverted_axes);
201
202 log::info!("Using fake motors controller {:?}", controller);
203
204 Box::new(controller)
205 }
206 #[cfg(feature = "build_ethercat")]
208 Orbita3dIOConfig::PoulpeEthercat(ethercat_config) => {
209 let controller = EthercatPoulpeController::new(
210 ðercat_config.url,
211 ethercat_config.id,
212 ethercat_config.name,
213 config.disks.zeros,
214 config.disks.reduction,
215 config.inverted_axes,
216 )?;
217 log::info!("Using poulpe ethercat controller {:?}", controller);
218
219 Box::new(controller)
220 }
221 };
222
223 Ok(Self {
224 inner: controller,
225 kinematics: config.kinematics_model,
226 })
227 }
228}
229
230impl Orbita3dController {
231 pub fn is_torque_on(&mut self) -> Result<bool> {
233 let torques = self.inner.is_torque_on()?;
234 assert!(torques.iter().all(|&t| t == torques[0]));
235 Ok(torques[0])
236 }
237 pub fn enable_torque(&mut self, reset_target: bool) -> Result<()> {
242 if !self.is_torque_on()? && reset_target {
243 let thetas = self.inner.get_current_position()?;
244 thread::sleep(Duration::from_millis(1));
245 self.inner.set_target_position(thetas)?;
246 thread::sleep(Duration::from_millis(1));
247 }
248 self.inner.set_torque([true; 3])
249 }
250 pub fn disable_torque(&mut self) -> Result<()> {
252 self.inner.set_torque([false; 3])
253 }
254
255 pub fn get_current_orientation(&mut self) -> Result<[f64; 4]> {
257 let thetas = self.inner.get_current_position()?;
258 let rot = self.kinematics.compute_forward_kinematics(thetas);
259 Ok(conversion::rotation_matrix_to_quaternion(rot))
260 }
261
262 pub fn get_current_rpy_orientation(&mut self) -> Result<[f64; 3]> {
264 let thetas = self.inner.get_current_position()?;
265 let inverted_axes = self.inner.output_inverted_axes();
266 let mut rpy = self
267 .kinematics
268 .compute_forward_kinematics_rpy_multiturn(thetas)?;
269
270 for i in 0..3 {
271 if let Some(inverted) = inverted_axes[i] {
272 if inverted {
273 rpy[i] = -rpy[i];
274 }
275 }
276 }
277
278 Ok(rpy)
279 }
280
281 pub fn get_current_velocity(&mut self) -> Result<[f64; 3]> {
283 let thetas = self.inner.get_current_position()?;
284 let input_velocity = self.inner.get_current_velocity()?;
285
286 let rot = self
287 .kinematics
288 .compute_output_velocity(thetas, input_velocity);
289 Ok(rot.into())
290 }
291 pub fn get_current_torque(&mut self) -> Result<[f64; 3]> {
293 let thetas = self.inner.get_current_position()?;
294 let input_torque = self.inner.get_current_torque()?;
295
296 Ok(self
297 .kinematics
298 .compute_output_torque(thetas, input_torque)
299 .into())
300 }
301
302 pub fn get_target_orientation(&mut self) -> Result<[f64; 4]> {
304 let thetas = self.inner.get_target_position()?;
305 let rot = self.kinematics.compute_forward_kinematics(thetas);
306 Ok(conversion::rotation_matrix_to_quaternion(rot))
307 }
308
309 pub fn get_target_rpy_orientation(&mut self) -> Result<[f64; 3]> {
311 let thetas = self.inner.get_target_position()?;
312 let rpy = self
313 .kinematics
314 .compute_forward_kinematics_rpy_multiturn(thetas)?;
315 Ok(rpy)
316 }
317
318 pub fn set_target_orientation(&mut self, target: [f64; 4]) -> Result<()> {
320 let rot =
321 conversion::quaternion_to_rotation_matrix(target[0], target[1], target[2], target[3]);
322 let thetas = self.kinematics.compute_inverse_kinematics(rot)?;
323 self.inner.set_target_position(thetas)
324 }
325
326 pub fn set_target_rpy_orientation(&mut self, target: [f64; 3]) -> Result<()> {
328 let inverted_axes = self.inner.output_inverted_axes();
329 let mut rpy_target = target;
330
331 for i in 0..3 {
332 if let Some(inverted) = inverted_axes[i] {
333 if inverted {
334 rpy_target[i] = -rpy_target[i];
335 }
336 }
337 }
338
339 let thetas = self
340 .kinematics
341 .compute_inverse_kinematics_rpy_multiturn(rpy_target)?;
342
343 self.inner.set_target_position(thetas)
344 }
345
346 pub fn set_target_orientation_fb(&mut self, target: [f64; 4]) -> Result<Orbita3dFeedback> {
348 let rot =
349 conversion::quaternion_to_rotation_matrix(target[0], target[1], target[2], target[3]);
350
351 let thetas = self.kinematics.compute_inverse_kinematics(rot)?;
352
353 let fb: Result<[f64; 3]> = self.inner.set_target_position_fb(thetas);
354 match fb {
355 Ok(fb) => {
356 let rot = self
357 .kinematics
358 .compute_forward_kinematics([fb[0], fb[1], fb[2]]); Ok(Orbita3dFeedback {
367 orientation: conversion::rotation_matrix_to_quaternion(rot),
368 })
371 }
372 Err(e) => Err(e),
373 }
374 }
375
376 pub fn set_target_rpy_orientation_fb(&mut self, target: [f64; 3]) -> Result<[f64; 3]> {
378 let inverted_axes = self.inner.output_inverted_axes();
379 let mut rpy_target = target;
380
381 for i in 0..3 {
382 if let Some(inverted) = inverted_axes[i] {
383 if inverted {
384 rpy_target[i] = -rpy_target[i];
385 }
386 }
387 }
388
389 let thetas = self
390 .kinematics
391 .compute_inverse_kinematics_rpy_multiturn(rpy_target)?;
392 let fb: Result<[f64; 3]> = self.inner.set_target_position_fb(thetas);
393
394 match fb {
395 Ok(fb) => {
396 let mut rpy = self
397 .kinematics
398 .compute_forward_kinematics_rpy_multiturn([fb[0], fb[1], fb[2]])?;
399 for i in 0..3 {
400 if let Some(inverted) = inverted_axes[i] {
401 if inverted {
402 rpy[i] = -rpy[i];
403 }
404 }
405 }
406 Ok(rpy)
407 }
408
409 Err(e) => Err(e),
410 }
411 }
412
413 pub fn get_raw_motors_positions(&mut self) -> Result<[f64; 3]> {
416 let red = self.inner.reduction();
417 match self.inner.get_current_position() {
418 Ok(p) => {
419 let mut pos = p;
420 for i in 0..3 {
421 pos[i] *= red[i].unwrap();
422 }
423 Ok(pos)
424 }
425 Err(e) => Err(e),
426 }
427 }
428
429 pub fn get_raw_motors_velocity_limit(&mut self) -> Result<[f64; 3]> {
432 self.inner.get_velocity_limit()
433 }
434 pub fn set_raw_motors_velocity_limit(&mut self, limit: [f64; 3]) -> Result<()> {
437 self.inner.set_velocity_limit(limit)
438 }
439 pub fn get_raw_motors_torque_limit(&mut self) -> Result<[f64; 3]> {
442 self.inner.get_torque_limit()
443 }
444 pub fn set_raw_motors_torque_limit(&mut self, limit: [f64; 3]) -> Result<()> {
447 self.inner.set_torque_limit(limit)
448 }
449 pub fn get_raw_motors_pid_gains(&mut self) -> Result<[PID; 3]> {
452 self.inner.get_pid_gains()
453 }
454 pub fn set_raw_motors_pid_gains(&mut self, gains: [PID; 3]) -> Result<()> {
457 self.inner.set_pid_gains(gains)
458 }
459
460 pub fn get_raw_motors_velocity(&mut self) -> Result<[f64; 3]> {
462 let red = self.inner.reduction();
463 match self.inner.get_current_velocity() {
464 Ok(v) => {
465 let mut vel = v;
466 for i in 0..3 {
467 vel[i] *= red[i].unwrap();
468 }
469 Ok(vel)
470 }
471 Err(e) => Err(e),
472 }
473 }
474 pub fn get_raw_motors_current(&mut self) -> Result<[f64; 3]> {
476 let red = self.inner.reduction();
477 match self.inner.get_current_torque() {
478 Ok(t) => {
479 let mut tor = t;
480 for i in 0..3 {
481 tor[i] /= red[i].unwrap();
482 }
483 Ok(tor)
484 }
485 Err(e) => Err(e),
486 }
487 }
488
489 pub fn get_axis_sensors(&mut self) -> Result<[f64; 3]> {
491 let red = self.inner.reduction();
492 match self.inner.get_axis_sensors() {
493 Ok(a) => {
494 let mut ax = a;
495 for i in 0..3 {
496 ax[i] *= red[i].unwrap();
497 }
498 Ok(ax)
499 }
500 Err(e) => Err(e),
501 }
502 }
503
504 pub fn get_axis_sensor_zeros(&mut self) -> Result<[f64; 3]> {
506 self.inner.get_axis_sensor_zeros()
507 }
508
509 pub fn get_error_codes(&mut self) -> Result<[i32; 3]> {
511 self.inner.get_error_codes()
512 }
513
514 pub fn get_motor_temperatures(&mut self) -> Result<[f64; 3]> {
516 self.inner.get_motor_temperatures()
517 }
518
519 pub fn get_board_temperatures(&mut self) -> Result<[f64; 3]> {
521 self.inner.get_board_temperatures()
522 }
523
524 pub fn get_board_state(&mut self) -> Result<u8> {
526 self.inner.get_board_state()
527 }
528 pub fn set_board_state(&mut self, state: u8) -> Result<()> {
530 self.inner.set_board_state(state)
531 }
532
533 pub fn get_control_mode(&mut self) -> Result<[u8; 3]> {
535 self.inner.get_control_mode()
536 }
537
538 pub fn set_control_mode(&mut self, mode: [u8; 3]) -> Result<()> {
540 self.inner.set_control_mode(mode)
541 }
542
543 pub fn emergency_stop(&mut self) {
545 self.inner.emergency_stop()
546 }
547}