franka/
utils.rs

1// Copyright (c) 2021 Marco Boneberger
2// Licensed under the EUPL-1.2-or-later
3
4//! contains useful type definitions and conversion functions.
5use crate::robot::control_types::{Finishable, JointPositions};
6use crate::robot::robot_state::RobotState;
7use nalgebra::{Isometry3, Matrix4, MatrixMN, MatrixN, Rotation3, Vector3, VectorN, U6, U7};
8use std::time::Duration;
9
10/// converts a 4x4 column-major homogenous matrix to an Isometry
11pub fn array_to_isometry(array: &[f64; 16]) -> Isometry3<f64> {
12    let rot = Rotation3::from_matrix(
13        &Matrix4::from_column_slice(array)
14            .remove_column(3)
15            .remove_row(3),
16    );
17    Isometry3::from_parts(
18        Vector3::new(array[12], array[13], array[14]).into(),
19        rot.into(),
20    )
21}
22/// A Vector with 7 entries
23pub type Vector7 = VectorN<f64, U7>;
24/// A Matrix with 6 rows and 7 columns
25pub type Matrix6x7 = MatrixMN<f64, U6, U7>;
26/// A Matrix with 7 rows and 7 columns
27pub type Matrix7 = MatrixN<f64, U7>;
28/// An example showing how to generate a joint pose motion to a goal position. Adapted from:
29/// Wisama Khalil and Etienne Dombre. 2002. Modeling, Identification and Control of Robots
30/// (Kogan Page Science Paper edition).
31#[derive(Debug)]
32pub struct MotionGenerator {
33    q_goal: Vector7,
34    q_start: Vector7,
35    delta_q: Vector7,
36    dq_max_sync: Vector7,
37    t_1_sync: Vector7,
38    t_2_sync: Vector7,
39    t_f_sync: Vector7,
40    q_1: Vector7,
41
42    time: f64,
43    dq_max: Vector7,
44    ddq_max_start: Vector7,
45    ddq_max_goal: Vector7,
46}
47
48impl MotionGenerator {
49    const DELTA_Q_MOTION_FINISHED: f64 = 1e-6;
50    /// Creates a new  MotionGenerator instance for a target q.
51    ///
52    /// # Arguments
53    /// * `speed_factor` - General speed factor in range [0, 1].
54    /// * `q_goal` - Target joint positions.
55    pub fn new(speed_factor: f64, q_goal: &[f64; 7]) -> Self {
56        let time = 0.;
57        let q_goal = Vector7::from_row_slice(q_goal);
58        let dq_max = Vector7::from_row_slice(&[2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5]) * speed_factor;
59        let ddq_max_start = Vector7::from_row_slice(&[5.; 7]) * speed_factor;
60        let ddq_max_goal = Vector7::from_row_slice(&[5.; 7]) * speed_factor;
61
62        let dq_max_sync = Vector7::zeros();
63        let q_start = Vector7::zeros();
64        let delta_q = Vector7::zeros();
65        let t_1_sync = Vector7::zeros();
66        let t_2_sync = Vector7::zeros();
67        let t_f_sync = Vector7::zeros();
68        let q_1 = Vector7::zeros();
69        MotionGenerator {
70            q_goal,
71            q_start,
72            delta_q,
73            dq_max_sync,
74            t_1_sync,
75            t_2_sync,
76            t_f_sync,
77            q_1,
78            time,
79            dq_max,
80            ddq_max_start,
81            ddq_max_goal,
82        }
83    }
84    fn calculate_desired_values(&self, t: f64, delta_q_d: &mut Vector7) -> bool {
85        let sign_delta_q = Vector7::from_iterator(self.delta_q.iter().map(|&x| x.signum()));
86        let t_d = self.t_2_sync - self.t_1_sync;
87        let delta_t_2_sync = self.t_f_sync - self.t_2_sync;
88        let mut joint_motion_finished = [false; 7];
89        for i in 0..7 {
90            if self.delta_q[i].abs() < MotionGenerator::DELTA_Q_MOTION_FINISHED {
91                delta_q_d[i] = 0.;
92                joint_motion_finished[i] = true;
93            } else if t < self.t_1_sync[i] {
94                delta_q_d[i] = -1. / self.t_1_sync[i].powi(3)
95                    * self.dq_max_sync[i]
96                    * sign_delta_q[i]
97                    * (0.5 * t - self.t_1_sync[i])
98                    * t.powi(3);
99            } else if t >= self.t_1_sync[i] && t < self.t_2_sync[i] {
100                delta_q_d[i] =
101                    self.q_1[i] + (t - self.t_1_sync[i]) * self.dq_max_sync[i] * sign_delta_q[i];
102            } else if t >= self.t_2_sync[i] && t < self.t_f_sync[i] {
103                delta_q_d[i] = self.delta_q[i]
104                    + 0.5
105                        * (1. / delta_t_2_sync[i].powi(3)
106                            * (t - self.t_1_sync[i] - 2.0 * delta_t_2_sync[i] - t_d[i])
107                            * (t - self.t_1_sync[i] - t_d[i]).powi(3)
108                            + (2. * t - 2. * self.t_1_sync[i] - delta_t_2_sync[i] - 2. * t_d[i]))
109                        * self.dq_max_sync[i]
110                        * sign_delta_q[i];
111            } else {
112                delta_q_d[i] = self.delta_q[i];
113                joint_motion_finished[i] = true;
114            }
115        }
116        joint_motion_finished.iter().all(|i| *i)
117    }
118    fn calculate_synchronized_values(&mut self) {
119        let mut dq_max_reach = self.dq_max;
120        let mut t_f: Vector7 = Vector7::zeros();
121        let mut delta_t_2 = Vector7::zeros();
122        let mut t_1 = Vector7::zeros();
123
124        let mut delta_t_2_sync = Vector7::zeros();
125        let sign_delta_q = Vector7::from_iterator(self.delta_q.iter().map(|&x| x.signum()));
126        for i in 0..7 {
127            if self.delta_q[i].abs() > MotionGenerator::DELTA_Q_MOTION_FINISHED {
128                if self.delta_q[i].abs()
129                    < (3.0 / 4.0 * (self.dq_max[i].powi(2) / self.ddq_max_start[i])
130                        + 3. / 4. * (self.dq_max[i].powi(2) / self.ddq_max_goal[i]))
131                {
132                    dq_max_reach[i] = f64::sqrt(
133                        4. / 3.
134                            * self.delta_q[i]
135                            * sign_delta_q[i]
136                            * (self.ddq_max_start[i] * self.ddq_max_goal[i])
137                            / (self.ddq_max_start[i] + self.ddq_max_goal[i]),
138                    )
139                }
140                t_1[i] = 1.5 * dq_max_reach[i] / self.ddq_max_start[i];
141                delta_t_2[i] = 1.5 * dq_max_reach[i] / self.ddq_max_goal[i];
142                t_f[i] =
143                    t_1[i] / 2. + delta_t_2[i] / 2. + f64::abs(self.delta_q[i]) / dq_max_reach[i];
144            }
145        }
146        let max_t_f = t_f.max();
147        for i in 0..7 {
148            if self.delta_q[i].abs() > MotionGenerator::DELTA_Q_MOTION_FINISHED {
149                let a = 1.5 / 2. * (self.ddq_max_goal[i] + self.ddq_max_start[i]);
150                let b = -1. * max_t_f * self.ddq_max_goal[i] * self.ddq_max_start[i];
151                let c = f64::abs(self.delta_q[i]) * self.ddq_max_goal[i] * self.ddq_max_start[i];
152                let delta = f64::max(b * b - 4. * a * c, 0.);
153                self.dq_max_sync[i] = (-1. * b - f64::sqrt(delta)) / (2. * a);
154                self.t_1_sync[i] = 1.5 * self.dq_max_sync[i] / self.ddq_max_start[i];
155                delta_t_2_sync[i] = 1.5 * self.dq_max_sync[i] / self.ddq_max_goal[i];
156                self.t_f_sync[i] = self.t_1_sync[i] / 2.
157                    + delta_t_2_sync[i] / 2.
158                    + f64::abs(self.delta_q[i] / self.dq_max_sync[i]);
159                self.t_2_sync[i] = self.t_f_sync[i] - delta_t_2_sync[i];
160                self.q_1[i] = self.dq_max_sync[i] * sign_delta_q[i] * (0.5 * self.t_1_sync[i])
161            }
162        }
163    }
164    /// Sends joint position calculations
165    ///
166    /// # Arguments
167    /// `robot_state` -  Current state of the robot.
168    /// `period` - Duration of execution.
169    ///
170    /// # Return
171    /// Joint positions for use inside a control loop.
172    pub fn generate_motion(
173        &mut self,
174        robot_state: &RobotState,
175        period: &Duration,
176    ) -> JointPositions {
177        self.time += period.as_secs_f64();
178
179        if self.time == 0. {
180            self.q_start = Vector7::from_column_slice(&robot_state.q_d);
181            self.delta_q = self.q_goal - self.q_start;
182            self.calculate_synchronized_values();
183        }
184        let mut delta_q_d = Vector7::zeros();
185        let motion_finished = self.calculate_desired_values(self.time, &mut delta_q_d);
186
187        let mut output = JointPositions::new((self.q_start + delta_q_d).into());
188        output.set_motion_finished(motion_finished);
189        output
190    }
191}
192
193#[cfg(test)]
194mod test {
195    use crate::{array_to_isometry, Finishable, MotionGenerator, RobotState};
196    use nalgebra::Rotation3;
197    use std::time::Duration;
198
199    fn slice_compare(a: &[f64], b: &[f64], thresh: f64) {
200        for i in 0..a.len() {
201            float_compare(a[i], b[i], thresh);
202        }
203    }
204
205    fn float_compare(a: f64, b: f64, thresh: f64) {
206        assert!((a - b).abs() < thresh);
207    }
208
209    #[test]
210    fn motion_generator() {
211        let q_des = [
212            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
213            [
214                0.005530250638946291,
215                0.005530250638946291,
216                0.005530250638946291,
217                0.005530250638946291,
218                0.005530250638946291,
219                0.005530250638946291,
220                0.005530250638946291,
221            ],
222            [
223                0.039797560667125885,
224                0.039797560667125885,
225                0.039797560667125885,
226                0.039797560667125885,
227                0.039797560667125885,
228                0.039797560667125885,
229                0.039797560667125885,
230            ],
231            [
232                0.11931676725154987,
233                0.11931676725154987,
234                0.11931676725154987,
235                0.11931676725154987,
236                0.11931676725154987,
237                0.11931676725154987,
238                0.11931676725154987,
239            ],
240            [
241                0.24726937422589595,
242                0.24726937422589595,
243                0.24726937422589595,
244                0.24726937422589595,
245                0.24726937422589595,
246                0.24726937422589595,
247                0.24726937422589595,
248            ],
249            [
250                0.4135035520905085,
251                0.4135035520905085,
252                0.4135035520905085,
253                0.4135035520905085,
254                0.4135035520905085,
255                0.4135035520905085,
256                0.4135035520905085,
257            ],
258            [
259                0.5946171259214752,
260                0.5946171259214752,
261                0.5946171259214752,
262                0.5946171259214752,
263                0.5946171259214752,
264                0.5946171259214752,
265                0.5946171259214752,
266            ],
267            [
268                0.7595171113649393,
269                0.7595171113649393,
270                0.7595171113649393,
271                0.7595171113649393,
272                0.7595171113649393,
273                0.7595171113649393,
274                0.7595171113649393,
275            ],
276            [
277                0.8853832940789487,
278                0.8853832940789487,
279                0.8853832940789487,
280                0.8853832940789487,
281                0.8853832940789487,
282                0.8853832940789487,
283                0.8853832940789487,
284            ],
285            [
286                0.9626711625624701,
287                0.9626711625624701,
288                0.9626711625624701,
289                0.9626711625624701,
290                0.9626711625624701,
291                0.9626711625624701,
292                0.9626711625624701,
293            ],
294            [
295                0.9951695386478036,
296                0.9951695386478036,
297                0.9951695386478036,
298                0.9951695386478036,
299                0.9951695386478036,
300                0.9951695386478036,
301                0.9951695386478036,
302            ],
303            [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
304        ];
305        let mut state = RobotState::default();
306        let mut motion_generator = MotionGenerator::new(1.0, &[1.; 7]);
307        let mut joint_pos = motion_generator.generate_motion(&state, &Duration::from_secs_f64(0.0));
308        slice_compare(&joint_pos.q, &q_des[0], 1e-10);
309        let mut counter = 1;
310        while !joint_pos.is_finished() {
311            state.q_d = joint_pos.q;
312            joint_pos = motion_generator.generate_motion(&state, &Duration::from_secs_f64(0.1));
313            slice_compare(&joint_pos.q, &q_des[counter], 1e-10);
314            counter += 1;
315        }
316        assert_eq!(counter, q_des.len())
317    }
318
319    #[test]
320    fn array_to_isometry_nan_test_1() {
321        let o_t_ee_c = [
322            1.0,
323            -0.000000011046552201160267,
324            0.000000008312911920110592,
325            0.0,
326            -0.000000011046552077288223,
327            -0.9999999999999999,
328            -0.000000014901161362226844,
329            0.0,
330            0.000000008312912084717049,
331            0.000000014901161270397825,
332            -0.9999999999999999,
333            0.0,
334            0.30689056578595225,
335            -0.000000003883240449999549,
336            0.486882056335292,
337            1.0,
338        ];
339        let last_o_t_ee_c = [
340            0.9999903734042683,
341            -0.000000011046444370332864,
342            0.00000000831299559264306,
343            0.0,
344            -0.000000011046444370332864,
345            -0.9999903734042683,
346            -0.0000000021131709957245164,
347            0.0,
348            0.000000008313075597526054,
349            0.0000000021131912467324254,
350            -0.9999999999999999,
351            0.0,
352            0.30689056578595225,
353            -0.000000003883240449999549,
354            0.486882056335292,
355            1.0,
356        ];
357        let commanded_pose = array_to_isometry(&o_t_ee_c);
358        let last_commanded_pose = array_to_isometry(&last_o_t_ee_c);
359
360        let mut rot_diff: Rotation3<f64> = commanded_pose.rotation.to_rotation_matrix()
361            * last_commanded_pose
362                .rotation
363                .to_rotation_matrix()
364                .transpose();
365        rot_diff.renormalize();
366        for i in rot_diff.matrix().iter() {
367            assert!(i.is_finite());
368        }
369    }
370
371    #[test]
372    fn array_to_isometry_nan_test_2() {
373        let y = [
374            0.9999903734042686,
375            0.0000000002540163079878255,
376            -0.00000000012581154368346085,
377            0.0,
378            0.0000000002540163079878255,
379            -0.9999903734042686,
380            0.00000000004614105974113725,
381            0.0,
382            -0.00000000012581275483128821,
383            -0.000000000046141503958700795,
384            -1.0,
385            0.0,
386            0.30689056659844144,
387            0.0000000000692086410879149,
388            0.4868820527992277,
389            1.0,
390        ];
391        assert!(array_to_isometry(&y).rotation.angle().is_finite())
392    }
393}