cob_hand_node.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2017 Mojin Robotics
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *   http://www.apache.org/licenses/LICENSE-2.0
00009 
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016  
00017 
00018 #include <cob_hand_bridge/InitFinger.h>
00019 #include <cob_hand_bridge/Status.h>
00020 
00021 #include <std_srvs/Trigger.h>
00022 
00023 #include <sensor_msgs/JointState.h>
00024 #include <control_msgs/FollowJointTrajectoryAction.h>
00025 
00026 #include <diagnostic_updater/publisher.h>
00027 
00028 #include <actionlib/server/simple_action_server.h>
00029 
00030 #include <ros/ros.h>
00031 
00032 #include <boost/thread/mutex.hpp>
00033 #include <boost/shared_ptr.hpp>
00034 #include <boost/scoped_ptr.hpp>
00035 
00036 #include <angles/angles.h>
00037 
00038 #include <gpio.h>
00039 #include <sdhx.h>
00040 
00041 boost::mutex g_mutex;
00042 
00043 boost::shared_ptr<cob_hand_bridge::Status> g_status;
00044 boost::shared_ptr<diagnostic_updater::TimeStampStatus> g_topic_status;
00045 
00046 ros::Publisher g_js_pub;
00047 sensor_msgs::JointState g_js;
00048 
00049 ros::ServiceServer g_init_srv;
00050 ros::ServiceServer g_halt_srv;
00051 ros::ServiceServer g_recover_srv;
00052 
00053 typedef actionlib::SimpleActionServer<control_msgs::FollowJointTrajectoryAction> FollowJointTrajectoryActionServer;
00054 boost::scoped_ptr<FollowJointTrajectoryActionServer> g_as;
00055 control_msgs::FollowJointTrajectoryGoalConstPtr g_goal;
00056 cob_hand_bridge::JointValues g_command;
00057 cob_hand_bridge::JointValues g_default_command;
00058 ros::Timer g_command_timer;
00059 ros::Timer g_deadline_timer;
00060 double g_stopped_velocity;
00061 double g_stopped_current;
00062 bool g_initialized;
00063 bool g_motion_stopped;
00064 bool g_control_stopped;
00065 bool g_motors_moved;
00066 std::vector<double> g_goal_tolerance;
00067 std::string g_port;
00068 
00069 // SDHX
00070 boost::scoped_ptr<SDHX> g_sdhx;
00071 
00072 bool isFingerReady_nolock() {
00073     return g_status && (g_status->status & (g_status->MASK_FINGER_READY | g_status->MASK_ERROR)) == g_status->MASK_FINGER_READY && g_status->rc == 0;
00074 }
00075 
00076 bool checkAction_nolock(bool deadline_exceeded){
00077     control_msgs::FollowJointTrajectoryResult result;
00078     if(g_as->isActive()){
00079         bool goal_reached = false;
00080         if(g_motion_stopped) {
00081             goal_reached = true;;
00082             for(size_t i = 0; i < g_status->joints.position_cdeg.size(); ++i){
00083                 if(fabs(g_status->joints.position_cdeg[i]-g_command.position_cdeg[i]) > g_goal_tolerance[i]){
00084                     goal_reached = false;
00085                     result.error_code = result.GOAL_TOLERANCE_VIOLATED;
00086                     break;
00087                 }
00088             }
00089         }
00090         if(!isFingerReady_nolock()) {
00091             g_deadline_timer.stop();
00092             g_as->setAborted();
00093         }else if(g_motion_stopped && (goal_reached || g_motors_moved)) {
00094             g_deadline_timer.stop();
00095             g_as->setSucceeded(result);
00096         }else if (deadline_exceeded) {
00097             g_deadline_timer.stop();
00098             result.error_code = result.GOAL_TOLERANCE_VIOLATED;
00099             g_as->setAborted(result, "goal not reached in time");
00100             return false;
00101         }
00102     }
00103     return true;
00104 }
00105 
00106 bool halt(){
00107     if(!g_sdhx || !g_sdhx->halt()){
00108         ROS_ERROR("Halt service did not succeed");
00109         return false;
00110     }
00111     else
00112         return true;
00113 }
00114 
00115 bool init(){
00116     ros::NodeHandle nh_priv("~");
00117     if(!g_sdhx){
00118         g_sdhx.reset(new SDHX);
00119         g_port = nh_priv.param<std::string>("sdhx/port", "/dev/ttyACM0");
00120         if(g_sdhx->init(g_port.c_str(),
00121                         nh_priv.param("sdhx/min_pwm0", 0),
00122                         nh_priv.param("sdhx/min_pwm1", 0),
00123                         nh_priv.param("sdhx/max_pwm0", 0),
00124                         nh_priv.param("sdhx/max_pwm1", 0)) ){
00125 
00126             g_status->rc = 0;
00127             g_status->status &= ~g_status->MASK_ERROR;
00128             return true;
00129         }
00130     }
00131     return false;
00132 }
00133 
00134 bool recover(){
00135     if(g_sdhx) {
00136         if((g_status->status & g_status->MASK_ERROR) == 0 && g_status->rc == 0) {
00137             return true;
00138         }else if(g_sdhx->isInitialized()) {
00139             g_sdhx.reset();
00140             g_status->rc = 0;
00141             if(init()){
00142                 g_status->status &= ~g_status->MASK_ERROR;
00143                 return true;
00144             }
00145         }else{
00146             return false;
00147         }
00148     }
00149     return false;
00150 }
00151 
00152 bool initCallback(std_srvs::Trigger::Request  &req, std_srvs::Trigger::Response &res){
00153     boost::mutex::scoped_lock lock(g_mutex);
00154     
00155     if(!g_status) {
00156         res.message = "hand is not yet connected";
00157     }else if(g_status->status == g_status->NOT_INITIALIZED) {
00158         lock.unlock();
00159 
00160         if(init()){
00161             res.success = true;
00162             g_as->start();
00163         }
00164         else{
00165             res.success = false;
00166             res.message = "Not initialized";
00167         }
00168 
00169         lock.lock();
00170         g_initialized = res.success;
00171     }else if(!g_initialized){
00172         g_as->start();
00173         res.success = true;
00174         res.message = "finger already initialized, restarting the controller";
00175         g_initialized = recover();
00176     }else{
00177         res.success = true;
00178         res.message = "already initialized";
00179     }
00180 
00181     return true;
00182 }
00183 
00184 bool haltCallback(std_srvs::Trigger::Request  &req, std_srvs::Trigger::Response &res){
00185     boost::mutex::scoped_lock lock(g_mutex);
00186     if(!g_status) {
00187         res.message = "hand is not yet connected";
00188         res.success = false;
00189     }else{
00190         res.success = halt();
00191     }
00192     return true;
00193 }
00194 
00195 bool recoverCallback(std_srvs::Trigger::Request  &req, std_srvs::Trigger::Response &res){
00196     boost::mutex::scoped_lock lock(g_mutex);
00197     if(!g_status) {
00198         res.message = "hand is not yet connected";
00199         res.success = false;
00200     }else{
00201         res.success = recover();
00202     }
00203     return true;
00204 }
00205 
00206 void statusCallback(const ros::TimerEvent& e){
00207     boost::mutex::scoped_lock lock(g_mutex);
00208     double dt = g_js.header.stamp.toSec() - e.current_real.toSec();
00209     bool first = !g_status;
00210     bool calc_vel = !first && dt != 0 ;
00211     if(first)
00212         g_status.reset ( new  cob_hand_bridge::Status() );
00213     g_status->stamp = e.current_real;
00214     g_status->status = g_status->NOT_INITIALIZED;
00215 
00216     cob_hand_bridge::JointValues &j= g_status->joints;
00217 
00218     if(g_sdhx && g_sdhx->isInitialized()){
00219         int16_t position_cdeg[2];
00220         int16_t velocity_cdeg_s[2];
00221         int16_t current_100uA[2];
00222         if(g_sdhx->getData(position_cdeg, velocity_cdeg_s, current_100uA, boost::chrono::seconds(1))){
00223             g_status->status |= g_status->MASK_FINGER_READY;
00224         }
00225         else
00226             g_status->status |= g_status->MASK_ERROR;
00227         j.position_cdeg[0] = position_cdeg[0];     j.position_cdeg[1] = position_cdeg[1];
00228         j.velocity_cdeg_s[0] = velocity_cdeg_s[0]; j.velocity_cdeg_s[1] = velocity_cdeg_s[1];
00229         j.current_100uA[0] = current_100uA[0];     j.current_100uA[1] = current_100uA[1];
00230         g_status->rc = g_sdhx->getRC();
00231     }
00232 
00233     g_topic_status->tick(g_status->stamp);
00234 
00235     g_motion_stopped = true;
00236     g_control_stopped = true;
00237 
00238     if(g_status->status & g_status->MASK_FINGER_READY){
00239         for(size_t i=0; i < g_status->joints.position_cdeg.size(); ++i){
00240             double new_pos = angles::from_degrees(g_status->joints.position_cdeg[i]/100.0);
00241             if(calc_vel){
00242                 g_js.velocity[i] = (new_pos - g_js.position[i]) / dt;
00243             }
00244             if(fabs(g_js.velocity[i]) > g_stopped_velocity){
00245                 g_motion_stopped = false;
00246                 g_motors_moved = true; 
00247             }
00248             if(fabs(g_status->joints.current_100uA[i]) > g_stopped_current){
00249                 g_control_stopped = false;
00250             }
00251             g_js.position[i] = new_pos;
00252         }
00253         g_js.header.stamp = g_status->stamp;
00254         g_js_pub.publish(g_js);
00255     }
00256     checkAction_nolock(false);
00257     
00258     if(first) g_init_srv = ros::NodeHandle("driver").advertiseService("init", initCallback);
00259 
00260     if(g_sdhx) g_sdhx->poll();
00261 }
00262 
00263 void reportDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat){
00264     boost::mutex::scoped_lock lock(g_mutex);
00265     
00266     if(!g_status){
00267         stat.summary(stat.ERROR, "not connected");
00268         return;
00269     }
00270     
00271     if(g_status->status == g_status->NOT_INITIALIZED){
00272         stat.summary(stat.WARN, "not initialized");
00273     }else if(g_status->status & g_status->MASK_ERROR){
00274         stat.summary(stat.ERROR, "Bridge has error");
00275     }else{
00276         stat.summary(stat.OK, "Connected and running");
00277     }
00278     stat.add("sdhx_ready", bool(g_status->status & g_status->MASK_FINGER_READY));
00279     stat.add("sdhx_rc", uint32_t(g_status->rc));
00280     stat.add("sdhx_motion_stopped", g_motion_stopped);
00281     stat.add("sdhx_control_stopped", g_control_stopped);
00282 
00283     if(g_status->rc > 0){
00284         stat.mergeSummary(stat.ERROR, "SDHx has error");
00285     }
00286 }
00287 
00288 void handleDeadline(const ros::TimerEvent &){
00289     boost::mutex::scoped_lock lock(g_mutex);
00290     if(g_as->isActive()){
00291         if(!checkAction_nolock(true)){
00292             g_command.position_cdeg = g_status->joints.position_cdeg;
00293             lock.unlock();
00294             halt();
00295             lock.lock();
00296         }
00297     }
00298 } 
00299 
00300 void goalCB() {
00301     control_msgs::FollowJointTrajectoryGoalConstPtr goal = g_as->acceptNewGoal();
00302     
00303     g_deadline_timer.stop();
00304 
00305     // goal is invalid if goal has more than 2 (or 0) points. If 2 point, the first needs time_from_start to be 0
00306     control_msgs::FollowJointTrajectoryResult result;
00307     result.error_code = result.INVALID_GOAL;
00308     if(goal->trajectory.points.size()!=1 && (goal->trajectory.points.size()!=2 || !goal->trajectory.points[0].time_from_start.isZero() ) ){
00309         g_as->setAborted(result, "goal is not valid");
00310         return;
00311     }
00312 
00313     cob_hand_bridge::JointValues new_command = g_default_command;
00314     size_t found = 0;
00315 
00316     for(size_t i=0; i < g_js.name.size(); ++i){
00317         for(size_t j=0; j < goal->trajectory.joint_names.size(); ++j){
00318             if(g_js.name[i] == goal->trajectory.joint_names[j]){
00319                 new_command.position_cdeg[i]= angles::to_degrees(goal->trajectory.points.back().positions[j])*100; // cdeg to rad
00320                 
00321                 if(goal->trajectory.points.back().effort.size() >  0){
00322                     if(goal->trajectory.points.back().effort.size() ==  new_command.current_100uA.size()){
00323                         new_command.current_100uA[i] = goal->trajectory.points.back().effort[j] * 1000.0; // (A -> 100uA)
00324                     }else{
00325                         g_as->setAborted(result, "Number of effort values  mismatch");
00326                         return;
00327                     }
00328                 }
00329                 
00330                 ++found;
00331                 break;
00332             }
00333         }
00334     }
00335 
00336     if(found != g_js.name.size()){
00337         g_as->setAborted(result, "Joint names mismatch");
00338         return;
00339     }
00340     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
00341 
00342     for(size_t i = 0; i < goal->goal_tolerance.size(); ++i){
00343         bool missing = true;
00344         for(size_t j=0; j < g_js.name.size(); ++j){
00345             if(goal->goal_tolerance[i].name == g_js.name[j]){
00346                 missing = false;
00347                 if(goal->goal_tolerance[i].position > 0.0){
00348                     goal_tolerance[j] = goal->goal_tolerance[i].position; 
00349                 }
00350                 break;
00351             }
00352         }
00353         if(missing){
00354             g_as->setAborted(result, "Goal tolerance invalid");
00355             return;
00356         }
00357     }
00358 
00359     ros::Time now = ros::Time::now();
00360     ros::Time trajectory_deadline = (goal->trajectory.header.stamp.isZero() ? now : goal->trajectory.header.stamp)
00361                                   + goal->trajectory.points.back().time_from_start + ros::Duration(goal->goal_time_tolerance);
00362     if(trajectory_deadline <= now){
00363         result.error_code = result.OLD_HEADER_TIMESTAMP;
00364         g_as->setAborted(result, "goal is not valid");
00365         return;
00366     }
00367 
00368     boost::mutex::scoped_lock lock(g_mutex);
00369 
00370     if(!isFingerReady_nolock()) {
00371         g_as->setAborted(result, "SDHx is not ready for commands");
00372         return;
00373     }
00374 
00375     g_command = new_command;
00376     
00377     g_goal_tolerance = goal_tolerance;
00378     g_motors_moved = false;    
00379     g_command_timer.stop();
00380 
00381     if(g_sdhx){
00382         int16_t position_cdeg[2];
00383         int16_t velocity_cdeg_s[2];
00384         int16_t current_100uA[2];
00385         position_cdeg[0]=g_command.position_cdeg[0];     position_cdeg[1]=g_command.position_cdeg[1];
00386         velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
00387         current_100uA[0]=g_command.current_100uA[0];     current_100uA[1]=g_command.current_100uA[1];
00388         g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
00389     }
00390 
00391     g_deadline_timer.setPeriod(trajectory_deadline-ros::Time::now());
00392     g_deadline_timer.start();
00393     g_command_timer.start();
00394 }
00395 
00396 void cancelCB() {
00397     boost::mutex::scoped_lock lock(g_mutex);
00398     g_command.position_cdeg = g_status->joints.position_cdeg;
00399     g_deadline_timer.stop();
00400     lock.unlock();
00401     
00402     halt();
00403     g_as->setPreempted();
00404 }
00405 
00406 void resendCommand(const ros::TimerEvent &e){
00407     boost::mutex::scoped_lock lock(g_mutex);
00408     if(isFingerReady_nolock()){
00409         if(g_control_stopped || !g_motion_stopped)
00410             if(g_sdhx){
00411                 int16_t position_cdeg[2];
00412                 int16_t velocity_cdeg_s[2];
00413                 int16_t current_100uA[2];
00414                 position_cdeg[0]=g_command.position_cdeg[0];     position_cdeg[1]=g_command.position_cdeg[1];
00415                 velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
00416                 current_100uA[0]=g_command.current_100uA[0];     current_100uA[1]=g_command.current_100uA[1];
00417                 g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
00418             }
00419     } else {
00420         g_command_timer.stop();
00421         lock.unlock();
00422         halt();
00423         ROS_WARN("finger is not ready, stopped resend timer");
00424     }
00425 }
00426 
00427 int main(int argc, char* argv[])
00428 {
00429     ros::init(argc, argv, "cob_hand_bridge_node");
00430     
00431     ros::NodeHandle nh;
00432     ros::NodeHandle nh_d("driver");
00433     ros::NodeHandle nh_priv("~");
00434     
00435     if(!nh_priv.getParam("sdhx/joint_names", g_js.name)){
00436         ROS_ERROR("Please provide joint names for SDHx");
00437         return 1;
00438     }
00439     
00440     if(g_command.position_cdeg.size() != g_js.name.size()){
00441         ROS_ERROR_STREAM("Number of joints does not match " << g_command.position_cdeg.size());
00442         return 1;
00443     }
00444     
00445     
00446     nh_priv.param("sdhx/stopped_velocity",g_stopped_velocity, 0.05);
00447     if(g_stopped_velocity <= 0.0){
00448         ROS_ERROR_STREAM("stopped_velocity must be a positive number");
00449         return 1;
00450     }
00451 
00452     double stopped_current= nh_priv.param("sdhx/stopped_current", 0.1);
00453     if(stopped_current <= 0.0){
00454         ROS_ERROR_STREAM("stopped_current must be a positive number");
00455         return 1;
00456     }
00457     g_stopped_current = stopped_current * 1000.0; // (A -> 100uA)
00458     
00459     std::vector<double> default_currents;
00460     if(nh_priv.getParam("sdhx/default_currents", default_currents)){
00461         if(default_currents.size() !=  g_default_command.current_100uA.size()){
00462             ROS_ERROR_STREAM("Number of current values does not match number of joints");
00463             return 1;
00464         }
00465         for(size_t i=0; i< g_default_command.current_100uA.size(); ++i){
00466             g_default_command.current_100uA[i] = default_currents[i] * 1000.0; // (A -> 100uA)
00467         }
00468     }else{
00469         g_default_command.current_100uA[0] = 2120;
00470         g_default_command.current_100uA[1] = 1400;
00471     }
00472 
00473     g_default_command.velocity_cdeg_s[0] = 1000;
00474     g_default_command.velocity_cdeg_s[1] = 1000;
00475 
00476     g_js.position.resize(g_command.position_cdeg.size());
00477     g_js.velocity.resize(g_command.position_cdeg.size());
00478 
00479     diagnostic_updater::Updater diag_updater;
00480     diag_updater.setHardwareID(nh_priv.param("hardware_id", std::string("none")));
00481     diag_updater.add("bridge", reportDiagnostics);
00482     
00483     diagnostic_updater::TimeStampStatusParam param(
00484         nh_priv.param("status/min_duration", -1.0),
00485         nh_priv.param("status/max_duration", 0.1)
00486     );
00487     g_topic_status.reset ( new diagnostic_updater::TimeStampStatus(param) );
00488     diag_updater.add("connection", boost::bind(&diagnostic_updater::TimeStampStatus::run, g_topic_status, _1));
00489     
00490     ros::Timer diag_timer = nh.createTimer(ros::Duration(diag_updater.getPeriod()/2.0),boost::bind(&diagnostic_updater::Updater::update, &diag_updater));
00491     
00492     double resend_period = nh_priv.param("sdhx/resend_period", 0.1);
00493     if(resend_period > 0.0) g_command_timer = nh.createTimer(ros::Duration(resend_period), resendCommand, false, false);
00494     
00495     g_deadline_timer = nh.createTimer(ros::Duration(1.0), handleDeadline, true, false); // period is not used, will be overwritten
00496 
00497     g_js_pub = nh.advertise<sensor_msgs::JointState>("joint_states", 1);
00498     
00499     ros::Timer status_timer = nh.createTimer(ros::Duration(1.0/20),statusCallback);
00500 
00501     g_halt_srv = nh_d.advertiseService("halt", haltCallback);
00502     g_recover_srv = nh_d.advertiseService("recover", recoverCallback);
00503 
00504     g_as.reset(new FollowJointTrajectoryActionServer(ros::NodeHandle("joint_trajectory_controller"), "follow_joint_trajectory",false));
00505     g_as->registerPreemptCallback(cancelCB);
00506     g_as->registerGoalCallback(goalCB);
00507     
00508     ros::spin();
00509     return 0;
00510 }


cob_hand_bridge
Author(s): Mathias Lüdtke
autogenerated on Thu Jun 6 2019 20:43:57