openrr_client/clients/
ik_client.rs

1use std::sync::Arc;
2
3use arci::{Error, JointTrajectoryClient, TrajectoryPoint, WaitFuture};
4use k::{nalgebra as na, Constraints, Isometry3};
5use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
6use serde::{Deserialize, Serialize};
7
8pub fn isometry(x: f64, y: f64, z: f64, roll: f64, pitch: f64, yaw: f64) -> k::Isometry3<f64> {
9    k::Isometry3::from_parts(
10        k::Translation3::new(x, y, z),
11        k::UnitQuaternion::from_euler_angles(roll, pitch, yaw),
12    )
13}
14
15#[derive(Debug)]
16pub struct IkSolverParameters {
17    pub allowable_position_error: f64, // unit: m
18    pub allowable_angle_error: f64,    // unit: rad
19    pub jacobian_multiplier: f64,
20    pub num_max_try: usize,
21}
22
23pub fn create_jacobian_ik_solver(parameters: &IkSolverParameters) -> k::JacobianIkSolver<f64> {
24    k::JacobianIkSolver::new(
25        parameters.allowable_position_error,
26        parameters.allowable_angle_error,
27        parameters.jacobian_multiplier,
28        parameters.num_max_try,
29    )
30}
31
32pub fn create_random_jacobian_ik_solver(
33    parameters: &IkSolverParameters,
34) -> openrr_planner::RandomInitializeIkSolver<f64, k::JacobianIkSolver<f64>> {
35    openrr_planner::RandomInitializeIkSolver::new(
36        create_jacobian_ik_solver(parameters),
37        parameters.num_max_try,
38    )
39}
40
41pub struct IkSolverWithChain {
42    ik_arm: k::SerialChain<f64>,
43    ik_solver: Arc<dyn k::InverseKinematicsSolver<f64> + Send + Sync>,
44    constraints: Constraints,
45}
46
47impl IkSolverWithChain {
48    pub fn end_transform(&self) -> k::Isometry3<f64> {
49        self.ik_arm.end_transform()
50    }
51
52    pub fn joint_names(&self) -> Vec<String> {
53        self.ik_arm
54            .iter_joints()
55            .map(|joint| joint.name.clone())
56            .collect()
57    }
58
59    pub fn joint_positions(&self) -> Vec<f64> {
60        self.ik_arm.joint_positions()
61    }
62
63    pub fn solve_with_constraints(
64        &self,
65        target_pose: &k::Isometry3<f64>,
66        constraints: &Constraints,
67    ) -> Result<(), Error> {
68        self.ik_solver
69            .solve_with_constraints(&self.ik_arm, target_pose, constraints)
70            .map_err(|e| Error::Other(e.into()))
71    }
72
73    pub fn solve(&self, target_pose: &k::Isometry3<f64>) -> Result<(), Error> {
74        self.solve_with_constraints(target_pose, &self.constraints)
75    }
76
77    pub fn set_joint_positions_clamped(&self, positions: &[f64]) {
78        self.ik_arm.set_joint_positions_clamped(positions)
79    }
80
81    pub fn new(
82        arm: k::SerialChain<f64>,
83        ik_solver: Arc<dyn k::InverseKinematicsSolver<f64> + Send + Sync>,
84        constraints: Constraints,
85    ) -> Self {
86        Self {
87            ik_arm: arm,
88            ik_solver,
89            constraints,
90        }
91    }
92
93    pub fn constraints(&self) -> &Constraints {
94        &self.constraints
95    }
96
97    pub fn generate_trajectory_with_interpolation(
98        &self,
99        current_pose: &Isometry3<f64>,
100        target_pose: &Isometry3<f64>,
101        duration_sec: f64,
102        max_resolution: f64,
103        min_number_of_points: i32,
104    ) -> Result<Vec<TrajectoryPoint>, Error> {
105        self.generate_trajectory_with_interpolation_and_constraints(
106            current_pose,
107            target_pose,
108            &self.constraints,
109            duration_sec,
110            max_resolution,
111            min_number_of_points,
112        )
113    }
114
115    pub fn generate_trajectory_with_interpolation_and_constraints(
116        &self,
117        current_pose: &Isometry3<f64>,
118        target_pose: &Isometry3<f64>,
119        constraints: &Constraints,
120        duration_sec: f64,
121        max_resolution: f64,
122        min_number_of_points: i32,
123    ) -> Result<Vec<TrajectoryPoint>, Error> {
124        let target_position = target_pose.translation.vector;
125        let target_rotation = target_pose.rotation;
126        let current_position = current_pose.translation.vector;
127        let current_rotation = current_pose.rotation;
128
129        let position_diff = target_position - current_position;
130        let n = std::cmp::max(
131            min_number_of_points,
132            (position_diff.norm() / max_resolution) as i32,
133        );
134        let mut traj = vec![];
135        if n == 0 {
136            self.solve_with_constraints(target_pose, constraints)?;
137            let trajectory = TrajectoryPoint::new(
138                self.joint_positions(),
139                std::time::Duration::from_secs_f64(duration_sec),
140            );
141            traj.push(trajectory);
142        } else {
143            for i in 1..n + 1 {
144                let t = i as f64 / n as f64;
145                let tar_pos = current_position.lerp(&target_position, t);
146                let tar_rot = current_rotation.slerp(&target_rotation, t);
147                self.solve_with_constraints(
148                    &k::Isometry3::from_parts(na::Translation3::from(tar_pos), tar_rot),
149                    constraints,
150                )?;
151                let trajectory = TrajectoryPoint::new(
152                    self.joint_positions(),
153                    std::time::Duration::from_secs_f64(t * duration_sec),
154                );
155                traj.push(trajectory);
156            }
157        }
158        Ok(traj)
159    }
160}
161
162pub struct IkClient<T>
163where
164    T: JointTrajectoryClient,
165{
166    pub client: T,
167    pub ik_solver_with_chain: Arc<IkSolverWithChain>,
168}
169
170impl<T> IkClient<T>
171where
172    T: JointTrajectoryClient + Send,
173{
174    pub fn new(client: T, ik_solver_with_chain: Arc<IkSolverWithChain>) -> Self {
175        if ik_solver_with_chain.ik_arm.dof() != client.joint_names().len() {
176            panic!(
177                "Invalid configuration : ik arm dof {} {:?} != joint_names length {} ({:?})",
178                ik_solver_with_chain.ik_arm.dof(),
179                ik_solver_with_chain
180                    .ik_arm
181                    .iter_joints()
182                    .map(|j| j.name.to_owned())
183                    .collect::<Vec<_>>(),
184                client.joint_names().len(),
185                client.joint_names()
186            );
187        }
188        Self {
189            client,
190            ik_solver_with_chain,
191        }
192    }
193
194    pub fn current_end_transform(&self) -> Result<k::Isometry3<f64>, Error> {
195        let current_joint_angles = self.client.current_joint_positions()?;
196        self.set_joint_positions_clamped(&current_joint_angles);
197        Ok(self.ik_solver_with_chain.end_transform())
198    }
199
200    pub fn move_ik_with_constraints(
201        &self,
202        target_pose: &k::Isometry3<f64>,
203        constraints: &Constraints,
204        duration_sec: f64,
205    ) -> Result<WaitFuture, Error> {
206        self.ik_solver_with_chain
207            .solve_with_constraints(target_pose, constraints)?;
208
209        let positions = self.ik_solver_with_chain.joint_positions();
210        let duration = std::time::Duration::from_secs_f64(duration_sec);
211        self.client.send_joint_positions(positions, duration)
212    }
213
214    pub fn move_ik_with_interpolation_and_constraints(
215        &self,
216        target_pose: &k::Isometry3<f64>,
217        constraints: &Constraints,
218        duration_sec: f64,
219        max_resolution: f64,
220        min_number_of_points: i32,
221    ) -> Result<WaitFuture, Error> {
222        self.client.send_joint_trajectory(
223            self.ik_solver_with_chain
224                .generate_trajectory_with_interpolation_and_constraints(
225                    &self.current_end_transform()?,
226                    target_pose,
227                    constraints,
228                    duration_sec,
229                    max_resolution,
230                    min_number_of_points,
231                )?,
232        )
233    }
234
235    pub fn move_ik(
236        &self,
237        target_pose: &k::Isometry3<f64>,
238        duration_sec: f64,
239    ) -> Result<WaitFuture, Error> {
240        self.ik_solver_with_chain.solve(target_pose)?;
241
242        let positions = self.ik_solver_with_chain.joint_positions();
243        let duration = std::time::Duration::from_secs_f64(duration_sec);
244        self.client.send_joint_positions(positions, duration)
245    }
246
247    pub fn move_ik_with_interpolation(
248        &self,
249        target_pose: &k::Isometry3<f64>,
250        duration_sec: f64,
251        max_resolution: f64,
252        min_number_of_points: i32,
253    ) -> Result<WaitFuture, Error> {
254        self.client.send_joint_trajectory(
255            self.ik_solver_with_chain
256                .generate_trajectory_with_interpolation(
257                    &self.current_end_transform()?,
258                    target_pose,
259                    duration_sec,
260                    max_resolution,
261                    min_number_of_points,
262                )?,
263        )
264    }
265
266    /// Get relative pose from current pose of the IK target
267    pub fn transform(&self, relative_pose: &k::Isometry3<f64>) -> Result<k::Isometry3<f64>, Error> {
268        Ok(self.current_end_transform()? * relative_pose)
269    }
270
271    // Reset the kinematic model for IK calculation like Jacobian method
272    pub fn set_zero_pose_for_kinematics(&self) -> Result<(), Error> {
273        let zero_angles = vec![0.0; self.ik_solver_with_chain.ik_arm.dof()];
274        self.ik_solver_with_chain
275            .ik_arm
276            .set_joint_positions(&zero_angles)
277            .map_err(|e| Error::Other(e.into()))?;
278        Ok(())
279    }
280
281    pub fn constraints(&self) -> &Constraints {
282        self.ik_solver_with_chain.constraints()
283    }
284
285    pub fn set_joint_positions_clamped(&self, positions: &[f64]) {
286        self.ik_solver_with_chain
287            .set_joint_positions_clamped(positions)
288    }
289}
290
291impl<T> JointTrajectoryClient for IkClient<T>
292where
293    T: JointTrajectoryClient,
294{
295    fn joint_names(&self) -> Vec<String> {
296        self.client.joint_names()
297    }
298
299    fn current_joint_positions(&self) -> Result<Vec<f64>, Error> {
300        self.client.current_joint_positions()
301    }
302
303    fn send_joint_positions(
304        &self,
305        positions: Vec<f64>,
306        duration: std::time::Duration,
307    ) -> Result<WaitFuture, Error> {
308        self.client.send_joint_positions(positions, duration)
309    }
310
311    fn send_joint_trajectory(&self, trajectory: Vec<TrajectoryPoint>) -> Result<WaitFuture, Error> {
312        self.client.send_joint_trajectory(trajectory)
313    }
314}
315
316#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
317#[serde(deny_unknown_fields)]
318pub struct IkSolverConfig {
319    pub root_node_name: Option<String>,
320    pub ik_target: String,
321    #[serde(default)]
322    pub use_random_ik: bool,
323    #[serde(default = "default_allowable_position_error_m")]
324    pub allowable_position_error_m: f64,
325    #[serde(default = "default_allowable_angle_error_rad")]
326    pub allowable_angle_error_rad: f64,
327    #[serde(default = "default_jacobian_multiplier")]
328    pub jacobian_multiplier: f64,
329    #[serde(default = "default_num_max_try")]
330    pub num_max_try: usize,
331    #[serde(default)]
332    #[schemars(schema_with = "constraints_schema")]
333    pub constraints: Constraints,
334}
335
336fn default_allowable_position_error_m() -> f64 {
337    0.005
338}
339fn default_allowable_angle_error_rad() -> f64 {
340    0.005
341}
342fn default_jacobian_multiplier() -> f64 {
343    0.1
344}
345fn default_num_max_try() -> usize {
346    300
347}
348
349fn constraints_schema(gen: &mut SchemaGenerator) -> Schema {
350    fn default_true() -> bool {
351        true
352    }
353    // https://docs.rs/k/0.24/k/struct.Constraints.html
354    /// A bundle of flags determining which coordinates are constrained for a target
355    #[allow(dead_code)]
356    #[derive(Serialize, JsonSchema)]
357    struct ConstraintsSchema {
358        /// true means the constraint is used.
359        ///  The coordinates is the world, not the end of the arm.
360        #[serde(default = "default_true")]
361        position_x: bool,
362        #[serde(default = "default_true")]
363        position_y: bool,
364        #[serde(default = "default_true")]
365        position_z: bool,
366        #[serde(default = "default_true")]
367        rotation_x: bool,
368        #[serde(default = "default_true")]
369        rotation_y: bool,
370        #[serde(default = "default_true")]
371        rotation_z: bool,
372    }
373    impl Default for ConstraintsSchema {
374        fn default() -> Self {
375            Self {
376                position_x: default_true(),
377                position_y: default_true(),
378                position_z: default_true(),
379                rotation_x: default_true(),
380                rotation_y: default_true(),
381                rotation_z: default_true(),
382            }
383        }
384    }
385
386    ConstraintsSchema::json_schema(gen)
387}
388
389pub fn create_ik_solver_with_chain(
390    full_chain: &k::Chain<f64>,
391    config: &IkSolverConfig,
392) -> IkSolverWithChain {
393    let chain = if let Some(root_node_name) = &config.root_node_name {
394        k::SerialChain::from_end_to_root(
395            full_chain
396                .find(&config.ik_target)
397                .unwrap_or_else(|| panic!("ik_target: {} not found", config.ik_target)),
398            full_chain
399                .find(root_node_name)
400                .unwrap_or_else(|| panic!("root_node_name: {root_node_name} not found")),
401        )
402    } else {
403        k::SerialChain::from_end(
404            full_chain
405                .find(&config.ik_target)
406                .unwrap_or_else(|| panic!("ik_target: {} not found", config.ik_target)),
407        )
408    };
409
410    let parameters = IkSolverParameters {
411        allowable_position_error: config.allowable_position_error_m,
412        allowable_angle_error: config.allowable_angle_error_rad,
413        jacobian_multiplier: config.jacobian_multiplier,
414        num_max_try: config.num_max_try,
415    };
416
417    IkSolverWithChain::new(
418        chain,
419        if config.use_random_ik {
420            Arc::new(create_random_jacobian_ik_solver(&parameters))
421        } else {
422            Arc::new(create_jacobian_ik_solver(&parameters))
423        },
424        config.constraints.clone(),
425    )
426}