rapier3d/dynamics/joint/multibody_joint/
multibody_joint.rs

1use crate::dynamics::solver::JointGenericOneBodyConstraint;
2use crate::dynamics::{
3    FixedJointBuilder, GenericJoint, IntegrationParameters, Multibody, MultibodyLink,
4    RigidBodyVelocity, joint,
5};
6use crate::math::{
7    ANG_DIM, DIM, Isometry, JacobianViewMut, Real, Rotation, SPATIAL_DIM, SpacialVector,
8    Translation, Vector,
9};
10use na::{DVector, DVectorViewMut};
11#[cfg(feature = "dim3")]
12use na::{UnitQuaternion, Vector3};
13
14#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
15#[derive(Copy, Clone, Debug)]
16/// An joint attached to two bodies based on the reduced coordinates formalism.
17pub struct MultibodyJoint {
18    /// The joint’s description.
19    pub data: GenericJoint,
20    /// Is the joint a kinematic joint?
21    ///
22    /// Kinematic joint velocities are never changed by the physics engine. This gives the user
23    /// total control over the values of their degrees of freedoms.
24    pub kinematic: bool,
25    pub(crate) coords: SpacialVector<Real>,
26    pub(crate) joint_rot: Rotation<Real>,
27}
28
29impl MultibodyJoint {
30    /// Creates a new multibody joint from its description.
31    pub fn new(data: GenericJoint, kinematic: bool) -> Self {
32        Self {
33            data,
34            kinematic,
35            coords: na::zero(),
36            joint_rot: Rotation::identity(),
37        }
38    }
39
40    pub(crate) fn free(pos: Isometry<Real>) -> Self {
41        let mut result = Self::new(GenericJoint::default(), false);
42        result.set_free_pos(pos);
43        result
44    }
45
46    pub(crate) fn fixed(pos: Isometry<Real>) -> Self {
47        Self::new(
48            FixedJointBuilder::new().local_frame1(pos).build().into(),
49            false,
50        )
51    }
52
53    pub(crate) fn set_free_pos(&mut self, pos: Isometry<Real>) {
54        self.coords
55            .fixed_rows_mut::<DIM>(0)
56            .copy_from(&pos.translation.vector);
57        self.joint_rot = pos.rotation;
58    }
59
60    // pub(crate) fn local_joint_rot(&self) -> &Rotation<Real> {
61    //     &self.joint_rot
62    // }
63
64    fn num_free_lin_dofs(&self) -> usize {
65        let locked_bits = self.data.locked_axes.bits();
66        DIM - (locked_bits & ((1 << DIM) - 1)).count_ones() as usize
67    }
68
69    /// The number of degrees of freedom allowed by the multibody_joint.
70    pub fn ndofs(&self) -> usize {
71        SPATIAL_DIM - self.data.locked_axes.bits().count_ones() as usize
72    }
73
74    /// The position of the multibody link containing this multibody_joint relative to its parent.
75    pub fn body_to_parent(&self) -> Isometry<Real> {
76        let locked_bits = self.data.locked_axes.bits();
77        let mut transform = self.joint_rot * self.data.local_frame2.inverse();
78
79        for i in 0..DIM {
80            if (locked_bits & (1 << i)) == 0 {
81                transform = Translation::from(Vector::ith(i, self.coords[i])) * transform;
82            }
83        }
84
85        self.data.local_frame1 * transform
86    }
87
88    /// Integrate the position of this multibody_joint.
89    #[profiling::function]
90    pub fn integrate(&mut self, dt: Real, vels: &[Real]) {
91        let locked_bits = self.data.locked_axes.bits();
92        let mut curr_free_dof = 0;
93
94        for i in 0..DIM {
95            if (locked_bits & (1 << i)) == 0 {
96                self.coords[i] += vels[curr_free_dof] * dt;
97                curr_free_dof += 1;
98            }
99        }
100
101        let locked_ang_bits = locked_bits >> DIM;
102        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
103        match num_free_ang_dofs {
104            0 => { /* No free dofs. */ }
105            1 => {
106                let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
107                self.coords[DIM + dof_id] += vels[curr_free_dof] * dt;
108                #[cfg(feature = "dim2")]
109                {
110                    self.joint_rot = Rotation::new(self.coords[DIM + dof_id]);
111                }
112                #[cfg(feature = "dim3")]
113                {
114                    self.joint_rot = Rotation::from_axis_angle(
115                        &Vector::ith_axis(dof_id),
116                        self.coords[DIM + dof_id],
117                    );
118                }
119            }
120            2 => {
121                todo!()
122            }
123            #[cfg(feature = "dim3")]
124            3 => {
125                let angvel = Vector3::from_row_slice(&vels[curr_free_dof..curr_free_dof + 3]);
126                let disp = UnitQuaternion::new_eps(angvel * dt, 0.0);
127                self.joint_rot = disp * self.joint_rot;
128                self.coords[3] += angvel[0] * dt;
129                self.coords[4] += angvel[1] * dt;
130                self.coords[5] += angvel[2] * dt;
131            }
132            _ => unreachable!(),
133        }
134    }
135
136    /// Apply a displacement to the multibody_joint.
137    pub fn apply_displacement(&mut self, disp: &[Real]) {
138        self.integrate(1.0, disp);
139    }
140
141    /// Sets in `out` the non-zero entries of the multibody_joint jacobian transformed by `transform`.
142    pub fn jacobian(&self, transform: &Rotation<Real>, out: &mut JacobianViewMut<Real>) {
143        let locked_bits = self.data.locked_axes.bits();
144        let mut curr_free_dof = 0;
145
146        for i in 0..DIM {
147            if (locked_bits & (1 << i)) == 0 {
148                let transformed_axis = transform * Vector::ith(i, 1.0);
149                out.fixed_view_mut::<DIM, 1>(0, curr_free_dof)
150                    .copy_from(&transformed_axis);
151                curr_free_dof += 1;
152            }
153        }
154
155        let locked_ang_bits = locked_bits >> DIM;
156        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
157        match num_free_ang_dofs {
158            0 => { /* No free dofs. */ }
159            1 => {
160                #[cfg(feature = "dim2")]
161                {
162                    out[(DIM, curr_free_dof)] = 1.0;
163                }
164
165                #[cfg(feature = "dim3")]
166                {
167                    let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
168                    let rotmat = transform.to_rotation_matrix().into_inner();
169                    out.fixed_view_mut::<ANG_DIM, 1>(DIM, curr_free_dof)
170                        .copy_from(&rotmat.column(dof_id));
171                }
172            }
173            2 => {
174                todo!()
175            }
176            #[cfg(feature = "dim3")]
177            3 => {
178                let rotmat = transform.to_rotation_matrix();
179                out.fixed_view_mut::<3, 3>(3, curr_free_dof)
180                    .copy_from(rotmat.matrix());
181            }
182            _ => unreachable!(),
183        }
184    }
185
186    /// Multiply the multibody_joint jacobian by generalized velocities to obtain the
187    /// relative velocity of the multibody link containing this multibody_joint.
188    pub fn jacobian_mul_coordinates(&self, acc: &[Real]) -> RigidBodyVelocity {
189        let locked_bits = self.data.locked_axes.bits();
190        let mut result = RigidBodyVelocity::zero();
191        let mut curr_free_dof = 0;
192
193        for i in 0..DIM {
194            if (locked_bits & (1 << i)) == 0 {
195                result.linvel += Vector::ith(i, acc[curr_free_dof]);
196                curr_free_dof += 1;
197            }
198        }
199
200        let locked_ang_bits = locked_bits >> DIM;
201        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
202        match num_free_ang_dofs {
203            0 => { /* No free dofs. */ }
204            1 => {
205                #[cfg(feature = "dim2")]
206                {
207                    result.angvel += acc[curr_free_dof];
208                }
209                #[cfg(feature = "dim3")]
210                {
211                    let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
212                    result.angvel[dof_id] += acc[curr_free_dof];
213                }
214            }
215            2 => {
216                todo!()
217            }
218            #[cfg(feature = "dim3")]
219            3 => {
220                let angvel = Vector3::from_row_slice(&acc[curr_free_dof..curr_free_dof + 3]);
221                result.angvel += angvel;
222            }
223            _ => unreachable!(),
224        }
225        result
226    }
227
228    /// Fill `out` with the non-zero entries of a damping that can be applied by default to ensure a good stability of the multibody_joint.
229    pub fn default_damping(&self, out: &mut DVectorViewMut<Real>) {
230        let locked_bits = self.data.locked_axes.bits();
231        let mut curr_free_dof = self.num_free_lin_dofs();
232
233        // A default damping only for the angular dofs
234        for i in DIM..SPATIAL_DIM {
235            if locked_bits & (1 << i) == 0 {
236                // This is a free angular DOF.
237                out[curr_free_dof] = 0.1;
238                curr_free_dof += 1;
239            }
240        }
241    }
242
243    /// Maximum number of velocity constrains that can be generated by this multibody_joint.
244    pub fn num_velocity_constraints(&self) -> usize {
245        let locked_bits = self.data.locked_axes.bits();
246        let limit_bits = self.data.limit_axes.bits();
247        let motor_bits = self.data.motor_axes.bits();
248        let mut num_constraints = 0;
249
250        for i in 0..SPATIAL_DIM {
251            if (locked_bits & (1 << i)) == 0 {
252                if (limit_bits & (1 << i)) != 0 {
253                    num_constraints += 1;
254                }
255                if (motor_bits & (1 << i)) != 0 {
256                    num_constraints += 1;
257                }
258            }
259        }
260
261        num_constraints
262    }
263
264    /// Initialize and generate velocity constraints to enforce, e.g., multibody_joint limits and motors.
265    pub fn velocity_constraints(
266        &self,
267        params: &IntegrationParameters,
268        multibody: &Multibody,
269        link: &MultibodyLink,
270        mut j_id: usize,
271        jacobians: &mut DVector<Real>,
272        constraints: &mut [JointGenericOneBodyConstraint],
273    ) -> usize {
274        let j_id = &mut j_id;
275        let locked_bits = self.data.locked_axes.bits();
276        let limit_bits = self.data.limit_axes.bits();
277        let motor_bits = self.data.motor_axes.bits();
278        let mut num_constraints = 0;
279        let mut curr_free_dof = 0;
280
281        for i in 0..DIM {
282            if (locked_bits & (1 << i)) == 0 {
283                let limits = if (limit_bits & (1 << i)) != 0 {
284                    Some([self.data.limits[i].min, self.data.limits[i].max])
285                } else {
286                    None
287                };
288
289                if (motor_bits & (1 << i)) != 0 {
290                    joint::unit_joint_motor_constraint(
291                        params,
292                        multibody,
293                        link,
294                        &self.data.motors[i],
295                        self.coords[i],
296                        limits,
297                        curr_free_dof,
298                        j_id,
299                        jacobians,
300                        constraints,
301                        &mut num_constraints,
302                    );
303                }
304
305                if (limit_bits & (1 << i)) != 0 {
306                    joint::unit_joint_limit_constraint(
307                        params,
308                        multibody,
309                        link,
310                        [self.data.limits[i].min, self.data.limits[i].max],
311                        self.coords[i],
312                        curr_free_dof,
313                        j_id,
314                        jacobians,
315                        constraints,
316                        &mut num_constraints,
317                    );
318                }
319                curr_free_dof += 1;
320            }
321        }
322
323        /*
324        let locked_ang_bits = locked_bits >> DIM;
325        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
326        match num_free_ang_dofs {
327            0 => { /* No free dofs. */ }
328            1 => {}
329            2 => {
330                todo!()
331            }
332            3 => {}
333            _ => unreachable!(),
334        }
335         */
336        // TODO: we should make special cases for multi-angular-dofs limits/motors
337        for i in DIM..SPATIAL_DIM {
338            if (locked_bits & (1 << i)) == 0 {
339                let limits = if (limit_bits & (1 << i)) != 0 {
340                    let limits = [self.data.limits[i].min, self.data.limits[i].max];
341                    joint::unit_joint_limit_constraint(
342                        params,
343                        multibody,
344                        link,
345                        limits,
346                        self.coords[i],
347                        curr_free_dof,
348                        j_id,
349                        jacobians,
350                        constraints,
351                        &mut num_constraints,
352                    );
353                    Some(limits)
354                } else {
355                    None
356                };
357
358                if (motor_bits & (1 << i)) != 0 {
359                    joint::unit_joint_motor_constraint(
360                        params,
361                        multibody,
362                        link,
363                        &self.data.motors[i],
364                        self.coords[i],
365                        limits,
366                        curr_free_dof,
367                        j_id,
368                        jacobians,
369                        constraints,
370                        &mut num_constraints,
371                    );
372                }
373                curr_free_dof += 1;
374            }
375        }
376
377        num_constraints
378    }
379}