1use crate::duration::Duration;
6use crate::errors::Errors;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub enum RobotMode {
12 Other,
14 Idle,
16 Move,
18 Guiding,
21 Reflex,
24 UserStopped,
26 AutomaticErrorRecovery,
29}
30
31impl RobotMode {
32 pub const fn from_u8(v: u8) -> Option<RobotMode> {
34 Some(match v {
35 0 => RobotMode::Other,
36 1 => RobotMode::Idle,
37 2 => RobotMode::Move,
38 3 => RobotMode::Guiding,
39 4 => RobotMode::Reflex,
40 5 => RobotMode::UserStopped,
41 6 => RobotMode::AutomaticErrorRecovery,
42 _ => return None,
43 })
44 }
45}
46
47impl std::fmt::Display for RobotMode {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 let s = match self {
51 RobotMode::Other => "Other",
52 RobotMode::Idle => "Idle",
53 RobotMode::Move => "Move",
54 RobotMode::Guiding => "Guiding",
55 RobotMode::Reflex => "Reflex",
56 RobotMode::UserStopped => "User stopped",
57 RobotMode::AutomaticErrorRecovery => "Automatic error recovery",
58 };
59 f.write_str(s)
60 }
61}
62
63pub const IDENTITY_TRANSFORM: [f64; 16] = [
65 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
66];
67
68#[derive(Debug, Clone, Copy, PartialEq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct RobotState {
76 pub O_T_EE: [f64; 16],
78 pub O_T_EE_d: [f64; 16],
80 pub F_T_EE: [f64; 16],
82 pub F_T_NE: [f64; 16],
84 pub NE_T_EE: [f64; 16],
86 pub EE_T_K: [f64; 16],
88 pub m_ee: f64,
90 pub I_ee: [f64; 9],
92 pub F_x_Cee: [f64; 3],
94 pub m_load: f64,
96 pub I_load: [f64; 9],
98 pub F_x_Cload: [f64; 3],
100 pub m_total: f64,
102 pub I_total: [f64; 9],
104 pub F_x_Ctotal: [f64; 3],
106 pub elbow: [f64; 2],
108 pub elbow_d: [f64; 2],
110 pub elbow_c: [f64; 2],
112 pub delbow_c: [f64; 2],
114 pub ddelbow_c: [f64; 2],
116 pub tau_J: [f64; 7],
118 pub tau_J_d: [f64; 7],
120 pub dtau_J: [f64; 7],
122 pub q: [f64; 7],
124 pub q_d: [f64; 7],
126 pub dq: [f64; 7],
128 pub dq_d: [f64; 7],
130 pub ddq_d: [f64; 7],
132 pub joint_contact: [f64; 7],
134 pub cartesian_contact: [f64; 6],
136 pub joint_collision: [f64; 7],
138 pub cartesian_collision: [f64; 6],
140 pub tau_ext_hat_filtered: [f64; 7],
142 pub O_F_ext_hat_K: [f64; 6],
144 pub K_F_ext_hat_K: [f64; 6],
146 pub O_dP_EE_d: [f64; 6],
148 pub O_ddP_O: [f64; 3],
150 pub O_T_EE_c: [f64; 16],
152 pub O_dP_EE_c: [f64; 6],
154 pub O_ddP_EE_c: [f64; 6],
156 pub theta: [f64; 7],
158 pub dtheta: [f64; 7],
160 pub accelerometer_top: [[f64; 3]; 6],
162 pub accelerometer_bottom: [[f64; 3]; 6],
164 pub current_errors: Errors,
166 pub last_motion_errors: Errors,
168 pub control_command_success_rate: f64,
170 pub robot_mode: RobotMode,
172 pub time: Duration,
174}
175
176impl Default for RobotState {
177 fn default() -> Self {
180 RobotState {
181 O_T_EE: IDENTITY_TRANSFORM,
182 O_T_EE_d: IDENTITY_TRANSFORM,
183 F_T_EE: IDENTITY_TRANSFORM,
184 F_T_NE: IDENTITY_TRANSFORM,
185 NE_T_EE: IDENTITY_TRANSFORM,
186 EE_T_K: IDENTITY_TRANSFORM,
187 m_ee: 0.0,
188 I_ee: [0.0; 9],
189 F_x_Cee: [0.0; 3],
190 m_load: 0.0,
191 I_load: [0.0; 9],
192 F_x_Cload: [0.0; 3],
193 m_total: 0.0,
194 I_total: [0.0; 9],
195 F_x_Ctotal: [0.0; 3],
196 elbow: [0.0; 2],
197 elbow_d: [0.0; 2],
198 elbow_c: [0.0; 2],
199 delbow_c: [0.0; 2],
200 ddelbow_c: [0.0; 2],
201 tau_J: [0.0; 7],
202 tau_J_d: [0.0; 7],
203 dtau_J: [0.0; 7],
204 q: [0.0; 7],
205 q_d: [0.0; 7],
206 dq: [0.0; 7],
207 dq_d: [0.0; 7],
208 ddq_d: [0.0; 7],
209 joint_contact: [0.0; 7],
210 cartesian_contact: [0.0; 6],
211 joint_collision: [0.0; 7],
212 cartesian_collision: [0.0; 6],
213 tau_ext_hat_filtered: [0.0; 7],
214 O_F_ext_hat_K: [0.0; 6],
215 K_F_ext_hat_K: [0.0; 6],
216 O_dP_EE_d: [0.0; 6],
217 O_ddP_O: [0.0, 0.0, -9.81],
218 O_T_EE_c: IDENTITY_TRANSFORM,
219 O_dP_EE_c: [0.0; 6],
220 O_ddP_EE_c: [0.0; 6],
221 theta: [0.0; 7],
222 dtheta: [0.0; 7],
223 accelerometer_top: [[0.0; 3]; 6],
224 accelerometer_bottom: [[0.0; 3]; 6],
225 current_errors: Errors::default(),
226 last_motion_errors: Errors::default(),
227 control_command_success_rate: 0.0,
228 robot_mode: RobotMode::Other,
229 time: Duration::default(),
230 }
231 }
232}
233
234pub fn combine_center_of_mass(
239 m_ee: f64,
240 F_x_Cee: &[f64; 3],
241 m_load: f64,
242 F_x_Cload: &[f64; 3],
243) -> [f64; 3] {
244 let mut F_x_Ctotal = [0.0; 3];
245 if (m_ee + m_load) > 0.0 {
246 for i in 0..3 {
247 F_x_Ctotal[i] = (m_ee * F_x_Cee[i] + m_load * F_x_Cload[i]) / (m_ee + m_load);
248 }
249 }
250 F_x_Ctotal
251}
252
253fn skew_symmetric(v: &[f64; 3]) -> nalgebra::Matrix3<f64> {
255 nalgebra::Matrix3::new(0.0, -v[2], v[1], v[2], 0.0, -v[0], -v[1], v[0], 0.0)
256}
257
258#[allow(clippy::too_many_arguments)]
266pub fn combine_inertia_tensor(
267 m_ee: f64,
268 F_x_Cee: &[f64; 3],
269 I_ee: &[f64; 9],
270 m_load: f64,
271 F_x_Cload: &[f64; 3],
272 I_load: &[f64; 9],
273 m_total: f64,
274 F_x_Ctotal: &[f64; 3],
275) -> [f64; 9] {
276 if m_total == 0.0 {
277 return [0.0; 9];
278 }
279
280 let mut inertia_ee = nalgebra::Matrix3::from_column_slice(I_ee);
281 let mut inertia_load = nalgebra::Matrix3::from_column_slice(I_load);
282 if m_ee == 0.0 {
283 inertia_ee = nalgebra::Matrix3::zeros();
284 }
285 if m_load == 0.0 {
286 inertia_load = nalgebra::Matrix3::zeros();
287 }
288
289 let skew_ee = skew_symmetric(F_x_Cee);
290 let skew_load = skew_symmetric(F_x_Cload);
291 let skew_total = skew_symmetric(F_x_Ctotal);
292
293 let inertia_ee_flange = inertia_ee - m_ee * (skew_ee * skew_ee);
294 let inertia_load_flange = inertia_load - m_load * (skew_load * skew_load);
295 let inertia_total =
296 inertia_ee_flange + inertia_load_flange + m_total * (skew_total * skew_total);
297
298 let mut out = [0.0; 9];
299 out.copy_from_slice(inertia_total.as_slice());
300 out
301}
302
303impl RobotState {
304 pub fn from_wire(w: &crate::wire::robot::RobotState) -> RobotState {
313 use crate::wire::{accel_to_f64, f32s_to_f64};
314
315 let m_ee = w.m_ee.get() as f64;
316 let m_load = w.m_load.get() as f64;
317 let I_ee = f32s_to_f64(&w.I_ee);
318 let I_load = f32s_to_f64(&w.I_load);
319 let F_x_Cee = f32s_to_f64(&w.F_x_Cee);
320 let F_x_Cload = f32s_to_f64(&w.F_x_Cload);
321
322 let m_total = f64::from(w.m_ee.get() + w.m_load.get());
326 let F_x_Ctotal = combine_center_of_mass(m_ee, &F_x_Cee, m_load, &F_x_Cload);
327 let I_total = combine_inertia_tensor(
328 m_ee,
329 &F_x_Cee,
330 &I_ee,
331 m_load,
332 &F_x_Cload,
333 &I_load,
334 m_total,
335 &F_x_Ctotal,
336 );
337
338 RobotState {
339 O_T_EE: f32s_to_f64(&w.O_T_EE),
340 O_T_EE_d: f32s_to_f64(&w.O_T_EE_d),
341 F_T_EE: f32s_to_f64(&w.F_T_EE),
342 F_T_NE: f32s_to_f64(&w.F_T_NE),
343 NE_T_EE: f32s_to_f64(&w.NE_T_EE),
344 EE_T_K: f32s_to_f64(&w.EE_T_K),
345 m_ee,
346 I_ee,
347 F_x_Cee,
348 m_load,
349 I_load,
350 F_x_Cload,
351 m_total,
352 I_total,
353 F_x_Ctotal,
354 elbow: f32s_to_f64(&w.elbow),
355 elbow_d: f32s_to_f64(&w.elbow_d),
356 elbow_c: f32s_to_f64(&w.elbow_c),
357 delbow_c: f32s_to_f64(&w.delbow_c),
358 ddelbow_c: f32s_to_f64(&w.ddelbow_c),
359 tau_J: f32s_to_f64(&w.tau_J),
360 tau_J_d: f32s_to_f64(&w.tau_J_d),
361 dtau_J: f32s_to_f64(&w.dtau_J),
362 q: f32s_to_f64(&w.q),
363 q_d: f32s_to_f64(&w.q_d),
364 dq: f32s_to_f64(&w.dq),
365 dq_d: f32s_to_f64(&w.dq_d),
366 ddq_d: f32s_to_f64(&w.ddq_d),
367 joint_contact: f32s_to_f64(&w.joint_contact),
368 cartesian_contact: f32s_to_f64(&w.cartesian_contact),
369 joint_collision: f32s_to_f64(&w.joint_collision),
370 cartesian_collision: f32s_to_f64(&w.cartesian_collision),
371 tau_ext_hat_filtered: f32s_to_f64(&w.tau_ext_hat_filtered),
372 O_F_ext_hat_K: f32s_to_f64(&w.O_F_ext_hat_K),
373 K_F_ext_hat_K: f32s_to_f64(&w.K_F_ext_hat_K),
374 O_dP_EE_d: f32s_to_f64(&w.O_dP_EE_d),
375 O_ddP_O: f32s_to_f64(&w.O_ddP_O),
376 O_T_EE_c: f32s_to_f64(&w.O_T_EE_c),
377 O_dP_EE_c: f32s_to_f64(&w.O_dP_EE_c),
378 O_ddP_EE_c: f32s_to_f64(&w.O_ddP_EE_c),
379 theta: f32s_to_f64(&w.theta),
380 dtheta: f32s_to_f64(&w.dtheta),
381 accelerometer_top: accel_to_f64(&w.accelerometer_top),
382 accelerometer_bottom: accel_to_f64(&w.accelerometer_bottom),
383 current_errors: Errors::from(w.errors),
384 last_motion_errors: Errors::from(w.reflex_reason),
385 control_command_success_rate: w.control_command_success_rate.get() as f64,
386 robot_mode: RobotMode::from_u8(w.robot_mode).unwrap_or(RobotMode::Other),
387 time: Duration::from_millis(w.message_id.get()),
388 }
389 }
390
391 pub fn from_wire_v5(w: &crate::wire::robot::v5::RobotState) -> RobotState {
402 use crate::wire::f64s_to_f64;
403
404 let m_ee = w.m_ee.get();
405 let m_load = w.m_load.get();
406 let I_ee = f64s_to_f64(&w.I_ee);
407 let I_load = f64s_to_f64(&w.I_load);
408 let F_x_Cee = f64s_to_f64(&w.F_x_Cee);
409 let F_x_Cload = f64s_to_f64(&w.F_x_Cload);
410
411 let m_total = m_ee + m_load;
412 let F_x_Ctotal = combine_center_of_mass(m_ee, &F_x_Cee, m_load, &F_x_Cload);
413 let I_total = combine_inertia_tensor(
414 m_ee,
415 &F_x_Cee,
416 &I_ee,
417 m_load,
418 &F_x_Cload,
419 &I_load,
420 m_total,
421 &F_x_Ctotal,
422 );
423
424 RobotState {
425 O_T_EE: f64s_to_f64(&w.O_T_EE),
426 O_T_EE_d: f64s_to_f64(&w.O_T_EE_d),
427 F_T_EE: f64s_to_f64(&w.F_T_EE),
428 F_T_NE: f64s_to_f64(&w.F_T_NE),
429 NE_T_EE: f64s_to_f64(&w.NE_T_EE),
430 EE_T_K: f64s_to_f64(&w.EE_T_K),
431 m_ee,
432 I_ee,
433 F_x_Cee,
434 m_load,
435 I_load,
436 F_x_Cload,
437 m_total,
438 I_total,
439 F_x_Ctotal,
440 elbow: f64s_to_f64(&w.elbow),
441 elbow_d: f64s_to_f64(&w.elbow_d),
442 elbow_c: f64s_to_f64(&w.elbow_c),
443 delbow_c: f64s_to_f64(&w.delbow_c),
444 ddelbow_c: f64s_to_f64(&w.ddelbow_c),
445 tau_J: f64s_to_f64(&w.tau_J),
446 tau_J_d: f64s_to_f64(&w.tau_J_d),
447 dtau_J: f64s_to_f64(&w.dtau_J),
448 q: f64s_to_f64(&w.q),
449 q_d: f64s_to_f64(&w.q_d),
450 dq: f64s_to_f64(&w.dq),
451 dq_d: f64s_to_f64(&w.dq_d),
452 ddq_d: f64s_to_f64(&w.ddq_d),
453 joint_contact: f64s_to_f64(&w.joint_contact),
454 cartesian_contact: f64s_to_f64(&w.cartesian_contact),
455 joint_collision: f64s_to_f64(&w.joint_collision),
456 cartesian_collision: f64s_to_f64(&w.cartesian_collision),
457 tau_ext_hat_filtered: f64s_to_f64(&w.tau_ext_hat_filtered),
458 O_F_ext_hat_K: f64s_to_f64(&w.O_F_ext_hat_K),
459 K_F_ext_hat_K: f64s_to_f64(&w.K_F_ext_hat_K),
460 O_dP_EE_d: f64s_to_f64(&w.O_dP_EE_d),
461 O_ddP_O: f64s_to_f64(&w.O_ddP_O),
462 O_T_EE_c: f64s_to_f64(&w.O_T_EE_c),
463 O_dP_EE_c: f64s_to_f64(&w.O_dP_EE_c),
464 O_ddP_EE_c: f64s_to_f64(&w.O_ddP_EE_c),
465 theta: f64s_to_f64(&w.theta),
466 dtheta: f64s_to_f64(&w.dtheta),
467 accelerometer_top: [[0.0; 3]; 6],
468 accelerometer_bottom: [[0.0; 3]; 6],
469 current_errors: Errors::from(w.errors),
470 last_motion_errors: Errors::from(w.reflex_reason),
471 control_command_success_rate: w.control_command_success_rate.get(),
472 robot_mode: RobotMode::from_u8(w.robot_mode).unwrap_or(RobotMode::Other),
473 time: Duration::from_millis(w.message_id.get()),
474 }
475 }
476}
477
478#[cfg(test)]
479mod tests;