qb_delta_kinematic_controller.cpp
Go to the documentation of this file.
1 /***
2  * Software License Agreement: BSD 3-Clause License
3  *
4  * Copyright (c) 2016-2018, qbrobotics®
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
8  * following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, this list of conditions and the
11  * following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
14  * following disclaimer in the documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
17  * products derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
25  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
29 
30 using namespace qb_chain_controllers;
31 
33  : spinner_(1, callback_queue_.get()), // the dedicated callback queue is needed to avoid deadlocks caused by action client calls (together with controller manager update loop)
34  callback_queue_(boost::make_shared<ros::CallbackQueue>()),
35  node_handle_control_(ros::NodeHandle(), "control") {
37  spinner_.start();
38 }
39 
41  spinner_.stop();
42 }
43 
44 void DeltaKinematicController::actionActiveCallback(const std::string &controller) {
45  ROS_INFO_STREAM_NAMED("delta_controller", "Controller [" << controller << "] action start.");
46 }
47 
48 void DeltaKinematicController::actionDoneCallback(const actionlib::SimpleClientGoalState &state, const control_msgs::FollowJointTrajectoryResultConstPtr &result, const std::string &controller) {
49  if (result->error_code != result->SUCCESSFUL) {
50  ROS_WARN_STREAM_NAMED("delta_controller", "Controller [" << controller << "] action ended in state [" << state.toString() <<"] with error code [" << result->error_code << "]");
51  }
52  else {
53  ROS_INFO_STREAM_NAMED("delta_controller", "Controller [" << controller << "] action ended in state [" << state.toString() <<"].");
54  }
55 
56  if (!use_waypoints_) {
57  // erase all but last point
58  for (auto &trajectory : motor_joint_trajectories_) {
59  trajectory.second.points.erase(trajectory.second.points.begin(), trajectory.second.points.end()-1);
60  trajectory.second.points.front().time_from_start = ros::Duration(0.1);
61  }
62  return;
63  }
64 
65  // if waypoints are active
66  if (!motor_joint_trajectories_.empty()) {
67  if (std::all_of(motor_names_.begin(), motor_names_.end(), [this](auto m){ return motor_action_clients_.at(m)->getState().isDone(); })) {
69  }
70  }
71 }
72 
73 void DeltaKinematicController::actionFeedbackCallback(const control_msgs::FollowJointTrajectoryFeedbackConstPtr &feedback, const std::string &controller) {
74  for (int i=0; i<feedback->joint_names.size(); i++) {
75  ROS_DEBUG_STREAM_NAMED("delta_controller", "Controller [" << controller << "] joint [" << feedback->joint_names.at(i) << "] state is [" << feedback->actual.positions.at(i) << "] (expecting [" << feedback->desired.positions.at(i) << "]).");
76  }
77 }
78 
79 void DeltaKinematicController::buildCube(visualization_msgs::InteractiveMarker &interactive_marker) {
80  visualization_msgs::Marker marker;
81  marker.type = visualization_msgs::Marker::CUBE;
82  marker.scale.x = interactive_marker.scale * 0.4;
83  marker.scale.y = interactive_marker.scale * 0.4;
84  marker.scale.z = interactive_marker.scale * 0.4;
85  marker.color.r = 0.35;
86  marker.color.g = 0.35;
87  marker.color.b = 0.35;
88  marker.color.a = 0.75;
89 
90  visualization_msgs::InteractiveMarkerControl control;
91  control.always_visible = true;
92  control.markers.push_back(marker);
93  control.name = "move_xyz";
94  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_3D;
95  interactive_marker.controls.push_back(control);
96 }
97 
98 void DeltaKinematicController::buildEndEffectorControl(visualization_msgs::InteractiveMarker &interactive_marker) {
99  visualization_msgs::InteractiveMarkerControl control;
100  control.orientation.w = 1;
101  control.orientation.x = 1;
102  control.orientation.y = 0;
103  control.orientation.z = 0;
104  control.name = "move_yz";
105  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_PLANE;
106  interactive_marker.controls.push_back(control);
107  control.name = "move_x";
108  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
109  interactive_marker.controls.push_back(control);
110  control.orientation.w = 1;
111  control.orientation.x = 0;
112  control.orientation.y = 1;
113  control.orientation.z = 0;
114  control.name = "move_xy";
115  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_PLANE;
116  interactive_marker.controls.push_back(control);
117  control.name = "move_z";
118  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
119  interactive_marker.controls.push_back(control);
120  control.orientation.w = 1;
121  control.orientation.x = 0;
122  control.orientation.y = 0;
123  control.orientation.z = 1;
124  control.name = "move_xz";
125  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_PLANE;
126  interactive_marker.controls.push_back(control);
127  control.name = "move_y";
128  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
129  interactive_marker.controls.push_back(control);
130 }
131 
132 bool DeltaKinematicController::cartesianLinearPlanner(const geometry_msgs::Point &target_pose, std::vector<std::vector<double>> &joint_positions) {
133  geometry_msgs::Point target_pick_pose;
134  target_pick_pose.x = target_pose.x - ee_offset_.x;
135  target_pick_pose.y = target_pose.y - ee_offset_.y;
136  target_pick_pose.z = target_pose.z - ee_offset_.z;
137 
138  std::vector<double> target_joint_positions;
139  if (!inverseKinematics(target_pick_pose, target_joint_positions)) {
140  return false;
141  }
142 
143  std::vector<geometry_msgs::Point> intermediate_poses = computeIntermediatePosesTo(target_pick_pose);
144  for (auto const &intermediate_pose : intermediate_poses) {
145  std::vector<double> intermediate_joint_positions;
146  if (!inverseKinematics(intermediate_pose, intermediate_joint_positions)) {
147  return false;
148  }
149  joint_positions.push_back(intermediate_joint_positions);
150  }
151  return true; // intermediate_poses can be empty if distance is 0, i.e. joint_positions is unchanged
152 }
153 
154 double DeltaKinematicController::computeDistance(const geometry_msgs::Point &from_pose, const geometry_msgs::Point &to_pose) {
155  return std::sqrt(std::pow(to_pose.x-from_pose.x,2) + std::pow(to_pose.y-from_pose.y,2) + std::pow(to_pose.z-from_pose.z,2));
156 }
157 
158 std::vector<geometry_msgs::Point> DeltaKinematicController::computeIntermediatePosesTo(const geometry_msgs::Point &target_pose) {
159  std::vector<geometry_msgs::Point> intermediate_poses;
160  geometry_msgs::Point ee_pose;
162  double distance = computeDistance(ee_pose, target_pose);
163  if (distance > 0) {
164  int size = std::floor(distance/max_displacement_)+1;
165  double displacement_x = (target_pose.x - ee_pose.x)/size;
166  double displacement_y = (target_pose.y - ee_pose.y)/size;
167  double displacement_z = (target_pose.z - ee_pose.z)/size;
168  for (int i=0; i<size+1; i++) {
169  geometry_msgs::Point pose;
170  pose.x = ee_pose.x + i*displacement_x;
171  pose.y = ee_pose.y + i*displacement_y;
172  pose.z = ee_pose.z + i*displacement_z;
173  intermediate_poses.push_back(pose);
174  }
175  }
176  }
177  return intermediate_poses;
178 }
179 
180 std::vector<std::vector<double>> DeltaKinematicController::computeIntermediateStiffnessesTo(const std::vector<double> &target_joint_stiffnesses, const int &size) {
181  std::vector<std::vector<double>> joint_stiffnesses;
182  std::vector<double> old_joint_stiffnesses = getTrajectoryLastStiffnesses();
183 
184  std::vector<double> displacements;
185  for (int j=0; j<old_joint_stiffnesses.size(); j++) {
186  displacements.push_back((target_joint_stiffnesses.at(j) - old_joint_stiffnesses.at(j))/size);
187  }
188 
189  for (int i=0; i<size; i++) {
190  std::vector<double> point;
191  for (int j=0; j<displacements.size(); j++) {
192  point.push_back(old_joint_stiffnesses.at(j) + (i+1)*displacements.at(j));
193  }
194  joint_stiffnesses.push_back(point);
195  }
196 
197  return joint_stiffnesses;
198 }
199 
200 std::map<std::string, trajectory_msgs::JointTrajectory> DeltaKinematicController::computeJointTrajectories(const std::vector<std::vector<double>> &motor_positions, const std::vector<std::vector<double>> &motor_stiffness, const double &target_time_from_start, const double &previous_time_from_start) {
201  std::map<std::string, trajectory_msgs::JointTrajectory> trajectories;
202  for (int id=1; id<=3; id++) {
203  trajectory_msgs::JointTrajectory trajectory;
204  trajectory.header.stamp = ros::Time(0);
205  trajectory.header.frame_id = getMotorName(id);
206  trajectory.joint_names = getMotorJointNames(id);
207  for (int i=0; i<motor_positions.size(); i++) {
208  trajectory_msgs::JointTrajectoryPoint point;
209  point.positions = {motor_positions.at(i).at(id-1), motor_stiffness.at(i).at(id-1)};
210  if (i == 0 || i == motor_positions.size()-1) {
211  point.velocities.resize(trajectory.joint_names.size());
212  point.accelerations.resize(trajectory.joint_names.size());
213  }
214  point.time_from_start = ros::Duration(previous_time_from_start + (i+1)*(target_time_from_start-previous_time_from_start)/motor_positions.size());
215  trajectory.points.push_back(point);
216  }
217  trajectories.insert(std::make_pair(getMotorName(id), trajectory));
218  }
219  return trajectories;
220 }
221 
222 bool DeltaKinematicController::deltaStatePublisher(const std::vector<double> &motor_joints, const geometry_msgs::Point &ee_pose) {
223  double R_motors = 0.1; // radius from the center of the motors plane to each motor [meters]
224  double R_ee = 0.0455; // radius from the center of the end-effector plane to each arm [meters]
225  double l_motors = 0.09; // upper arm length [meters]
226  double l_ee = 0.156; // forearm length [meters]
227  double phi_1 = 0; // motor 1 angular position in the motors plane (front/central)
228  double phi_2 = 2*M_PI/3; // motor 2 angular position in the motors plane (behind/left)
229  double phi_3 = 4*M_PI/3; // motor 3 angular position in the motors plane (behind/right)
230  double theta_1 = motor_joints.at(0) - 1;
231  double theta_2 = motor_joints.at(1) - 1;
232  double theta_3 = motor_joints.at(2) - 1;
233 
234  sensor_msgs::JointState msg;
235  std::vector<double> phi = {phi_1, phi_2, phi_3};
236  std::vector<double> theta = {theta_1, theta_2, theta_3};
237  for (int i=0; i<3; i++) {
238  std::string base_name = "delta_qbmove_" + std::to_string(i+1);
239 
240  geometry_msgs::Point upperarm_pose; // upperarm_poses: [cos(phi_i)*(l_motors*cos(theta_i)+R_motors) -sin(phi_i)*(l_motors*cos(theta_i)+R_motors) -l_motors*sin(theta_i)]
241  upperarm_pose.x = cos(phi.at(i))*(l_motors*cos(theta.at(i))+R_motors);
242  upperarm_pose.y = sin(phi.at(i))*(l_motors*cos(theta.at(i))+R_motors);
243  upperarm_pose.z = -l_motors*sin(theta.at(i));
244 
245  geometry_msgs::Point forearm_pose; // forearm_poses: [cos(phi_i)*l_ee -sin(phi_i)*l_ee 0] + ee_pose
246  forearm_pose.x = cos(phi.at(i))*R_ee + ee_pose.x;
247  forearm_pose.y = sin(phi.at(i))*R_ee + ee_pose.y;
248  forearm_pose.z = 0 + ee_pose.z;
249 
250  geometry_msgs::Point diff_pose;
251  diff_pose.x = forearm_pose.x - upperarm_pose.x;
252  diff_pose.y = forearm_pose.y - upperarm_pose.y;
253  diff_pose.z = forearm_pose.z - upperarm_pose.z;
254 
255  geometry_msgs::Point forearm_to_upperarm_vector;
256  forearm_to_upperarm_vector.x = std::cos(-phi.at(i))*diff_pose.x + -std::sin(-phi.at(i))*diff_pose.y;
257  forearm_to_upperarm_vector.y = std::sin(-phi.at(i))*diff_pose.x + std::cos(-phi.at(i))*diff_pose.y;
258  forearm_to_upperarm_vector.z = diff_pose.z;
259 
260  double pitch = -std::atan2(forearm_to_upperarm_vector.z, -forearm_to_upperarm_vector.x);
261  double yaw = std::atan2(forearm_to_upperarm_vector.y, std::hypot(forearm_to_upperarm_vector.x, forearm_to_upperarm_vector.z));
262  msg.name.push_back(base_name + "_free_down_joint");
263  msg.name.push_back(base_name + "_free_l_joint");
264  msg.position.push_back(pitch);
265  msg.position.push_back(yaw);
266  }
267  msg.header.stamp = ros::Time::now();
269 
270  geometry_msgs::TransformStamped transform;
271  transform.header.frame_id = "delta_base_frame_link";
272  transform.header.stamp = ros::Time::now();
273  transform.child_frame_id = "delta_ee_frame_link";
274  transform.transform.translation.x = ee_pose.x;
275  transform.transform.translation.y = ee_pose.y;
276  transform.transform.translation.z = ee_pose.z;
277  transform.transform.rotation.w = 1; // others are 0
278  tf_broadcaster_.sendTransform(transform);
279  return true;
280 }
281 
282 void DeltaKinematicController::fillMotorJointTrajectories(const std::map<std::string, trajectory_msgs::JointTrajectory> &motor_joint_trajectories) {
283  for (auto const &trajectory : motor_joint_trajectories) {
284  if (!motor_joint_trajectories_.count(trajectory.first)) {
285  motor_joint_trajectories_.insert(std::make_pair(trajectory.first, trajectory.second));
286  continue;
287  }
288  motor_joint_trajectories_.at(trajectory.first).points.insert(std::end(motor_joint_trajectories_.at(trajectory.first).points), std::begin(trajectory.second.points), std::end(trajectory.second.points));
289  }
290 }
291 
292 void DeltaKinematicController::filter(const std::vector<double> &b, const std::vector<double> &a, const std::vector<double> &x, std::vector<double> &y) {
293  if (x.size() < b.size() - 1) {
294  ROS_WARN_STREAM_NAMED("delta_controller", "Measurements are shorter than parameters.");
295  return;
296  }
297 
298  // builds first filtered values (to compute aj_yi for all aj in the following)
299  if (y.size() < a.size() - 1) {
300  if (y.empty()) {
301  // y(1) = b1 * x(1) / a1
302  y.push_back((b.front() * x.front()) / a.front());
303  }
304 
305  auto it_a = a.begin();
306  auto it_b = b.begin();
307  while (y.size() < a.size() - 1) {
308  // bj_xi = b1_xk + b2_xk-1 + ... + bnb_xk-nb-1
309  double bj_xi = 0;
310  for (auto it_x = x.begin(); it_x != x.begin() + y.size(); it_x++) {
311  bj_xi += *it_x * *(it_b + y.size() - (it_x - x.begin()));
312  }
313 
314  // aj_yi = a1_yk-1 + a2_yk-2 + ... + ana_yk-na-1
315  double aj_yi = 0;
316  for (auto it_y = y.begin(); it_y != y.end(); it_y++) {
317  aj_yi += *it_y * *(it_a + y.size() - (it_y - y.begin()));
318  }
319 
320  y.push_back((bj_xi - aj_yi) / a.front());
321  }
322  }
323 
324  int num_elements_x_exceed_y = x.size() - y.size();
325  if (num_elements_x_exceed_y < 0) { // few measurements
326  ROS_WARN_STREAM_NAMED("delta_controller", "Previous filtered data exceeds measurements.");
327  return;
328  }
329  if (num_elements_x_exceed_y == 0 && y.size() != 1) { // already updated vector
330  ROS_WARN_STREAM_NAMED("delta_controller", "There are no new measurements.");
331  return;
332  }
333 
334  // there are new measurements to be evaluated
335  for (auto it_x = x.end() - num_elements_x_exceed_y; it_x != x.end(); it_x++) {
336  // bj_xi = b1_xk + b2_xk-1 + ... + bnb_xk-nb-1
337  double bj_xi = 0;
338  for (auto it_b = b.begin(); it_b != b.end(); it_b++) {
339  bj_xi += *it_b * *(it_x - (it_b - b.begin()));
340  }
341 
342  // aj_yi = a1_yk-1 + a2_yk-2 + ... + ana_yk-na-1
343  auto it_y = y.begin() + (it_x - x.begin());
344  double aj_yi = 0;
345  if (a.size() > 1) {
346  for (auto it_a = a.begin() + 1; it_a != a.end(); it_a++) {
347  aj_yi += *it_a * *(it_y - (it_a - a.begin()));
348  }
349  }
350 
351  y.push_back((bj_xi - aj_yi) / a.front());
352  }
353 
354  return;
355 }
356 
357 void DeltaKinematicController::filterMotorJointTrajectories(const std::vector<double> &b, const std::vector<double> &a) {
358  for (int id=1; id<=3; id++) {
359  if (!motor_joint_trajectories_.count(getMotorName(id))) {
360  return;
361  }
363  }
364 }
365 
366 void DeltaKinematicController::filterMotorJointTrajectory(const std::vector<double> &b, const std::vector<double> &a, trajectory_msgs::JointTrajectory &motor_joint_trajectory) {
367  if (motor_joint_trajectory.points.size() <= filter_param_a_.size()) {
368  ROS_WARN_STREAM_NAMED("delta_controller", "Trajectory points are less than filter parameters.");
369  return;
370  }
371 
372  std::vector<std::vector<double>> matrix_x(motor_joint_trajectory.joint_names.size()/* *3 */, std::vector<double>());
373  std::vector<std::vector<double>> matrix_y(motor_joint_trajectory.joint_names.size()/* *3 */, std::vector<double>());
374  for (auto const &point : motor_joint_trajectory.points) {
375  for (int i=0; i<point.positions.size(); i++) {
376  matrix_x.at(i).push_back(point.positions.at(i));
377  }
378 // for (int i=0; i<point.velocities.size(); i++) {
379 // matrix_x.at(i + motor_joint_trajectory.joint_names.size()).push_back(point.velocities.at(i));
380 // }
381 // for (int i=0; i<point.accelerations.size(); i++) {
382 // matrix_x.at(i + motor_joint_trajectory.joint_names.size()*2).push_back(point.accelerations.at(i));
383 // }
384  }
385 
386  for (int i=0; i<matrix_x.size(); i++) {
387  matrix_y.at(i).insert(std::end(matrix_y.at(i)), std::begin(matrix_x.at(i)), std::begin(matrix_x.at(i))+filter_param_a_.size());
388  filter(b, a, matrix_x.at(i), matrix_y.at(i));
389  }
390 
391  for (int j=0; j<matrix_y.at(0).size(); j++) {
392  for (int i=0; i<motor_joint_trajectory.joint_names.size(); i++) {
393  motor_joint_trajectory.points.at(j).positions.at(i) = matrix_y.at(i).at(j);
394  }
395 // for (int i=0; i<motor_joint_trajectory.joint_names.size(); i++) {
396 // motor_joint_trajectory.points.at(j).velocities.at(i + trajectory_y.joint_names.size()) = matrix_y.at(i).at(j);
397 // }
398 // for (int i=0; i<motor_joint_trajectory.joint_names.size(); i++) {
399 // motor_joint_trajectory.points.at(j).accelerations.at(i + trajectory_y.joint_names.size()*2) = matrix_y.at(i).at(j);
400 // }
401  }
402 }
403 
404 bool DeltaKinematicController::forwardKinematics(const std::vector<double> &motor_joints, geometry_msgs::Point &ee_pose) {
405  double R_motors = 0.1; // radius from the center of the motors plane to each motor [meters]
406  double R_ee = 0.0455; // radius from the center of the end-effector plane to each arm [meters]
407  double l_motors = 0.09; // upper arm length [meters]
408  double l_ee = 0.156; // forearm length [meters]
409  double phi_1 = 0; // motor 1 angular position in the motors plane (front/central)
410  double phi_2 = 2*M_PI/3; // motor 2 angular position in the motors plane (behind/left)
411  double phi_3 = 4*M_PI/3; // motor 3 angular position in the motors plane (behind/right)
412  double theta_1 = motor_joints.at(0) - 1;
413  double theta_2 = motor_joints.at(1) - 1;
414  double theta_3 = motor_joints.at(2) - 1;
415  double R = R_motors - R_ee;
416 
417  // The forward kinematics for a 3-limb-delta structure comes from the intersection of the three spheres with radius
418  // equals to the forearm and centered at the end of each upper limb.
419  // Actually several assumptions are taken to simplify the system of equations, e.g. even if it is not true, we can
420  // consider all the forearms to be linked in the center of the end-effector plane and use "R = R_motors - R_ee".
421  //
422  // The generic system is as follows:
423  //
424  // { (x-[cos(phi_1)*(l_motors*cos(theta_1)+R)])^2 + (y-[-sin(phi_1)*(l_motors*cos(theta_1)+R)])^2 + (z-[-l_motors*sin(theta_1)])^2 = l_ee^2
425  // { (x-[cos(phi_2)*(l_motors*cos(theta_2)+R)])^2 + (y-[-sin(phi_2)*(l_motors*cos(theta_2)+R)])^2 + (z-[-l_motors*sin(theta_2)])^2 = l_ee^2
426  // { (x-[cos(phi_3)*(l_motors*cos(theta_3)+R)])^2 + (y-[-sin(phi_3)*(l_motors*cos(theta_3)+R)])^2 + (z-[-l_motors*sin(theta_3)])^2 = l_ee^2
427  //
428  // which can have in general 0, 1 or 2 solutions.
429  // Note that if 2 solutions exist, only one is feasible.
430 
431  // eq_1: (x-x_1)^2 + (y)^2 + (z-z_1)^2 == l_ee^2 (phi_1 is hardcoded to simplify the computation)
432  double x_1 = l_motors*cos(theta_1) + R;
433  double z_1 = -l_motors*sin(theta_1);
434 
435  // eq_2: (x-x_2)^2 + (y-y_2)^2 + (z-z_2)^2 == l_ee^2
436  double x_2 = std::cos(phi_2)*(l_motors*cos(theta_2) + R);
437  double y_2 = -std::tan(phi_2)*x_2;
438  double z_2 = -l_motors*sin(theta_2);
439 
440  // eq_3: (x-x_3)^2 + (y-y_3)^2 + (z-z_3)^2 == l_ee^2
441  double x_3 = std::cos(phi_3)*(l_motors*cos(theta_3) + R);
442  double y_3 = -std::tan(phi_3)*x_3;
443  double z_3 = -l_motors*sin(theta_3);
444 
445  // "eq_4 = eq_1-eq2" and "eq_5 = eq1-eq3" produce two linear equations which are more easily to be solved
446  // { eq_4 - k_1*eq_5 --> x = (a_1*z + b_1)/d
447  // { eq_4 - k_2*eq_5 --> y = (a_2*z + b_2)/d
448  double w_1 = std::pow(x_1,2) + std::pow(z_1,2);
449  double w_2 = std::pow(x_2,2) + std::pow(y_2,2) + std::pow(z_2,2);
450  double w_3 = std::pow(x_3,2) + std::pow(y_3,2) + std::pow(z_3,2);
451 
452  double d = (x_1-x_2)*y_3 - (x_1-x_3)*y_2;
453  if (d == 0) {
454  return false;
455  }
456 
457  double a_1 = y_2*(z_1-z_3) - y_3*(z_1-z_2);
458  double b_1 = (y_3*(w_1-w_2) - y_2*(w_1-w_3))/2;
459  double a_2 = -(x_1-x_3)*(z_1-z_2)+(x_1-x_2)*(z_1-z_3);
460  double b_2 = ((x_1-x_3)*(w_1-w_2)-(x_1-x_2)*(w_1-w_3))/2;
461 
462  // Now we can solve for z by substitution, e.g. in eq_1
463  double a = std::pow(a_1,2) + std::pow(a_2,2) + std::pow(d,2);
464  double b = 2*(a_1*b_1 + a_2*b_2 - z_1*std::pow(d,2) - x_1*a_1*d);
465  double c = std::pow(b_1,2) + std::pow(b_2,2) + std::pow(x_1,2)*std::pow(d,2) + std::pow(z_1,2)*std::pow(d,2) - 2*x_1*b_1*d - std::pow(l_ee,2)*std::pow(d,2);
466  double delta = std::pow(b,2) - 4*a*c;
467  if (delta < 0) {
468  return false;
469  }
470 
471  ee_pose.z = -0.5*(b-std::sqrt(delta))/a;
472  ee_pose.x = (a_1*ee_pose.z + b_1)/d;
473  ee_pose.y = -(a_2*ee_pose.z + b_2)/d;
474  return true;
475 }
476 
477 std::string DeltaKinematicController::getMotorName(const int &id) {
478  return motor_names_.at(id-1);
479 }
480 
482  return motor_joints_.at(getMotorJointNames(id).front()).getPosition();
483 }
484 
487 }
488 
490  return motor_joints_.at(getMotorJointNames(id).back()).getPosition();
491 }
492 
495 }
496 
497 std::vector<std::string> DeltaKinematicController::getMotorJointNames(const int &id) {
498  return motor_joint_names_.at(getMotorName(id));
499 }
500 
501 trajectory_msgs::JointTrajectory DeltaKinematicController::getMotorJointTrajectory(const int &id) {
503 }
504 
506  if (!motor_joint_trajectories_.count(getMotorName(1)) || getMotorJointTrajectory(1).points.empty()) { // can be used also ID 2 or 3, they are filled and emptied together (ID 4, i.e. the gripper can be different)
507  return getMotorPositions();
508  }
509  return {getMotorJointTrajectory(1).points.back().positions.at(0), getMotorJointTrajectory(2).points.back().positions.at(0), getMotorJointTrajectory(3).points.back().positions.at(0)};
510 }
511 
513  if (!motor_joint_trajectories_.count(getMotorName(1)) || getMotorJointTrajectory(1).points.empty()) { // can be used also ID 2 or 3, they are filled and emptied together (ID 4, i.e. the gripper can be different)
514  return getMotorStiffnesses();
515  }
516  return {getMotorJointTrajectory(1).points.back().positions.at(1), getMotorJointTrajectory(2).points.back().positions.at(1), getMotorJointTrajectory(3).points.back().positions.at(1)};
517 }
518 
520  if (!motor_joint_trajectories_.count(getMotorName(1)) || getMotorJointTrajectory(1).points.empty()) { // can be used also ID 2 or 3, they are filled and emptied together (ID 4, i.e. the gripper can be different)
521  return 0;
522  }
523  return getMotorJointTrajectory(1).points.back().time_from_start.toSec();
524 }
525 
528  if (!controller_nh.getParam("device_names", motor_names_)) {
529  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot retrieve 'joint_controllers' from the Parameter Service [" << controller_nh.getNamespace() << "].");
530  return false;
531  }
532  controller_nh.param("max_displacement", max_displacement_, 0.001); // default is 1mm
533  use_waypoints_ = node_handle_.param<bool>("use_waypoints", false);
534  use_interactive_markers_ = node_handle_.param<bool>("use_interactive_markers", false) && !use_waypoints_; // cannot be used together (waypoint is dominant)
535  ee_offset_.x = 0.0;
536  ee_offset_.y = 0.024;
537  ee_offset_.z = 0.142;
538 
539  for (auto const &device_name : motor_names_) {
540  std::string position_joint = device_name + "_shaft_joint";
541  std::string stiffness_joint = device_name + "_stiffness_preset_virtual_joint";
542  motor_joints_.insert(std::make_pair(position_joint, robot_hw->getHandle(position_joint)));
543  motor_joints_.insert(std::make_pair(stiffness_joint, robot_hw->getHandle(stiffness_joint)));
544  motor_joint_names_.insert(std::make_pair(device_name, std::vector<std::string>({position_joint, stiffness_joint})));
545 
546  std::string device_action = device_name + "_position_and_preset_trajectory_controller/follow_joint_trajectory";
547  motor_action_clients_.insert(std::make_pair(device_name, std::make_unique<actionlib::SimpleActionClient<control_msgs::FollowJointTrajectoryAction>>(node_handle_control_, device_action, false)));
548  }
549  robot_hw->clearClaims(); // this controller never set commands directly to the handled devices, i.e. use their Actions
550 
551  if (use_interactive_markers_) {
552  initMarkers();
553  feedback_active_ = false;
554  }
555  if (use_waypoints_) {
556  filter_trajectory_ = controller_nh.param<bool>("filter_trajectory", false);
557  if (filter_trajectory_) {
558  filter_param_b_ = controller_nh.param<std::vector<double>>("filter_param_b", {0.7});
559  filter_param_a_ = controller_nh.param<std::vector<double>>("filter_param_a", {1, -0.3});
560  }
561  waypoint_namespace_ = node_handle_.param<std::string>("waypoint_namespace", "waypoints");
562  waypoint_setup_timer_ = node_handle_.createWallTimer(ros::WallDuration(2.0), [this](auto e){ this->startWaypointTrajectory(); }, true); // oneshot
563  }
564 
565  free_joint_state_publisher_ = control_nh.advertise<sensor_msgs::JointState>("joint_states", 1);
566  target_poses_sub_ = controller_nh.subscribe("target_poses", 1, &DeltaKinematicController::targetPosesCallback, this);
567 
568  return true;
569 }
570 
572  interactive_commands_server_ = std::make_unique<interactive_markers::InteractiveMarkerServer>("qbdelta_end_effector_interactive_commands");
573 
574  controls_.header.frame_id = "delta_base_frame_link";
575  controls_.name = "qbdelta_end_effector_position_reference_controls";
576  controls_.description = "qbdelta end-effector 3D pose reference.";
577  controls_.scale = 0.05;
580 
581  interactive_commands_server_->insert(controls_, std::bind(&DeltaKinematicController::interactiveMarkerCallback, this, std::placeholders::_1));
582  interactive_commands_server_->applyChanges();
583 }
584 
585 void DeltaKinematicController::interactiveMarkerCallback(const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback) {
586  if (feedback->event_type == feedback->MOUSE_UP) {
587  feedback_active_ = false;
588  return;
589  }
590  if (feedback->event_type == feedback->MOUSE_DOWN) {
591  feedback_position_old_ = feedback->pose.position;
592  feedback_active_ = true;
593  return;
594  }
595 
596  double feedback_distance = 0;
597  if (feedback_active_ && feedback->event_type == feedback->POSE_UPDATE) {
598  feedback_distance = computeDistance(feedback->pose.position, feedback_position_old_);
599  feedback_position_old_ = feedback->pose.position;
600  }
601  if (feedback_distance > 0.001) { // at least 1mm in cartesian space
602  geometry_msgs::PointStamped target_pose;
603  target_pose.header = feedback->header;
604  target_pose.point = feedback->pose.position;
605  targetPosesCallback(target_pose);
606  }
607 }
608 
609 bool DeltaKinematicController::inverseKinematics(const geometry_msgs::Point &ee_pose, std::vector<double> &joint_positions) {
610  double R_motors = 0.1; // radius from the center of the motors plane to each motor [meters]
611  double R_ee = 0.0375; // radius from the center of the end-effector plane to each arm [meters]
612  double l_motors = 0.09; // upper arm length [meters]
613  double l_ee = 0.16; // forearm length [meters]
614  double phi_1 = 0; // motor 1 angular position in the motors plane (behind/right)
615  double phi_2 = 2*M_PI/3; // motor 2 angular position in the motors plane (front/central)
616  double phi_3 = 4*M_PI/3; // motor 3 angular position in the motors plane (behind/left)
617  double R = R_motors - R_ee;
618  joint_positions.resize(3);
619 
620  auto arm_ik = [&](const geometry_msgs::Point &ee_pose, const double &phi, double &joint_position) {
621  auto acos_safe = [](const double &num, const double &den, double &angle) {
622  angle = std::acos(num/den); // it may be NaN
623  return std::abs(num) < std::abs(den);
624  };
625 
626  double x = std::cos(phi)*ee_pose.x + std::sin(phi)*ee_pose.y - R;
627  double y = -std::sin(phi)*ee_pose.x + std::cos(phi)*ee_pose.y;
628  double z = ee_pose.z;
629  double theta_1, theta_2;
630  if (!acos_safe(y, l_ee, theta_1) || !acos_safe((std::pow(x,2) + std::pow(y,2) + std::pow(z,2) - std::pow(l_motors,2) - std::pow(l_ee,2)), (2*l_motors*l_ee*std::sin(theta_1)), theta_2)) {
631  return false;
632  }
633  double c_1 = l_ee*std::cos(theta_2)*std::sin(theta_1) + l_motors;
634  double c_2 = l_ee*std::sin(theta_2)*std::sin(theta_1);
635  joint_position = -(std::atan2(-c_2*x + c_1*z, c_1*x + c_2*z) - 1); // includes motor offset
636  return true;
637  };
638 
639  return arm_ik(ee_pose, phi_1, joint_positions.at(0)) && arm_ik(ee_pose, phi_2, joint_positions.at(1)) && arm_ik(ee_pose, phi_3, joint_positions.at(2));
640 }
641 
643  for (auto const &trajectory : motor_joint_trajectories_) {
644  move(trajectory.second, trajectory.first);
645  }
646 }
647 
648 void DeltaKinematicController::move(const trajectory_msgs::JointTrajectory &joint_trajectory, const std::string &motor) {
649  control_msgs::FollowJointTrajectoryGoal goal;
650  goal.trajectory = joint_trajectory;
651  motor_action_clients_.at(motor)->sendGoal(goal,
652  std::bind(&DeltaKinematicController::actionDoneCallback, this, std::placeholders::_1, std::placeholders::_2, motor),
653  std::bind(&DeltaKinematicController::actionActiveCallback, this, motor),
654  std::bind(&DeltaKinematicController::actionFeedbackCallback, this, std::placeholders::_1, motor));
655 }
656 
657 bool DeltaKinematicController::parseVector(const XmlRpc::XmlRpcValue &xml_value, const int &size, std::vector<double> &vector) {
658  if (xml_value.size() != size) {
659  ROS_ERROR_STREAM_NAMED("delta_controller", "Fails while setting the joint trajectory (joints size mismatch).");
660  return false;
661  }
662  for (int j=0; j<xml_value.size(); j++) {
663  vector.push_back(xmlCast<double>(xml_value[j]));
664  }
665  return true;
666 }
667 
669  XmlRpc::XmlRpcValue waypoints;
670  if (!node_handle.getParam(waypoint_namespace_, waypoints)) {
671  ROS_ERROR_STREAM_NAMED("delta_controller", "No waypoints specified in the Parameter Server under [" << node_handle.getNamespace() << "/" + waypoint_namespace_ + "].");
672  return;
673  }
674 
675  // erase all but last point
676  for (auto &trajectory : motor_joint_trajectories_) {
677  trajectory.second.points.erase(trajectory.second.points.begin(), trajectory.second.points.end()-1);
678  trajectory.second.points.front().time_from_start = ros::Duration(0.1);
679  }
680 
681  trajectory_msgs::JointTrajectory gripper_joint_trajectory;
682  for (int i=0; i<waypoints.size(); i++) {
683  trajectory_msgs::JointTrajectoryPoint point;
684 
685  if (!waypoints[i].hasMember("time") && !waypoints[i].hasMember("duration")) {
686  continue;
687  }
688 
689  if (!waypoints[i].hasMember("gripper") || !parseVector(waypoints[i]["gripper"], 2, point.positions)) {
690  continue;
691  }
692  point.velocities = std::vector<double>(point.positions.size(), 0.0);
693  point.accelerations = std::vector<double>(point.positions.size(), 0.0);
694 
695  // set joint waypoint for the whole time interval (be aware of joint trajectory interpolation)
696  for (int j=0; j<(waypoints[i].hasMember("time") ? waypoints[i]["time"].size() : waypoints[i]["duration"].size()); j++) {
697  point.time_from_start = ros::Duration(waypoints[i].hasMember("time") ? static_cast<double>(waypoints[i]["time"][j]) : (gripper_joint_trajectory.points.empty() ? 0 : gripper_joint_trajectory.points.back().time_from_start.toSec()) + static_cast<double>(waypoints[i]["duration"][j]));
698  gripper_joint_trajectory.points.push_back(point);
699  }
700  }
701  if (!gripper_joint_trajectory.points.empty()) {
702  gripper_joint_trajectory.header.frame_id = getMotorName(4);
703  gripper_joint_trajectory.header.stamp = ros::Time(0);
704  gripper_joint_trajectory.joint_names = getMotorJointNames(4);
705  std::map<std::string, trajectory_msgs::JointTrajectory> gripper_joint_trajectory_map; //TODO: this is not the best solution
706  gripper_joint_trajectory_map.insert(std::make_pair(getMotorName(4), gripper_joint_trajectory));
707  fillMotorJointTrajectories(gripper_joint_trajectory_map);
708  }
709 
710  for (int i=0; i<waypoints.size(); i++) {
711  if (!waypoints[i].hasMember("time") && !waypoints[i].hasMember("duration")) {
712  continue;
713  }
714 
715  std::vector<std::vector<double>> joint_positions;
716  std::vector<double> end_effector_position;
717  if (waypoints[i].hasMember("end_effector") && parseVector(waypoints[i]["end_effector"], 3, end_effector_position)) {
718  geometry_msgs::Point target_pose;
719  target_pose.x = end_effector_position.at(0);
720  target_pose.y = end_effector_position.at(1);
721  target_pose.z = end_effector_position.at(2);
722  if (!cartesianLinearPlanner(target_pose, joint_positions)) {
723  ROS_WARN_STREAM_NAMED("delta_controller", "The Cartesian Linear Planner has produced a solution which cannot be inverted.");
724  continue;
725  }
726  }
727  else {
728  joint_positions.push_back(getTrajectoryLastPositions());
729  }
730 
731  std::vector<double> target_joint_stiffnesses;
732  if (!waypoints[i].hasMember("joint_stiffness") || !parseVector(waypoints[i]["joint_stiffness"], 3, target_joint_stiffnesses)) {
733  target_joint_stiffnesses = getTrajectoryLastStiffnesses();
734  }
735 
736  std::vector<std::vector<double>> joint_stiffnesses;
737  joint_stiffnesses = computeIntermediateStiffnessesTo(target_joint_stiffnesses, joint_positions.size());
738 
739  std::map<std::string, trajectory_msgs::JointTrajectory> motor_joint_trajectories;
740  double time_from_start = waypoints[i].hasMember("time") ? static_cast<double>(waypoints[i]["time"][0]) : getTrajectoryLastTimeFromStart() + static_cast<double>(waypoints[i]["duration"][0]);
741  motor_joint_trajectories = computeJointTrajectories(joint_positions, joint_stiffnesses, time_from_start, getTrajectoryLastTimeFromStart());
742  fillMotorJointTrajectories(motor_joint_trajectories);
743 
744  // set joint waypoint for the whole time interval (be aware of joint trajectory interpolation)
745  for (int j=1; j<(waypoints[i].hasMember("time") ? waypoints[i]["time"].size() : waypoints[i]["duration"].size()); j++) {
746  double time_from_start = waypoints[i].hasMember("time") ? static_cast<double>(waypoints[i]["time"][j]) : getTrajectoryLastTimeFromStart() + static_cast<double>(waypoints[i]["duration"][j]);
747  motor_joint_trajectories = computeJointTrajectories({joint_positions.back()}, {joint_stiffnesses.back()}, time_from_start, getTrajectoryLastTimeFromStart());
748  fillMotorJointTrajectories(motor_joint_trajectories);
749  }
750  }
751 }
752 
755  if (filter_trajectory_) {
757  }
758  move();
759 }
760 
761 //TODO: add a general callback to set pose together with stiffness values and timing constraints
762 void DeltaKinematicController::targetPosesCallback(const geometry_msgs::PointStamped &target_pose) {
763  std::vector<std::vector<double>> motor_positions;
764  if (!cartesianLinearPlanner(target_pose.point, motor_positions)) {
765  return;
766  }
767 
768  // motor stiffnesses are constant for this callback
769  motor_joint_trajectories_ = computeJointTrajectories(motor_positions, std::vector<std::vector<double>>(motor_positions.size(), getMotorStiffnesses()), 2.0, 0.5);
770  move();
771 }
772 
773 void DeltaKinematicController::update(const ros::Time &time, const ros::Duration &period) {
774  // update the end-effector pose and the state of all free joints (arm joints)
775  std::vector<double> motor_joints(getMotorPositions());
776  geometry_msgs::Pose ee_pose;
777  if (forwardKinematics(motor_joints, ee_pose.position)) {
778  deltaStatePublisher(motor_joints, ee_pose.position);
779  }
781  interactive_commands_server_->setPose(controls_.name, ee_pose);
782  interactive_commands_server_->applyChanges();
783  }
784 }
785 
786 template<class T>
788  // XmlRpcValue does not handle conversion among types but throws an exception if an improper cast is invoked
789  if (xml_value.getType() == XmlRpc::XmlRpcValue::TypeBoolean) {
790  return static_cast<bool>(xml_value);
791  }
792  if (xml_value.getType() == XmlRpc::XmlRpcValue::TypeDouble) {
793  return static_cast<double>(xml_value);
794  }
795  if (xml_value.getType() == XmlRpc::XmlRpcValue::TypeInt) {
796  return static_cast<int>(xml_value);
797  }
798  ROS_ERROR_STREAM_NAMED("delta_controller", "Fails while casting the XmlRpcValue [" << xml_value << "].");
799  return 0;
800 }
801 
d
std::vector< geometry_msgs::Point > computeIntermediatePosesTo(const geometry_msgs::Point &target_pose)
Compute a linear-in-space interpolation from the last computed value to the given target end-effector...
void update(const ros::Time &time, const ros::Duration &period) override
Update the delta state.
std::map< std::string, std::vector< std::string > > motor_joint_names_
#define ROS_DEBUG_STREAM_NAMED(name, args)
void publish(const boost::shared_ptr< M > &message) const
#define ROS_ERROR_STREAM_NAMED(name, args)
void initMarkers()
Initialize the interactive_markers::InteractiveMarkerServer and the marker displayed in rviz to contr...
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
double computeDistance(const geometry_msgs::Point &from_pose, const geometry_msgs::Point &to_pose)
Compute the absolute 3D distance between the given points.
int size() const
void buildCube(visualization_msgs::InteractiveMarker &interactive_marker)
Create a cubic marker which can be moved interactively in all the Cartesian space.
void actionDoneCallback(const actionlib::SimpleClientGoalState &state, const control_msgs::FollowJointTrajectoryResultConstPtr &result, const std::string &controller)
Restart the waypoint trajectory automatically if waypoints have been retrieved during the initializat...
#define ROS_INFO_STREAM_NAMED(name, args)
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
Type const & getType() const
std::unique_ptr< interactive_markers::InteractiveMarkerServer > interactive_commands_server_
TFSIMD_FORCE_INLINE const tfScalar & y() const
bool cartesianLinearPlanner(const geometry_msgs::Point &target_pose, std::vector< std::vector< double >> &joint_positions)
Compute the joint trajectory of all the upper motors from the last computed value to the given target...
std::vector< std::string > getMotorJointNames(const int &id)
bool deltaStatePublisher(const std::vector< double > &motor_joints, const geometry_msgs::Point &ee_pose)
Compute all the free joint positions (i.e.
void filterMotorJointTrajectories(const std::vector< double > &b, const std::vector< double > &a)
Filter all the trajectories stored in the joint trajectories private map.
void interactiveMarkerCallback(const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback)
If waypoints are not used, this become the core method of the class, where commands are computed w...
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
DeltaKinematicController()
Start the async spinner and do nothing else.
std::string toString() const
bool init(hardware_interface::PositionJointInterface *robot_hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh) override
The init function is called to initialize the controller from a non-realtime thread with a pointer to...
void buildEndEffectorControl(visualization_msgs::InteractiveMarker &interactive_marker)
Add six distinct interactive controls to the given interactive marker.
std::map< std::string, trajectory_msgs::JointTrajectory > computeJointTrajectories(const std::vector< std::vector< double >> &motor_positions, const std::vector< std::vector< double >> &motor_stiffness, const double &target_time_from_start, const double &previous_time_from_start)
Compute a segment of trajectories for all the upper motors from the given parameters.
void filterMotorJointTrajectory(const std::vector< double > &b, const std::vector< double > &a, trajectory_msgs::JointTrajectory &motor_joint_trajectory)
Filter the given joint trajectory, by considering the whole data as a vector of new measurements (eac...
ROSCPP_DECL bool get(const std::string &key, std::string &s)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void setCallbackQueue(CallbackQueueInterface *queue)
T xmlCast(XmlRpc::XmlRpcValue xml_value)
Cast an XmlRpcValue from TypeDouble, TypeInt or TypeBoolean to the specified template type...
const std::string & getNamespace() const
void targetPosesCallback(const geometry_msgs::PointStamped &msg)
Call the Cartesian Planner to compute the joint trajectory from the current position of the end-effec...
WallTimer createWallTimer(WallDuration period, void(T::*callback)(const WallTimerEvent &), T *obj, bool oneshot=false, bool autostart=true) const
TFSIMD_FORCE_INLINE const tfScalar & x() const
std::vector< std::vector< double > > computeIntermediateStiffnessesTo(const std::vector< double > &target_joint_stiffnesses, const int &size)
Compute the linearized trajectory from the last computed value to the given target joint stiffness of...
std::map< std::string, trajectory_msgs::JointTrajectory > motor_joint_trajectories_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void sendTransform(const geometry_msgs::TransformStamped &transform)
TFSIMD_FORCE_INLINE const tfScalar & z() const
void startWaypointTrajectory()
Parse all the waypoints, filter the whole generated joint trajectory and send the commands to the dev...
void actionFeedbackCallback(const control_msgs::FollowJointTrajectoryFeedbackConstPtr &feedback, const std::string &controller)
Do nothing apart from debug info.
JointHandle getHandle(const std::string &name)
bool hasMember(const std::string &name) const
bool parseVector(const XmlRpc::XmlRpcValue &xml_value, const int &size, std::vector< double > &vector)
Parse the given XmlRpcValue as a std::vector, since the XmlRpc::XmlRpcValue class does not handle thi...
std::map< std::string, hardware_interface::JointHandle > motor_joints_
bool inverseKinematics(const geometry_msgs::Point &ee_pose, std::vector< double > &joint_positions)
Compute the inverse kinematics of the delta device.
void fillMotorJointTrajectories(const std::map< std::string, trajectory_msgs::JointTrajectory > &motor_joint_trajectories)
Append the given trajectories to the joint trajectories private map.
void move()
Make all the calls to the Action Servers relative to all the trajectories previously parsed (either f...
bool getParam(const std::string &key, std::string &s) const
static Time now()
This controller is made to simplify the usage and the user interaction with the delta device...
void actionActiveCallback(const std::string &controller)
Do nothing apart from debug info.
void parseWaypointTrajectory(ros::NodeHandle &node_handle)
Parse all the waypoints set up in the Parameter Server at <robot_namespace>/waypoints.
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
void filter(const std::vector< double > &b, const std::vector< double > &a, const std::vector< double > &x, std::vector< double > &y)
Filter data following a discrete parametrization of the samples.
bool forwardKinematics(const std::vector< double > &joint_positions, geometry_msgs::Point &ee_pose)
Compute the forward kinematics of the delta device.
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
trajectory_msgs::JointTrajectory getMotorJointTrajectory(const int &id)
std::map< std::string, std::unique_ptr< actionlib::SimpleActionClient< control_msgs::FollowJointTrajectoryAction > > > motor_action_clients_
#define ROS_WARN_STREAM_NAMED(name, args)


qb_chain_controllers
Author(s): qbrobotics®
autogenerated on Thu Jun 13 2019 19:33:39