dynamics_joint/
revolute.rs1use crate::{
4 joint::{JointModel, JointType, JointWrapper},
5 joint_data::{JointData, JointDataWrapper, JointError},
6 limits::JointLimits,
7};
8use dynamics_spatial::{
9 configuration::Configuration,
10 motion::{SpatialMotion, SpatialRotation},
11 se3::SE3,
12 vector3d::Vector3D,
13};
14use rand::rngs::ThreadRng;
15
16#[derive(Clone, Debug)]
20pub struct JointModelRevolute {
21 pub axis: Vector3D,
23 pub limits: JointLimits,
25}
26
27impl JointModelRevolute {
28 #[must_use]
37 pub fn new(axis: Vector3D) -> Self {
38 JointModelRevolute {
39 axis,
40 limits: JointLimits::new_unbounded(1),
41 }
42 }
43
44 #[must_use]
49 pub fn new_rx() -> Self {
50 Self::new(Vector3D::x())
51 }
52
53 #[must_use]
58 pub fn new_ry() -> Self {
59 Self::new(Vector3D::y())
60 }
61
62 #[must_use]
67 pub fn new_rz() -> Self {
68 Self::new(Vector3D::z())
69 }
70}
71
72impl JointModel for JointModelRevolute {
73 fn get_joint_type(&self) -> JointType {
74 JointType::Revolute
75 }
76
77 fn nq(&self) -> usize {
78 1
79 }
80
81 fn nv(&self) -> usize {
82 1
83 }
84
85 fn neutral(&self) -> Configuration {
86 Configuration::zeros(1)
87 }
88
89 fn create_joint_data(&self) -> JointDataWrapper {
90 JointDataWrapper::revolute(JointDataRevolute::new(self))
91 }
92
93 fn get_axis(&self) -> Vec<SpatialMotion> {
94 vec![SpatialMotion::from_rotational_axis(&self.axis)]
95 }
96
97 fn random_configuration(&self, rng: &mut ThreadRng) -> Configuration {
98 Configuration::random(
99 1,
100 rng,
101 &self.limits.min_configuration,
102 &self.limits.max_configuration,
103 )
104 }
105
106 fn subspace(&self, v: &Configuration) -> SpatialMotion {
107 assert_eq!(
108 v.len(),
109 1,
110 "Revolute joint model expects a single velocity value."
111 );
112 v[0] * SpatialMotion::from_rotational_axis(&self.axis)
113 }
114
115 fn subspace_dual(&self, f: &dynamics_spatial::force::SpatialForce) -> Configuration {
116 Configuration::from_row_slice(&[f.rotation().dot(&self.axis)])
117 }
118
119 fn bias(&self) -> SpatialMotion {
120 SpatialMotion::zero()
121 }
122}
123
124#[derive(Debug, Clone)]
126pub struct JointDataRevolute {
127 pub joint_q: Configuration,
129 pub joint_v: Configuration,
131 pub placement: SE3,
133 pub joint_velocity: SpatialMotion,
135}
136
137impl JointDataRevolute {
138 #[must_use]
147 pub fn new(_joint_model: &JointModelRevolute) -> Self {
148 JointDataRevolute {
149 joint_q: Configuration::zeros(1),
150 joint_v: Configuration::zeros(1),
151 placement: SE3::identity(),
152 joint_velocity: SpatialMotion::zero(),
153 }
154 }
155}
156
157impl JointData for JointDataRevolute {
158 fn get_joint_q(&self) -> &Configuration {
159 &self.joint_q
160 }
161
162 fn get_joint_v(&self) -> &Configuration {
163 &self.joint_v
164 }
165
166 fn get_joint_placement(&self) -> SE3 {
167 self.placement
168 }
169
170 fn update(
171 &mut self,
172 joint_model: &JointWrapper,
173 joint_q: &Configuration,
174 joint_v: Option<&Configuration>,
175 ) -> Result<(), JointError> {
176 assert_eq!(
177 joint_q.len(),
178 1,
179 "Revolute joint model expects a single angle."
180 );
181 if let Some(joint_v) = joint_v {
182 assert_eq!(
183 joint_v.len(),
184 1,
185 "Revolute joint model expects a single velocity value."
186 );
187 }
188
189 self.joint_q = joint_q.clone();
191 if let Some(joint_v) = joint_v {
192 self.joint_v = joint_v.clone();
193 self.joint_velocity = self.joint_v[0] * joint_model.get_axis()[0].clone();
194 }
195
196 let axis = match joint_model.get_axis().len() {
197 1 => &joint_model.get_axis()[0],
198 _ => return Err(JointError::MissingAttributeError("axis".to_string())),
199 };
200
201 let rot = SpatialRotation::from_axis_angle(&axis.rotation(), self.joint_q[0]);
202 self.placement = rot.to_se3(&Vector3D::zeros());
203 Ok(())
204 }
205
206 fn get_joint_velocity(&self) -> &SpatialMotion {
207 &self.joint_velocity
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use approx::assert_relative_eq;
214
215 use super::*;
216
217 #[test]
218 fn test_joint_model_revolute() {
219 let joint = JointModelRevolute::new(Vector3D::new(1.0, 0.0, 0.0));
220 assert_eq!(joint.get_joint_type(), JointType::Revolute);
221 assert_eq!(joint.nq(), 1);
222 assert_eq!(joint.nv(), 1);
223 assert_eq!(joint.neutral(), Configuration::zeros(1));
224 let _ = joint.create_joint_data();
225 let _ = joint.get_axis();
226 let _ = joint.random_configuration(&mut rand::rng());
227 }
228
229 #[test]
230 fn test_joint_data_revolute_xaxis() {
231 let joint_model = JointModelRevolute::new(Vector3D::new(1.0, 0.0, 0.0));
232 let mut joint_data = joint_model.create_joint_data();
233 let q = Configuration::ones(1);
234 joint_data
235 .update(&JointWrapper::revolute(joint_model), &q, None)
236 .unwrap();
237
238 assert_relative_eq!(joint_data.get_joint_placement().rotation().angle(), q[0]);
239 assert_eq!(
240 joint_data.get_joint_placement().translation(),
241 Vector3D::zeros()
242 );
243 }
244}