#![allow(non_snake_case)]
use super::ArticulatedTree;
use gizmo_math::spatial::SpatialVector;
use gizmo_math::Vec3;
pub fn compute_aba(tree: &mut ArticulatedTree, gravity: Vec3) {
if tree.links.is_empty() {
return;
}
let n = tree.links.len();
let a_grav = SpatialVector::new(Vec3::ZERO, -gravity);
for i in 0..n {
tree.links[i].S = tree.links[i].compute_motion_subspace();
let x_i = tree.links[i].compute_spatial_transform();
let v_J = tree.links[i].S * tree.links[i].q_dot;
if tree.links[i].parent_index == usize::MAX {
if tree.is_fixed_base {
tree.links[i].v = v_J;
tree.links[i].c = SpatialVector::ZERO;
} else {
let v_parent_transformed = x_i.inverse_transform_motion(tree.base_velocity);
tree.links[i].v = v_parent_transformed + v_J;
tree.links[i].c = tree.links[i].v.cross_motion(v_J);
}
} else {
let p_idx = tree.links[i].parent_index;
let v_parent_transformed = x_i.inverse_transform_motion(tree.links[p_idx].v);
tree.links[i].v = v_parent_transformed + v_J;
tree.links[i].c = tree.links[i].v.cross_motion(v_J);
}
tree.links[i].i_a = tree.links[i].inertia.to_matrix();
tree.links[i].p_a = tree.links[i].v.cross_force(tree.links[i].inertia.mul_vec(tree.links[i].v));
}
for i in (0..n).rev() {
let u = tree.links[i].joint_force - tree.links[i].S.dot(tree.links[i].p_a);
let u_vec = tree.links[i].i_a.mul_vec(tree.links[i].S);
let d_val = tree.links[i].S.dot(u_vec);
tree.links[i].u = u;
tree.links[i].u_vec = u_vec;
tree.links[i].d_val = d_val;
if tree.links[i].parent_index != usize::MAX {
let p_idx = tree.links[i].parent_index;
let x_i = tree.links[i].compute_spatial_transform();
if d_val > 1e-6 {
let U_outer = u_vec.outer_product(u_vec).mul_scalar(1.0 / d_val);
let ia_minus = tree.links[i].i_a - U_outer;
let pa_plus = tree.links[i].p_a + tree.links[i].i_a.mul_vec(tree.links[i].c) + u_vec * (u / d_val);
let rot = x_i.rotation;
let t = x_i.translation;
let tx = gizmo_math::Mat3::from_cols(
Vec3::new(0.0, t.z, -t.y),
Vec3::new(-t.z, 0.0, t.x),
Vec3::new(t.y, -t.x, 0.0)
);
let i_00 = rot * ia_minus.m00 * rot.transpose();
let i_01 = rot * ia_minus.m01 * rot.transpose();
let i_10 = rot * ia_minus.m10 * rot.transpose();
let i_11 = rot * ia_minus.m11 * rot.transpose();
let i_parent_add = gizmo_math::spatial::SpatialMatrix {
m00: i_00 + tx * i_10 + i_01 * tx.transpose() + tx * i_11 * tx.transpose(),
m01: i_01 + tx * i_11,
m10: i_10 + i_11 * tx.transpose(),
m11: i_11,
};
tree.links[p_idx].i_a = tree.links[p_idx].i_a + i_parent_add;
tree.links[p_idx].p_a = tree.links[p_idx].p_a + x_i.transform_force(pa_plus);
}
}
}
for i in 0..n {
let x_i = tree.links[i].compute_spatial_transform();
let a_parent = if tree.links[i].parent_index == usize::MAX {
if tree.is_fixed_base {
x_i.inverse_transform_motion(a_grav) } else {
x_i.inverse_transform_motion(tree.base_acceleration) }
} else {
let p_idx = tree.links[i].parent_index;
x_i.inverse_transform_motion(tree.links[p_idx].a)
};
let a_prime = a_parent + tree.links[i].c;
if tree.links[i].d_val > 1e-6 {
tree.links[i].q_ddot = (tree.links[i].u - tree.links[i].u_vec.dot(a_prime)) / tree.links[i].d_val;
} else {
tree.links[i].q_ddot = 0.0;
}
tree.links[i].a = a_prime + tree.links[i].S * tree.links[i].q_ddot;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::multibody::{ArticulatedLink, JointType};
use gizmo_math::spatial::{SpatialInertia, SpatialMatrix, SpatialVector};
use gizmo_math::{Mat3, Quat, Vec3};
#[test]
fn test_single_pendulum_aba() {
let mut tree = ArticulatedTree::default();
tree.is_fixed_base = true;
let inertia = SpatialInertia::new(
1.0,
Mat3::IDENTITY, Vec3::new(0.0, -1.0, 0.0), );
let link = ArticulatedLink {
parent_index: usize::MAX, joint_type: JointType::Revolute(Vec3::Z),
transform_to_parent: Vec3::ZERO,
rotation_to_parent: Quat::IDENTITY,
inertia,
q: 0.0, q_dot: 0.0,
q_ddot: 0.0,
joint_force: 0.0,
v: SpatialVector::ZERO,
a: SpatialVector::ZERO,
c: SpatialVector::ZERO,
i_a: SpatialMatrix::ZERO,
p_a: SpatialVector::ZERO,
S: SpatialVector::ZERO,
u: 0.0,
d_val: 0.0,
u_vec: SpatialVector::ZERO,
};
tree.links.push(link);
let gravity = Vec3::new(0.0, -9.81, 0.0);
tree.links[0].q = std::f32::consts::PI / 2.0;
compute_aba(&mut tree, gravity);
println!("Pendulum test trace:");
println!(" Gravity: {:?}", gravity);
println!(" tree.links[0].d_val = {}", tree.links[0].d_val);
println!(" tree.links[0].u_vec = {:?}", tree.links[0].u_vec);
println!(" tree.links[0].u = {}", tree.links[0].u);
println!(" tree.links[0].p_a = {:?}", tree.links[0].p_a);
println!(" tree.links[0].i_a = {:?}", tree.links[0].i_a);
println!(" tree.links[0].S = {:?}", tree.links[0].S);
println!(" tree.links[0].a = {:?}", tree.links[0].a);
assert!(tree.links[0].q_ddot.abs() > 0.1, "Pendulum should have angular acceleration under gravity. Got: {}", tree.links[0].q_ddot);
}
}