1extern crate nalgebra as na;
45
46use std::sync::Arc;
47use na::{Isometry3};
48use nalgebra::{Translation3};
49use crate::constraints::Constraints;
50use crate::kinematic_traits::{Joints, Kinematics, Pose, Singularity, Solutions};
51
52
53#[derive(Clone)]
59pub struct Tool {
60 pub robot: Arc<dyn Kinematics>, pub tool: Isometry3<f64>,
64}
65
66#[derive(Clone)]
72pub struct Base {
73 pub robot: Arc<dyn Kinematics>, pub base: Isometry3<f64>,
77}
78
79
80impl Kinematics for Tool {
81 fn inverse(&self, tcp: &Pose) -> Solutions {
82 self.robot.inverse(&(tcp * self.tool.inverse()))
83 }
84
85 fn inverse_5dof(&self, tcp: &Pose, j6: f64) -> Solutions {
86 self.robot.inverse_5dof(&(tcp * self.tool.inverse()), j6)
87 }
88
89 fn inverse_continuing_5dof(&self, tcp: &Pose, previous: &Joints) -> Solutions {
90 self.robot.inverse_continuing(&(tcp * self.tool.inverse()), previous)
91 }
92
93 fn inverse_continuing(&self, tcp: &Pose, previous: &Joints) -> Solutions {
94 self.robot.inverse_continuing(&(tcp * self.tool.inverse()), previous)
95 }
96
97 fn forward(&self, qs: &Joints) -> Pose {
98 let tip_joint = self.robot.forward(qs);
100 let tcp = tip_joint * self.tool;
101 tcp
102 }
103
104 fn forward_with_joint_poses(&self, joints: &Joints) -> [Pose; 6] {
109 self.robot.forward_with_joint_poses(joints)
110 }
111
112 fn kinematic_singularity(&self, qs: &Joints) -> Option<Singularity> {
114 self.robot.kinematic_singularity(qs)
115 }
116
117 fn constraints(&self) -> &Option<Constraints> {
118 self.robot.constraints()
119 }
120}
121
122impl Kinematics for Base {
123 fn inverse(&self, tcp: &Pose) -> Solutions {
124 self.robot.inverse(&(self.base.inverse() * tcp))
125 }
126
127 fn inverse_continuing(&self, tcp: &Pose, previous: &Joints) -> Solutions {
128 self.robot.inverse_continuing(&(self.base.inverse() * tcp), &previous)
129 }
130
131 fn inverse_5dof(&self, tcp: &Pose, j6: f64) -> Solutions {
132 self.robot.inverse_5dof(&(self.base.inverse() * tcp), j6)
133 }
134
135 fn inverse_continuing_5dof(&self, tcp: &Pose, previous: &Joints) -> Solutions {
136 self.robot.inverse_continuing_5dof(&(self.base.inverse() * tcp), &previous)
137 }
138
139 fn forward(&self, joints: &Joints) -> Pose {
140 self.base * self.robot.forward(joints)
141 }
142
143 fn forward_with_joint_poses(&self, joints: &Joints) -> [Pose; 6] {
144 let mut poses = self.robot.forward_with_joint_poses(joints);
146
147 for pose in poses.iter_mut() {
149 *pose = self.base * *pose;
150 }
151
152 poses
153 }
154
155 fn kinematic_singularity(&self, qs: &Joints) -> Option<Singularity> {
156 self.robot.kinematic_singularity(qs)
157 }
158
159 fn constraints(&self) -> &Option<Constraints> {
160 self.robot.constraints()
161 }
162}
163
164#[derive(Clone)]
169pub struct LinearAxis {
170 robot: Arc<dyn Kinematics>,
171 axis: u32,
172 pub base: Isometry3<f64>,
174}
175
176#[derive(Clone)]
180pub struct Gantry {
181 robot: Arc<dyn Kinematics>,
182 pub base: Isometry3<f64>,
184}
185
186impl LinearAxis {
190 pub fn forward(&self, distance: f64, joint_angles: &[f64; 6]) -> Isometry3<f64> {
192 let cart_translation = match self.axis {
193 0 => Translation3::new(distance, 0.0, 0.0),
194 1 => Translation3::new(0.0, distance, 0.0),
195 2 => Translation3::new(0.0, 0.0, distance),
196 _ => panic!("Invalid axis index; must be 0 (x), 1 (y), or 2 (z)"),
197 };
198 let robot_pose = self.robot.forward(joint_angles);
199 self.base * cart_translation * robot_pose
200 }
201}
202
203impl Gantry {
204 pub fn forward(&self, translation: &Translation3<f64>, joint_angles: &[f64; 6]) -> Isometry3<f64> {
206 let robot_pose = self.robot.forward(joint_angles);
207 self.base * translation * robot_pose
208 }
209}
210
211
212#[cfg(test)]
213mod tests {
214 use std::f64::consts::PI;
215 use super::*;
216 use nalgebra::{Isometry3, Translation3, UnitQuaternion};
217 use crate::kinematics_impl::OPWKinematics;
218 use crate::parameters::opw_kinematics::Parameters;
219
220 pub(crate) fn assert_diff(a: &Translation3<f64>, b: &Translation3<f64>, expected_diff: [f64; 3], epsilon: f64) {
222 let actual_diff = a.vector - b.vector;
223
224 assert!(
225 (actual_diff.x - expected_diff[0]).abs() <= epsilon,
226 "X difference is not as expected: actual difference = {}, expected difference = {}",
227 actual_diff.x, expected_diff[0]
228 );
229 assert!(
230 (actual_diff.y - expected_diff[1]).abs() <= epsilon,
231 "Y difference is not as expected: actual difference = {}, expected difference = {}",
232 actual_diff.y, expected_diff[1]
233 );
234 assert!(
235 (actual_diff.z - expected_diff[2]).abs() <= epsilon,
236 "Z difference is not as expected: actual difference = {}, expected difference = {}",
237 actual_diff.z, expected_diff[2]
238 );
239 }
240
241 fn diff(robot_without: &dyn Kinematics, robot_with: &dyn Kinematics, joints: &[f64; 6]) -> (Pose, Pose) {
242 let tcp_without_tool = robot_without.forward(&joints);
243 let tcp_with_tool = robot_with.forward(&joints);
244 (tcp_without_tool, tcp_with_tool)
245 }
246
247 #[test]
248 fn test_tool() {
249 let robot_without_tool = OPWKinematics::new(Parameters::staubli_tx2_160l());
251
252 let tool_translation = Isometry3::from_parts(
254 Translation3::new(0.0, 0.0, 1.0).into(),
255 UnitQuaternion::identity(),
256 );
257
258 let robot_with_tool = Tool {
260 robot: Arc::new(robot_without_tool),
261 tool: tool_translation,
262 };
263
264 let joints = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
266
267 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
268 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation, [0., 0., 1.], 1E-6);
269
270 let joints = [0.0, 0.0, 0.0, 0.0, 0.0, PI / 6.0];
273 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
274 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation, [0., 0., 1.], 1E-6);
275
276 let joints = [0.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
278 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
279 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation, [1.0, 0.0, 0.], 1E-6);
280
281 let joints = [PI, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
283 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
284 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation, [-1.0, 0.0, 0.], 1E-6);
285
286 let joints = [PI / 2.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
288 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
289 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation, [0.0, 1.0, 0.], 1E-6);
290
291 let joints = [PI / 4.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
293 let catet = 45.0_f64.to_radians().sin();
294 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
295 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation,
296 [catet, catet, 0.], 1E-6);
297
298 let joints = [PI / 4.0, 0.0, 0.0, 0.0, PI / 4.0, 0.0];
300 let (tcp_without_tool, tcp_with_tool) = diff(&robot_without_tool, &robot_with_tool, &joints);
301 assert_diff(&tcp_with_tool.translation, &tcp_without_tool.translation,
302 [0.5, 0.5, 2.0_f64.sqrt() / 2.0], 1E-6);
303 }
304
305 #[test]
306 fn test_base() {
307 let robot_without_base = OPWKinematics::new(Parameters::staubli_tx2_160l());
309
310 let base_translation = Isometry3::from_parts(
312 Translation3::new(0.0, 0.0, 1.0).into(),
313 UnitQuaternion::identity(),
314 );
315
316 let robot_with_base = Base {
317 robot: Arc::new(robot_without_base),
318 base: base_translation,
319 };
320
321 let joints = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
323
324 let (tcp_without_base, tcp_with_base) = diff(&robot_without_base, &robot_with_base, &joints);
325 assert_diff(&tcp_with_base.translation, &tcp_without_base.translation, [0., 0., 1.], 1E-6);
326
327 let joints = [PI / 3.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
329 let (tcp_without_base, tcp_with_base) = diff(&robot_without_base, &robot_with_base, &joints);
330 assert_diff(&tcp_with_base.translation, &tcp_without_base.translation, [0.0, 0.0, 1.0], 1E-6);
331 }
332
333 #[test]
334 fn test_linear_axis_forward() {
335 let robot_without_base = OPWKinematics::new(Parameters::staubli_tx2_160l());
336 let base_translation = Isometry3::from_parts(
337 Translation3::new(0.1, 0.2, 0.3).into(),
338 UnitQuaternion::identity(),
339 );
340
341 let cart = LinearAxis {
342 robot: Arc::new(robot_without_base),
343 axis: 1,
344 base: base_translation,
345 };
346
347 let joint_angles = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
348 let result = cart.forward(7.0, &joint_angles);
349
350 assert_eq!(result.translation.vector.y,
351 robot_without_base.forward(&joint_angles).translation.vector.y + 7.0 + 0.2);
352 }
353
354 #[test]
355 fn test_gantry_forward() {
356 let robot_without_base = OPWKinematics::new(Parameters::staubli_tx2_160l());
357
358 let base_translation = Isometry3::from_parts(
359 Translation3::new(0.1, 0.2, 0.3).into(),
360 UnitQuaternion::identity(),
361 );
362
363 let gantry = Gantry {
364 robot: Arc::new(robot_without_base),
365 base: base_translation,
366 };
367
368 let joint_angles = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
369 let r = gantry.forward(
370 &Translation3::new(7.0, 8.0, 9.0),
371 &joint_angles).translation;
372
373 let alone = robot_without_base.forward(&joint_angles).translation;
374
375 assert_eq!(r.x, alone.x + 7.1);
377 assert_eq!(r.y, alone.y + 8.2);
378 assert_eq!(r.z, alone.z + 9.3);
379 }
380
381 #[test]
384 fn test_complete_robot() {
385 fn diff(alone: &dyn Kinematics, riding: &LinearAxis, axis: f64, joints: &[f64; 6]) -> (Pose, Pose) {
386 let tcp_alone = alone.forward(&joints);
387 let tcp = riding.forward(axis, &joints);
388 (tcp_alone, tcp)
389 }
390
391 let robot_alone = OPWKinematics::new(Parameters::staubli_tx2_160l());
392
393 let pedestal = 0.5;
395 let base_translation = Isometry3::from_parts(
396 Translation3::new(0.0, 0.0, pedestal).into(),
397 UnitQuaternion::identity(),
398 );
399
400 let robot_with_base = Base {
401 robot: Arc::new(robot_alone),
402 base: base_translation,
403 };
404
405 let sword = 1.0;
407 let tool_translation = Isometry3::from_parts(
408 Translation3::new(0.0, 0.0, sword).into(),
409 UnitQuaternion::identity(),
410 );
411
412 let robot_complete = Tool {
414 robot: Arc::new(robot_with_base),
415 tool: tool_translation,
416 };
417
418
419 let gantry_base = 0.75;
421 let gantry_translation = Isometry3::from_parts(
422 Translation3::new(0.0, gantry_base, 0.0).into(),
423 UnitQuaternion::identity(),
424 );
425
426 let riding_robot = LinearAxis {
427 robot: Arc::new(robot_complete),
428 axis: 0,
429 base: gantry_translation,
430 };
431
432 let joints = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
434 let axis = 0.0;
435
436 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
437 assert_diff(&tcp.translation, &tcp_alone.translation,
438 [0., gantry_base, pedestal + sword], 1E-6);
439
440 let joints = [0.0, 0.0, 0.0, 0.0, 0.0, PI / 6.0];
443 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
444 assert_diff(&tcp.translation, &tcp_alone.translation,
445 [0., gantry_base, pedestal + sword], 1E-6);
446
447 let joints = [0.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
449 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
450 assert_diff(&tcp.translation, &tcp_alone.translation,
451 [sword, gantry_base, pedestal], 1E-6);
452
453 let joints = [PI, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
455 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
456 assert_diff(&tcp.translation, &tcp_alone.translation,
457 [-sword, gantry_base, pedestal], 1E-6);
458
459 let joints = [PI / 2.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
461 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
462 assert_diff(&tcp.translation, &tcp_alone.translation,
463 [0.0, gantry_base + sword, pedestal], 1E-6);
464
465 let joints = [PI / 4.0, 0.0, 0.0, 0.0, PI / 2.0, 0.0];
467 let catet = 45.0_f64.to_radians().sin();
468 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
469 assert_diff(&tcp.translation, &tcp_alone.translation,
470 [catet, catet + gantry_base, pedestal], 1E-6);
471
472 let joints = [PI / 4.0, 0.0, 0.0, 0.0, PI / 4.0, 0.0];
474 let (tcp_alone, tcp) = diff(&robot_alone, &riding_robot, axis, &joints);
475 assert_diff(&tcp.translation, &tcp_alone.translation,
476 [sword * 0.5, sword * 0.5 + gantry_base, sword * 2.0_f64.sqrt() / 2.0 + pedestal], 1E-6);
477
478
479 let ride = 10.0;
481 let tcp_translation = riding_robot.forward(ride, &joints).translation;
482 assert_diff(&tcp_translation, &tcp_alone.translation,
483 [sword * 0.5 + ride, sword * 0.5 + gantry_base,
484 sword * 2.0_f64.sqrt() / 2.0 + pedestal], 1E-6);
485 }
486}
487