dynamics_joint/
revolute.rs1use std::fmt::Display;
4
5use crate::{
6 joint::{JointModel, JointType, JointWrapper},
7 joint_data::{JointData, JointDataWrapper},
8 limits::JointLimits,
9};
10use dynamics_spatial::{
11 configuration::Configuration,
12 motion::{SpatialMotion, SpatialRotation},
13 se3::SE3,
14 vector3d::Vector3D,
15};
16use rand::rngs::ThreadRng;
17
18#[derive(Clone, Debug)]
22pub struct JointModelRevolute {
23 pub axis: SpatialMotion,
25 pub limits: JointLimits,
27 pub bias: SpatialMotion,
29}
30
31impl JointModelRevolute {
32 #[must_use]
43 pub fn new(axis: Vector3D) -> Self {
44 JointModelRevolute {
45 axis: SpatialMotion::from_rotational_axis(&axis),
46 limits: JointLimits::new_unbounded(1),
47 bias: SpatialMotion::zero(),
48 }
49 }
50
51 #[must_use]
56 pub fn new_rx() -> Self {
57 Self::new(Vector3D::x())
58 }
59
60 #[must_use]
65 pub fn new_ry() -> Self {
66 Self::new(Vector3D::y())
67 }
68
69 #[must_use]
74 pub fn new_rz() -> Self {
75 Self::new(Vector3D::z())
76 }
77}
78
79impl JointModel for JointModelRevolute {
80 fn get_joint_type(&self) -> JointType {
81 JointType::Revolute
82 }
83
84 fn nq(&self) -> usize {
85 1
86 }
87
88 fn nv(&self) -> usize {
89 1
90 }
91
92 fn neutral(&self) -> Configuration {
93 Configuration::zeros(1)
94 }
95
96 fn create_joint_data(&self) -> JointDataWrapper {
97 JointDataWrapper::revolute(JointDataRevolute::new())
98 }
99
100 fn get_axis(&self) -> &SpatialMotion {
101 &self.axis
102 }
103
104 fn random_configuration(&self, rng: &mut ThreadRng) -> Configuration {
105 Configuration::random(
106 1,
107 rng,
108 &self.limits.min_configuration,
109 &self.limits.max_configuration,
110 )
111 }
112
113 fn subspace(&self, v: &Configuration) -> SpatialMotion {
114 assert_eq!(
115 v.len(),
116 1,
117 "Revolute joint model expects a single velocity value."
118 );
119 SpatialMotion::from_rotational_axis(&(v[0] * self.axis.rotation()))
120 }
121
122 fn subspace_dual(&self, f: &dynamics_spatial::force::SpatialForce) -> Configuration {
123 Configuration::from_row_slice(&[f.rotation().dot(&self.axis.rotation())])
124 }
125
126 fn subspace_se3(&self, se3: &SE3) -> SpatialMotion {
127 let rot = se3.rotation();
128 let v = rot * &self.axis.rotation();
129 SpatialMotion::from_rotational_axis(&v)
130 }
131
132 fn bias(&self) -> &SpatialMotion {
133 &self.bias
134 }
135
136 fn integrate(&self, q: &Configuration, v: &Configuration) -> Configuration {
137 Configuration::from_row_slice(&[q[0] + v[0]])
138 }
139}
140
141impl Display for JointModelRevolute {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 write!(f, "JointModelRevolute(axis: {:?})", self.axis.as_slice())
144 }
145}
146
147#[derive(Debug, Clone)]
149pub struct JointDataRevolute {
150 pub joint_q: Configuration,
152 pub joint_v: Configuration,
154 pub placement: SE3,
156 pub joint_velocity: SpatialMotion,
158}
159
160impl JointDataRevolute {
161 #[must_use]
170 pub fn new() -> Self {
171 JointDataRevolute {
172 joint_q: Configuration::zeros(1),
173 joint_v: Configuration::zeros(1),
174 placement: SE3::identity(),
175 joint_velocity: SpatialMotion::zero(),
176 }
177 }
178}
179
180impl Default for JointDataRevolute {
181 fn default() -> Self {
182 JointDataRevolute::new()
183 }
184}
185
186impl JointData for JointDataRevolute {
187 fn get_joint_q(&self) -> &Configuration {
188 &self.joint_q
189 }
190
191 fn get_joint_v(&self) -> &Configuration {
192 &self.joint_v
193 }
194
195 fn get_joint_placement(&self) -> SE3 {
196 self.placement
197 }
198
199 fn update(
200 &mut self,
201 joint_model: &JointWrapper,
202 joint_q: &Configuration,
203 joint_v: Option<&Configuration>,
204 ) {
205 assert_eq!(
206 joint_q.len(),
207 1,
208 "Revolute joint model expects a single angle."
209 );
210 if let Some(joint_v) = joint_v {
211 assert_eq!(
212 joint_v.len(),
213 1,
214 "Revolute joint model expects a single velocity value."
215 );
216 }
217
218 self.joint_q = joint_q.clone();
220 if let Some(joint_v) = joint_v {
221 self.joint_v = joint_v.clone();
222 self.joint_velocity = self.joint_v[0] * joint_model.get_axis().clone();
223 }
224
225 let rot =
226 SpatialRotation::from_axis_angle(&joint_model.get_axis().rotation(), self.joint_q[0]);
227 self.placement = rot.to_se3(&Vector3D::zeros());
228 }
229
230 fn get_joint_velocity(&self) -> &SpatialMotion {
231 &self.joint_velocity
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use approx::assert_relative_eq;
238
239 use super::*;
240
241 #[test]
242 fn test_joint_model_revolute() {
243 let joint = JointModelRevolute::new(Vector3D::new(1.0, 0.0, 0.0));
244 assert_eq!(joint.get_joint_type(), JointType::Revolute);
245 assert_eq!(joint.nq(), 1);
246 assert_eq!(joint.nv(), 1);
247 assert_eq!(joint.neutral(), Configuration::zeros(1));
248 let _ = joint.create_joint_data();
249 let _ = joint.get_axis();
250 let _ = joint.random_configuration(&mut rand::rng());
251 }
252
253 #[test]
254 fn test_joint_data_revolute_xaxis() {
255 let joint_model = JointModelRevolute::new(Vector3D::new(1.0, 0.0, 0.0));
256 let mut joint_data = joint_model.create_joint_data();
257 let q = Configuration::ones(1);
258 joint_data.update(&JointWrapper::revolute(joint_model), &q, None);
259
260 assert_relative_eq!(joint_data.get_joint_placement().rotation().angle(), q[0]);
261 assert_eq!(
262 joint_data.get_joint_placement().translation(),
263 Vector3D::zeros()
264 );
265 }
266}