dynamics_joint/
continuous.rs1use std::fmt::Display;
9
10use dynamics_spatial::{
11 configuration::Configuration,
12 force::SpatialForce,
13 motion::{SpatialMotion, SpatialRotation},
14 se3::SE3,
15 vector3d::Vector3D,
16};
17use rand::Rng;
18
19use crate::{
20 joint::{JointModel, JointType, JointWrapper},
21 joint_data::{JointData, JointDataWrapper},
22 limits::JointLimits,
23};
24
25#[derive(Clone, Debug)]
29pub struct JointModelContinuous {
30 pub axis: SpatialMotion,
32 pub limits: JointLimits,
34 pub bias: SpatialMotion,
36}
37
38impl JointModelContinuous {
39 #[must_use]
50 pub fn new(axis: Vector3D) -> Self {
51 let mut limits = JointLimits::new_unbounded(2);
52 limits.min_configuration[0] = -1.01;
53 limits.max_configuration[0] = 1.01;
54 limits.min_configuration[1] = -1.01;
55 limits.max_configuration[1] = 1.01;
56
57 JointModelContinuous {
58 axis: SpatialMotion::from_rotational_axis(&axis),
59 limits,
60 bias: SpatialMotion::zero(),
61 }
62 }
63
64 #[must_use]
69 pub fn new_rux() -> Self {
70 Self::new(Vector3D::x())
71 }
72
73 #[must_use]
78 pub fn new_ruy() -> Self {
79 Self::new(Vector3D::y())
80 }
81
82 #[must_use]
87 pub fn new_ruz() -> Self {
88 Self::new(Vector3D::z())
89 }
90}
91
92impl JointModel for JointModelContinuous {
93 fn get_joint_type(&self) -> JointType {
94 JointType::Continuous
95 }
96
97 fn nq(&self) -> usize {
98 2
99 }
100
101 fn nv(&self) -> usize {
102 1
103 }
104
105 fn neutral(&self) -> Configuration {
106 Configuration::from_row_slice(&[1.0, 0.0])
107 }
108
109 fn get_axis(&self) -> &SpatialMotion {
110 &self.axis
111 }
112
113 fn create_joint_data(&self) -> crate::joint_data::JointDataWrapper {
114 JointDataWrapper::continuous(JointDataContinuous::new())
115 }
116
117 fn random_configuration(&self, rng: &mut rand::rngs::ThreadRng) -> Configuration {
118 let angle: f64 = rng.random_range(0.0..(2.0 * std::f64::consts::PI));
119 Configuration::from_row_slice(&[angle.cos(), angle.sin()])
120 }
121
122 fn subspace(&self, v: &Configuration) -> SpatialMotion {
123 assert_eq!(
124 v.len(),
125 1,
126 "Continuous joint model expects a single velocity value."
127 );
128 SpatialMotion::from_rotational_axis(&(v[0] * self.axis.rotation()))
129 }
130
131 fn subspace_dual(&self, f: &SpatialForce) -> Configuration {
132 Configuration::from_row_slice(&[f.rotation().dot(&self.axis.rotation())])
133 }
134
135 fn subspace_se3(&self, se3: &SE3) -> SpatialMotion {
136 let rot = se3.rotation();
137 let v = rot * &self.axis.rotation();
138 SpatialMotion::from_rotational_axis(&v)
139 }
140
141 fn bias(&self) -> &SpatialMotion {
142 &self.bias
143 }
144
145 fn integrate(&self, q: &Configuration, v: &Configuration) -> Configuration {
146 let angle = q[1].atan2(q[0]);
148
149 let new_angle = angle + v[0];
151
152 Configuration::from_row_slice(&[new_angle.cos(), new_angle.sin()])
154 }
155}
156
157impl Display for JointModelContinuous {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 write!(f, "JointModelContinuous(axis: {:?})", self.axis.as_slice())
160 }
161}
162
163#[derive(Debug, Clone)]
165pub struct JointDataContinuous {
166 pub joint_q: Configuration,
168 pub joint_v: Configuration,
170 pub placement: SE3,
172 pub joint_velocity: SpatialMotion,
174}
175
176impl Default for JointDataContinuous {
177 fn default() -> Self {
178 JointDataContinuous {
179 joint_q: Configuration::from_row_slice(&[1.0, 0.0]),
180 joint_v: Configuration::from_row_slice(&[0.0]),
181 placement: SE3::identity(),
182 joint_velocity: SpatialMotion::zero(),
183 }
184 }
185}
186
187impl JointDataContinuous {
188 #[must_use]
197 pub fn new() -> Self {
198 JointDataContinuous::default()
199 }
200
201 #[must_use]
203 pub fn cos(&self) -> f64 {
204 self.joint_q[0]
205 }
206
207 #[must_use]
209 pub fn sin(&self) -> f64 {
210 self.joint_q[1]
211 }
212}
213
214impl JointData for JointDataContinuous {
215 fn get_joint_q(&self) -> &Configuration {
216 &self.joint_q
217 }
218
219 fn get_joint_v(&self) -> &Configuration {
220 &self.joint_v
221 }
222
223 fn update(
224 &mut self,
225 joint_model: &JointWrapper,
226 joint_q: &Configuration,
227 joint_v: Option<&Configuration>,
228 ) {
229 assert_eq!(
232 joint_q.len(),
233 2,
234 "Continuous joint model expects two values (cosine and sine)."
235 );
236 if let Some(joint_v) = joint_v {
237 assert_eq!(
238 joint_v.len(),
239 1,
240 "Continuous joint model expects a single velocity value."
241 );
242 }
243
244 self.joint_q = joint_q.clone();
246 if let Some(joint_v) = joint_v {
247 self.joint_v = joint_v.clone();
248 self.joint_velocity = self.joint_v[0] * joint_model.get_axis().clone();
249 }
250
251 let angle = self.sin().atan2(self.cos());
253
254 let rot = SpatialRotation::from_axis_angle(&joint_model.get_axis().rotation(), angle);
256 self.placement = rot.to_se3(&Vector3D::zeros());
257 }
258
259 fn get_joint_placement(&self) -> SE3 {
260 self.placement
261 }
262
263 fn get_joint_velocity(&self) -> &SpatialMotion {
264 &self.joint_velocity
265 }
266}