nabled_dynamics/
spatial.rs1use nabled_core::scalar::NabledReal;
4use nabled_model::joint::{JointAxis, JointType};
5use nabled_model::link::InertialSpec;
6use ndarray::{Array1, Array2, ArrayView1};
7
8pub fn from_inertial_spec<T: NabledReal>(spec: &InertialSpec<T>) -> SpatialInertia<T> {
10 SpatialInertia {
11 mass: spec.mass,
12 com: ndarray::arr1(&spec.com),
13 inertia: spec.inertia.clone(),
14 }
15}
16
17pub fn spatial_inertia_6x6<T: NabledReal>(spec: &InertialSpec<T>) -> Array2<T> {
19 let m = spec.mass;
20 let c = ndarray::arr1(&spec.com);
21 let i3 = &spec.inertia;
22 let cx = nabled_linalg::geometry::so3::hat(&c.view());
23 let mcx = cx.mapv(|v| v * m);
24 let top_left = i3 + &mcx.t().dot(&cx);
25 let top_right = mcx.t();
26 let bottom_left = mcx.clone();
27 let mut spatial = Array2::<T>::zeros((6, 6));
28 for i in 0..3 {
29 for j in 0..3 {
30 spatial[[i, j]] = top_left[[i, j]];
31 spatial[[i, j + 3]] = top_right[[i, j]];
32 spatial[[i + 3, j]] = bottom_left[[i, j]];
33 spatial[[i + 3, j + 3]] = if i == j { m } else { T::zero() };
34 }
35 }
36 spatial
37}
38
39pub fn spatial_inertia_apply<T: NabledReal>(
41 inertia: &Array2<T>,
42 v: &ArrayView1<'_, T>,
43) -> Array1<T> {
44 inertia.dot(v)
45}
46
47pub fn spatial_inertia_transform<T: NabledReal>(
49 inertia: &Array2<T>,
50 adjoint: &Array2<T>,
51) -> Array2<T> {
52 adjoint.t().dot(inertia).dot(adjoint)
53}
54
55pub fn joint_motion_subspace<T: NabledReal>(joint_type: JointType, axis: JointAxis) -> Array1<T> {
57 let unit = axis.unit_vector::<T>();
58 let mut s = Array1::<T>::zeros(6);
59 match joint_type {
60 JointType::Revolute => {
61 s[0] = unit[0];
62 s[1] = unit[1];
63 s[2] = unit[2];
64 }
65 JointType::Prismatic => {
66 s[3] = unit[0];
67 s[4] = unit[1];
68 s[5] = unit[2];
69 }
70 JointType::Fixed => {}
71 }
72 s
73}
74
75pub fn spatial_gravity<T: NabledReal>(gravity: &[T; 3]) -> Array1<T> {
77 let mut accel = Array1::<T>::zeros(6);
78 accel[3] = gravity[0];
79 accel[4] = gravity[1];
80 accel[5] = gravity[2];
81 accel
82}
83
84pub fn motion_cross_product<T: NabledReal>(
86 a: &ArrayView1<'_, T>,
87 b: &ArrayView1<'_, T>,
88) -> Array1<T> {
89 nabled_linalg::geometry::twist::motion_cross(a, b)
90}
91
92pub fn force_cross_product<T: NabledReal>(
94 a: &ArrayView1<'_, T>,
95 b: &ArrayView1<'_, T>,
96) -> Array1<T> {
97 nabled_linalg::geometry::twist::force_cross(a, b)
98}
99
100#[derive(Debug, Clone, PartialEq)]
101pub struct SpatialInertia<T> {
102 pub mass: T,
104 pub com: Array1<T>,
106 pub inertia: Array2<T>,
108}
109
110#[cfg(test)]
111mod tests {
112 use approx::assert_relative_eq;
113
114 use super::*;
115
116 #[test]
117 fn point_mass_inertia_diagonal() {
118 let spec = InertialSpec {
119 mass: 2.0_f64,
120 com: [0.5, 0.0, 0.0],
121 inertia: Array2::<f64>::zeros((3, 3)),
122 };
123 let i6 = spatial_inertia_6x6(&spec);
124 assert_relative_eq!(i6[[5, 5]], 2.0, epsilon = 1e-12);
125 assert_relative_eq!(i6[[4, 4]], 2.0, epsilon = 1e-12);
126 }
127
128 #[test]
129 fn spatial_inertia_apply_matches_hand_calc() {
130 let spec = InertialSpec {
131 mass: 1.0_f64,
132 com: [0.0, 0.0, 0.0],
133 inertia: Array2::<f64>::eye(3),
134 };
135 let i6 = spatial_inertia_6x6(&spec);
136 let v = ndarray::arr1(&[1.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
137 let f = spatial_inertia_apply(&i6, &v.view());
138 assert_relative_eq!(f[0], 1.0, epsilon = 1e-12);
139 }
140
141 #[test]
142 fn revolute_z_subspace() {
143 let s = joint_motion_subspace::<f64>(JointType::Revolute, JointAxis::Z);
144 assert_relative_eq!(s[2], 1.0, epsilon = 1e-12);
145 }
146}