1use std::f64::{consts::PI};
4use crate::kinematic_traits::{Kinematics, Solutions, Pose, Singularity, Joints, JOINTS_AT_ZERO};
5use crate::kinematic_traits::{J4, J5, J6};
6use crate::parameters::opw_kinematics::{Parameters};
7use crate::utils::opw_kinematics::{is_valid};
8use nalgebra::{Isometry3, Matrix3, OVector, Rotation3, Translation3, U3, Unit, UnitQuaternion,
9 Vector3};
10use crate::constraints::{BY_CONSTRAINS, BY_PREV, Constraints};
11
12const DEBUG: bool = false;
13
14#[derive(Debug, Copy, Clone)]
15pub struct OPWKinematics {
16 parameters: Parameters,
18 constraints: Option<Constraints>,
19
20 unit_z: Unit<OVector<f64, U3>>,
21}
22
23impl OPWKinematics {
24 #[allow(dead_code)]
26 pub fn new(parameters: Parameters) -> Self {
27 OPWKinematics {
28 parameters,
29 unit_z: Unit::new_normalize(Vector3::z_axis().into_inner()),
30 constraints: None,
31 }
32 }
33
34 pub fn new_with_constraints(parameters: Parameters, constraints: Constraints) -> Self {
37 OPWKinematics {
38 parameters,
39 unit_z: Unit::new_normalize(Vector3::z_axis().into_inner()),
40 constraints: Some(constraints),
41 }
42 }
43}
44
45
46const MM: f64 = 0.001;
47const DISTANCE_TOLERANCE: f64 = 0.001 * MM;
48const ANGULAR_TOLERANCE: f64 = 1E-6;
49
50const SINGULARITY_ANGLE_THR: f64 = 0.01 * PI / 180.0;
52
53impl Kinematics for OPWKinematics {
54 fn inverse(&self, pose: &Pose) -> Solutions {
59 if self.parameters.dof == 5 {
60 self.inverse_intern_5_dof(pose, f64::NAN)
62 } else {
63 self.filter_constraints_compliant(self.inverse_intern(&pose))
64 }
65 }
66
67 fn inverse_continuing(&self, pose: &Pose, prev: &Joints) -> Solutions {
71 if self.parameters.dof == 5 {
72 return self.inverse_intern_5_dof(pose, prev[5]);
73 }
74
75 let previous;
76 if prev[0].is_nan() {
77 previous = self.constraint_centers();
79 } else {
80 previous = prev;
81 }
82
83 const SINGULARITY_SHIFT: f64 = DISTANCE_TOLERANCE / 8.;
84 const SINGULARITY_SHIFTS: [[f64; 3]; 4] =
85 [[0., 0., 0., ], [SINGULARITY_SHIFT, 0., 0.],
86 [0., SINGULARITY_SHIFT, 0.], [0., 0., SINGULARITY_SHIFT]];
87
88 let mut solutions: Vec<Joints> = Vec::with_capacity(9);
89 let pt = pose.translation;
90
91 let rotation = pose.rotation;
92 'shifts: for d in SINGULARITY_SHIFTS {
93 let shifted = Pose::from_parts(
94 Translation3::new(pt.x + d[0], pt.y + d[1], pt.z + d[2]), rotation);
95 let ik = self.inverse_intern(&shifted);
96 if solutions.is_empty() {
98 solutions.extend(&ik);
100 }
101
102 for s_idx in 0..ik.len() {
103 let singularity =
104 self.kinematic_singularity(&ik[s_idx]);
105 if singularity.is_some() && is_valid(&ik[s_idx]) {
106 let s;
107 let s_n;
108 if let Some(Singularity::A) = singularity {
109 let mut now = ik[s_idx];
110 if are_angles_close(now[J5], 0.) {
111 s = previous[J4] + previous[J6];
113 s_n = now[J4] + now[J6];
114 } else {
115 s = previous[J4] - previous[J6];
119 s_n = now[J4] - now[J6];
120
121 normalize_near(&mut now[J5], previous[J5]);
123 }
124
125 let mut angle = s_n - s;
126 while angle > PI {
127 angle -= 2.0 * PI;
128 }
129 while angle < -PI {
130 angle += 2.0 * PI;
131 }
132 let j_d = angle / 2.0;
133
134 now[J4] = previous[J4] + j_d;
135 now[J6] = previous[J6] - j_d;
136
137 let check_pose = self.forward(&now);
139 if compare_poses(&pose, &check_pose, DISTANCE_TOLERANCE, ANGULAR_TOLERANCE) &&
140 self.constraints_compliant(now) {
141 solutions.push(now);
143 break 'shifts;
145 }
146 }
147
148 break;
149 }
150 }
151 }
152 for s_idx in 0..solutions.len() {
155 for joint_idx in 0..6 {
156 normalize_near(&mut solutions[s_idx][joint_idx], previous[joint_idx]);
157 }
158 }
159 self.sort_by_closeness(&mut solutions, &previous);
160 self.filter_constraints_compliant(solutions)
161 }
162
163 fn forward(&self, joints: &Joints) -> Pose {
164 let p = &self.parameters;
165
166 let q1 = joints[0] * p.sign_corrections[0] as f64 - p.offsets[0];
168 let q2 = joints[1] * p.sign_corrections[1] as f64 - p.offsets[1];
169 let q3 = joints[2] * p.sign_corrections[2] as f64 - p.offsets[2];
170 let q4 = joints[3] * p.sign_corrections[3] as f64 - p.offsets[3];
171 let q5 = joints[4] * p.sign_corrections[4] as f64 - p.offsets[4];
172 let q6 = joints[5] * p.sign_corrections[5] as f64 - p.offsets[5];
173
174 let psi3 = f64::atan2(p.a2, p.c3);
175 let k = f64::sqrt(p.a2 * p.a2 + p.c3 * p.c3);
176
177 let q23_psi3 = q2 + q3 + psi3;
179 let sin_q23_psi3 = q23_psi3.sin();
180 let cos_q23_psi3 = q23_psi3.cos();
181
182 let cx1 = p.c2 * f64::sin(q2) + k * sin_q23_psi3 + p.a1;
183 let cy1 = p.b;
184 let cz1 = p.c2 * f64::cos(q2) + k * cos_q23_psi3;
185
186 let cx0 = cx1 * f64::cos(q1) - cy1 * f64::sin(q1);
187 let cy0 = cx1 * f64::sin(q1) + cy1 * f64::cos(q1);
188 let cz0 = cz1 + p.c1;
189
190 let (s1, c1) = q1.sin_cos();
192 let (s2, c2) = q2.sin_cos();
193 let (s3, c3) = q3.sin_cos();
194 let (s4, c4) = q4.sin_cos();
195 let (s5, c5) = q5.sin_cos();
196 let (s6, c6) = q6.sin_cos();
197
198 let r_0c = Matrix3::new(
200 c1 * c2 * c3 - c1 * s2 * s3, -s1, c1 * c2 * s3 + c1 * s2 * c3,
201 s1 * c2 * c3 - s1 * s2 * s3, c1, s1 * c2 * s3 + s1 * s2 * c3,
202 -s2 * c3 - c2 * s3, 0.0, -s2 * s3 + c2 * c3,
203 );
204
205 let r_ce = Matrix3::new(
207 c4 * c5 * c6 - s4 * s6, -c4 * c5 * s6 - s4 * c6, c4 * s5,
208 s4 * c5 * c6 + c4 * s6, -s4 * c5 * s6 + c4 * c6, s4 * s5,
209 -s5 * c6, s5 * s6, c5,
210 );
211
212 let r_oe = r_0c * r_ce;
214
215 let translation = Vector3::new(cx0, cy0, cz0) + p.c4 * r_oe * *self.unit_z;
217
218 let rotation = Rotation3::from_matrix_unchecked(r_oe);
220
221 Pose::from_parts(
223 Translation3::from(translation),
224 UnitQuaternion::from_rotation_matrix(&rotation),
225 )
226 }
227
228 fn forward_with_joint_poses(&self, joints: &Joints) -> [Pose; 6] {
229 let p = &self.parameters;
230
231 let q1 = joints[0] * p.sign_corrections[0] as f64 - p.offsets[0];
232 let q2 = joints[1] * p.sign_corrections[1] as f64 - p.offsets[1];
233 let q3 = joints[2] * p.sign_corrections[2] as f64 - p.offsets[2];
234 let q4 = joints[3] * p.sign_corrections[3] as f64 - p.offsets[3];
235 let q5 = joints[4] * p.sign_corrections[4] as f64 - p.offsets[4];
236 let q6 = joints[5] * p.sign_corrections[5] as f64 - p.offsets[5];
237
238 let pose1 = Isometry3::from_parts(
240 Translation3::new(0.0, 0.0, p.c1),
241 UnitQuaternion::from_axis_angle(&Vector3::z_axis(), q1),
242 );
243
244 let pose2 = pose1 * Isometry3::from_parts(
246 Translation3::new(p.a1, p.b, 0.0),
247 UnitQuaternion::from_axis_angle(&nalgebra::Vector3::y_axis(), q2),
248 );
249
250 let pose3 = pose2 * Isometry3::from_parts(
252 Translation3::new(0.0, 0.0, p.c2),
253 UnitQuaternion::from_axis_angle(&nalgebra::Vector3::y_axis(), q3),
254 );
255
256 let pose4 = pose3 * Isometry3::from_parts(
258 Translation3::new(p.a2, 0.0, 0.0),
259 UnitQuaternion::from_axis_angle(&nalgebra::Vector3::z_axis(), q4),
260 );
261
262 let pose5 = pose4 * Isometry3::from_parts(
264 Translation3::new(0.0, 0.0, p.c3),
265 UnitQuaternion::from_axis_angle(&nalgebra::Vector3::y_axis(), q5),
266 );
267
268 let pose6 = pose5 * Isometry3::from_parts(
271 Translation3::new(0.0, 0.0, p.c4),
272 UnitQuaternion::from_axis_angle(&nalgebra::Vector3::z_axis(), q6),
273 );
274
275 [pose1, pose2, pose3, pose4, pose5, pose6]
276 }
277
278
279 fn inverse_5dof(&self, pose: &Pose, j6: f64) -> Solutions {
280 self.filter_constraints_compliant(self.inverse_intern_5_dof(&pose, j6))
281 }
282
283 fn inverse_continuing_5dof(&self, pose: &Pose, prev: &Joints) -> Solutions {
284 let previous;
285 if prev[0].is_nan() {
286 previous = self.constraint_centers();
288 } else {
289 previous = prev;
290 }
291
292 let mut solutions = self.inverse_intern_5_dof(pose, prev[5]);
293
294 for s_idx in 0..solutions.len() {
297 for joint_idx in 0..6 {
298 normalize_near(&mut solutions[s_idx][joint_idx], previous[joint_idx]);
299 }
300 }
301 self.sort_by_closeness(&mut solutions, &previous);
302 self.filter_constraints_compliant(solutions)
303 }
304
305 fn kinematic_singularity(&self, joints: &Joints) -> Option<Singularity> {
306 if is_close_to_multiple_of_pi(joints[J5], SINGULARITY_ANGLE_THR) {
307 Some(Singularity::A)
308 } else {
309 None
310 }
311 }
312
313 fn constraints(&self) -> &Option<Constraints> {
314 &self.constraints
315 }
316}
317
318impl OPWKinematics {
319 fn inverse_intern(&self, pose: &Pose) -> Solutions {
320 let params = &self.parameters;
321
322 let matrix = pose.rotation.to_rotation_matrix();
324 let translation_vector = &pose.translation.vector; let scaled_z_axis = params.c4 * matrix.transform_vector(&Vector3::z_axis()); let c = translation_vector - scaled_z_axis;
328
329 let nx1 = ((c.x * c.x + c.y * c.y) - params.b * params.b).sqrt() - params.a1;
330
331 let tmp1 = c.y.atan2(c.x); let tmp2 = params.b.atan2(nx1 + params.a1);
333
334 let theta1_i = tmp1 - tmp2;
335 let theta1_ii = tmp1 + tmp2 - PI;
336
337 let tmp3 = c.z - params.c1; let s1_2 = nx1 * nx1 + tmp3 * tmp3;
339
340 let tmp4 = nx1 + 2.0 * params.a1;
341 let s2_2 = tmp4 * tmp4 + tmp3 * tmp3;
342 let kappa_2 = params.a2 * params.a2 + params.c3 * params.c3;
343
344 let c2_2 = params.c2 * params.c2;
345
346 let tmp5 = s1_2 + c2_2 - kappa_2;
347
348 let s1 = f64::sqrt(s1_2);
349 let s2 = f64::sqrt(s2_2);
350
351 let tmp13 = f64::acos(tmp5 / (2.0 * s1 * params.c2));
352 let tmp14 = f64::atan2(nx1, c.z - params.c1);
353 let theta2_i = -tmp13 + tmp14;
354 let theta2_ii = tmp13 + tmp14;
355
356 let tmp6 = s2_2 + c2_2 - kappa_2;
357
358 let tmp15 = f64::acos(tmp6 / (2.0 * s2 * params.c2));
359 let tmp16 = f64::atan2(nx1 + 2.0 * params.a1, c.z - params.c1);
360 let theta2_iii = -tmp15 - tmp16;
361 let theta2_iv = tmp15 - tmp16;
362
363 let tmp7 = s1_2 - c2_2 - kappa_2;
365 let tmp8 = s2_2 - c2_2 - kappa_2;
366 let tmp9 = 2.0 * params.c2 * f64::sqrt(kappa_2);
367 let tmp10 = f64::atan2(params.a2, params.c3);
368
369 let tmp11 = f64::acos(tmp7 / tmp9);
370 let theta3_i = tmp11 - tmp10;
371 let theta3_ii = -tmp11 - tmp10;
372
373 let tmp12 = f64::acos(tmp8 / tmp9);
374 let theta3_iii = tmp12 - tmp10;
375 let theta3_iv = -tmp12 - tmp10;
376
377 let (theta1_i_sin, theta1_i_cos) = theta1_i.sin_cos();
378 let (theta1_ii_sin, theta1_ii_cos) = theta1_ii.sin_cos();
379
380 let sin1: [f64; 4] = [
382 theta1_i_sin, theta1_i_sin, theta1_ii_sin, theta1_ii_sin,
383 ];
384
385 let cos1: [f64; 4] = [
386 theta1_i_cos, theta1_i_cos, theta1_ii_cos, theta1_ii_cos
387 ];
388
389 let s23: [f64; 4] = [
390 (theta2_i + theta3_i).sin(),
391 (theta2_ii + theta3_ii).sin(),
392 (theta2_iii + theta3_iii).sin(),
393 (theta2_iv + theta3_iv).sin(),
394 ];
395
396 let c23: [f64; 4] = [
397 (theta2_i + theta3_i).cos(),
398 (theta2_ii + theta3_ii).cos(),
399 (theta2_iii + theta3_iii).cos(),
400 (theta2_iv + theta3_iv).cos(),
401 ];
402
403 let m: [f64; 4] = [
404 matrix[(0, 2)] * s23[0] * cos1[0] + matrix[(1, 2)] * s23[0] * sin1[0] + matrix[(2, 2)] * c23[0],
405 matrix[(0, 2)] * s23[1] * cos1[1] + matrix[(1, 2)] * s23[1] * sin1[1] + matrix[(2, 2)] * c23[1],
406 matrix[(0, 2)] * s23[2] * cos1[2] + matrix[(1, 2)] * s23[2] * sin1[2] + matrix[(2, 2)] * c23[2],
407 matrix[(0, 2)] * s23[3] * cos1[3] + matrix[(1, 2)] * s23[3] * sin1[3] + matrix[(2, 2)] * c23[3],
408 ];
409
410 let theta5_i = f64::atan2((1.0 - m[0] * m[0]).sqrt(), m[0]);
411 let theta5_ii = f64::atan2((1.0 - m[1] * m[1]).sqrt(), m[1]);
412 let theta5_iii = f64::atan2((1.0 - m[2] * m[2]).sqrt(), m[2]);
413 let theta5_iv = f64::atan2((1.0 - m[3] * m[3]).sqrt(), m[3]);
414
415 let theta5_v = -theta5_i;
416 let theta5_vi = -theta5_ii;
417 let theta5_vii = -theta5_iii;
418 let theta5_viii = -theta5_iv;
419
420 let theta4_i;
421 let theta6_i;
422
423 let theta4_iy = matrix[(1, 2)] * cos1[0] - matrix[(0, 2)] * sin1[0];
424 let theta4_ix = matrix[(0, 2)] * c23[0] * cos1[0] + matrix[(1, 2)] * c23[0] * sin1[0] - matrix[(2, 2)] * s23[0];
425 theta4_i = theta4_iy.atan2(theta4_ix);
426
427 let theta6_iy = matrix[(0, 1)] * s23[0] * cos1[0] + matrix[(1, 1)] * s23[0] * sin1[0] + matrix[(2, 1)] * c23[0];
428 let theta6_ix = -matrix[(0, 0)] * s23[0] * cos1[0] - matrix[(1, 0)] * s23[0] * sin1[0] - matrix[(2, 0)] * c23[0];
429 theta6_i = theta6_iy.atan2(theta6_ix);
430
431 let theta4_ii;
432 let theta6_ii;
433
434 let theta4_iiy = matrix[(1, 2)] * cos1[1] - matrix[(0, 2)] * sin1[1];
435 let theta4_iix = matrix[(0, 2)] * c23[1] * cos1[1] + matrix[(1, 2)] * c23[1] * sin1[1] - matrix[(2, 2)] * s23[1];
436 theta4_ii = theta4_iiy.atan2(theta4_iix);
437
438 let theta6_iiy = matrix[(0, 1)] * s23[1] * cos1[1] + matrix[(1, 1)] * s23[1] * sin1[1] + matrix[(2, 1)] * c23[1];
439 let theta6_iix = -matrix[(0, 0)] * s23[1] * cos1[1] - matrix[(1, 0)] * s23[1] * sin1[1] - matrix[(2, 0)] * c23[1];
440 theta6_ii = theta6_iiy.atan2(theta6_iix);
441
442 let theta4_iii;
443 let theta6_iii;
444
445 let theta4_iiiy = matrix[(1, 2)] * cos1[2] - matrix[(0, 2)] * sin1[2];
446 let theta4_iiix = matrix[(0, 2)] * c23[2] * cos1[2] + matrix[(1, 2)] * c23[2] * sin1[2] - matrix[(2, 2)] * s23[2];
447 theta4_iii = theta4_iiiy.atan2(theta4_iiix);
448
449 let theta6_iiiy = matrix[(0, 1)] * s23[2] * cos1[2] + matrix[(1, 1)] * s23[2] * sin1[2] + matrix[(2, 1)] * c23[2];
450 let theta6_iiix = -matrix[(0, 0)] * s23[2] * cos1[2] - matrix[(1, 0)] * s23[2] * sin1[2] - matrix[(2, 0)] * c23[2];
451 theta6_iii = theta6_iiiy.atan2(theta6_iiix);
452
453 let theta4_iv;
454 let theta6_iv;
455
456 let theta4_ivy = matrix[(1, 2)] * cos1[3] - matrix[(0, 2)] * sin1[3];
457 let theta4_ivx = matrix[(0, 2)] * c23[3] * cos1[3] + matrix[(1, 2)] * c23[3] * sin1[3] - matrix[(2, 2)] * s23[3];
458 theta4_iv = theta4_ivy.atan2(theta4_ivx);
459
460 let theta6_ivy = matrix[(0, 1)] * s23[3] * cos1[3] + matrix[(1, 1)] * s23[3] * sin1[3] + matrix[(2, 1)] * c23[3];
461 let theta6_ivx = -matrix[(0, 0)] * s23[3] * cos1[3] - matrix[(1, 0)] * s23[3] * sin1[3] - matrix[(2, 0)] * c23[3];
462 theta6_iv = theta6_ivy.atan2(theta6_ivx);
463
464 let theta4_v = theta4_i + PI;
465 let theta4_vi = theta4_ii + PI;
466 let theta4_vii = theta4_iii + PI;
467 let theta4_viii = theta4_iv + PI;
468
469 let theta6_v = theta6_i - PI;
470 let theta6_vi = theta6_ii - PI;
471 let theta6_vii = theta6_iii - PI;
472 let theta6_viii = theta6_iv - PI;
473
474 let theta: [[f64; 6]; 8] = [
475 [theta1_i, theta2_i, theta3_i, theta4_i, theta5_i, theta6_i],
476 [theta1_i, theta2_ii, theta3_ii, theta4_ii, theta5_ii, theta6_ii],
477 [theta1_ii, theta2_iii, theta3_iii, theta4_iii, theta5_iii, theta6_iii],
478 [theta1_ii, theta2_iv, theta3_iv, theta4_iv, theta5_iv, theta6_iv],
479 [theta1_i, theta2_i, theta3_i, theta4_v, theta5_v, theta6_v],
480 [theta1_i, theta2_ii, theta3_ii, theta4_vi, theta5_vi, theta6_vi],
481 [theta1_ii, theta2_iii, theta3_iii, theta4_vii, theta5_vii, theta6_vii],
482 [theta1_ii, theta2_iv, theta3_iv, theta4_viii, theta5_viii, theta6_viii],
483 ];
484
485 let mut sols: [[f64; 6]; 8] = [[f64::NAN; 6]; 8];
486 for si in 0..sols.len() {
487 for ji in 0..6 {
488 sols[si][ji] = (theta[si][ji] + params.offsets[ji]) *
489 params.sign_corrections[ji] as f64;
490 }
491 }
492
493 let mut result: Solutions = Vec::with_capacity(8);
494
495 for si in 0..sols.len() {
498 let mut valid = true;
499 for ji in 0..6 {
500 let mut angle = sols[si][ji];
501 if angle.is_finite() {
502 while angle > PI {
503 angle -= 2.0 * PI;
504 }
505 while angle < -PI {
506 angle += 2.0 * PI;
507 }
508 sols[si][ji] = angle;
509 } else {
510 valid = false;
511 break;
512 }
513 };
514 if valid {
515 let check_pose = self.forward(&sols[si]);
516 if compare_poses(&pose, &check_pose, DISTANCE_TOLERANCE, ANGULAR_TOLERANCE) {
517 result.push(sols[si]);
518 } else {
519 if DEBUG {
520 println!("********** Pose Failure sol {} *********", si);
521 }
522 }
523 }
524 }
525
526 result
527 }
528
529 fn inverse_intern_5_dof(&self, pose: &Pose, j6: f64) -> Solutions {
530 let params = &self.parameters;
531
532 let matrix = pose.rotation.to_rotation_matrix();
534 let translation_vector = &pose.translation.vector; let scaled_z_axis = params.c4 * matrix.transform_vector(&Vector3::z_axis()); let c = translation_vector - scaled_z_axis;
538
539 let nx1 = ((c.x * c.x + c.y * c.y) - params.b * params.b).sqrt() - params.a1;
540
541 let tmp1 = c.y.atan2(c.x); let tmp2 = params.b.atan2(nx1 + params.a1);
543
544 let theta1_i = tmp1 - tmp2;
545 let theta1_ii = tmp1 + tmp2 - PI;
546
547 let tmp3 = c.z - params.c1; let s1_2 = nx1 * nx1 + tmp3 * tmp3;
549
550 let tmp4 = nx1 + 2.0 * params.a1;
551 let s2_2 = tmp4 * tmp4 + tmp3 * tmp3;
552 let kappa_2 = params.a2 * params.a2 + params.c3 * params.c3;
553
554 let c2_2 = params.c2 * params.c2;
555
556 let tmp5 = s1_2 + c2_2 - kappa_2;
557
558 let s1 = f64::sqrt(s1_2);
559 let s2 = f64::sqrt(s2_2);
560
561 let tmp13 = f64::acos(tmp5 / (2.0 * s1 * params.c2));
562 let tmp14 = f64::atan2(nx1, c.z - params.c1);
563 let theta2_i = -tmp13 + tmp14;
564 let theta2_ii = tmp13 + tmp14;
565
566 let tmp6 = s2_2 + c2_2 - kappa_2;
567
568 let tmp15 = f64::acos(tmp6 / (2.0 * s2 * params.c2));
569 let tmp16 = f64::atan2(nx1 + 2.0 * params.a1, c.z - params.c1);
570 let theta2_iii = -tmp15 - tmp16;
571 let theta2_iv = tmp15 - tmp16;
572
573 let tmp7 = s1_2 - c2_2 - kappa_2;
575 let tmp8 = s2_2 - c2_2 - kappa_2;
576 let tmp9 = 2.0 * params.c2 * f64::sqrt(kappa_2);
577 let tmp10 = f64::atan2(params.a2, params.c3);
578
579 let tmp11 = f64::acos(tmp7 / tmp9);
580 let theta3_i = tmp11 - tmp10;
581 let theta3_ii = -tmp11 - tmp10;
582
583 let tmp12 = f64::acos(tmp8 / tmp9);
584 let theta3_iii = tmp12 - tmp10;
585 let theta3_iv = -tmp12 - tmp10;
586
587 let (theta1_i_sin, theta1_i_cos) = theta1_i.sin_cos();
588 let (theta1_ii_sin, theta1_ii_cos) = theta1_ii.sin_cos();
589
590 let sin1: [f64; 4] = [
592 theta1_i_sin, theta1_i_sin, theta1_ii_sin, theta1_ii_sin,
593 ];
594
595 let cos1: [f64; 4] = [
596 theta1_i_cos, theta1_i_cos, theta1_ii_cos, theta1_ii_cos
597 ];
598
599 let s23: [f64; 4] = [
600 (theta2_i + theta3_i).sin(),
601 (theta2_ii + theta3_ii).sin(),
602 (theta2_iii + theta3_iii).sin(),
603 (theta2_iv + theta3_iv).sin(),
604 ];
605
606 let c23: [f64; 4] = [
607 (theta2_i + theta3_i).cos(),
608 (theta2_ii + theta3_ii).cos(),
609 (theta2_iii + theta3_iii).cos(),
610 (theta2_iv + theta3_iv).cos(),
611 ];
612
613 let m: [f64; 4] = [
614 matrix[(0, 2)] * s23[0] * cos1[0] + matrix[(1, 2)] * s23[0] * sin1[0] + matrix[(2, 2)] * c23[0],
615 matrix[(0, 2)] * s23[1] * cos1[1] + matrix[(1, 2)] * s23[1] * sin1[1] + matrix[(2, 2)] * c23[1],
616 matrix[(0, 2)] * s23[2] * cos1[2] + matrix[(1, 2)] * s23[2] * sin1[2] + matrix[(2, 2)] * c23[2],
617 matrix[(0, 2)] * s23[3] * cos1[3] + matrix[(1, 2)] * s23[3] * sin1[3] + matrix[(2, 2)] * c23[3],
618 ];
619
620 let theta5_i = f64::atan2((1.0 - m[0] * m[0]).sqrt(), m[0]);
621 let theta5_ii = f64::atan2((1.0 - m[1] * m[1]).sqrt(), m[1]);
622 let theta5_iii = f64::atan2((1.0 - m[2] * m[2]).sqrt(), m[2]);
623 let theta5_iv = f64::atan2((1.0 - m[3] * m[3]).sqrt(), m[3]);
624
625 let theta5_v = -theta5_i;
626 let theta5_vi = -theta5_ii;
627 let theta5_vii = -theta5_iii;
628 let theta5_viii = -theta5_iv;
629
630 let theta4_i;
631
632 let theta4_iy = matrix[(1, 2)] * cos1[0] - matrix[(0, 2)] * sin1[0];
633 let theta4_ix = matrix[(0, 2)] * c23[0] * cos1[0] + matrix[(1, 2)] * c23[0] * sin1[0] - matrix[(2, 2)] * s23[0];
634 theta4_i = theta4_iy.atan2(theta4_ix);
635
636 let theta4_ii;
637
638 let theta4_iiy = matrix[(1, 2)] * cos1[1] - matrix[(0, 2)] * sin1[1];
639 let theta4_iix = matrix[(0, 2)] * c23[1] * cos1[1] + matrix[(1, 2)] * c23[1] * sin1[1] - matrix[(2, 2)] * s23[1];
640 theta4_ii = theta4_iiy.atan2(theta4_iix);
641
642 let theta4_iii;
643
644 let theta4_iiiy = matrix[(1, 2)] * cos1[2] - matrix[(0, 2)] * sin1[2];
645 let theta4_iiix = matrix[(0, 2)] * c23[2] * cos1[2] + matrix[(1, 2)] * c23[2] * sin1[2] - matrix[(2, 2)] * s23[2];
646 theta4_iii = theta4_iiiy.atan2(theta4_iiix);
647
648 let theta4_iv;
649
650 let theta4_ivy = matrix[(1, 2)] * cos1[3] - matrix[(0, 2)] * sin1[3];
651 let theta4_ivx = matrix[(0, 2)] * c23[3] * cos1[3] + matrix[(1, 2)] * c23[3] * sin1[3] - matrix[(2, 2)] * s23[3];
652 theta4_iv = theta4_ivy.atan2(theta4_ivx);
653
654 let theta4_v = theta4_i + PI;
655 let theta4_vi = theta4_ii + PI;
656 let theta4_vii = theta4_iii + PI;
657 let theta4_viii = theta4_iv + PI;
658
659 let theta: [[f64; 5]; 8] = [
660 [theta1_i, theta2_i, theta3_i, theta4_i, theta5_i],
661 [theta1_i, theta2_ii, theta3_ii, theta4_ii, theta5_ii],
662 [theta1_ii, theta2_iii, theta3_iii, theta4_iii, theta5_iii],
663 [theta1_ii, theta2_iv, theta3_iv, theta4_iv, theta5_iv],
664 [theta1_i, theta2_i, theta3_i, theta4_v, theta5_v],
665 [theta1_i, theta2_ii, theta3_ii, theta4_vi, theta5_vi],
666 [theta1_ii, theta2_iii, theta3_iii, theta4_vii, theta5_vii],
667 [theta1_ii, theta2_iv, theta3_iv, theta4_viii, theta5_viii],
668 ];
669
670 let mut sols: [[f64; 6]; 8] = [[f64::NAN; 6]; 8];
671 for si in 0..sols.len() {
672 for ji in 0..5 {
673 sols[si][ji] = (theta[si][ji] + params.offsets[ji]) *
674 params.sign_corrections[ji] as f64;
675 }
676 sols[si][5] = j6 }
678
679 let mut result: Solutions = Vec::with_capacity(8);
680
681 for si in 0..sols.len() {
684 let mut valid = true;
685 for ji in 0..5 { let mut angle = sols[si][ji];
687 if angle.is_finite() {
688 while angle > PI {
689 angle -= 2.0 * PI;
690 }
691 while angle < -PI {
692 angle += 2.0 * PI;
693 }
694 sols[si][ji] = angle;
695 } else {
696 valid = false;
697 break;
698 }
699 };
700 if valid {
701 let check_xyz = self.forward(&sols[si]).translation;
702 if Self::compare_xyz_only(&pose.translation, &check_xyz, DISTANCE_TOLERANCE) {
703 result.push(sols[si]);
704 } else {
705 if DEBUG {
706 println!("********** Pose Failure 5DOF sol {} *********", si);
707 }
708 }
709 }
710 }
711
712 result
713 }
714
715 fn compare_xyz_only(pose_translation: &Translation3<f64>, check_xyz: &Translation3<f64>, tolerance: f64) -> bool {
716 (pose_translation.vector - check_xyz.vector).norm() <= tolerance
717 }
718
719 fn filter_constraints_compliant(&self, solutions: Solutions) -> Solutions {
720 match &self.constraints {
721 Some(constraints) => constraints.filter(&solutions),
722 None => solutions
723 }
724 }
725
726 fn constraints_compliant(&self, solution: Joints) -> bool {
727 match &self.constraints {
728 Some(constraints) => constraints.compliant(&solution),
729 None => true
730 }
731 }
732
733 fn sort_by_closeness(&self, solutions: &mut Solutions, previous: &Joints) {
736 let sorting_weight = self.constraints.as_ref()
737 .map_or(BY_PREV, |c| c.sorting_weight);
738 if sorting_weight == BY_PREV {
739 solutions.sort_by(|a, b| {
741 let distance_a = calculate_distance(a, previous);
742 let distance_b = calculate_distance(b, previous);
743 distance_a.partial_cmp(&distance_b).unwrap_or(std::cmp::Ordering::Equal)
744 });
745 } else {
746 let constraints = self.constraints.as_ref().unwrap();
747 solutions.sort_by(|a, b| {
748 let prev_a;
749 let prev_b;
750 if sorting_weight != BY_CONSTRAINS {
751 prev_a = calculate_distance(a, previous);
752 prev_b = calculate_distance(b, previous);
753 } else {
754 prev_a = 0.0;
756 prev_b = 0.0;
757 }
758
759 let constr_a = calculate_distance(a, &constraints.centers);
760 let constr_b = calculate_distance(b, &constraints.centers);
761
762 let distance_a = prev_a * (1.0 - sorting_weight) + constr_a * sorting_weight;
763 let distance_b = prev_b * (1.0 - sorting_weight) + constr_b * sorting_weight;
764 distance_a.partial_cmp(&distance_b).unwrap_or(std::cmp::Ordering::Equal)
765 });
766 }
767 }
768
769 fn constraint_centers(&self) -> &Joints {
771 self.constraints.as_ref()
772 .map_or(&JOINTS_AT_ZERO, |c| &c.centers)
773 }
774}
775
776fn is_close_to_multiple_of_pi(joint_value: f64, threshold: f64) -> bool {
778
779 let normalized_angle = joint_value.rem_euclid(2.0 * PI);
781 normalized_angle < threshold ||
783 (PI - normalized_angle).abs() < threshold
784}
785
786fn are_angles_close(angle1: f64, angle2: f64) -> bool {
787 let mut diff = (angle1 - angle2).abs();
788 diff = diff % (2.0 * PI);
789 while diff > PI {
790 diff = (2.0 * PI) - diff;
791 }
792 diff < SINGULARITY_ANGLE_THR
793}
794
795fn normalize_near(now: &mut f64, must_be_near: f64) {
802 let two_pi = 2.0 * PI;
803 let diff = (*now - must_be_near + PI).rem_euclid(two_pi) - PI;
805 *now = must_be_near + diff;
806}
807
808fn calculate_distance(joint1: &Joints, joint2: &Joints) -> f64 {
809 joint1.iter()
810 .zip(joint2.iter())
811 .map(|(a, b)| (a - b).abs())
812 .sum()
813}
814
815fn compare_poses(ta: &Isometry3<f64>, tb: &Isometry3<f64>,
817 distance_tolerance: f64, angular_tolerance: f64) -> bool {
818 let translation_distance = (ta.translation.vector - tb.translation.vector).norm();
819 let angular_distance = ta.rotation.angle_to(&tb.rotation);
820
821 if translation_distance.abs() > distance_tolerance {
822 if DEBUG {
823 println!("Positioning error: {}", translation_distance);
824 }
825 return false;
826 }
827
828 if angular_distance.abs() > angular_tolerance {
829 if DEBUG {
830 println!("Orientation errors: {}", angular_distance);
831 }
832 return false;
833 }
834 true
835}
836
837#[allow(dead_code)]
838fn dump_shifted_solutions(d: [f64; 3], ik: &Solutions) {
839 println!("Shifted solutions {} {} {}", d[0], d[1], d[2]);
840 for sol_idx in 0..ik.len() {
841 let mut row_str = String::new();
842 for joint_idx in 0..6 {
843 let computed = ik[sol_idx][joint_idx];
844 row_str.push_str(&format!("{:5.2} ", computed.to_degrees()));
845 }
846 println!("[{}]", row_str.trim_end()); }
848}
849
850#[cfg(test)]
851mod tests {
852 use super::*;
853 use crate::kinematic_traits::{Joints, Kinematics};
854 use crate::kinematics_impl::OPWKinematics;
855 use crate::parameters::opw_kinematics::Parameters;
856
857 #[test]
858 fn test_inverse_continuing_large_j6_angles() {
859 let robot = OPWKinematics::new(Parameters::irb2400_10());
860
861 let angles_deg:[f64; 10] = [-90000.0, -9000.0, -900.0, -90.0, -9.0, 9.0, 90.0, 900.0, 9000.0, 90000.0];
862
863 for &angle_deg in &angles_deg {
864 let j6_rad = angle_deg.to_radians();
865
866 let pose = robot.forward(&[0.0, 0.1, 0.2, 0.3, 0.1, j6_rad]);
867
868 let previous: Joints = [0.0, 0.1, 0.2, 0.3, 0.1, j6_rad];
869 let solutions = robot.inverse_continuing(&pose, &previous);
870
871 assert!(!solutions.is_empty(), "No solutions found for angle {} degrees", angle_deg);
872
873 let solution_j6 = solutions[0][J6];
874
875 let mut normalized_solution_j6 = solution_j6;
877 normalize_near(&mut normalized_solution_j6, previous[J6]);
878
879 let diff = (normalized_solution_j6 - previous[J6]).abs();
880
881 assert!(diff < 1e-6, "J6 mismatch for angle {} degrees: difference was {} radians", angle_deg, diff);
883 }
884 }
885
886 #[test]
887 fn test_inverse_continuing_adds_blended_solution_at_j5_pi() {
888 use std::f64::consts::PI;
889 use crate::kinematic_traits::{Joints, Kinematics, J4, J5, J6};
890
891 let robot = OPWKinematics::new(Parameters::irb2400_10());
893
894 let previous: Joints = [0.0, 0.1, 0.2, 0.3, PI, -0.8];
898
899 let delta = 0.20_f64;
902 let target: Joints = [
903 previous[0],
904 previous[1],
905 previous[2],
906 previous[J4] + delta,
907 previous[J5], previous[J6] - delta,
909 ];
910
911 let pose = robot.forward(&target);
913
914 let base = robot.inverse(&pose);
916 assert!(
917 !base.is_empty(),
918 "baseline IK returned no solutions for the target pose"
919 );
920
921 let cont = robot.inverse_continuing(&pose, &previous);
924 assert!(
925 !cont.is_empty(),
926 "inverse_continuing returned no solutions"
927 );
928 assert_eq!(
929 cont.len(),
930 base.len() + 1,
931 "expected one additional blended continuity solution at the J5≈π singularity"
932 );
933
934 let mut best = cont[0];
936 for j in 0..6 {
937 normalize_near(&mut best[j], previous[j]);
938 }
939
940 let d4 = best[J4] - previous[J4];
942 let d6 = best[J6] - previous[J6];
943 assert!(
944 (d4 + d6).abs() < 1e-6,
945 "expected opposite-direction update at J5≈π, got δ4={} δ6={}",
946 d4,
947 d6
948 );
949
950 let mut best_diff = best[J4] - best[J6];
952 let target_diff = target[J4] - target[J6];
953 normalize_near(&mut best_diff, target_diff);
954 assert!(
955 (best_diff - target_diff).abs() < 1e-6,
956 "q4 - q6 mismatch: got {}, want {}",
957 best_diff,
958 target_diff
959 );
960 }
961
962}
963