rs_opw_kinematics/
kinematics_with_shape.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
//! Defines a structure that combines the kinematic model of a robot with its geometrical shape.
//! This struct provides both the kinematic functionality for computing joint positions and
//! the physical structure used for collision detection and other geometric calculations.

use crate::collisions::{BaseBody, CheckMode, CollisionBody, RobotBody, SafetyDistances};
use crate::constraints::Constraints;
use crate::kinematic_traits::{Joints, Kinematics, Pose, Singularity, Solutions, J6};
use crate::kinematics_impl::OPWKinematics;
use crate::parameters::opw_kinematics::Parameters;
use crate::tool::{Base, Tool};
use nalgebra::Isometry3;
use parry3d::shape::TriMesh;
use std::sync::Arc;

/// Struct that combines the kinematic model of a robot with its geometrical shape.
/// This struct provides both the kinematic functionality for computing joint positions and
/// the physical structure used for collision detection and other geometric calculations.
pub struct KinematicsWithShape {
    /// The kinematic model of the robot, typically used to compute forward and inverse kinematics.
    /// This is an abstract trait (`Kinematics`), allowing for different implementations of kinematic models.
    pub kinematics: Arc<dyn Kinematics>,

    /// The physical structure of the robot, represented by its joint geometries.
    /// This `RobotBody` contains information about the geometrical shapes of the joints
    /// and provides functionality for collision detection.
    pub body: RobotBody,
}

impl KinematicsWithShape {
    /// Constructs a new `KinematicsWithShape` instance for a 6DOF robot.
    /// This method consumes all parameters, moving them inside the robot.
    /// This is important for meshes that are bulky.
    ///
    /// # Parameters
    ///
    /// * `opw_parameters` - OPW parameters defining this robot
    ///
    /// * `constraints`: joint constraint limits
    ///
    /// * `joint_meshes` - An array of [`TriMesh; 6`] representing the meshes for each joint's body.
    ///
    /// * `base_mesh` - The mesh of the robot base.
    /// * `base_transform` - The transform to apply to the base mesh. This transform brings
    /// the robot into its intended location inside the robotic cell.
    ///
    /// * `tool_mesh` - The mesh of the robot's tool, which will also be checked for collisions.
    /// * `tool_transform` - The transform to apply to the tool mesh. It defines the location
    /// of the "action point" of the robot whose location and rotation (pose) is the pose
    /// for direct and inverse kinematics calls.
    ///
    /// * `collision_environment` - A vector of collision objects represented by `CollisionBody`,
    /// where each object includes a mesh and its transform.
    ///
    /// * `first_pose_only` - As collision check may be expensive, check if we need multiple
    ///  checked solutions if inverse kinematics, or the first (best) is enough
    ///
    /// # Returns
    ///
    /// A `KinematicsWithShape` instance with defined kinematic structure and shapes for collision detection.
    pub fn new(
        opw_parameters: Parameters,
        constraints: Constraints,
        joint_meshes: [TriMesh; 6],
        base_mesh: TriMesh,
        base_transform: Isometry3<f64>,
        tool_mesh: TriMesh,
        tool_transform: Isometry3<f64>,
        collision_environment: Vec<CollisionBody>,
        first_collision_only: bool,
    ) -> Self {
        KinematicsWithShape {
            kinematics: Arc::new(Self::create_robot_with_base_and_tool(
                base_transform,
                tool_transform,
                opw_parameters,
                constraints,
            )),
            body: RobotBody {
                joint_meshes,
                base: Some(BaseBody {
                    mesh: base_mesh,
                    base_pose: base_transform.cast(),
                }),
                tool: Some(tool_mesh),
                collision_environment,
                safety: SafetyDistances::standard(
                    if first_collision_only {
                        CheckMode::FirstCollisionOnly
                    } else {
                        CheckMode::AllCollsions
                    }),
            },
        }
    }

