1use std::sync::mpsc::SyncSender;
9use std::sync::Arc;
10
11use nalgebra::Matrix3;
12
13use super::rotation::{
14 angle_between, checked_pose, distance, exp, from_quaternion, log, pose_from, rotation_of,
15 to_quaternion, translation_of, unit_quaternion,
16};
17use super::runner::Step;
18use super::torque::{PoseTracker, TorqueLoop};
19use super::{
20 check_posture, joint_position_limits, spawn, Backend, Handle, ImpedanceOptions, Runner, Shared,
21 TargetControlOptions,
22};
23use crate::control_types::CartesianPose;
24use crate::error::FrankaResult;
25use crate::lowpass_filter::MAX_CUTOFF_FREQUENCY;
26use crate::math_utils::orthonormalized_rotation;
27use crate::model::Model;
28use crate::otg::OtgLimits;
29use crate::rate_limiting::{
30 limit_rate_cartesian_pose, DELTA_T, FACTOR_CARTESIAN_ROTATION_POSE_INTERFACE,
31};
32use crate::robot::Robot;
33use crate::robot_state::RobotState;
34use crate::wire::robot::codec::FciVersion;
35
36#[derive(Debug, Clone, Copy, PartialEq)]
38pub struct CartesianSent {
39 pub pose: [f64; 16],
42 pub orientation: [f64; 4],
44 pub target: [f64; 3],
46 pub target_orientation: [f64; 4],
48 pub velocity: [f64; 3],
50 pub acceleration: [f64; 3],
52 pub angular_velocity: [f64; 3],
54 pub angular_acceleration: [f64; 3],
56 pub backstop_alteration: f64,
59 pub backstop_angular_alteration: f64,
61 pub q_goal: [f64; 7],
63 pub tau: [f64; 7],
66 pub ik_error: f64,
69 pub leash_alteration: f64,
73 pub leash_angular_alteration: f64,
75}
76
77pub type CartesianObserver = Box<dyn FnMut(&RobotState, &CartesianSent) + Send>;
80
81pub struct CartesianTargetControl {
85 inner: Handle<7>,
86}
87
88impl CartesianTargetControl {
89 pub fn set_position(&self, position_in_base: [f64; 3]) -> FrankaResult<()> {
97 self.inner
98 .modify_target(|target| target[..3].copy_from_slice(&position_in_base))
99 }
100
101 pub fn set_orientation(&self, orientation_xyzw: [f64; 4]) -> FrankaResult<()> {
110 let orientation = unit_quaternion(orientation_xyzw)?;
111 self.inner
112 .modify_target(|target| target[3..].copy_from_slice(&orientation))
113 }
114
115 pub fn set_target(
121 &self,
122 position_in_base: [f64; 3],
123 orientation_xyzw: [f64; 4],
124 ) -> FrankaResult<()> {
125 let orientation = unit_quaternion(orientation_xyzw)?;
126 self.inner.set_target(join(&position_in_base, &orientation))
127 }
128
129 pub fn set_pose(&self, pose: &[f64; 16]) -> FrankaResult<()> {
138 let (position, rotation) = checked_pose(pose)?;
139 self.set_target(position, to_quaternion(&rotation))
140 }
141
142 pub fn target(&self) -> [f64; 3] {
144 split(&self.inner.target()).0
145 }
146
147 pub fn target_orientation(&self) -> [f64; 4] {
149 split(&self.inner.target()).1
150 }
151
152 pub fn target_pose(&self) -> [f64; 16] {
154 let (position, orientation) = split(&self.inner.target());
155 pose_from(&from_quaternion(&orientation), &position)
156 }
157
158 pub fn state(&self) -> RobotState {
160 self.inner.state()
161 }
162
163 pub fn is_running(&self) -> bool {
165 self.inner.is_running()
166 }
167
168 pub fn stop(self) -> FrankaResult<()> {
172 self.inner.stop()
173 }
174}
175
176pub(super) type Placement = ([f64; 3], Matrix3<f64>);
178
179pub(super) fn chart(target: &[f64; 7], commanded: &[f64; 7]) -> ([f64; 6], [f64; 6]) {
183 let ([tx, ty, tz], target_orientation) = split(target);
184 let ([x, y, z], orientation) = split(commanded);
185 let r_target = from_quaternion(&target_orientation);
186 let r_commanded = from_quaternion(&orientation);
187 let [a, b, c] = log(&(r_target * r_commanded.transpose()));
188 ([x, y, z, 0.0, 0.0, 0.0], [tx, ty, tz, a, b, c])
189}
190
191pub(super) fn placement(pose: &[f64; 16]) -> Placement {
194 (
195 translation_of(pose),
196 orthonormalized_rotation(&rotation_of(pose)),
197 )
198}
199
200pub(super) fn pose_of(placement: &Placement) -> [f64; 16] {
202 pose_from(&placement.1, &placement.0)
203}
204
205pub(super) fn compose(step: &Step<6, 7>, rotation: &Matrix3<f64>) -> Placement {
208 let [x, y, z, a, b, c] = step.position;
209 ([x, y, z], exp(&[a, b, c]) * rotation)
210}
211
212pub(super) fn strayed(
214 state: &RobotState,
215 start: &Placement,
216 max_deviation: f64,
217 max_angular: f64,
218) -> bool {
219 distance(&translation_of(&state.O_T_EE), &start.0) > max_deviation
220 || angle_between(&start.1, &rotation_of(&state.O_T_EE)) > max_angular
221}
222
223pub(super) fn sent(step: &Step<6, 7>, pose: [f64; 16]) -> CartesianSent {
225 let [vx, vy, vz, wx, wy, wz] = step.velocity;
226 let [ax, ay, az, bx, by, bz] = step.acceleration;
227 let (target, target_orientation) = split(&step.target);
228 CartesianSent {
229 pose,
230 orientation: to_quaternion(&rotation_of(&pose)),
231 target,
232 target_orientation,
233 velocity: [vx, vy, vz],
234 acceleration: [ax, ay, az],
235 angular_velocity: [wx, wy, wz],
236 angular_acceleration: [bx, by, bz],
237 backstop_alteration: 0.0,
238 backstop_angular_alteration: 0.0,
239 q_goal: [0.0; 7],
240 tau: [0.0; 7],
241 ik_error: 0.0,
242 leash_alteration: 0.0,
243 leash_angular_alteration: 0.0,
244 }
245}
246
247fn increment(pose: &[f64; 16], echo: &Placement) -> [f64; 6] {
249 let [x, y, z] = translation_of(pose);
250 let [a, b, c] = log(&(rotation_of(pose) * echo.1.transpose()));
251 [x - echo.0[0], y - echo.0[1], z - echo.0[2], a, b, c]
252}
253
254pub(super) fn slot_values(translation: &[f64; 3], rotation: &Matrix3<f64>) -> [f64; 7] {
256 join(translation, &to_quaternion(rotation))
257}
258
259fn join(position: &[f64; 3], orientation: &[f64; 4]) -> [f64; 7] {
260 let mut slot = [0.0; 7];
261 slot[..3].copy_from_slice(position);
262 slot[3..].copy_from_slice(orientation);
263 slot
264}
265
266fn split(slot: &[f64; 7]) -> ([f64; 3], [f64; 4]) {
267 let [x, y, z, a, b, c, d] = *slot;
268 ([x, y, z], [a, b, c, d])
269}
270
271pub(super) fn axis_limits(limits: OtgLimits, rotation_limits: OtgLimits) -> [OtgLimits; 6] {
274 let translational = limits.per_axis_for_norm(3);
275 let rotational = rotation_limits
276 .scaled(FACTOR_CARTESIAN_ROTATION_POSE_INTERFACE)
277 .per_axis_for_norm(3);
278 [
279 translational,
280 translational,
281 translational,
282 rotational,
283 rotational,
284 rotational,
285 ]
286}
287
288const THREAD: &str = "franka-cartesian-target";
289
290pub(super) fn start(
291 robot: &Arc<Robot>,
292 options: TargetControlOptions,
293) -> FrankaResult<CartesianTargetControl> {
294 options.validate()?;
295 check_posture(
296 &options.backend,
297 &joint_position_limits(robot.fci_version()),
298 )?;
299 let shared = Arc::new(Shared::<7>::default());
300 let loop_shared = Arc::clone(&shared);
301 let priority = options.realtime_priority;
302 let inner = match options.backend {
303 Backend::RobotController => spawn(
304 THREAD,
305 robot,
306 shared,
307 priority,
308 move |robot: &Robot, started| pose_loop(robot, started, options, loop_shared),
309 )?,
310 Backend::Impedance(impedance) => {
311 let model = Arc::new(robot.load_model()?);
312 spawn(
313 THREAD,
314 robot,
315 shared,
316 priority,
317 move |robot: &Robot, started| {
318 let limit_rate = options.limit_rate;
319 let version = robot.fci_version();
320 torque_loop(options, impedance, model, version, loop_shared, started)?
321 .run(robot, limit_rate)
322 },
323 )?
324 }
325 };
326 Ok(CartesianTargetControl { inner })
327}
328
329pub(super) fn torque_loop(
332 options: TargetControlOptions,
333 impedance: ImpedanceOptions,
334 model: Arc<Model>,
335 version: FciVersion,
336 shared: Arc<Shared<7>>,
337 started: SyncSender<()>,
338) -> FrankaResult<TorqueLoop<6, 7, PoseTracker>> {
339 let runner = Runner::new(
340 shared,
341 started,
342 axis_limits(options.limits, options.rotation_limits),
343 options.settle,
344 chart,
345 )?;
346 let tracker = PoseTracker::new(
347 &options,
348 &impedance,
349 Arc::clone(&model),
350 joint_position_limits(version),
351 );
352 Ok(TorqueLoop::new(
353 runner,
354 model,
355 impedance,
356 tracker,
357 options.observer,
358 ))
359}
360
361fn pose_loop(
364 robot: &Robot,
365 started: SyncSender<()>,
366 options: TargetControlOptions,
367 shared: Arc<Shared<7>>,
368) -> FrankaResult<()> {
369 let TargetControlOptions {
370 limits,
371 rotation_limits,
372 controller_mode,
373 max_deviation,
374 max_angular_deviation,
375 settle,
376 limit_rate,
377 mut observer,
378 ..
379 } = options;
380 let mut runner = Runner::new(
381 shared,
382 started,
383 axis_limits(limits, rotation_limits),
384 settle,
385 chart,
386 )?;
387 let mut start: Option<Placement> = None;
388 let mut held: Option<[f64; 16]> = None;
389 let (mut last_twist, mut last_acceleration) = ([0.0; 6], [0.0; 6]);
392 let result = robot.control_cartesian_pose(
393 |state: &RobotState, _period| {
394 let echo = placement(&state.O_T_EE_c);
395 let start = *start.get_or_insert(echo);
396 let strayed = strayed(state, &start, max_deviation, max_angular_deviation);
397 let step = runner.cycle(state, slot_values(&echo.0, &echo.1), strayed);
398
399 let (mut backstop_alteration, mut backstop_angular_alteration) = (0.0, 0.0);
400 let pose = if step.hold {
401 *held.get_or_insert(state.O_T_EE_c)
403 } else {
404 let (position, rotation) = compose(&step, &echo.1);
405 let mut pose = pose_from(&rotation, &position);
406 if limit_rate {
407 #[rustfmt::skip]
409 let limited = limit_rate_cartesian_pose(
410 limits.max_velocity, limits.max_acceleration, limits.max_jerk,
411 rotation_limits.max_velocity, rotation_limits.max_acceleration,
412 rotation_limits.max_jerk,
413 &pose, &state.O_T_EE_c, &last_twist, &last_acceleration,
414 ).unwrap_or(state.O_T_EE_c);
415 backstop_alteration = distance(&translation_of(&limited), &position);
416 backstop_angular_alteration = angle_between(&rotation, &rotation_of(&limited));
417 pose = limited;
418 }
419 pose
420 };
421 let twist = increment(&pose, &echo).map(|d| d / DELTA_T);
422 last_acceleration = std::array::from_fn(|i| (twist[i] - last_twist[i]) / DELTA_T);
423 last_twist = twist;
424 if let Some(observe) = observer.as_mut() {
425 observe(
426 state,
427 &CartesianSent {
428 backstop_alteration,
429 backstop_angular_alteration,
430 ..sent(&step, pose)
431 },
432 );
433 }
434 let mut output = CartesianPose::new(pose);
435 output.motion_finished = step.finished;
436 output
437 },
438 controller_mode,
439 limit_rate,
440 MAX_CUTOFF_FREQUENCY,
441 );
442 runner.finish(result)
443}