cob_hand_node.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Mojin Robotics
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
19 #include <cob_hand_bridge/Status.h>
20 
21 #include <std_srvs/Trigger.h>
22 
23 #include <sensor_msgs/JointState.h>
24 #include <control_msgs/FollowJointTrajectoryAction.h>
25 
27 
29 
30 #include <ros/ros.h>
31 
32 #include <boost/thread/mutex.hpp>
33 #include <boost/shared_ptr.hpp>
34 #include <boost/scoped_ptr.hpp>
35 
36 #include <angles/angles.h>
37 
38 #include <gpio.h>
39 #include <sdhx.h>
40 
41 boost::mutex g_mutex;
42 
45 
47 sensor_msgs::JointState g_js;
48 
52 
54 boost::scoped_ptr<FollowJointTrajectoryActionServer> g_as;
55 control_msgs::FollowJointTrajectoryGoalConstPtr g_goal;
66 std::vector<double> g_goal_tolerance;
67 std::string g_port;
68 
69 // SDHX
70 boost::scoped_ptr<SDHX> g_sdhx;
71 
73  return g_status && (g_status->status & (g_status->MASK_FINGER_READY | g_status->MASK_ERROR)) == g_status->MASK_FINGER_READY && g_status->rc == 0;
74 }
75 
76 bool checkAction_nolock(bool deadline_exceeded){
77  control_msgs::FollowJointTrajectoryResult result;
78  if(g_as->isActive()){
79  bool goal_reached = false;
80  if(g_motion_stopped) {
81  goal_reached = true;;
82  for(size_t i = 0; i < g_status->joints.position_cdeg.size(); ++i){
83  if(fabs(g_status->joints.position_cdeg[i]-g_command.position_cdeg[i]) > g_goal_tolerance[i]){
84  goal_reached = false;
85  result.error_code = result.GOAL_TOLERANCE_VIOLATED;
86  break;
87  }
88  }
89  }
90  if(!isFingerReady_nolock()) {
91  g_deadline_timer.stop();
92  g_as->setAborted();
93  }else if(g_motion_stopped && (goal_reached || g_motors_moved)) {
94  g_deadline_timer.stop();
95  g_as->setSucceeded(result);
96  }else if (deadline_exceeded) {
97  g_deadline_timer.stop();
98  result.error_code = result.GOAL_TOLERANCE_VIOLATED;
99  g_as->setAborted(result, "goal not reached in time");
100  return false;
101  }
102  }
103  return true;
104 }
105 
106 bool halt(){
107  if(!g_sdhx || !g_sdhx->halt()){
108  ROS_ERROR("Halt service did not succeed");
109  return false;
110  }
111  else
112  return true;
113 }
114 
115 bool init(){
116  ros::NodeHandle nh_priv("~");
117  if(!g_sdhx){
118  g_sdhx.reset(new SDHX);
119  g_port = nh_priv.param<std::string>("sdhx/port", "/dev/ttyACM0");
120  if(g_sdhx->init(g_port.c_str(),
121  nh_priv.param("sdhx/min_pwm0", 0),
122  nh_priv.param("sdhx/min_pwm1", 0),
123  nh_priv.param("sdhx/max_pwm0", 0),
124  nh_priv.param("sdhx/max_pwm1", 0)) ){
125 
126  g_status->rc = 0;
127  g_status->status &= ~g_status->MASK_ERROR;
128  return true;
129  }
130  }
131  return false;
132 }
133 
134 bool recover(){
135  if(g_sdhx) {
136  if((g_status->status & g_status->MASK_ERROR) == 0 && g_status->rc == 0) {
137  return true;
138  }else if(g_sdhx->isInitialized()) {
139  g_sdhx.reset();
140  g_status->rc = 0;
141  if(init()){
142  g_status->status &= ~g_status->MASK_ERROR;
143  return true;
144  }
145  }else{
146  return false;
147  }
148  }
149  return false;
150 }
151 
153  boost::mutex::scoped_lock lock(g_mutex);
154 
155  if(!g_status) {
156  res.message = "hand is not yet connected";
157  }else if(g_status->status == g_status->NOT_INITIALIZED) {
158  lock.unlock();
159 
160  if(init()){
161  res.success = true;
162  g_as->start();
163  }
164  else{
165  res.success = false;
166  res.message = "Not initialized";
167  }
168 
169  lock.lock();
170  g_initialized = res.success;
171  }else if(!g_initialized){
172  g_as->start();
173  res.success = true;
174  res.message = "finger already initialized, restarting the controller";
176  }else{
177  res.success = true;
178  res.message = "already initialized";
179  }
180 
181  return true;
182 }
183 
185  boost::mutex::scoped_lock lock(g_mutex);
186  if(!g_status) {
187  res.message = "hand is not yet connected";
188  res.success = false;
189  }else{
190  res.success = halt();
191  }
192  return true;
193 }
194 
196  boost::mutex::scoped_lock lock(g_mutex);
197  if(!g_status) {
198  res.message = "hand is not yet connected";
199  res.success = false;
200  }else{
201  res.success = recover();
202  }
203  return true;
204 }
205 
207  boost::mutex::scoped_lock lock(g_mutex);
208  double dt = g_js.header.stamp.toSec() - e.current_real.toSec();
209  bool first = !g_status;
210  bool calc_vel = !first && dt != 0 ;
211  if(first)
212  g_status.reset ( new cob_hand_bridge::Status() );
213  g_status->stamp = e.current_real;
214  g_status->status = g_status->NOT_INITIALIZED;
215 
216  cob_hand_bridge::JointValues &j= g_status->joints;
217 
218  if(g_sdhx && g_sdhx->isInitialized()){
219  int16_t position_cdeg[2];
220  int16_t velocity_cdeg_s[2];
221  int16_t current_100uA[2];
222  if(g_sdhx->getData(position_cdeg, velocity_cdeg_s, current_100uA, boost::chrono::seconds(1))){
223  g_status->status |= g_status->MASK_FINGER_READY;
224  }
225  else
226  g_status->status |= g_status->MASK_ERROR;
227  j.position_cdeg[0] = position_cdeg[0]; j.position_cdeg[1] = position_cdeg[1];
228  j.velocity_cdeg_s[0] = velocity_cdeg_s[0]; j.velocity_cdeg_s[1] = velocity_cdeg_s[1];
229  j.current_100uA[0] = current_100uA[0]; j.current_100uA[1] = current_100uA[1];
230  g_status->rc = g_sdhx->getRC();
231  }
232 
233  g_topic_status->tick(g_status->stamp);
234 
235  g_motion_stopped = true;
236  g_control_stopped = true;
237 
238  if(g_status->status & g_status->MASK_FINGER_READY){
239  for(size_t i=0; i < g_status->joints.position_cdeg.size(); ++i){
240  double new_pos = angles::from_degrees(g_status->joints.position_cdeg[i]/100.0);
241  if(calc_vel){
242  g_js.velocity[i] = (new_pos - g_js.position[i]) / dt;
243  }
244  if(fabs(g_js.velocity[i]) > g_stopped_velocity){
245  g_motion_stopped = false;
246  g_motors_moved = true;
247  }
248  if(fabs(g_status->joints.current_100uA[i]) > g_stopped_current){
249  g_control_stopped = false;
250  }
251  g_js.position[i] = new_pos;
252  }
253  g_js.header.stamp = g_status->stamp;
254  g_js_pub.publish(g_js);
255  }
256  checkAction_nolock(false);
257 
258  if(first) g_init_srv = ros::NodeHandle("driver").advertiseService("init", initCallback);
259 
260  if(g_sdhx) g_sdhx->poll();
261 }
262 
264  boost::mutex::scoped_lock lock(g_mutex);
265 
266  if(!g_status){
267  stat.summary(stat.ERROR, "not connected");
268  return;
269  }
270 
271  if(g_status->status == g_status->NOT_INITIALIZED){
272  stat.summary(stat.WARN, "not initialized");
273  }else if(g_status->status & g_status->MASK_ERROR){
274  stat.summary(stat.ERROR, "Bridge has error");
275  }else{
276  stat.summary(stat.OK, "Connected and running");
277  }
278  stat.add("sdhx_ready", bool(g_status->status & g_status->MASK_FINGER_READY));
279  stat.add("sdhx_rc", uint32_t(g_status->rc));
280  stat.add("sdhx_motion_stopped", g_motion_stopped);
281  stat.add("sdhx_control_stopped", g_control_stopped);
282 
283  if(g_status->rc > 0){
284  stat.mergeSummary(stat.ERROR, "SDHx has error");
285  }
286 }
287 
289  boost::mutex::scoped_lock lock(g_mutex);
290  if(g_as->isActive()){
291  if(!checkAction_nolock(true)){
292  g_command.position_cdeg = g_status->joints.position_cdeg;
293  lock.unlock();
294  halt();
295  lock.lock();
296  }
297  }
298 }
299 
300 void goalCB() {
301  control_msgs::FollowJointTrajectoryGoalConstPtr goal = g_as->acceptNewGoal();
302 
303  g_deadline_timer.stop();
304 
305  // goal is invalid if goal has more than 2 (or 0) points. If 2 point, the first needs time_from_start to be 0
306  control_msgs::FollowJointTrajectoryResult result;
307  result.error_code = result.INVALID_GOAL;
308  if(goal->trajectory.points.size()!=1 && (goal->trajectory.points.size()!=2 || !goal->trajectory.points[0].time_from_start.isZero() ) ){
309  g_as->setAborted(result, "goal is not valid");
310  return;
311  }
312 
314  size_t found = 0;
315 
316  for(size_t i=0; i < g_js.name.size(); ++i){
317  for(size_t j=0; j < goal->trajectory.joint_names.size(); ++j){
318  if(g_js.name[i] == goal->trajectory.joint_names[j]){
319  new_command.position_cdeg[i]= angles::to_degrees(goal->trajectory.points.back().positions[j])*100; // cdeg to rad
320 
321  if(goal->trajectory.points.back().effort.size() > 0){
322  if(goal->trajectory.points.back().effort.size() == new_command.current_100uA.size()){
323  new_command.current_100uA[i] = goal->trajectory.points.back().effort[j] * 1000.0; // (A -> 100uA)
324  }else{
325  g_as->setAborted(result, "Number of effort values mismatch");
326  return;
327  }
328  }
329 
330  ++found;
331  break;
332  }
333  }
334  }
335 
336  if(found != g_js.name.size()){
337  g_as->setAborted(result, "Joint names mismatch");
338  return;
339  }
340  std::vector<double> goal_tolerance(g_command.position_cdeg.size(), angles::to_degrees(g_stopped_velocity)*100); // assume 1s movement is allowed, cdeg to rad
341 
342  for(size_t i = 0; i < goal->goal_tolerance.size(); ++i){
343  bool missing = true;
344  for(size_t j=0; j < g_js.name.size(); ++j){
345  if(goal->goal_tolerance[i].name == g_js.name[j]){
346  missing = false;
347  if(goal->goal_tolerance[i].position > 0.0){
348  goal_tolerance[j] = goal->goal_tolerance[i].position;
349  }
350  break;
351  }
352  }
353  if(missing){
354  g_as->setAborted(result, "Goal tolerance invalid");
355  return;
356  }
357  }
358 
359  ros::Time now = ros::Time::now();
360  ros::Time trajectory_deadline = (goal->trajectory.header.stamp.isZero() ? now : goal->trajectory.header.stamp)
361  + goal->trajectory.points.back().time_from_start + ros::Duration(goal->goal_time_tolerance);
362  if(trajectory_deadline <= now){
363  result.error_code = result.OLD_HEADER_TIMESTAMP;
364  g_as->setAborted(result, "goal is not valid");
365  return;
366  }
367 
368  boost::mutex::scoped_lock lock(g_mutex);
369 
370  if(!isFingerReady_nolock()) {
371  g_as->setAborted(result, "SDHx is not ready for commands");
372  return;
373  }
374 
375  g_command = new_command;
376 
377  g_goal_tolerance = goal_tolerance;
378  g_motors_moved = false;
379  g_command_timer.stop();
380 
381  if(g_sdhx){
382  int16_t position_cdeg[2];
383  int16_t velocity_cdeg_s[2];
384  int16_t current_100uA[2];
385  position_cdeg[0]=g_command.position_cdeg[0]; position_cdeg[1]=g_command.position_cdeg[1];
386  velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
387  current_100uA[0]=g_command.current_100uA[0]; current_100uA[1]=g_command.current_100uA[1];
388  g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
389  }
390 
391  g_deadline_timer.setPeriod(trajectory_deadline-ros::Time::now());
392  g_deadline_timer.start();
393  g_command_timer.start();
394 }
395 
396 void cancelCB() {
397  boost::mutex::scoped_lock lock(g_mutex);
398  g_command.position_cdeg = g_status->joints.position_cdeg;
399  g_deadline_timer.stop();
400  lock.unlock();
401 
402  halt();
403  g_as->setPreempted();
404 }
405 
407  boost::mutex::scoped_lock lock(g_mutex);
408  if(isFingerReady_nolock()){
410  if(g_sdhx){
411  int16_t position_cdeg[2];
412  int16_t velocity_cdeg_s[2];
413  int16_t current_100uA[2];
414  position_cdeg[0]=g_command.position_cdeg[0]; position_cdeg[1]=g_command.position_cdeg[1];
415  velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
416  current_100uA[0]=g_command.current_100uA[0]; current_100uA[1]=g_command.current_100uA[1];
417  g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
418  }
419  } else {
420  g_command_timer.stop();
421  lock.unlock();
422  halt();
423  ROS_WARN("finger is not ready, stopped resend timer");
424  }
425 }
426 
427 int main(int argc, char* argv[])
428 {
429  ros::init(argc, argv, "cob_hand_bridge_node");
430 
431  ros::NodeHandle nh;
432  ros::NodeHandle nh_d("driver");
433  ros::NodeHandle nh_priv("~");
434 
435  if(!nh_priv.getParam("sdhx/joint_names", g_js.name)){
436  ROS_ERROR("Please provide joint names for SDHx");
437  return 1;
438  }
439 
440  if(g_command.position_cdeg.size() != g_js.name.size()){
441  ROS_ERROR_STREAM("Number of joints does not match " << g_command.position_cdeg.size());
442  return 1;
443  }
444 
445 
446  nh_priv.param("sdhx/stopped_velocity",g_stopped_velocity, 0.05);
447  if(g_stopped_velocity <= 0.0){
448  ROS_ERROR_STREAM("stopped_velocity must be a positive number");
449  return 1;
450  }
451 
452  double stopped_current= nh_priv.param("sdhx/stopped_current", 0.1);
453  if(stopped_current <= 0.0){
454  ROS_ERROR_STREAM("stopped_current must be a positive number");
455  return 1;
456  }
457  g_stopped_current = stopped_current * 1000.0; // (A -> 100uA)
458 
459  std::vector<double> default_currents;
460  if(nh_priv.getParam("sdhx/default_currents", default_currents)){
461  if(default_currents.size() != g_default_command.current_100uA.size()){
462  ROS_ERROR_STREAM("Number of current values does not match number of joints");
463  return 1;
464  }
465  for(size_t i=0; i< g_default_command.current_100uA.size(); ++i){
466  g_default_command.current_100uA[i] = default_currents[i] * 1000.0; // (A -> 100uA)
467  }
468  }else{
469  g_default_command.current_100uA[0] = 2120;
470  g_default_command.current_100uA[1] = 1400;
471  }
472 
473  g_default_command.velocity_cdeg_s[0] = 1000;
474  g_default_command.velocity_cdeg_s[1] = 1000;
475 
476  g_js.position.resize(g_command.position_cdeg.size());
477  g_js.velocity.resize(g_command.position_cdeg.size());
478 
479  diagnostic_updater::Updater diag_updater;
480  diag_updater.setHardwareID(nh_priv.param("hardware_id", std::string("none")));
481  diag_updater.add("bridge", reportDiagnostics);
482 
484  nh_priv.param("status/min_duration", -1.0),
485  nh_priv.param("status/max_duration", 0.1)
486  );
487  g_topic_status.reset ( new diagnostic_updater::TimeStampStatus(param) );
488  diag_updater.add("connection", boost::bind(&diagnostic_updater::TimeStampStatus::run, g_topic_status, _1));
489 
490  ros::Timer diag_timer = nh.createTimer(ros::Duration(diag_updater.getPeriod()/2.0),boost::bind(&diagnostic_updater::Updater::update, &diag_updater));
491 
492  double resend_period = nh_priv.param("sdhx/resend_period", 0.1);
493  if(resend_period > 0.0) g_command_timer = nh.createTimer(ros::Duration(resend_period), resendCommand, false, false);
494 
495  g_deadline_timer = nh.createTimer(ros::Duration(1.0), handleDeadline, true, false); // period is not used, will be overwritten
496 
497  g_js_pub = nh.advertise<sensor_msgs::JointState>("joint_states", 1);
498 
499  ros::Timer status_timer = nh.createTimer(ros::Duration(1.0/20),statusCallback);
500 
501  g_halt_srv = nh_d.advertiseService("halt", haltCallback);
502  g_recover_srv = nh_d.advertiseService("recover", recoverCallback);
503 
504  g_as.reset(new FollowJointTrajectoryActionServer(ros::NodeHandle("joint_trajectory_controller"), "follow_joint_trajectory",false));
505  g_as->registerPreemptCallback(cancelCB);
506  g_as->registerGoalCallback(goalCB);
507 
508  ros::spin();
509  return 0;
510 }
bool g_control_stopped
bool param(const std::string &param_name, T &param_val, const T &default_val)
const char * message
Definition: Trigger.h:59
std::vector< double > g_goal_tolerance
void publish(const boost::shared_ptr< M > &message) const
actionlib::SimpleActionServer< control_msgs::FollowJointTrajectoryAction > FollowJointTrajectoryActionServer
Definition: time.h:30
void summary(unsigned char lvl, const std::string s)
cob_hand_bridge::JointValues g_command
void setHardwareID(const std::string &hwid)
void stop()
void setPeriod(const Duration &period, bool reset=true)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
boost::scoped_ptr< FollowJointTrajectoryActionServer > g_as
void start()
void add(const std::string &name, TaskFunction f)
NodeHandle_< EmbeddedLinuxHardware > NodeHandle
Definition: ros.h:32
void handleDeadline(const ros::TimerEvent &)
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
virtual void run(diagnostic_updater::DiagnosticStatusWrapper &stat)
#define ROS_WARN(...)
Definition: sdhx.h:25
void cancelCB()
int main(int argc, char *argv[])
bool init()
ros::Timer g_deadline_timer
std::string g_port
bool haltCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
ros::Timer g_command_timer
static double from_degrees(double degrees)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool isFingerReady_nolock()
ros::ServiceServer g_halt_srv
void goalCB()
boost::scoped_ptr< SDHX > g_sdhx
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
double g_stopped_velocity
void resendCommand(const ros::TimerEvent &e)
static double to_degrees(double radians)
bool g_motors_moved
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
ROSCPP_DECL void spin()
boost::shared_ptr< diagnostic_updater::TimeStampStatus > g_topic_status
double g_stopped_current
ros::ServiceServer g_recover_srv
void mergeSummary(unsigned char lvl, const std::string s)
void reportDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
bool recover()
bool initCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
ros::ServiceServer g_init_srv
bool getParam(const std::string &key, std::string &s) const
boost::mutex g_mutex
control_msgs::FollowJointTrajectoryGoalConstPtr g_goal
sensor_msgs::JointState g_js
void statusCallback(const ros::TimerEvent &e)
bool g_initialized
bool recoverCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
static Time now()
bool g_motion_stopped
ros::Publisher g_js_pub
cob_hand_bridge::JointValues g_default_command
void add(const std::string &key, const T &val)
bool advertiseService(ServiceServer< MReq, MRes > &srv)
Definition: node_handle.h:348
#define ROS_ERROR_STREAM(args)
bool checkAction_nolock(bool deadline_exceeded)
boost::shared_ptr< cob_hand_bridge::Status > g_status
#define ROS_ERROR(...)
bool halt()


cob_hand_bridge
Author(s): Mathias Lüdtke
autogenerated on Tue Oct 20 2020 03:35:57