1pub(super) mod active_set;
11mod bounds;
12mod priority;
13pub(super) mod stall;
14
15use std::sync::Arc;
16
17use nalgebra::{Matrix3, Vector3};
18
19use super::position::JointLimits;
20use super::rotation::{log, rotation_of, translation_of};
21use crate::error::{FrankaError, FrankaResult};
22use crate::model::{Frame, Model};
23use active_set::{solve, Bias, Jacobian, Joints, Task};
24use bounds::Bounds;
25#[cfg(test)]
26pub(super) use bounds::{AS_RUN_LIMIT_MARGIN, POSTURE_LIMIT_DISTANCE};
27use priority::Priority;
28#[cfg(test)]
29pub(super) use priority::PRIORITY_RAMP_CYCLES;
30use stall::Stall;
31
32#[derive(Debug, Clone, Copy, PartialEq)]
36pub struct IkOptions {
37 pub damping: f64,
40 pub nullspace_gain: f64,
43 pub iterations: u32,
45 pub tolerance: f64,
47 pub rotation_weight: f64,
52}
53
54pub const MAX_POSTURE_RATE: f64 = 0.5;
58
59impl Default for IkOptions {
60 fn default() -> Self {
61 IkOptions {
62 damping: 0.05,
63 nullspace_gain: 1.0,
64 iterations: 3,
65 tolerance: 1e-6,
66 rotation_weight: 0.1,
67 }
68 }
69}
70
71impl IkOptions {
72 pub fn validate(&self) -> FrankaResult<()> {
77 let non_negative = |x: f64| x.is_finite() && x >= 0.0;
78 let valid = self.damping.is_finite()
79 && self.damping > 0.0
80 && non_negative(self.nullspace_gain)
81 && non_negative(self.tolerance)
82 && self.rotation_weight > 0.0
83 && self.rotation_weight <= 1.0
84 && self.iterations > 0;
85 if !valid {
86 return Err(FrankaError::InvalidArgument(format!(
87 "target control: ik needs a finite, positive damping, finite and non-negative \
88 nullspace_gain and tolerance, a rotation_weight within (0, 1], and at least \
89 one iteration, got {self:?}"
90 )));
91 }
92 Ok(())
93 }
94}
95
96pub(super) struct Ik {
98 model: Arc<Model>,
99 options: IkOptions,
100 bounds: Bounds,
101 f_t_ee: [f64; 16],
102 ee_t_k: [f64; 16],
103 q: [f64; 7],
104 jacobian: Jacobian,
106 act: [i8; 7],
107 position_act: [i8; 7],
109 priority: Priority,
110 pinned: [i8; 7],
111 stall: Stall,
112 passes: u32,
113 step_norm: f64,
116 clipped_norm: f64,
117 #[cfg(test)]
118 fallbacks: u64,
119 cap_scale: f64,
120}
121
122impl Ik {
123 pub(super) fn new(
124 model: Arc<Model>,
125 options: IkOptions,
126 limits: JointLimits,
127 q0: [f64; 7],
128 f_t_ee: [f64; 16],
129 ee_t_k: [f64; 16],
130 ) -> Self {
131 let bounds = Bounds::Guarded(limits);
132 Ik::with_bounds(model, options, bounds, q0, f_t_ee, ee_t_k)
133 }
134
135 #[cfg(test)]
139 pub(super) fn as_run(
140 model: Arc<Model>,
141 options: IkOptions,
142 limits: ([f64; 7], [f64; 7]),
143 max_velocity: [f64; 7],
144 q0: [f64; 7],
145 f_t_ee: [f64; 16],
146 ee_t_k: [f64; 16],
147 ) -> Self {
148 let (bounds, q0) = Bounds::as_run(limits, max_velocity, q0);
149 let options = IkOptions {
150 rotation_weight: 1.0,
151 ..options
152 };
153 Ik::with_bounds(model, options, bounds, q0, f_t_ee, ee_t_k)
154 }
155
156 fn with_bounds(
157 model: Arc<Model>,
158 options: IkOptions,
159 bounds: Bounds,
160 q: [f64; 7],
161 f_t_ee: [f64; 16],
162 ee_t_k: [f64; 16],
163 ) -> Self {
164 let mut ik = Ik {
165 model,
166 options,
167 bounds,
168 f_t_ee,
169 ee_t_k,
170 q,
171 jacobian: Jacobian::zeros(),
172 act: [0; 7],
173 position_act: [0; 7],
174 priority: Priority::default(),
175 pinned: [0; 7],
176 stall: Stall::default(),
177 passes: 0,
178 step_norm: 0.0,
179 clipped_norm: 0.0,
180 #[cfg(test)]
181 fallbacks: 0,
182 cap_scale: 1.0,
183 };
184 ik.jacobian = ik.jacobian_at(&q);
185 ik
186 }
187
188 pub(super) fn retune(&mut self, damping: f64, nullspace_gain: f64) {
193 self.options.damping = damping;
194 self.options.nullspace_gain = nullspace_gain;
195 }
196
197 pub(super) fn damping(&self) -> f64 {
200 self.options.damping
201 }
202
203 #[cfg(test)]
205 pub(super) fn options(&self) -> IkOptions {
206 self.options
207 }
208
209 pub(super) fn step(
214 &mut self,
215 pose: &[f64; 16],
216 posture: &[f64; 7],
217 dt: f64,
218 ) -> ([f64; 7], f64) {
219 let target = (Vector3::from(translation_of(pose)), rotation_of(pose));
220 let from = Joints::from(self.q);
221 let gain = self.options.nullspace_gain * dt;
222 let cycle = self.bounds.cycle(&self.q, posture, gain, dt);
223 self.priority.update(&self.stall);
224 let saved_act = (self.act, self.position_act);
225 let (mut q, mut j) = (from, self.jacobian);
226 let mut error = None;
227 self.passes = 0;
228 (self.step_norm, self.clipped_norm) = (0.0, 0.0);
229 for iteration in 0..self.options.iterations {
230 let e = self.error(&q, &target);
231 if iteration > 0 && e.norm() < self.options.tolerance {
232 error = Some(e);
233 break;
234 }
235 if iteration > 0 {
236 j = self.jacobian_at(&q.into());
237 }
238 let lo = Joints::from(cycle.bounds.lower) - (q - from);
239 let hi = Joints::from(cycle.bounds.upper) - (q - from);
240 let bias = (iteration == 0).then_some(Bias {
241 rate: cycle.posture,
242 cap: MAX_POSTURE_RATE * dt,
243 });
244 match self.prioritised(&j, &e, (&lo, &hi), bias, cycle.passes) {
245 Some(d) => q += d,
246 None => {
247 (q, j) = (from, self.jacobian);
251 (self.act, self.position_act) = saved_act;
252 self.passes = 0;
253 (self.step_norm, self.clipped_norm) = (0.0, 0.0);
254 #[cfg(test)]
255 {
256 self.fallbacks += 1;
257 }
258 error = Some(self.error(&q, &target));
259 break;
260 }
261 }
262 }
263 let mut goal: [f64; 7] = q.into();
264 self.cap_scale = self.bounds.backstop(&self.q, &mut goal, &cycle);
265 let e = match error {
266 Some(e) if self.cap_scale == 1.0 => e,
267 _ => self.error(&goal.into(), &target),
268 };
269 if goal != self.q {
270 j = self.jacobian_at(&goal);
271 }
272 (self.q, self.jacobian) = (goal, j);
273 self.pinned = std::array::from_fn(|i| cycle.bounds.pin(i, self.act[i]));
274 self.stall
275 .update(&j, &e, &self.pinned, self.options.damping);
276 (goal, e.norm())
277 }
278
279 fn prioritised(
282 &mut self,
283 j: &Jacobian,
284 e: &Task,
285 (lo, hi): (&Joints, &Joints),
286 bias: Option<Bias>,
287 passes: u32,
288 ) -> Option<Joints> {
289 let damping = self.options.damping;
290 let mut run = |j: &Jacobian, e: &Task, lo: &Joints, hi: &Joints, bias, act: &mut _| {
291 let solution = solve(j, e, (lo, hi), bias, act, damping, passes)?;
292 self.passes += solution.passes;
293 if solution.step > self.step_norm {
294 (self.step_norm, self.clipped_norm) = (solution.step, solution.clipped);
295 }
296 #[cfg(test)]
297 {
298 self.fallbacks += u64::from(!solution.converged);
299 }
300 Some(solution.d)
301 };
302 if self.priority.blend == 0.0 {
303 let d = run(j, e, lo, hi, bias, &mut self.act);
304 self.position_act = self.act;
306 return d;
307 }
308 let (jp, ep) = Priority::position(j, e);
309 let shift = run(&jp, &ep, lo, hi, None, &mut self.position_act)? * self.priority.blend;
310 let (jr, er) = self.priority.held(j, e, &shift);
311 let d = run(&jr, &er, &(lo - shift), &(hi - shift), bias, &mut self.act)?;
312 Some(d + shift)
313 }
314
315 pub(super) fn pinned(&self) -> [i8; 7] {
318 self.pinned
319 }
320
321 pub(super) fn stall(&self) -> Stall {
323 self.stall
324 }
325
326 pub(super) fn passes(&self) -> u32 {
328 self.passes
329 }
330
331 pub(super) fn step_norms(&self) -> (f64, f64) {
335 (self.step_norm, self.clipped_norm)
336 }
337
338 #[cfg(test)]
340 pub(super) fn fallbacks(&self) -> u64 {
341 self.fallbacks
342 }
343
344 pub(super) fn priority(&self) -> f64 {
346 self.priority.blend
347 }
348
349 #[cfg(test)]
352 pub(super) fn set_priority_ramp(&mut self, cycles: u32) {
353 self.priority.set_ramp(cycles);
354 }
355
356 pub(super) fn cap_scale(&self) -> f64 {
358 self.cap_scale
359 }
360
361 pub(super) fn set_frames(
364 &mut self,
365 f_t_ee: &[f64; 16],
366 ee_t_k: &[f64; 16],
367 ) -> Option<[f64; 16]> {
368 let bits = |t: &[f64; 16]| t.map(f64::to_bits);
369 if bits(&self.f_t_ee) == bits(f_t_ee) && bits(&self.ee_t_k) == bits(ee_t_k) {
370 return None;
371 }
372 let old = std::mem::replace(&mut self.f_t_ee, *f_t_ee);
373 self.ee_t_k = *ee_t_k;
374 self.jacobian = self.jacobian_at(&self.q);
375 Some(old)
376 }
377
378 pub(super) fn pose_of(&self, q: &[f64; 7]) -> [f64; 16] {
380 self.model
381 .pose_q(Frame::EndEffector, q, &self.f_t_ee, &self.ee_t_k)
382 }
383
384 pub(super) fn q(&self) -> [f64; 7] {
386 self.q
387 }
388
389 fn jacobian_at(&self, q: &[f64; 7]) -> Jacobian {
391 let mut j = Jacobian::from_column_slice(&self.model.zero_jacobian_q(
392 Frame::EndEffector,
393 q,
394 &self.f_t_ee,
395 &self.ee_t_k,
396 ));
397 j.fixed_rows_mut::<3>(3)
398 .scale_mut(self.options.rotation_weight);
399 j
400 }
401
402 fn error(&self, q: &Joints, (p_des, r_des): &(Vector3<f64>, Matrix3<f64>)) -> Task {
404 let fk = self.pose_of(&(*q).into());
405 let p = p_des - Vector3::from(translation_of(&fk));
406 let turn =
407 log(&(r_des * rotation_of(&fk).transpose())).map(|x| x * self.options.rotation_weight);
408 Task::new(p[0], p[1], p[2], turn[0], turn[1], turn[2])
409 }
410}