    /// Constructs a new `KinematicsWithShape` instance for a 6DOF robot.
    /// This method consumes all parameters, moving them inside the robot.
    /// This is important for meshes that are bulky.
    ///
    /// # Parameters
    ///
    /// * `opw_parameters` - OPW parameters defining this robot
    ///
    /// * `constraints`: joint constraint limits
    ///
    /// * `joint_meshes` - An array of [`TriMesh; 6`] representing the meshes for each joint's body.
    ///
    /// * `base_mesh` - The mesh of the robot base.
    /// * `base_transform` - The transform to apply to the base mesh. This transform brings
    /// the robot into its intended location inside the robotic cell.
    ///
    /// * `tool_mesh` - The mesh of the robot's tool, which will also be checked for collisions.
    /// * `tool_transform` - The transform to apply to the tool mesh. It defines the location
    /// of the "action point" of the robot whose location and rotation (pose) is the pose
    /// for direct and inverse kinematics calls.
    ///
    /// * `collision_environment` - A vector of collision objects represented by `CollisionBody`,
    /// where each object includes a mesh and its transform.
    ///
    /// * `safety` - defines the minimal allowed distances between collision objects
    /// and specifies other details on how collisions are checked. In practice
    /// robot must stay at some safe distance from collision objects rather than touching them.
    ///
    /// # Returns
    ///
    /// A `KinematicsWithShape` instance with defined kinematic structure and shapes for collision detection.
    pub fn with_safety(
        opw_parameters: Parameters,
        constraints: Constraints,
        joint_meshes: [TriMesh; 6],
        base_mesh: TriMesh,
        base_transform: Isometry3<f64>,
        tool_mesh: TriMesh,
        tool_transform: Isometry3<f64>,
        collision_environment: Vec<CollisionBody>,
        safety: SafetyDistances,
    ) -> Self {
        KinematicsWithShape {
            kinematics: Arc::new(Self::create_robot_with_base_and_tool(
                base_transform,
                tool_transform,
                opw_parameters,
                constraints,
            )),
            body: RobotBody {
                joint_meshes,
                base: Some(BaseBody {
                    mesh: base_mesh,
                    base_pose: base_transform.cast(),
                }),
                tool: Some(tool_mesh),
                collision_environment,
                safety: safety,
            },
        }
    }

    fn create_robot_with_base_and_tool(
        base_transform: Isometry3<f64>,
        tool_transform: Isometry3<f64>,
        opw_parameters: Parameters,
        constraints: Constraints,
    ) -> Tool {
        let plain_robot = OPWKinematics::new_with_constraints(opw_parameters, constraints);

        let robot_with_base = Base {
            robot: Arc::new(plain_robot),
            base: base_transform.clone(),
        };

        let robot_with_base_and_tool = Tool {
            robot: Arc::new(robot_with_base),
            tool: tool_transform.clone(),
        };

        robot_with_base_and_tool
    }
}

/// Struct representing a robot with positioned joints.
/// It contains a vector of references to `PositionedJoint`, where each joint has its precomputed global transform.
///
/// This struct is useful when performing operations that require knowing the precomputed positions and orientations
/// of all the joints in the robot, such as forward kinematics, inverse kinematics, and collision detection.
pub struct PositionedRobot<'a> {
    /// A vector of references to `PositionedJoint`, representing the joints and their precomputed transforms.
    pub joints: Vec<PositionedJoint<'a>>,
    pub tool: Option<PositionedJoint<'a>>,
    pub environment: Vec<&'a CollisionBody>,
}

/// Struct representing a positioned joint, which consists of a reference to a `JointBody`
/// and its precomputed combined transform. This is the global transform of the joint combined with
/// the local transforms of the collision shapes within the joint.
///
/// This struct simplifies access to the final world-space transform of the joint's shapes.
pub struct PositionedJoint<'a> {
    /// A reference to the associated `JointBody` that defines the shapes and collision behavior of the joint.
    pub joint_body: &'a TriMesh,

    /// The combined transformation matrix (Isometry3), representing the precomputed global position
    /// and orientation of the joint in the world space.
    pub transform: Isometry3<f32>,
}

impl KinematicsWithShape {
    /// Method to compute and return a `PositionedRobot` from the current `RobotBody` and a set of joint positions.
    ///
    /// This method uses the forward kinematics to compute the global transforms of each joint
    /// and then creates the corresponding `PositionedJoint` instances, which are collected into a `PositionedRobot`.
    ///
    /// # Arguments
    ///
    /// * `joint_positions` - A reference to the joint values (angles/positions) to compute the forward kinematics.
    ///
    /// # Returns
    ///
    /// * A new instance of `PositionedRobot` containing the positioned joints with precomputed transforms.
    pub fn positioned_robot(&self, joint_positions: &Joints) -> PositionedRobot {
        // Compute the global transforms for each joint using forward kinematics
        let global_transforms: [Isometry3<f32>; 6] = self
            .kinematics
            .forward_with_joint_poses(joint_positions)
            .map(|pose| pose.cast::<f32>());

        // Create a vector of PositionedJoints without mut
        let positioned_joints: Vec<PositionedJoint> = self
            .body
            .joint_meshes
            .iter()
            .enumerate()
            .map(|(i, joint_body)| PositionedJoint {
                joint_body,
                transform: global_transforms[i],
            })
            .collect();

        // Return the PositionedRobot with all the positioned joints
        let positioned_tool = if let Some(tool) = self.body.tool.as_ref() {
            Some(PositionedJoint {
                joint_body: tool,
                transform: global_transforms[J6], // TCP pose
            })
        } else {
            None
        };

        // Convert to vector of references
        let referenced_environment = self.body.collision_environment.iter().collect();

        PositionedRobot {
            joints: positioned_joints,
            tool: positioned_tool,
            environment: referenced_environment,
        }
    }
}

