Skip to main content

dynamics_joint/
prismatic.rs

1//! Prismatic joint, constraining two objects to translate along a given axis.
2
3use std::fmt::Display;
4
5use dynamics_spatial::{
6    configuration::Configuration,
7    motion::{SpatialMotion, SpatialRotation},
8    se3::SE3,
9    vector3d::Vector3D,
10};
11use rand::rngs::ThreadRng;
12
13use crate::{
14    joint::{JointModel, JointType, JointWrapper},
15    joint_data::{JointData, JointDataWrapper},
16    limits::JointLimits,
17};
18
19/// Model of a prismatic joint.
20///
21/// This joint constraints two objects to translate along a given axis.
22#[derive(Clone, Debug)]
23pub struct JointModelPrismatic {
24    /// The axis of translation expressed in the local frame of the joint.
25    pub axis: SpatialMotion,
26    /// The joint limits.
27    pub limits: JointLimits,
28    /// The (null) bias of the joint.
29    pub bias: SpatialMotion,
30}
31
32impl JointModelPrismatic {
33    /// Creates a new `JointModelPrismatic` with the given axis of translation and unbounded limits.
34    ///
35    /// The axis of translation should be normalized and expressed in the local frame of the joint.
36    ///
37    /// # Arguments
38    ///
39    /// * `axis` - The normalized axis of translation expressed in the local frame of the joint.
40    ///
41    /// # Returns
42    /// A new `JointModelPrismatic` object.
43    #[must_use]
44    pub fn new(axis: Vector3D) -> Self {
45        JointModelPrismatic {
46            axis: SpatialMotion::from_translational_axis(&axis),
47            limits: JointLimits::new_unbounded(1),
48            bias: SpatialMotion::zero(),
49        }
50    }
51
52    /// Creates a new prismatic joint model with `x` as axis of translation.
53    ///
54    /// # Returns
55    /// A new `JointModelPrismatic` object.
56    #[must_use]
57    pub fn new_px() -> Self {
58        Self::new(Vector3D::x())
59    }
60
61    /// Creates a new prismatic joint model with `y` as axis of translation.
62    ///
63    /// # Returns
64    /// A new `JointModelPrismatic` object.
65    #[must_use]
66    pub fn new_py() -> Self {
67        Self::new(Vector3D::y())
68    }
69
70    /// Creates a new prismatic joint model with `z` as axis of translation.
71    ///
72    /// # Returns
73    /// A new `JointModelPrismatic` object.
74    #[must_use]
75    pub fn new_pz() -> Self {
76        Self::new(Vector3D::z())
77    }
78}
79
80impl JointModel for JointModelPrismatic {
81    fn get_joint_type(&self) -> JointType {
82        JointType::Prismatic
83    }
84
85    fn nq(&self) -> usize {
86        1
87    }
88
89    fn nv(&self) -> usize {
90        1
91    }
92
93    fn neutral(&self) -> Configuration {
94        Configuration::from_row_slice(&[0.0])
95    }
96
97    fn create_joint_data(&self) -> JointDataWrapper {
98        JointDataWrapper::prismatic(JointDataPrismatic::new())
99    }
100
101    fn get_axis(&self) -> &SpatialMotion {
102        &self.axis
103    }
104
105    fn random_configuration(&self, rng: &mut ThreadRng) -> Configuration {
106        Configuration::random(
107            1,
108            rng,
109            &self.limits.min_configuration,
110            &self.limits.max_configuration,
111        )
112    }
113
114    fn subspace(&self, v: &Configuration) -> SpatialMotion {
115        assert_eq!(
116            v.len(),
117            1,
118            "Prismatic joint model expects a single velocity value."
119        );
120        SpatialMotion::from_translational_axis(&(v[0] * self.axis.translation()))
121    }
122
123    fn subspace_dual(&self, f: &dynamics_spatial::force::SpatialForce) -> Configuration {
124        Configuration::from_row_slice(&[f.translation().dot(&self.axis.translation())])
125    }
126
127    fn subspace_se3(&self, se3: &SE3) -> SpatialMotion {
128        let trans = se3.translation();
129        let v = trans.dot(&self.axis.translation()) * self.axis.translation();
130        SpatialMotion::from_translational_axis(&v)
131    }
132
133    fn bias(&self) -> &SpatialMotion {
134        &self.bias
135    }
136
137    fn integrate(&self, q: &Configuration, v: &Configuration) -> Configuration {
138        Configuration::from_row_slice(&[q[0] + v[0]])
139    }
140}
141
142impl Display for JointModelPrismatic {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(f, "JointModelPrismatic(axis: {:?})", self.axis.as_slice())
145    }
146}
147
148/// Data associated to a prismatic joint.
149#[derive(Clone, Debug)]
150pub struct JointDataPrismatic {
151    /// The joint configuration value (translation along the axis).
152    pub joint_q: Configuration,
153    /// The joint velocity value (velocity along the axis).
154    pub joint_v: Configuration,
155    /// The placement of the joint in the local frame.
156    pub placement: SE3,
157    /// The joint velocity as a spatial motion.
158    pub joint_velocity: SpatialMotion,
159}
160
161impl JointDataPrismatic {
162    /// Creates a new `JointDataPrismatic` associated to the given joint model.
163    ///
164    /// # Arguments
165    ///
166    /// * `model` - The prismatic joint model.
167    ///
168    /// # Returns
169    /// A new `JointDataPrismatic` object.
170    #[must_use]
171    pub fn new() -> Self {
172        JointDataPrismatic {
173            joint_q: Configuration::zeros(1),
174            joint_v: Configuration::zeros(1),
175            placement: SE3::identity(),
176            joint_velocity: SpatialMotion::zero(),
177        }
178    }
179}
180
181impl Default for JointDataPrismatic {
182    fn default() -> Self {
183        JointDataPrismatic::new()
184    }
185}
186
187impl JointData for JointDataPrismatic {
188    fn get_joint_placement(&self) -> SE3 {
189        self.placement
190    }
191
192    fn get_joint_q(&self) -> &Configuration {
193        &self.joint_q
194    }
195
196    fn get_joint_v(&self) -> &Configuration {
197        &self.joint_v
198    }
199
200    fn update(
201        &mut self,
202        joint_model: &JointWrapper,
203        joint_q: &Configuration,
204        joint_v: Option<&Configuration>,
205    ) {
206        assert_eq!(
207            joint_q.len(),
208            1,
209            "Prismatic joint data update expects a single position value."
210        );
211        if let Some(joint_v) = joint_v {
212            assert_eq!(
213                joint_v.len(),
214                1,
215                "Prismatic joint model expects a single velocity value."
216            );
217        }
218
219        // store q and v
220        self.joint_q = joint_q.clone();
221        if let Some(joint_v) = joint_v {
222            self.joint_v = joint_v.clone();
223            self.joint_velocity = self.joint_v[0] * joint_model.get_axis().clone();
224        }
225
226        self.placement = SE3::from_parts(
227            joint_model.get_axis().translation() * joint_q[0],
228            SpatialRotation::identity(),
229        );
230    }
231
232    fn get_joint_velocity(&self) -> &SpatialMotion {
233        &self.joint_velocity
234    }
235}