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],
54 pub motor_gearbox_params: Option<MotorGearboxConfig>,
55 pub default_mode: Option<u8>,
56}
57
58#[derive(Debug, Deserialize, Serialize)]
59pub struct MotorGearboxConfig {
61 pub motor_gearbox_ratio: f64,
63 pub motor_nominal_current: f64,
64 pub motor_nominal_torque: f64,
65 pub motor_efficiency: f64,
66 pub motor_gearbox_efficiency: f64,
67}
68
69#[derive(Debug, Deserialize, Serialize)]
70pub struct DisksConfig {
72 pub zeros: ZeroType,
74 pub reduction: f64,
76}
77
78#[derive(Debug, Deserialize, Serialize)]
79pub enum ZeroType {
82 ApproximateHardwareZero(ApproximateHardwareZero),
84 ZeroStartup(ZeroStartup),
86 HallZero(HallZero),
88 FirmwareZero(FirmwareZero),
90}
91
92#[derive(Debug, Deserialize, Serialize)]
93pub struct FirmwareZero;
95
96#[derive(Debug, Deserialize, Serialize)]
97pub struct ApproximateHardwareZero {
99 pub hardware_zero: [f64; 3],
101}
102
103#[derive(Debug, Deserialize, Serialize)]
104pub struct HallZero {
106 pub hardware_zero: [f64; 3],
108
109 pub hall_indice: [u8; 3],
111}
112
113#[derive(Debug, Deserialize, Serialize)]
114pub struct ZeroStartup;
116
117pub struct Orbita3dController {
119 inner: Box<dyn MotorsController<3> + Send>,
120 pub kinematics: Orbita3dKinematicsModel,
121}
122
123#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
124pub struct Orbita3dFeedback {
126 pub orientation: [f64; 4],
127}
128
129impl Orbita3dController {
130 pub fn with_config(configfile: &str) -> Result<Self> {
132 log::info!("Loading config file: {}", configfile);
133
134 let f = match std::fs::File::open(configfile) {
135 Ok(f) => f,
136 Err(e) => {
137 log::error!("Error opening config file: {}", e);
138 return Err(e.into());
139 }
140 };
141 let config: Orbita3dConfig = serde_yaml::from_reader(f)?;
142
143 let controller: Box<dyn MotorsController<3> + Send> = match config.io {
144 #[cfg(feature = "build_dynamixel")]
146 Orbita3dIOConfig::DynamixelSerial(dxl_config) => match dxl_config.use_cache {
147 true => {
148 let controller = CachedDynamixelSerialController::new(
149 &dxl_config.serial_port,
150 dxl_config.id,
151 config.disks.zeros,
152 config.disks.reduction,
153 )?;
154
155 log::info!("Using cached dynamixel controller {:?}", controller);
156
157 Box::new(controller)
158 }
159 false => {
160 let controller = DynamixelSerialController::new(
161 &dxl_config.serial_port,
162 dxl_config.id,
163 config.disks.zeros,
164 config.disks.reduction,
165 )?;
166
167 log::info!("Using dynamixel controller {:?}", controller);
168
169 Box::new(controller)
170 }
171 },
172 #[cfg(feature = "build_dynamixel")]
174 Orbita3dIOConfig::DynamixelPoulpe(dxl_config) => match dxl_config.use_cache {
175 true => {
176 let controller = CachedDynamixelPoulpeController::new(
177 &dxl_config.serial_port,
178 dxl_config.id,
179 config.disks.zeros,
180 config.disks.reduction,
181 )?;
182
183 log::info!("Using cached poulpe dynamixel controller {:?}", controller);
184
185 Box::new(controller)
186 }
187 false => {
188 let controller = DynamixelPoulpeController::new(
189 &dxl_config.serial_port,
190 dxl_config.id,
191 config.disks.zeros,
192 config.disks.reduction,
193 )?;
194
195 log::info!("Using poulpe dynamixel controller {:?}", controller);
196
197 Box::new(controller)
198 }
199 },
200 Orbita3dIOConfig::FakeMotors(_) => {
202 let mut controller = FakeMotorsController::<3>::new();
203
204 let offsets = match config.disks.zeros {
205 ZeroType::ApproximateHardwareZero(zero) => zero.hardware_zero,
206 ZeroType::ZeroStartup(_) => controller.get_current_position()?,
207 ZeroType::HallZero(zero) => zero.hardware_zero,
208 ZeroType::FirmwareZero(_) => [0.0, 0.0, 0.0],
209 };
210
211 let controller = controller
212 .with_offsets(offsets.map(Some))
213 .with_reduction([Some(config.disks.reduction); 3])
214 .with_inverted_axes(config.inverted_axes);
215
216 log::info!("Using fake motors controller {:?}", controller);
217
218 Box::new(controller)
219 }
220 #[cfg(feature = "build_ethercat")]
222 Orbita3dIOConfig::PoulpeEthercat(ethercat_config) => {
223 let controller = EthercatPoulpeController::new(
224 ðercat_config.url,
225 ethercat_config.id,
226 ethercat_config.name,
227 config.disks.zeros,
228 config.disks.reduction,
229 config.inverted_axes,
230 config.motor_gearbox_params,
231 config.default_mode,
232 )?;
233 log::info!("Using poulpe ethercat controller {:?}", controller);
234
235 Box::new(controller)
236 }
237 };
238
239 Ok(Self {
240 inner: controller,
241 kinematics: config.kinematics_model,
242 })
243 }
244}
245
246impl Orbita3dController {
247 pub fn is_torque_on(&mut self) -> Result<bool> {
249 let torques = self.inner.is_torque_on()?;
250 assert!(torques.iter().all(|&t| t == torques[0]));
251 Ok(torques[0])
252 }
253 pub fn enable_torque(&mut self, reset_target: bool) -> Result<()> {
258 if !self.is_torque_on()? && reset_target {
259 let thetas = self.inner.get_current_position()?;
260 thread::sleep(Duration::from_millis(1));
261 self.inner.set_target_position(thetas)?;
262 thread::sleep(Duration::from_millis(1));
263 }
264 self.inner.set_torque([true; 3])
265 }
266 pub fn disable_torque(&mut self) -> Result<()> {
268 self.inner.set_torque([false; 3])
269 }
270
271 pub fn get_current_orientation(&mut self) -> Result<[f64; 4]> {
273 let thetas = self.inner.get_current_position()?;
274 let mut rot = self.kinematics.compute_forward_kinematics(thetas);
275
276 let inverted_axes = self.inner.output_inverted_axes();
279 let mut euler = conversion::matrix_to_intrinsic_roll_pitch_yaw(rot);
280 for i in 0..3 {
281 if let Some(inverted) = inverted_axes[i] {
282 if inverted {
283 euler[i] = -euler[i];
284 }
285 }
286 }
287 rot = conversion::intrinsic_roll_pitch_yaw_to_matrix(euler[0], euler[1], euler[2]);
288 Ok(conversion::rotation_matrix_to_quaternion(rot))
289 }
290
291 pub fn get_current_rpy_orientation(&mut self) -> Result<[f64; 3]> {
293 let thetas = self.inner.get_current_position()?;
294 let inverted_axes = self.inner.output_inverted_axes();
295 let mut rpy = self
296 .kinematics
297 .compute_forward_kinematics_rpy_multiturn(thetas)?;
298
299 for i in 0..3 {
300 if let Some(inverted) = inverted_axes[i] {
301 if inverted {
302 rpy[i] = -rpy[i];
303 }
304 }
305 }
306
307 Ok(rpy)
308 }
309
310 pub fn get_current_velocity(&mut self) -> Result<[f64; 3]> {
312 let thetas = self.inner.get_current_position()?;
313 let input_velocity = self.inner.get_current_velocity()?;
314
315 let mut vel = self
316 .kinematics
317 .compute_output_velocity(thetas, input_velocity);
318
319 let inverted_axes = self.inner.output_inverted_axes();
324 for i in 0..3 {
325 if let Some(inverted) = inverted_axes[i] {
326 if inverted {
327 vel[i] = -vel[i];
328 }
329 }
330 }
331
332 Ok(vel.into())
333 }
334 pub fn get_current_torque(&mut self) -> Result<[f64; 3]> {
336 let thetas = self.inner.get_current_position()?;
337 let input_torque = self.inner.get_current_torque()?;
338
339 let mut torque = self.kinematics.compute_output_torque(thetas, input_torque);
340
341 let inverted_axes = self.inner.output_inverted_axes();
346 for i in 0..3 {
347 if let Some(inverted) = inverted_axes[i] {
348 if inverted {
349 torque[i] = -torque[i];
350 }
351 }
352 }
353
354 if let Some(ratio) = self.inner.torque_current_ratio() {
356 torque.iter_mut().for_each(|t| *t *= ratio);
357 Ok(torque.into())
358 } else {
359 Ok(torque.into())
360 }
361 }
362
363 pub fn get_target_orientation(&mut self) -> Result<[f64; 4]> {
365 let thetas = self.inner.get_target_position()?;
366 let mut rot = self.kinematics.compute_forward_kinematics(thetas);
367 let inverted_axes = self.inner.output_inverted_axes();
370 let mut euler = conversion::matrix_to_intrinsic_roll_pitch_yaw(rot);
371 for i in 0..3 {
372 if let Some(inverted) = inverted_axes[i] {
373 if inverted {
374 euler[i] = -euler[i];
375 }
376 }
377 }
378 rot = conversion::intrinsic_roll_pitch_yaw_to_matrix(euler[0], euler[1], euler[2]);
379 Ok(conversion::rotation_matrix_to_quaternion(rot))
380 }
381
382 pub fn get_target_rpy_orientation(&mut self) -> Result<[f64; 3]> {
384 let thetas = self.inner.get_target_position()?;
385 let mut rpy = self
386 .kinematics
387 .compute_forward_kinematics_rpy_multiturn(thetas)?;
388
389 let inverted_axes = self.inner.output_inverted_axes();
391 for i in 0..3 {
392 if let Some(inverted) = inverted_axes[i] {
393 if inverted {
394 rpy[i] = -rpy[i];
395 }
396 }
397 }
398 Ok(rpy)
399 }
400
401 pub fn set_target_orientation(&mut self, target: [f64; 4]) -> Result<()> {
403 let mut rot =
404 conversion::quaternion_to_rotation_matrix(target[0], target[1], target[2], target[3]);
405
406 let inverted_axes = self.inner.output_inverted_axes();
409 let mut euler = conversion::matrix_to_intrinsic_roll_pitch_yaw(rot);
410 for i in 0..3 {
411 if let Some(inverted) = inverted_axes[i] {
412 if inverted {
413 euler[i] = -euler[i];
414 }
415 }
416 }
417 rot = conversion::intrinsic_roll_pitch_yaw_to_matrix(euler[0], euler[1], euler[2]);
418
419 let thetas = self.kinematics.compute_inverse_kinematics(rot)?;
420 self.inner.set_target_position(thetas)
421 }
422
423 pub fn set_target_rpy_orientation(&mut self, target: [f64; 3]) -> Result<()> {
425 let inverted_axes = self.inner.output_inverted_axes();
426 let mut rpy_target = target;
427
428 for i in 0..3 {
429 if let Some(inverted) = inverted_axes[i] {
430 if inverted {
431 rpy_target[i] = -rpy_target[i];
432 }
433 }
434 }
435
436 let thetas = self
437 .kinematics
438 .compute_inverse_kinematics_rpy_multiturn(rpy_target)?;
439
440 self.inner.set_target_position(thetas)
441 }
442
443 pub fn set_target_orientation_fb(&mut self, target: [f64; 4]) -> Result<Orbita3dFeedback> {
445 let rot =
446 conversion::quaternion_to_rotation_matrix(target[0], target[1], target[2], target[3]);
447
448 let thetas = self.kinematics.compute_inverse_kinematics(rot)?;
449
450 let fb: Result<[f64; 3]> = self.inner.set_target_position_fb(thetas);
451 match fb {
452 Ok(fb) => {
453 let rot = self
454 .kinematics
455 .compute_forward_kinematics([fb[0], fb[1], fb[2]]); Ok(Orbita3dFeedback {
464 orientation: conversion::rotation_matrix_to_quaternion(rot),
465 })
468 }
469 Err(e) => Err(e),
470 }
471 }
472
473 pub fn set_target_rpy_orientation_fb(&mut self, target: [f64; 3]) -> Result<[f64; 3]> {
475 let inverted_axes = self.inner.output_inverted_axes();
476 let mut rpy_target = target;
477
478 for i in 0..3 {
479 if let Some(inverted) = inverted_axes[i] {
480 if inverted {
481 rpy_target[i] = -rpy_target[i];
482 }
483 }
484 }
485
486 let thetas = self
487 .kinematics
488 .compute_inverse_kinematics_rpy_multiturn(rpy_target)?;
489 let fb: Result<[f64; 3]> = self.inner.set_target_position_fb(thetas);
490
491 match fb {
492 Ok(fb) => {
493 let mut rpy = self
494 .kinematics
495 .compute_forward_kinematics_rpy_multiturn([fb[0], fb[1], fb[2]])?;
496 for i in 0..3 {
497 if let Some(inverted) = inverted_axes[i] {
498 if inverted {
499 rpy[i] = -rpy[i];
500 }
501 }
502 }
503 Ok(rpy)
504 }
505
506 Err(e) => Err(e),
507 }
508 }
509
510 pub fn get_raw_motors_positions(&mut self) -> Result<[f64; 3]> {
513 let red = self.inner.reduction();
514 match self.inner.get_current_position() {
515 Ok(p) => {
516 let mut pos = p;
517 for i in 0..3 {
518 pos[i] *= red[i].unwrap();
519 }
520 Ok(pos)
521 }
522 Err(e) => Err(e),
523 }
524 }
525
526 pub fn get_raw_motors_velocity_limit(&mut self) -> Result<[f64; 3]> {
529 self.inner.get_velocity_limit()
530 }
531 pub fn set_raw_motors_velocity_limit(&mut self, limit: [f64; 3]) -> Result<()> {
534 self.inner.set_velocity_limit(limit)
535 }
536 pub fn get_raw_motors_torque_limit(&mut self) -> Result<[f64; 3]> {
539 self.inner.get_torque_limit()
540 }
541 pub fn set_raw_motors_torque_limit(&mut self, limit: [f64; 3]) -> Result<()> {
544 self.inner.set_torque_limit(limit)
545 }
546 pub fn get_raw_motors_pid_gains(&mut self) -> Result<[PID; 3]> {
549 self.inner.get_pid_gains()
550 }
551 pub fn set_raw_motors_pid_gains(&mut self, gains: [PID; 3]) -> Result<()> {
554 self.inner.set_pid_gains(gains)
555 }
556
557 pub fn get_raw_motors_velocity(&mut self) -> Result<[f64; 3]> {
559 let red = self.inner.reduction();
560 match self.inner.get_current_velocity() {
561 Ok(v) => {
562 let mut vel = v;
563 for i in 0..3 {
564 vel[i] *= red[i].unwrap();
565 }
566 Ok(vel)
567 }
568 Err(e) => Err(e),
569 }
570 }
571 pub fn get_raw_motors_current(&mut self) -> Result<[f64; 3]> {
573 let red = self.inner.reduction();
574 match self.inner.get_current_torque() {
575 Ok(t) => {
576 let mut tor = t;
577 for i in 0..3 {
578 tor[i] /= red[i].unwrap();
579 }
580 Ok(tor)
581 }
582 Err(e) => Err(e),
583 }
584 }
585
586 pub fn get_axis_sensors(&mut self) -> Result<[f64; 3]> {
588 let red = self.inner.reduction();
589 match self.inner.get_axis_sensors() {
590 Ok(a) => {
591 let mut ax = a;
592 for i in 0..3 {
593 ax[i] *= red[i].unwrap();
594 }
595 Ok(ax)
596 }
597 Err(e) => Err(e),
598 }
599 }
600
601 pub fn get_axis_sensor_zeros(&mut self) -> Result<[f64; 3]> {
603 self.inner.get_axis_sensor_zeros()
604 }
605
606 pub fn get_error_codes(&mut self) -> Result<[i32; 3]> {
608 self.inner.get_error_codes()
609 }
610
611 pub fn get_motor_temperatures(&mut self) -> Result<[f64; 3]> {
613 self.inner.get_motor_temperatures()
614 }
615
616 pub fn get_board_temperatures(&mut self) -> Result<[f64; 3]> {
618 self.inner.get_board_temperatures()
619 }
620
621 pub fn get_board_state(&mut self) -> Result<u8> {
623 self.inner.get_board_state()
624 }
625 pub fn set_board_state(&mut self, state: u8) -> Result<()> {
627 self.inner.set_board_state(state)
628 }
629
630 pub fn get_control_mode(&mut self) -> Result<[u8; 3]> {
632 self.inner.get_control_mode()
633 }
634
635 pub fn set_control_mode(&mut self, mode: [u8; 3]) -> Result<()> {
637 self.inner.set_control_mode(mode)
638 }
639
640 pub fn emergency_stop(&mut self) {
642 self.inner.emergency_stop()
643 }
644
645 pub fn set_target_velocity(&mut self, target: [f64; 3]) -> Result<()> {
650 let mut target_vel = target;
651 let inverted_axes = self.inner.output_inverted_axes();
653 for i in 0..3 {
654 if let Some(inverted) = inverted_axes[i] {
655 if inverted {
656 target_vel[i] = -target_vel[i];
657 }
658 }
659 }
660
661 let thetas = self.inner.get_current_position()?;
663 let mut theta_vel = self
665 .kinematics
666 .compute_input_velocity(thetas, target_vel.into());
667
668 let red = self.inner.reduction();
670 for i in 0..3 {
671 theta_vel[i] *= red[i].unwrap();
672 }
673
674 self.inner.set_target_velocity(theta_vel)
675 }
676
677 pub fn set_target_torque(&mut self, target: [f64; 3]) -> Result<()> {
707 let mut target_torque = target;
708 let inverted_axes = self.inner.output_inverted_axes();
710 for i in 0..3 {
711 if let Some(inverted) = inverted_axes[i] {
712 if inverted {
713 target_torque[i] = -target_torque[i];
714 }
715 }
716 }
717 let thetas = self.inner.get_current_position()?;
719 let mut theta_torque = self
721 .kinematics
722 .compute_input_torque(thetas, target_torque.into());
723 let red = self.inner.reduction();
725 for i in 0..3 {
726 theta_torque[i] /= red[i].unwrap();
727 }
728
729 self.inner.set_target_torque(theta_torque)
730 }
731
732 }