use crate::core::scalar::ControlScalar;
pub struct Jacobian2R<S: ControlScalar> {
pub l1: S,
pub l2: S,
}
impl<S: ControlScalar> Jacobian2R<S> {
pub fn new(l1: S, l2: S) -> Self {
Self { l1, l2 }
}
pub fn compute(&self, q1: S, q2: S) -> [[S; 2]; 2] {
let s1 = q1.sin();
let c1 = q1.cos();
let s12 = (q1 + q2).sin();
let c12 = (q1 + q2).cos();
[
[-self.l1 * s1 - self.l2 * s12, -self.l2 * s12],
[self.l1 * c1 + self.l2 * c12, self.l2 * c12],
]
}
pub fn determinant(&self, _q1: S, q2: S) -> S {
self.l1 * self.l2 * q2.sin()
}
pub fn apply(&self, q1: S, q2: S, dq1: S, dq2: S) -> (S, S) {
let j = self.compute(q1, q2);
let vx = j[0][0] * dq1 + j[0][1] * dq2;
let vy = j[1][0] * dq1 + j[1][1] * dq2;
(vx, vy)
}
pub fn pseudo_inverse(&self, q1: S, q2: S) -> Option<[[S; 2]; 2]> {
let j = self.compute(q1, q2);
let det = j[0][0] * j[1][1] - j[0][1] * j[1][0];
if det.abs() < S::from_f64(1e-6) {
return None;
}
let inv_det = S::ONE / det;
Some([
[j[1][1] * inv_det, -j[0][1] * inv_det],
[-j[1][0] * inv_det, j[0][0] * inv_det],
])
}
pub fn ik_step(&self, q1: S, q2: S, dx: S, dy: S) -> Option<(S, S)> {
let jp = self.pseudo_inverse(q1, q2)?;
let dq1 = jp[0][0] * dx + jp[0][1] * dy;
let dq2 = jp[1][0] * dx + jp[1][1] * dy;
Some((dq1, dq2))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn jacobian_2r_at_zero_config() {
let j = Jacobian2R::new(1.0_f64, 1.0);
let jac = j.compute(0.0, 0.0);
assert!(jac[0][0].abs() < 1e-10);
assert!(jac[0][1].abs() < 1e-10);
assert!((jac[1][0] - 2.0).abs() < 1e-10);
assert!((jac[1][1] - 1.0).abs() < 1e-10);
}
#[test]
fn determinant_formula() {
let j = Jacobian2R::new(1.0_f64, 1.0);
let det = j.determinant(0.5, core::f64::consts::PI / 2.0);
assert!((det - 1.0).abs() < 1e-10);
}
#[test]
fn singularity_at_zero_q2() {
let j = Jacobian2R::new(1.0_f64, 1.0);
let result = j.pseudo_inverse(0.5, 0.0);
assert!(result.is_none());
}
#[test]
fn pseudo_inverse_roundtrip() {
let j = Jacobian2R::new(1.0_f64, 0.5);
let q2 = core::f64::consts::PI / 3.0;
let jp = j.pseudo_inverse(0.3, q2).unwrap();
let jac = j.compute(0.3, q2);
let ij00 = jac[0][0] * jp[0][0] + jac[0][1] * jp[1][0];
let ij11 = jac[1][0] * jp[0][1] + jac[1][1] * jp[1][1];
assert!((ij00 - 1.0).abs() < 1e-10, "ij00={}", ij00);
assert!((ij11 - 1.0).abs() < 1e-10, "ij11={}", ij11);
}
}