franka/robot/target_control/
joint.rs1use std::sync::mpsc::SyncSender;
6use std::sync::Arc;
7
8use super::runner::{identity, Step};
9use super::torque::{JointTracker, TorqueLoop};
10use super::{
11 check_joint_limits, check_posture, joint_position_limits, spawn, Backend, Handle,
12 ImpedanceOptions, JointTargetControlOptions, Runner, Shared, DEFAULT_LIMIT_FRACTION,
13};
14use crate::control_types::JointPositions;
15use crate::error::FrankaResult;
16use crate::lowpass_filter::MAX_CUTOFF_FREQUENCY;
17use crate::model::Model;
18use crate::otg::OtgLimits;
19use crate::rate_limiting::limit_rate_joint_positions;
20use crate::robot::Robot;
21use crate::robot_state::RobotState;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct JointSent {
26 pub q: [f64; 7],
29 pub target: [f64; 7],
31 pub velocity: [f64; 7],
33 pub acceleration: [f64; 7],
35 pub backstop_alteration: f64,
40 pub q_goal: [f64; 7],
42 pub tau: [f64; 7],
45 pub leash_alteration: f64,
49}
50
51pub type JointObserver = Box<dyn FnMut(&RobotState, &JointSent) + Send>;
54
55pub struct JointTargetControl {
57 inner: Handle<7>,
58 limits: ([f64; 7], [f64; 7]),
59}
60
61impl JointTargetControl {
62 pub fn set_joints(&self, q: [f64; 7]) -> FrankaResult<()> {
71 if q.iter().all(|v| v.is_finite()) {
72 check_joint_limits(&q, &self.limits, "target")?;
73 }
74 self.inner.set_target(q)
75 }
76
77 pub fn target(&self) -> [f64; 7] {
79 self.inner.target()
80 }
81
82 pub fn state(&self) -> RobotState {
84 self.inner.state()
85 }
86
87 pub fn is_running(&self) -> bool {
89 self.inner.is_running()
90 }
91
92 pub fn stop(self) -> FrankaResult<()> {
96 self.inner.stop()
97 }
98}
99
100pub(super) fn max_abs_difference(a: &[f64; 7], b: &[f64; 7]) -> f64 {
101 a.iter()
102 .zip(b)
103 .map(|(x, y)| (x - y).abs())
104 .fold(0.0, f64::max)
105}
106
107pub(super) fn sent(step: &Step<7, 7>, q: [f64; 7]) -> JointSent {
109 JointSent {
110 q,
111 target: step.target,
112 velocity: step.velocity,
113 acceleration: step.acceleration,
114 backstop_alteration: 0.0,
115 q_goal: [0.0; 7],
116 tau: [0.0; 7],
117 leash_alteration: 0.0,
118 }
119}
120
121const THREAD: &str = "franka-joint-target";
122
123pub(super) fn start(
124 robot: &Arc<Robot>,
125 options: JointTargetControlOptions,
126) -> FrankaResult<JointTargetControl> {
127 options.validate()?;
128 let joint_limits = joint_position_limits(robot.fci_version());
129 check_posture(&options.backend, &joint_limits)?;
130 let limits = options.limits.unwrap_or_else(|| {
131 JointTargetControlOptions::scaled_limits(robot.fci_version(), DEFAULT_LIMIT_FRACTION)
132 });
133 let shared = Arc::new(Shared::<7>::default());
134 let loop_shared = Arc::clone(&shared);
135 let priority = options.realtime_priority;
136 let inner = match options.backend {
137 Backend::RobotController => spawn(
138 THREAD,
139 robot,
140 shared,
141 priority,
142 move |robot: &Robot, started| {
143 position_loop(robot, started, options, limits, loop_shared)
144 },
145 )?,
146 Backend::Impedance(impedance) => {
147 let model = Arc::new(robot.load_model()?);
148 spawn(
149 THREAD,
150 robot,
151 shared,
152 priority,
153 move |robot: &Robot, started| {
154 let limit_rate = options.limit_rate;
155 torque_loop(options, limits, impedance, model, loop_shared, started)?
156 .run(robot, limit_rate)
157 },
158 )?
159 }
160 };
161 Ok(JointTargetControl {
162 inner,
163 limits: joint_limits,
164 })
165}
166
167pub(super) fn torque_loop(
170 options: JointTargetControlOptions,
171 limits: [OtgLimits; 7],
172 impedance: ImpedanceOptions,
173 model: Arc<Model>,
174 shared: Arc<Shared<7>>,
175 started: SyncSender<()>,
176) -> FrankaResult<TorqueLoop<7, 7, JointTracker>> {
177 let runner = Runner::new(shared, started, limits, options.settle, identity)?;
178 let tracker = JointTracker::new(&options);
179 Ok(TorqueLoop::new(
180 runner,
181 model,
182 impedance,
183 tracker,
184 options.observer,
185 ))
186}
187
188fn position_loop(
191 robot: &Robot,
192 started: SyncSender<()>,
193 options: JointTargetControlOptions,
194 limits: [OtgLimits; 7],
195 shared: Arc<Shared<7>>,
196) -> FrankaResult<()> {
197 let JointTargetControlOptions {
198 controller_mode,
199 max_deviation,
200 settle,
201 limit_rate,
202 mut observer,
203 ..
204 } = options;
205 let mut runner = Runner::new(shared, started, limits, settle, identity)?;
206 let velocity = limits.map(|l| l.max_velocity);
207 let acceleration = limits.map(|l| l.max_acceleration);
208 let jerk = limits.map(|l| l.max_jerk);
209 let mut start: Option<[f64; 7]> = None;
210 let result = robot.control_joint_positions(
211 |state: &RobotState, _period| {
212 let start = *start.get_or_insert(state.q_d);
213 let strayed = max_abs_difference(&state.q, &start) > max_deviation;
214 let step = runner.cycle(state, state.q_d, strayed);
215
216 let mut q = step.position;
217 let mut backstop_alteration = 0.0;
218 if limit_rate && !step.hold {
219 let upper = robot.upper_joint_velocity_limits(&state.q);
224 let lower = robot.lower_joint_velocity_limits(&state.q);
225 let upper: [f64; 7] = std::array::from_fn(|i| upper[i].min(velocity[i]));
226 let lower: [f64; 7] = std::array::from_fn(|i| lower[i].max(-velocity[i]));
227 #[rustfmt::skip]
228 let limited = limit_rate_joint_positions(
229 &upper, &lower, &acceleration, &jerk,
230 &q, &state.q_d, &state.dq_d, &state.ddq_d,
231 ).unwrap_or(state.q_d);
232 backstop_alteration = max_abs_difference(&limited, &q);
233 q = limited;
234 }
235 if let Some(observe) = observer.as_mut() {
236 observe(
237 state,
238 &JointSent {
239 backstop_alteration,
240 ..sent(&step, q)
241 },
242 );
243 }
244 let mut output = JointPositions::new(q);
245 output.motion_finished = step.finished;
246 output
247 },
248 controller_mode,
249 limit_rate,
250 MAX_CUTOFF_FREQUENCY,
251 );
252 runner.finish(result)
253}