rapier3d/dynamics/joint/multibody_joint/
multibody_joint.rs1use crate::dynamics::solver::GenericJointConstraint;
2use crate::dynamics::{
3 FixedJointBuilder, GenericJoint, IntegrationParameters, Multibody, MultibodyLink,
4 RigidBodyVelocity, joint,
5};
6use crate::math::{
7 ANG_DIM, DIM, DVector, JacobianViewMut, Pose, Real, Rotation, SPATIAL_DIM, SpatialVector,
8 Vector,
9};
10use parry::math::VectorExt;
11
12#[cfg(feature = "dim2")]
13use crate::math::rotation_from_angle;
14#[cfg(feature = "dim3")]
15use crate::utils::RotationOps;
16use crate::utils::vect_to_na;
17use na::DVectorViewMut;
18
19#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
20#[derive(Copy, Clone, Debug)]
21pub struct MultibodyJoint {
23 pub data: GenericJoint,
25 pub kinematic: bool,
30 pub(crate) coords: SpatialVector,
31 pub(crate) joint_rot: Rotation,
32 pub(crate) spring_stiffness: SpatialVector,
35 pub(crate) spring_ref: SpatialVector,
39}
40
41impl MultibodyJoint {
42 pub fn new(data: GenericJoint, kinematic: bool) -> Self {
44 Self {
45 data,
46 kinematic,
47 coords: Default::default(),
48 joint_rot: Rotation::IDENTITY,
49 spring_stiffness: Default::default(),
50 spring_ref: Default::default(),
51 }
52 }
53
54 pub fn set_spring(&mut self, axis: usize, stiffness: Real, rest: Real) {
58 self.spring_stiffness[axis] = stiffness;
59 self.spring_ref[axis] = rest;
60 }
61
62 pub fn spring(&self, axis: usize) -> (Real, Real) {
65 (self.spring_stiffness[axis], self.spring_ref[axis])
66 }
67
68 pub(crate) fn free(pos: Pose) -> Self {
69 let mut result = Self::new(GenericJoint::default(), false);
70 result.set_free_pos(pos);
71 result
72 }
73
74 pub(crate) fn fixed(pos: Pose) -> Self {
75 Self::new(
76 FixedJointBuilder::new().local_frame1(pos).build().into(),
77 false,
78 )
79 }
80
81 pub(crate) fn set_free_pos(&mut self, pos: Pose) {
82 #[cfg(feature = "dim2")]
83 {
84 self.coords.x = pos.translation.x;
85 self.coords.y = pos.translation.y;
86 }
87 #[cfg(feature = "dim3")]
88 {
89 self.coords[0] = pos.translation.x;
90 self.coords[1] = pos.translation.y;
91 self.coords[2] = pos.translation.z;
92 }
93 self.joint_rot = pos.rotation;
94 }
95
96 pub fn joint_rot(&self) -> Rotation {
98 self.joint_rot
99 }
100
101 fn num_free_lin_dofs(&self) -> usize {
102 let locked_bits = self.data.locked_axes.bits();
103 DIM - (locked_bits & ((1 << DIM) - 1)).count_ones() as usize
104 }
105
106 pub fn coords(&self) -> SpatialVector {
108 self.coords
109 }
110
111 pub fn ndofs(&self) -> usize {
113 SPATIAL_DIM - self.data.locked_axes.bits().count_ones() as usize
114 }
115
116 pub fn body_to_parent(&self) -> Pose {
118 let locked_bits = self.data.locked_axes.bits();
119 let mut transform = Pose::from_rotation(self.joint_rot) * self.data.local_frame2.inverse();
120
121 for i in 0..DIM {
122 if (locked_bits & (1 << i)) == 0 {
123 let translation = Vector::ith(i, self.coords[i]);
125 transform = Pose::from_translation(translation) * transform;
126 }
127 }
128
129 self.data.local_frame1 * transform
130 }
131
132 #[profiling::function]
134 pub fn integrate(&mut self, dt: Real, vels: &[Real]) {
135 let locked_bits = self.data.locked_axes.bits();
136 let mut curr_free_dof = 0;
137
138 for i in 0..DIM {
139 if (locked_bits & (1 << i)) == 0 {
140 self.coords[i] += vels[curr_free_dof] * dt;
141 curr_free_dof += 1;
142 }
143 }
144
145 let locked_ang_bits = locked_bits >> DIM;
146 let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
147 match num_free_ang_dofs {
148 0 => { }
149 1 => {
150 let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
151 self.coords[DIM + dof_id] += vels[curr_free_dof] * dt;
152 #[cfg(feature = "dim2")]
153 {
154 self.joint_rot = rotation_from_angle(self.coords[DIM + dof_id]);
155 }
156 #[cfg(feature = "dim3")]
157 {
158 self.joint_rot = Rotation::from_axis_angle(
159 Vector::ith(dof_id, 1.0),
160 self.coords[DIM + dof_id],
161 );
162 }
163 }
164 2 => {
165 todo!()
166 }
167 #[cfg(feature = "dim3")]
168 3 => {
169 let angvel = Vector::from_slice(&vels[curr_free_dof..curr_free_dof + 3]);
170 let disp = Rotation::from_scaled_axis(angvel * dt);
171 self.joint_rot = disp * self.joint_rot;
172 self.coords[3] += angvel[0] * dt;
173 self.coords[4] += angvel[1] * dt;
174 self.coords[5] += angvel[2] * dt;
175 }
176 _ => unreachable!(),
177 }
178 }
179
180 pub fn apply_displacement(&mut self, disp: &[Real]) {
182 self.integrate(1.0, disp);
183 }
184
185 pub fn jacobian(&self, transform: &Rotation, out: &mut JacobianViewMut<Real>) {
187 let locked_bits = self.data.locked_axes.bits();
188 let mut curr_free_dof = 0;
189
190 for i in 0..DIM {
191 if (locked_bits & (1 << i)) == 0 {
192 let transformed_axis = (*transform) * Vector::ith(i, 1.0);
193 out.fixed_view_mut::<DIM, 1>(0, curr_free_dof)
194 .copy_from(&vect_to_na(transformed_axis));
195 curr_free_dof += 1;
196 }
197 }
198
199 let locked_ang_bits = locked_bits >> DIM;
200 let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
201 match num_free_ang_dofs {
202 0 => { }
203 1 => {
204 #[cfg(feature = "dim2")]
205 {
206 out[(DIM, curr_free_dof)] = 1.0;
207 }
208
209 #[cfg(feature = "dim3")]
210 {
211 let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
212 let rotmat = transform.to_mat();
213 out.fixed_view_mut::<ANG_DIM, 1>(DIM, curr_free_dof)
214 .copy_from_slice(rotmat.col(dof_id).as_ref());
215 }
216 }
217 2 => {
218 todo!()
219 }
220 #[cfg(feature = "dim3")]
221 3 => {
222 let rotmat = transform.to_mat();
223 out.fixed_view_mut::<3, 3>(3, curr_free_dof)
224 .copy_from_slice(rotmat.as_ref());
225 }
226 _ => unreachable!(),
227 }
228 }
229
230 pub fn jacobian_mul_coordinates(&self, acc: &[Real]) -> RigidBodyVelocity<Real> {
233 let locked_bits = self.data.locked_axes.bits();
234 let mut result = RigidBodyVelocity::zero();
235 let mut curr_free_dof = 0;
236
237 for i in 0..DIM {
238 if (locked_bits & (1 << i)) == 0 {
239 result.linvel += Vector::ith(i, acc[curr_free_dof]);
240 curr_free_dof += 1;
241 }
242 }
243
244 let locked_ang_bits = locked_bits >> DIM;
245 let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
246 match num_free_ang_dofs {
247 0 => { }
248 1 => {
249 #[cfg(feature = "dim2")]
250 {
251 result.angvel += acc[curr_free_dof];
252 }
253 #[cfg(feature = "dim3")]
254 {
255 let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
256 result.angvel[dof_id] += acc[curr_free_dof];
257 }
258 }
259 2 => {
260 todo!()
261 }
262 #[cfg(feature = "dim3")]
263 3 => {
264 let angvel = Vector::from_slice(&acc[curr_free_dof..curr_free_dof + 3]);
265 result.angvel += angvel;
266 }
267 _ => unreachable!(),
268 }
269 result
270 }
271
272 pub fn default_damping(&self, out: &mut DVectorViewMut<Real>) {
274 let locked_bits = self.data.locked_axes.bits();
275 let mut curr_free_dof = self.num_free_lin_dofs();
276
277 for i in DIM..SPATIAL_DIM {
279 if locked_bits & (1 << i) == 0 {
280 out[curr_free_dof] = 0.1;
282 curr_free_dof += 1;
283 }
284 }
285 }
286
287 pub fn num_velocity_constraints(&self) -> usize {
289 let locked_bits = self.data.locked_axes.bits();
290 let limit_bits = self.data.limit_axes.bits();
291 let motor_bits = self.data.motor_axes.bits();
292 let mut num_constraints = 0;
293
294 for i in 0..SPATIAL_DIM {
295 if (locked_bits & (1 << i)) == 0 {
296 if (limit_bits & (1 << i)) != 0 {
297 num_constraints += 1;
298 }
299 if (motor_bits & (1 << i)) != 0 {
300 num_constraints += 1;
301 }
302 }
303 }
304
305 num_constraints
306 }
307
308 pub fn velocity_constraints(
310 &self,
311 params: &IntegrationParameters,
312 multibody: &Multibody,
313 link: &MultibodyLink,
314 mut j_id: usize,
315 jacobians: &mut DVector,
316 constraints: &mut [GenericJointConstraint],
317 ) -> usize {
318 let j_id = &mut j_id;
319 let locked_bits = self.data.locked_axes.bits();
320 let limit_bits = self.data.limit_axes.bits();
321 let motor_bits = self.data.motor_axes.bits();
322 let mut num_constraints = 0;
323 let mut curr_free_dof = 0;
324
325 for i in 0..DIM {
326 if (locked_bits & (1 << i)) == 0 {
327 let limits = if (limit_bits & (1 << i)) != 0 {
328 Some([self.data.limits[i].min, self.data.limits[i].max])
329 } else {
330 None
331 };
332
333 if (motor_bits & (1 << i)) != 0 {
334 joint::unit_joint_motor_constraint(
335 params,
336 multibody,
337 link,
338 &self.data.motors[i],
339 self.coords[i],
340 limits,
341 curr_free_dof,
342 j_id,
343 jacobians,
344 constraints,
345 &mut num_constraints,
346 );
347 }
348
349 if (limit_bits & (1 << i)) != 0 {
350 joint::unit_joint_limit_constraint(
351 params,
352 multibody,
353 link,
354 [self.data.limits[i].min, self.data.limits[i].max],
355 self.coords[i],
356 curr_free_dof,
357 j_id,
358 jacobians,
359 constraints,
360 &mut num_constraints,
361 self.data.softness,
362 );
363 }
364 curr_free_dof += 1;
365 }
366 }
367
368 for i in DIM..SPATIAL_DIM {
383 if (locked_bits & (1 << i)) == 0 {
384 let limits = if (limit_bits & (1 << i)) != 0 {
385 let limits = [self.data.limits[i].min, self.data.limits[i].max];
386 joint::unit_joint_limit_constraint(
387 params,
388 multibody,
389 link,
390 limits,
391 self.coords[i],
392 curr_free_dof,
393 j_id,
394 jacobians,
395 constraints,
396 &mut num_constraints,
397 self.data.softness,
398 );
399 Some(limits)
400 } else {
401 None
402 };
403
404 if (motor_bits & (1 << i)) != 0 {
405 joint::unit_joint_motor_constraint(
406 params,
407 multibody,
408 link,
409 &self.data.motors[i],
410 self.coords[i],
411 limits,
412 curr_free_dof,
413 j_id,
414 jacobians,
415 constraints,
416 &mut num_constraints,
417 );
418 }
419 curr_free_dof += 1;
420 }
421 }
422
423 num_constraints
424 }
425}