impl KinematicsWithShape {
    /// Remove solutions that are reported as result the robot colliding with self or
    /// environment. Only retain non-colliding solutions. If safety distances are specified
    /// with the distance, also discards solutions where robot comes too close to
    /// collision rather than collides.
    pub(crate) fn remove_collisions(&self, solutions: Solutions) -> Solutions {
        let mut filtered_solutions = Vec::with_capacity(solutions.len());
        for solution in solutions {
            if !self.body.collides(&solution, self.kinematics.as_ref()) {
                filtered_solutions.push(solution);
            }
        }
        filtered_solutions
    }

    /// Check for collisions for the given joint position. Both self-collisions and collisions
    /// with environment are checked. This method simply returns true (if collides) or false (if not)
    // If safety distances are specified, also returns true for solutions where robot comes 
    // too close to collision rather than collides.
    pub fn collides(&self, joints: &Joints) -> bool {
        self.body.collides(joints, self.kinematics.as_ref())
    }

    /// Return non colliding offsets, tweaking each joint plus minus either side, either into
    /// 'to' or into 'from'. This is required for planning algorithms like A*. We can do
    ///  less collision checks as we only need to check the joint branch of the robot we moved.
    // If safety distances are specified, also discards solutions where robot comes 
    // too close to collision rather than collides. 
    pub fn non_colliding_offsets(&self, joints: &Joints, from: &Joints, to: &Joints) -> Solutions {
        self.body
            .non_colliding_offsets(joints, from, to, self.kinematics.as_ref())
    }

    /// Provide details about he collision, who with whom collides or comes too near.
    /// Depending on if the RobotBody has been configured for complete check,
    /// either all collisions or only the first found colliding pair
    /// is returned.
    pub fn collision_details(&self, joints: &Joints) -> Vec<(usize, usize)> {
        self.body
            .collision_details(joints, self.kinematics.as_ref())
    }

    /// Allows overriding collision check with the custom instance of safety distance.
    /// This is needed when the safety configuration is adaptive/dynamic rather than fixed.    
    pub fn near(&self, joints: &Joints, safety: &SafetyDistances) -> Vec<(usize, usize)> {
        self.body.near(joints, self.kinematics.as_ref(), safety)
    }
}

impl Kinematics for KinematicsWithShape {
    /// Delegates call to underlying Kinematics, but will filter away colliding poses
    fn inverse(&self, pose: &Pose) -> Solutions {
        let solutions = self.kinematics.inverse(pose);
        self.remove_collisions(solutions)
    }

    /// Delegates call to underlying Kinematics, but will filter away colliding poses
    fn inverse_continuing(&self, pose: &Pose, previous: &Joints) -> Solutions {
        let solutions = self.kinematics.inverse_continuing(pose, previous);
        self.remove_collisions(solutions)
    }

    fn forward(&self, qs: &Joints) -> Pose {
        self.kinematics.forward(qs)
    }

    /// Delegates call to underlying Kinematics, but will filter away colliding poses
    fn inverse_5dof(&self, pose: &Pose, j6: f64) -> Solutions {
        let solutions = self.kinematics.inverse_5dof(pose, j6);
        self.remove_collisions(solutions)
    }

    /// Delegates call to underlying Kinematics, but will filter away colliding poses
    fn inverse_continuing_5dof(&self, pose: &Pose, prev: &Joints) -> Solutions {
        let solutions = self.kinematics.inverse_continuing_5dof(pose, prev);
        self.remove_collisions(solutions)
    }

    fn constraints(&self) -> &Option<Constraints> {
        self.kinematics.constraints()
    }

    fn kinematic_singularity(&self, qs: &Joints) -> Option<Singularity> {
        self.kinematics.kinematic_singularity(qs)
    }

    fn forward_with_joint_poses(&self, joints: &Joints) -> [Pose; 6] {
        self.kinematics.forward_with_joint_poses(joints)
    }
}