rs_opw_kinematics/parallelogram.rs
1//! Support for parallelogram mechanism some robots have. Like Tool and Base, Parallelogram
2//! both wraps arround some instance of Kinematics and implements Kinematics itself.
3
4use std::sync::Arc;
5use crate::constraints::Constraints;
6use crate::kinematic_traits::{Joints, Kinematics, Pose, Singularity, Solutions};
7
8/// Parallelogram Mechanism:
9/// The parallelogram mechanism introduces a geometric dependency between two specific joints,
10/// typically to maintain the orientation of the end-effector as the robot arm moves.
11/// This is useful in tasks that require a constant tool orientation, such as welding
12/// or handling objects, ensuring that the tool or end-effector remains level.
13///
14/// The mechanism links two joints, referred to as `joints[driven]` and `joints[coupled]`. The movement
15/// of the driven joint influences the coupled joint, maintaining the orientation of the end-effector
16/// during motion. The scaling factor determines the proportional influence of the driven joint on the
17/// coupled joint.
18///
19/// - **Forward Kinematics**: The coupled joint (`joints[coupled]`) is adjusted based on the driven joint:
20/// `joints[coupled]' = joints[coupled] - scaling * joints[driven]`. This adjustment maintains the correct
21/// alignment of the end-effector.
22/// - **Inverse Kinematics**: The dependency is reversed, adding the influence of the driven joint to the
23/// coupled joint: `joints[coupled]' = joints[coupled] + scaling * joints[driven]`. This ensures accurate
24/// calculation of joint angles to achieve the desired pose and orientation.
25///
26/// The `Parallelogram` structure automatically adjusts `joints[coupled]` based on `joints[driven]` using
27/// a scaling factor to account for the parallelogram mechanism.
28///
29/// # Fields:
30/// - `robot`: The underlying robot's kinematics model used to compute forward and inverse kinematics.
31/// - `scaling`: The factor that determines how much influence `joints[driven]` has on `joints[coupled]`.
32/// - `driven`: The index of the driven joint in the parallelogram mechanism (typically the primary joint).
33/// - `coupled`: The index of the coupled joint in the parallelogram mechanism (the secondary joint influenced by the driven joint).
34///
35/// # Example:
36/// ```rust
37/// use std::sync::Arc;
38/// // As J1 = 0, J2 = 1 and J3 = 2, so it is more clear with J-constants:
39/// use rs_opw_kinematics::kinematic_traits::{J2, J3};
40///
41/// use rs_opw_kinematics::kinematics_impl::OPWKinematics;
42/// use rs_opw_kinematics::parallelogram::Parallelogram;
43/// use rs_opw_kinematics::parameters::opw_kinematics::Parameters;
44///
45/// // Assuming a robot that implements the Kinematics trait
46/// let robot_kinematics = Arc::new(OPWKinematics::new(Parameters::irb2400_10()));
47///
48/// // Create the Parallelogram structure with a scaling factor of 0.5,
49/// // where joints[1] is the driven joint and joints[2] is the coupled joint.
50/// let parallelogram = Parallelogram {
51/// robot: robot_kinematics,
52/// scaling: 1.0, // typically there is 1-to-1 influence between driven and coupled joints
53/// driven: J2, // Joint 2 is most often the driven joint.
54/// coupled: J3, // Joint 3 is most often the coupled joint
55/// };
56/// ```
57/// As Parallelogram accepts and itself implements Kinematics, it is possible to chain multiple
58/// parallelograms if the robot has more than one.
59///
60#[derive(Clone)]
61pub struct Parallelogram {
62 /// The underlying robot's kinematics used for forward and inverse kinematics calculations.
63 pub robot: Arc<dyn Kinematics>,
64
65 /// The scaling factor that determines the proportional influence of `joints[driven]` on `joints[coupled]`.
66 pub scaling: f64,
67
68 /// The index of the driven joint in the parallelogram mechanism (`joints[driven]`).
69 pub driven: usize,
70
71 /// The index of the coupled joint in the parallelogram mechanism (`joints[coupled]`).
72 pub coupled: usize,
73}
74impl Kinematics for Parallelogram {
75 fn inverse(&self, tcp: &Pose) -> Solutions {
76 let mut solutions = self.robot.inverse(tcp);
77
78 // Reversing the influence of driven joint in inverse kinematics
79 solutions.iter_mut().for_each(|x| x[self.coupled] +=
80 self.scaling * x[self.driven]);
81 solutions
82 }
83
84 fn inverse_5dof(&self, tcp: &Pose, j6: f64) -> Solutions {
85 let mut solutions = self.robot.inverse_5dof(tcp, j6);
86
87 // Reversing the influence of driven joint in inverse kinematics
88 solutions.iter_mut().for_each(|x| x[self.coupled] +=
89 self.scaling * x[self.driven]);
90 solutions
91 }
92
93 fn inverse_continuing_5dof(&self, tcp: &Pose, previous: &Joints) -> Solutions {
94 let mut solutions = self.robot.inverse_continuing_5dof(tcp, previous);
95
96 // Reversing the influence of driven joint in inverse kinematics
97 solutions.iter_mut().for_each(|x| x[self.coupled] +=
98 self.scaling * x[self.driven]);
99 solutions
100 }
101
102 fn inverse_continuing(&self, tcp: &Pose, previous: &Joints) -> Solutions {
103 let mut solutions = self.robot.inverse_continuing(tcp, previous);
104
105 // Reversing the influence of driven joint in inverse kinematics
106 solutions.iter_mut().for_each(|x| x[self.coupled] +=
107 self.scaling * x[self.driven]);
108 solutions
109 }
110
111 fn forward(&self, qs: &Joints) -> Pose {
112 let mut joints = *qs;
113 // Adjusting coupled joint based on driven joint in forward kinematics
114 joints[self.coupled] -= self.scaling * joints[self.driven];
115 self.robot.forward(&joints)
116 }
117
118 fn forward_with_joint_poses(&self, joints: &Joints) -> [Pose; 6] {
119 let mut joints = *joints;
120 // Adjusting coupled joint based on driven joint in forward kinematics
121 joints[self.coupled] -= self.scaling * joints[self.driven];
122 self.robot.forward_with_joint_poses(&joints)
123 }
124
125 fn kinematic_singularity(&self, qs: &Joints) -> Option<Singularity> {
126 self.robot.kinematic_singularity(qs)
127 }
128
129 fn constraints(&self) -> &Option<Constraints> {
130 self.robot.constraints()
131 }
132}