franka/robot/target_control/
ik.rs1use std::sync::Arc;
8
9use nalgebra::{Matrix3, SMatrix, SVector, Vector3};
10
11use super::rotation::{log, rotation_of, translation_of};
12use crate::error::{FrankaError, FrankaResult};
13use crate::model::{Frame, Model};
14
15#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct IkOptions {
21 pub damping: f64,
23 pub nullspace_gain: f64,
26 pub iterations: u32,
28 pub tolerance: f64,
30 pub limit_margin: f64,
33 pub max_step: f64,
37}
38
39pub const MAX_POSTURE_RATE: f64 = 0.5;
43
44impl Default for IkOptions {
45 fn default() -> Self {
46 IkOptions {
47 damping: 0.05,
48 nullspace_gain: 1.0,
49 iterations: 3,
50 tolerance: 1e-6,
51 limit_margin: 0.02,
52 max_step: 0.01,
53 }
54 }
55}
56
57impl IkOptions {
58 pub fn validate(&self) -> FrankaResult<()> {
63 let positive = |x: f64| x.is_finite() && x > 0.0;
64 let non_negative = |x: f64| x.is_finite() && x >= 0.0;
65 let valid = positive(self.damping)
66 && positive(self.max_step)
67 && non_negative(self.nullspace_gain)
68 && non_negative(self.tolerance)
69 && non_negative(self.limit_margin)
70 && self.iterations > 0;
71 if !valid {
72 return Err(FrankaError::InvalidArgument(format!(
73 "target control: ik needs a finite, positive damping and max_step, finite and \
74 non-negative nullspace_gain, tolerance and limit_margin, and at least one \
75 iteration, got {self:?}"
76 )));
77 }
78 Ok(())
79 }
80}
81
82pub(super) struct Ik {
84 model: Arc<Model>,
85 options: IkOptions,
86 lower: [f64; 7],
87 upper: [f64; 7],
88 f_t_ee: [f64; 16],
89 ee_t_k: [f64; 16],
90 q: [f64; 7],
91}
92
93impl Ik {
94 pub(super) fn new(
95 model: Arc<Model>,
96 options: IkOptions,
97 limits: ([f64; 7], [f64; 7]),
98 q0: [f64; 7],
99 f_t_ee: [f64; 16],
100 ee_t_k: [f64; 16],
101 ) -> Self {
102 let mut lower = limits.0.map(|l| l + options.limit_margin);
103 let mut upper = limits.1.map(|u| u - options.limit_margin);
104 for i in 0..7 {
105 if lower[i] > upper[i] {
107 lower[i] = 0.5 * (limits.0[i] + limits.1[i]);
108 upper[i] = lower[i];
109 }
110 }
111 Ik {
112 model,
113 options,
114 lower,
115 upper,
116 f_t_ee,
117 ee_t_k,
118 q: std::array::from_fn(|i| q0[i].clamp(lower[i], upper[i])),
119 }
120 }
121
122 pub(super) fn step(
128 &mut self,
129 pose: &[f64; 16],
130 posture: &[f64; 7],
131 dt: f64,
132 ) -> ([f64; 7], f64) {
133 let p_des = Vector3::from(translation_of(pose));
134 let r_des = rotation_of(pose);
135 let posture = SVector::<f64, 7>::from(*posture);
136 let damping =
137 SMatrix::<f64, 6, 6>::identity() * (self.options.damping * self.options.damping);
138 let from = self.q;
139 let mut residual = 0.0;
140 for iteration in 0..=self.options.iterations {
141 let error = self.error(&p_des, &r_des);
142 residual = error.norm();
143 if iteration == self.options.iterations
144 || (iteration > 0 && residual < self.options.tolerance)
145 {
146 break;
147 }
148 let j = SMatrix::<f64, 6, 7>::from_column_slice(&self.model.zero_jacobian_q(
149 Frame::EndEffector,
150 &self.q,
151 &self.f_t_ee,
152 &self.ee_t_k,
153 ));
154 let Some(inverse) = (j * j.transpose() + damping).try_inverse() else {
155 break;
156 };
157 let pseudo_inverse = j.transpose() * inverse;
158 let q = SVector::<f64, 7>::from(self.q);
159 let mut next = q + pseudo_inverse * error;
160 if iteration == 0 {
161 let nullspace = SMatrix::<f64, 7, 7>::identity() - pseudo_inverse * j;
162 let mut bias = nullspace * (posture - q) * (self.options.nullspace_gain * dt);
163 let (largest, cap) = (bias.amax(), MAX_POSTURE_RATE * dt);
164 if largest > cap {
165 bias *= cap / largest;
166 }
167 next += bias;
168 }
169 for i in 0..7 {
170 self.q[i] = next[i].clamp(self.lower[i], self.upper[i]);
171 }
172 }
173 let largest = (0..7).fold(0.0f64, |m, i| m.max((self.q[i] - from[i]).abs()));
174 if largest > self.options.max_step {
175 let scale = self.options.max_step / largest;
177 for (q, from) in self.q.iter_mut().zip(&from) {
178 *q = from + scale * (*q - from);
179 }
180 residual = self.error(&p_des, &r_des).norm();
181 }
182 (self.q, residual)
183 }
184
185 #[cfg(test)]
186 pub(super) fn q(&self) -> [f64; 7] {
187 self.q
188 }
189
190 fn error(&self, p_des: &Vector3<f64>, r_des: &Matrix3<f64>) -> SVector<f64, 6> {
192 let fk = self
193 .model
194 .pose_q(Frame::EndEffector, &self.q, &self.f_t_ee, &self.ee_t_k);
195 let p = p_des - Vector3::from(translation_of(&fk));
196 let turn = log(&(r_des * rotation_of(&fk).transpose()));
197 SVector::<f64, 6>::new(p[0], p[1], p[2], turn[0], turn[1], turn[2])
198 }
199}