LCOV - code coverage report
Current view: top level - src - UniversalRosEndEffectorExecutor.cpp (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 200 239 83.7 %
Date: 2021-10-05 16:55:17 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2019 IIT-HHCM
       3             :  * Author: Luca Muratore
       4             :  * email:  luca.muratore@iit.it
       5             :  *
       6             :  * Licensed under the Apache License, Version 2.0 (the "License");
       7             :  * you may not use this file except in compliance with the License.
       8             :  * You may obtain a copy of the License at
       9             :  * http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             : */
      17             : 
      18             : #include <ros_end_effector/UniversalRosEndEffectorExecutor.h>
      19             : 
      20             : 
      21          41 : ROSEE::UniversalRosEndEffectorExecutor::UniversalRosEndEffectorExecutor ( std::string ns ) : _nh ( ns ) {
      22             : 
      23          41 :     if ( ! _nh.getParam ( "/rate", _rate ) ) {
      24           0 :         ROS_INFO_STREAM ( "Ros parameter for rate not found, I'm setting the default rate of 100 Hz" );
      25           0 :         _rate = 100.0;
      26             :     }
      27          41 :     _period = 1.0 / _rate;
      28          82 :     _loop_timer = _nh.createTimer ( ros::Duration ( _period ),
      29             :                                     &UniversalRosEndEffectorExecutor::timer_callback,
      30          41 :                                     this, false, false );
      31          41 :     _time = 0.0;
      32             : 
      33          82 :     ROSEE::Parser p ( _nh );
      34          41 :     p.init (); //TBD check return
      35          41 :     p.printEndEffectorFingerJointsMap();
      36             : 
      37             :     // retrieve the ee interface
      38          41 :     _ee = std::make_shared<ROSEE::EEInterface> ( p );
      39          41 :     ROS_INFO_STREAM ( "Fingers in EEInterface: " );
      40         145 :     for ( auto& f : _ee->getFingers() ) {
      41         104 :         ROS_INFO_STREAM ( f );
      42             :     }
      43             :     
      44          41 :     folderForActions = p.getActionPath();
      45          41 :     if ( folderForActions.size() == 0 ){ //if no action path is set in the yaml file...
      46           0 :         folderForActions = ROSEE::Utils::getPackagePath() + "/configs/actions/" + _ee->getName();
      47             :     }
      48             :     
      49          41 :     _motors_names =_ee->getActuatedJoints();
      50             : 
      51             :     // prepare publisher which publish motor references
      52          41 :     init_motor_reference_pub();
      53             :     
      54             :     //init q_ref and the filter
      55          41 :     init_qref_filter();
      56             : 
      57             :     // primitives
      58          41 :     init_grasping_primitive();
      59             :     
      60             :     //services, check on mapActionHandlerPtr is done inside the costructor
      61          41 :     _ros_service_handler = std::make_shared<RosServiceHandler>(&_nh, mapActionHandlerPtr, folderForActions+"/generics/");
      62          41 :     _ros_service_handler->init(_ee->getFingers().size());
      63             :     
      64             :     // actions
      65          82 :     std::string actionGraspingCommandName;
      66          41 :     _nh.param<std::string>("/rosee/rosAction_grasping_command", actionGraspingCommandName, "action_command");
      67          41 :     _ros_action_server = std::make_shared<RosActionServer> (actionGraspingCommandName , &_nh);
      68          41 :     timed_requested = false;
      69          41 :     timed_index = -1;
      70             :     
      71          41 :     init_joint_state_sub();
      72             :     
      73          41 : }
      74             : 
      75          41 : bool ROSEE::UniversalRosEndEffectorExecutor::init_motor_reference_pub() {
      76             :     
      77             :     //Fixed topic, Hal will read always from here
      78          82 :     std::string motor_reference_topic  = "motor_reference_pos";
      79          41 :     const int motor_reference_queue = 10;
      80             : 
      81             :     //We could use MotorPosition message... but with JointState the hal node has not to include
      82             :     //our rosee_msg package, so maybe it is better use official ros JointState
      83          41 :     _motor_reference_pub = _nh.advertise<sensor_msgs::JointState> ( motor_reference_topic, motor_reference_queue );
      84          41 :     _motors_num = _motors_names.size();
      85             :     //mr = motor reference
      86          41 :     _mr_msg.name = _motors_names;
      87          41 :     _mr_msg.position.resize ( _motors_num );
      88          41 :     _mr_msg.velocity.resize ( _motors_num );
      89          41 :     _mr_msg.effort.resize ( _motors_num ); 
      90             :     
      91          82 :     return true;
      92             : }
      93             : 
      94          41 : bool ROSEE::UniversalRosEndEffectorExecutor::init_qref_filter() {
      95             :     
      96             :     // filter TBD select filter profile
      97          41 :     const double DAMPING_FACT = 1.0;
      98          41 :     const double BW_MEDIUM = 2.0;
      99          41 :     double omega = 2.0 * M_PI * BW_MEDIUM;
     100             : 
     101          41 :     _filt_q.setDamping ( DAMPING_FACT );
     102          41 :     _filt_q.setTimeStep ( _period );
     103          41 :     _filt_q.setOmega ( omega );
     104             : 
     105             :     // initialize references
     106          41 :     _qref.resize ( _motors_num );
     107          41 :     _qref_optional.resize ( _motors_num );
     108          41 :     _qref_optional.setZero();
     109             :     // TBD init from current position reference
     110          41 :     _qref.setZero();
     111             :     // reset filter
     112          41 :     _filt_q.reset ( _qref );
     113             :     
     114          41 :     return true;
     115             : }
     116             : 
     117             : 
     118          41 : bool ROSEE::UniversalRosEndEffectorExecutor::init_grasping_primitive() {
     119             : 
     120             :     // get all action in the handler
     121          41 :     mapActionHandlerPtr = std::make_shared<ROSEE::MapActionHandler>();
     122          41 :     mapActionHandlerPtr->parseAllActions(folderForActions);
     123             : 
     124             :     // pinch tight
     125          41 :     _pinchParsedMap = mapActionHandlerPtr->getPrimitiveMap("pinchTight");
     126             :     
     127             :     // pinch loose
     128          41 :     if (mapActionHandlerPtr->getPrimitiveMap(ROSEE::ActionPrimitive::Type::PinchLoose).size()>0) {
     129             :         //another method to get the map
     130           8 :         _pinchLooseParsedMap = mapActionHandlerPtr->getPrimitiveMap(ROSEE::ActionPrimitive::Type::PinchLoose).at(0);
     131             :     } 
     132             :     
     133             :     // trig, tip flex and fing flex
     134          41 :     _trigParsedMap = mapActionHandlerPtr->getPrimitiveMap("trig");
     135          41 :     _tipFlexParsedMap = mapActionHandlerPtr->getPrimitiveMap("tipFlex");
     136          41 :     _fingFlexParsedMap = mapActionHandlerPtr->getPrimitiveMap("fingFlex");
     137             : 
     138             :     // NOTE maps useful just to recap
     139          41 :     ROS_INFO_STREAM ( "PINCHES-TIGHT:" );
     140          96 :     for ( auto &i : _pinchParsedMap ) {
     141          55 :         i.second->print();
     142             :     }
     143          41 :     ROS_INFO_STREAM ( "PINCHES-LOOSE:" );
     144          49 :     for ( auto &i : _pinchLooseParsedMap ) {
     145           8 :         i.second->print();
     146             :     }
     147          41 :     ROS_INFO_STREAM ( "TRIGGERS:" );
     148         129 :     for ( auto &i : _trigParsedMap ) {
     149          88 :         i.second->print();
     150             :     }
     151          41 :     ROS_INFO_STREAM ( "TIP FLEX:" );
     152         129 :     for ( auto &i : _tipFlexParsedMap ) {
     153          88 :         i.second->print();
     154             :     }
     155          41 :     ROS_INFO_STREAM ( "FINGER FLEX:" );
     156         129 :     for ( auto &i : _fingFlexParsedMap ) {
     157          88 :         i.second->print();
     158             :     }
     159             :     
     160             :     // composed actions
     161          41 :     _graspParsed = mapActionHandlerPtr->getGeneric("grasp");
     162             :     
     163             :     // recap
     164          41 :     ROS_INFO_STREAM ( "GRASP:" );
     165          41 :     if (_graspParsed != nullptr) {
     166          33 :         _graspParsed->print();
     167             :     }
     168             :     
     169          41 :     return true;
     170             : }
     171             : 
     172          41 : void ROSEE::UniversalRosEndEffectorExecutor::init_joint_state_sub () {
     173             : 
     174             :     //Fixed topic, Hal will publish always here
     175          82 :     std::string topic_name_js = "/ros_end_effector/joint_states";
     176             :     
     177          41 :     ROS_INFO_STREAM ( "Getting joint pos from '" << topic_name_js << "'" );
     178             :     
     179          82 :     _joint_state_sub = _nh.subscribe (topic_name_js, 1, 
     180          41 :                                  &ROSEE::UniversalRosEndEffectorExecutor::joint_state_clbk, this);
     181          41 : }
     182             : 
     183       20762 : void ROSEE::UniversalRosEndEffectorExecutor::joint_state_clbk(const sensor_msgs::JointStateConstPtr& msg) {
     184             :     
     185             :     //We store all what we receive from hal node, to not loose time in the clbk to not consider the not motors
     186      139033 :     for (int i=0; i< msg->name.size(); i++) {
     187      236542 :         std::vector <double> one_dof { msg->position.at(i) };
     188      118271 :         _joint_actual_pos[msg->name.at(i)] = one_dof;
     189             :     }
     190       20762 : }
     191             : 
     192             : 
     193             : // set q ref
     194          33 : bool ROSEE::UniversalRosEndEffectorExecutor::updateGoal() {
     195             :     
     196          66 :     rosee_msg::ROSEEActionControl goal = _ros_action_server->getGoal();
     197             :     
     198          33 :     timed_requested = false;
     199             :     
     200          33 :     if (goal.action_type != ROSEE::Action::Type::Timed){
     201          29 :         if (goal.percentage < 0 || goal.percentage > 1) { 
     202           0 :             ROS_ERROR_STREAM ( "Received an action-goal with percentage " << goal.percentage << 
     203             :                 " Please insert a value between 0 (for 0%) and 1 (for 100%) ");
     204           0 :             _ros_action_server->abortGoal("Percentage not valid");
     205           0 :             return false;
     206             :             
     207             :         }
     208             :     }
     209             :     
     210             :     // we need this as global member because in send_feedback we need it...
     211             :     //JointsInvolvedCount joint_involved_mask;
     212             :     
     213             :     //Be sure that action_type of goal is defined well in msg, it is not a real enum
     214          33 :     switch (goal.action_type) {
     215             :         
     216          14 :     case ROSEE::Action::Type::Primitive :
     217             :     {
     218          14 :         ROSEE::ActionPrimitive::Ptr primitive = mapActionHandlerPtr->getPrimitive (goal.action_name, goal.selectable_items);
     219             :         
     220          14 :         if (primitive == nullptr) {
     221             :             //error message already printed in getPrimitive
     222           0 :             _ros_action_server->abortGoal("Primitive Action not found");
     223           0 :             return false;
     224             :         }
     225             :         
     226          14 :         _motor_position_goal = primitive->getJointPos();
     227          14 :         _motor_involved_mask = primitive->getJointsInvolvedCount();
     228             : 
     229          14 :         break;
     230             :     }
     231          15 :     case ROSEE::Action::Type::Generic : // same thing as composed
     232             :     case ROSEE::Action::Type::Composed :
     233             :     {
     234          15 :         ROSEE::ActionGeneric::Ptr generic = mapActionHandlerPtr->getGeneric(goal.action_name);
     235             :         
     236          15 :         if (generic == nullptr) {
     237             :             //error message already printed in getGeneric
     238           0 :             _ros_action_server->abortGoal("Generic Action not found");
     239           0 :             return false;
     240             :         }
     241             :         
     242          15 :         _motor_position_goal = generic->getJointPos();
     243          15 :         _motor_involved_mask = generic->getJointsInvolvedCount();
     244             :         
     245          15 :         break;
     246             :     }
     247           4 :     case ROSEE::Action::Type::Timed : {
     248             :         // here we take the first of the timed action and we set refs for it
     249             :         //TODO first time margin (before) is not considered?
     250             : 
     251           4 :         timedAction = mapActionHandlerPtr->getTimed(goal.action_name);
     252             :         
     253           4 :         if (timedAction == nullptr) {
     254             :             //error message already printed in getTimed
     255           0 :             _ros_action_server->abortGoal("Timed Action not found");
     256           0 :             return false;
     257             :         }
     258             :         
     259           4 :         timed_requested = true;
     260           4 :         timed_index = 0;
     261           4 :         _motor_position_goal = timedAction->getAllJointPos().at(0);
     262          12 :         _motor_involved_mask = timedAction->getJointCountAction(
     263          12 :             timedAction->getInnerActionsNames().at(0));
     264             : 
     265           4 :         goal.percentage = 1; //so we are sure it is set, for timed is always 100%
     266             :         
     267           4 :         break;
     268             :     }
     269           0 :     case ROSEE::Action::Type::None :
     270             :     {
     271           0 :         ROS_ERROR_STREAM ( "Received an action-goal of type None (" << goal.action_type << 
     272             :             ") which is a no-type. Please use valid Action::Type ");
     273           0 :         _ros_action_server->abortGoal("Invalid type NONE");
     274           0 :         return false;
     275             :         break;
     276             :     }
     277           0 :     default : {
     278           0 :         ROS_ERROR_STREAM ( "Received an action-goal of type " << goal.action_type << 
     279             :             " which I do not recognize. Please use valid Action::Type ");
     280           0 :         _ros_action_server->abortGoal("Invalid type " + std::to_string(goal.action_type) );
     281           0 :         return false;
     282             :     }
     283             :     } 
     284             : 
     285             :     //WE reset this so if it is not used it stay to zero
     286          33 :     _qref_optional.setZero();
     287          33 :     if (goal.optional_motors_names.size() != 0) {
     288           0 :         readOptionalCommands(goal.optional_motors_names, goal.optional_motors_commands);
     289             :     }
     290             :     
     291          33 :     return updateRefGoal(goal.percentage);
     292             :     
     293             : }
     294             : 
     295           0 : bool ROSEE::UniversalRosEndEffectorExecutor::readOptionalCommands(
     296             :         std::vector<std::string> motors_names, std::vector<double> motors_commands) {
     297             :     
     298           0 :     bool flag = true;
     299             :     
     300           0 :     if (motors_names.size() != motors_commands.size() &&
     301           0 :         motors_names.size() != _motors_num) {
     302           0 :         ROS_ERROR_STREAM ( "In receiving the goal command, the optional field is formed badly: " 
     303             :             << "optional_motors_names and optional_motors_commands and number of motors have different size (" 
     304             :             <<  motors_names.size() << " and " << motors_commands.size() << " and " << _motors_num
     305             :             << " respectively).  I will ignore the optional command");
     306           0 :         return false;
     307             :     } 
     308             :     
     309             : 
     310           0 :     for (int i=0; i<motors_names.size(); i++) {
     311             :         
     312           0 :         int id =-1;
     313           0 :         _ee->getInternalIdForJoint ( motors_names.at(i), id );
     314           0 :         if( id >= 0 ) {
     315             :             // NOTE assume single dof
     316           0 :             _qref_optional[id] = motors_commands.at(i);
     317             :         }
     318             :         
     319             :         else {
     320           0 :             ROS_WARN_STREAM ( "Trying to send an optional command to motor: " << motors_names.at(i) 
     321             :             << " which is not defined" );
     322           0 :             flag = false;
     323             :         }
     324             :     }
     325             :     
     326             :     //TODO we have to filter also qref_optional ??
     327             :     
     328           0 :     return flag;
     329             : }
     330             : 
     331          38 : bool ROSEE::UniversalRosEndEffectorExecutor::updateRefGoal(double percentage) {
     332             :     
     333          38 :     normGoalFromInitialPos = 0;
     334         250 :     for ( auto it : _motor_involved_mask ) {
     335             : 
     336         212 :         if ( it.second  != 0 ) {
     337         134 :             int id = -1;
     338         134 :             _ee->getInternalIdForJoint ( it.first, id );
     339             :             
     340         134 :             if( id >= 0 ) {
     341             :                 // NOTE assume single joint
     342         134 :                 _qref[id] = _motor_position_goal.at ( it.first ).at ( 0 ) * percentage;
     343             :                 // to give the % as feedback, we store the initial distance from the goal
     344             :                 //TODO take care that initially jointPos can be empty...
     345         134 :                 normGoalFromInitialPos +=  pow (_qref[id] - _joint_actual_pos.at(it.first).at(0), 2 )  ;
     346             :             }
     347             :             else {
     348           0 :                 ROS_WARN_STREAM ( "Trying to move Joint: " << it.first << " with ID: " << id );
     349           0 :                 _ros_action_server->abortGoal("Invalid Joint id" );
     350           0 :                 return false;
     351             :             }
     352             :         }
     353             :     }
     354          38 :     normGoalFromInitialPos = sqrt(normGoalFromInitialPos); 
     355             :     
     356          38 :     if (timed_requested) {
     357             :         
     358             :         //we are in this function BEFORE the begin of execution of a inner... so
     359             :         //we set the toWait to (afterMargin of previous inner + beforeMargin of actual (todo) inner)
     360             :         //except when timed_index == 0 where we have only the before
     361             :         //the after margin of last inner is not considered.
     362           9 :         if (timed_index == 0) {
     363           4 :             msToWait = timedAction->getAllActionMargins().at(timed_index).first;
     364             :             
     365           5 :         } else if (timed_index < timedAction->getInnerActionsNames().size()) {
     366          15 :             msToWait = timedAction->getAllActionMargins().at(timed_index-1).second + 
     367          10 :                      timedAction->getAllActionMargins().at(timed_index).first;
     368             :         }
     369           9 :         msToWait *= 1000;
     370           9 :         timer.reset();
     371             :     }
     372             :     
     373          38 :     return true;
     374             : }
     375             : 
     376        2230 : double ROSEE::UniversalRosEndEffectorExecutor::sendFeedbackGoal(std::string currentAction) {
     377             :     //norm between goal and initial (when the goal arrived) position, but only considering the jointInvolved
     378             :     
     379        2230 :     double actualNorm = 0;
     380             :     
     381       15440 :     for ( auto it : _motor_involved_mask ) {
     382             : 
     383       13210 :         if ( it.second  != 0 ) {
     384        7455 :             int id = -1;
     385        7455 :             _ee->getInternalIdForJoint ( it.first, id );
     386             :             
     387        7455 :             if( id >= 0 ) {
     388             :                 // NOTE assume single joint
     389        7455 :                 actualNorm += pow ( _qref[id] - _joint_actual_pos.at(it.first).at(0) , 2 );
     390             :             }
     391             :             
     392             :             else {
     393           0 :                 ROS_ERROR_STREAM ( "YOU SHOULD NOT BE HERE, previous error should stop execution");
     394             :             }
     395             :         }
     396             :     }
     397        2230 :     actualNorm = sqrt(actualNorm);
     398             :     
     399             :     double actualCompletationPercentage;
     400             : 
     401        2230 :     if (actualNorm < _ros_action_server->getWantedNormError()) { 
     402         137 :         actualCompletationPercentage = 100;
     403             :     } else {
     404             :         
     405             :     //NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
     406        2093 :         actualCompletationPercentage =
     407        2093 :         (((actualNorm - normGoalFromInitialPos) * (100-0)) / (0 - normGoalFromInitialPos)) + 0;
     408             :     }
     409             : 
     410        2230 :     _ros_action_server->sendFeedback(actualCompletationPercentage, currentAction);
     411        2230 :     return actualCompletationPercentage;
     412             : }
     413             : 
     414             : 
     415       21341 : bool ROSEE::UniversalRosEndEffectorExecutor::publish_motor_reference() {
     416             : 
     417       21341 :     _mr_msg.header.stamp = ros::Time::now();
     418       21341 :     _mr_msg.header.seq = _seq_id++;
     419             : 
     420       21341 :     _qref_filtered = _filt_q.process ( _qref );
     421             :     
     422       21341 :     int id = -1;
     423      138189 :     for ( const auto& motor_name : _motors_names ) {
     424             : 
     425             :         //id to put take the qref from the right index in qref
     426      116848 :         _ee->getInternalIdForJoint ( motor_name, id );
     427      116848 :         _mr_msg.name[id] = motor_name;
     428      116848 :         _mr_msg.position[id] =_qref_filtered[id] ;
     429      116848 :         _mr_msg.effort[id] = _qref_optional[id];
     430             :     }
     431             : 
     432       21341 :     _motor_reference_pub.publish ( _mr_msg );
     433             :     
     434       21341 :     return true;
     435             : }
     436             : 
     437       21724 : void ROSEE::UniversalRosEndEffectorExecutor::timer_callback ( const ros::TimerEvent& timer_ev ) {
     438             :     
     439             :     //this is true only when a new goal has arrived... so new goal ovewrite the old one
     440       21724 :     if (_ros_action_server->hasNewGoal()) {
     441             :         
     442          33 :         updateGoal(); //store jp of goal and update qref
     443             :         
     444       21691 :     } else if (_ros_action_server->hasGoal()) {
     445             : 
     446        2230 :         if (! timed_requested ) {
     447             : 
     448        1392 :             if (sendFeedbackGoal() >= 100) {
     449             :                 
     450          29 :                 _ros_action_server->sendComplete();
     451             :             }
     452             :             
     453             :         } else { //a timed is running
     454             :             
     455         838 :             if ( sendFeedbackGoal(timedAction->getInnerActionsNames().at(timed_index)) >= 100 ) {
     456             :                 
     457         108 :                 if (timed_index == timedAction->getInnerActionsNames().size()-1)  {
     458             :                 // completed all the timed action (the last inner has finished)
     459             : 
     460           4 :                     _ros_action_server->sendComplete();
     461           4 :                     timed_requested = false;
     462             :                     
     463         104 :                 } else if ( 
     464         208 :                     timer.elapsed_time<double, std::chrono::milliseconds>() >
     465         208 :                         (timedAction->getAllActionMargins().at(timed_index).second*1000) ) {
     466             :                     
     467             :                 // if the time passed is greater than the .after margin of the last executed,
     468             :                 // then pass to the other action. This is done so we continue to send feedback
     469             :                 // of a inner (with value 100) until the margin after is passed. If we are here,
     470             :                 // a inner action (but not the last one) is completed (included after margin),
     471             :                 // so we have to execute the next one now
     472             :                     
     473           5 :                     timed_index++;
     474           5 :                     _motor_position_goal = timedAction->getAllJointPos().at(timed_index);
     475           5 :                     _motor_involved_mask = timedAction->getAllJointCountAction().at(timed_index);
     476           5 :                     updateRefGoal();
     477             :                     
     478             :                 } 
     479             :             }
     480             :         }  
     481             :     }
     482             : 
     483             : 
     484             :     //if it is a timed, we have to check if we need to still wait to execute the subsequent innet action
     485       21724 :     if (timed_requested) {
     486         838 :         if (timer.elapsed_time<double, std::chrono::milliseconds>()  >  msToWait )  {
     487         455 :             publish_motor_reference(); 
     488             :                 
     489             :         } else {
     490         383 :             ROS_INFO_STREAM ("Waiting time to execute timed action...");
     491             :         }
     492             :             
     493             :     } else {
     494       20886 :         publish_motor_reference(); 
     495             :     }
     496             : 
     497             :     // update time
     498       21724 :     _time += _period;
     499       21724 : }
     500             : 
     501             : 
     502          41 : void ROSEE::UniversalRosEndEffectorExecutor::spin() {
     503             : 
     504          41 :     _loop_timer.start();
     505             : 
     506          41 :     ROS_INFO_STREAM ( "Started looping @ " << 1./_period << "Hz" );
     507             : 
     508          41 :     ros::spin();
     509          41 : }
     510             : 
     511             : 
     512             : 
     513          41 : ROSEE::UniversalRosEndEffectorExecutor::~UniversalRosEndEffectorExecutor() {
     514             : 
     515         164 : }

Generated by: LCOV version 1.13