hekateros_as_follow_traj.cpp
Go to the documentation of this file.
00001 
00002 //
00003 // Package:   RoadNarrows Robotics Hekateros Robotiic Manipulator ROS Package
00004 //
00005 // Link:      https://github.com/roadnarrows-robotics/hekateros
00006 //
00007 // ROS Node:  hekateros_control
00008 //
00009 // File:      hekateros_as_follow_traj.cpp
00010 //
00026 /*
00027  * @EulaBegin@
00028  * 
00029  * Permission is hereby granted, without written agreement and without
00030  * license or royalty fees, to use, copy, modify, and distribute this
00031  * software and its documentation for any purpose, provided that
00032  * (1) The above copyright notice and the following two paragraphs
00033  * appear in all copies of the source code and (2) redistributions
00034  * including binaries reproduces these notices in the supporting
00035  * documentation.   Substantial modifications to this software may be
00036  * copyrighted by their authors and need not follow the licensing terms
00037  * described here, provided that the new terms are clearly indicated in
00038  * all files where they apply.
00039  * 
00040  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
00041  * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
00042  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
00043  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
00044  * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
00045  * THE POSSIBILITY OF SUCH DAMAGE.
00046  * 
00047  * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
00048  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
00049  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
00050  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
00051  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
00052  * 
00053  * @EulaEnd@
00054  */
00056 
00057 //
00058 // System and Boost
00059 //
00060 #include <sys/types.h>
00061 #include <unistd.h>
00062 #include <math.h>
00063 #include <boost/bind.hpp>
00064 #include <sstream>
00065 #include <string>
00066 
00067 //
00068 // ROS
00069 //
00070 #include "ros/ros.h"
00071 
00072 //
00073 // ROS generated core, industrial, and hekateros messages.
00074 //
00075 #include "std_msgs/String.h"
00076 #include "control_msgs/FollowJointTrajectoryAction.h"
00077 
00078 //
00079 // ROS generated action servers.
00080 //
00081 #include "actionlib/server/simple_action_server.h"
00082 
00083 //
00084 // RoadNarrows embedded hekateros library.
00085 //
00086 #include "Hekateros/hekateros.h"
00087 #include "Hekateros/hekJoint.h"
00088 #include "Hekateros/hekTraj.h"
00089 #include "Hekateros/hekUtils.h"
00090 #include "Hekateros/hekRobot.h"
00091 
00092 //
00093 // Node headers.
00094 //
00095 #include "hekateros_control.h"
00096 #include "hekateros_as_follow_traj.h"
00097 
00098 
00099 using namespace std;
00100 using namespace hekateros;
00101 using namespace control_msgs;
00102 using namespace trajectory_msgs;
00103 using namespace hekateros_control;
00104 
00105 //
00106 // Tuning
00107 //
00108 const double ASFollowTrajectory::TolWaypoint = 0.01745; // ~1.0 degrees
00109 const double ASFollowTrajectory::TolGoal     = 0.00087; // ~0.05 degrees
00110 const int    ASFollowTrajectory::MaxIters    = 10;      // ~1 second
00111 
00112 // RDK
00113 // RDK TODO HACK. Need to get max r/s per each joint
00114 #define HACK_MAX_GEAR_RATIO 4.0   // gear ratios vary from 1.5 to 4.0
00115 #define HACK_MIN_SERVO_RPM  45.0  // MXs vary from 45.0 to 78.0
00116 #define HACK_MAX_RADS_PER_SEC \
00117   ( (HACK_MIN_SERVO_RPM / HACK_MAX_GEAR_RATIO / 60.0) * M_TAU )
00118 
00119 // RDK TODO HACK
00120 inline double velPerCent(double velRadsPerSec)
00121 {
00122   double vel = fabs(velRadsPerSec) / HACK_MAX_RADS_PER_SEC * 100.0;
00123   
00124   if( vel < 0.0 )
00125   {
00126     return 0.0;
00127   }
00128   else if( vel > 100.0 )
00129   {
00130     return 100.0;
00131   }
00132   else
00133   {
00134     return vel;
00135   }
00136 }
00137 // RDK 
00138 
00139 void ASFollowTrajectory::execute_cb(
00140                                   const FollowJointTrajectoryGoalConstPtr &goal)
00141 {
00142   JointTrajectory   jt;           // the joint trajectory path
00143   ssize_t           numWaypoints; // number of  waypoints
00144   ssize_t           iWaypoint;    // working waypoint index
00145   int               rc;           // return code
00146 
00147   ROS_INFO("%s: Executing FollowJointTrajectory goal of %zu waypoints.",
00148       action_name_.c_str(), goal->trajectory.points.size());
00149 
00150   //
00151   // Initialize.
00152   //
00153 
00154   // The joint trajectory path.
00155   jt = goal->trajectory;
00156 
00157   numWaypoints = (ssize_t)jt.points.size();
00158 
00159   // No path. Is this an error or a null success?
00160   if( numWaypoints <= 0 )
00161   {
00162     ROS_ERROR("%s: No joint trajectory path.", action_name_.c_str());
00163     result_.error_code = FollowJointTrajectoryResult::INVALID_GOAL;
00164     as_.setAborted(result_);
00165     return;
00166   }
00167 
00168   // Fixed number of joints in fixed order per each way point, so preload names.
00169   feedback_.joint_names = jt.joint_names;
00170 
00171   iWaypoint = -1;
00172 
00173   // control, monitor, and feedback rate
00174   ros::Rate ctl_rate(10);
00175 
00176   //
00177   // Follow joint trajectory path.
00178   //
00179   while( (iWaypoint < numWaypoints) && ros::ok() )
00180   {
00181     //
00182     // Action was preempted.
00183     //
00184     if( as_.isPreemptRequested() )
00185     {
00186       ROS_INFO("%s: Action execution preempted on waypoint %zd.",
00187           action_name_.c_str(), iWaypoint);
00188       result_.error_code = FollowJointTrajectoryResult::INVALID_GOAL;
00189       as_.setPreempted(result_);
00190       return;
00191     }
00192 
00193     //
00194     // At start/current waypoint. Now move to new waypoint.
00195     //
00196     else if( (iWaypoint == -1) || atWaypoint(jt, iWaypoint) )
00197     {
00198       ++iWaypoint;  // index of next waypoint
00199 
00200       // no more waypoints
00201       if( iWaypoint >= numWaypoints )
00202       {
00203         break;
00204       }
00205 
00206       if( (rc = moveToWaypoint(jt, iWaypoint)) == HEK_OK )
00207       {
00208         ROS_INFO("%s: Moving to trajectory waypoint %zd.",
00209             action_name_.c_str(), iWaypoint);
00210         m_fTolerance  = iWaypoint < numWaypoints-1? TolWaypoint: TolGoal;
00211         m_iterMonitor = 0;
00212         feedback_.desired = jt.points[iWaypoint];
00213       }
00214       else
00215       {
00216         ROS_ERROR("%s: Failed moving arm to trajectory waypoint %zd.",
00217               action_name_.c_str(), iWaypoint);
00218         result_.error_code = FollowJointTrajectoryResult::INVALID_JOINTS;
00219         as_.setAborted(result_);
00220         return;
00221       }
00222     }
00223 
00224     //
00225     // Failed to reach waypoint. Abort.
00226     //
00227     else if ( failedWaypoint(jt, iWaypoint) )
00228     {
00229       ROS_ERROR("%s: Failed to reach waypoint %zd.",
00230           action_name_.c_str(), iWaypoint);
00231       result_.error_code = FollowJointTrajectoryResult::INVALID_GOAL;
00232       as_.setAborted(result_);
00233       return;
00234     }
00235 
00236     //
00237     // Monitor movement and provide feedback.
00238     //
00239     monitorMove(jt, iWaypoint);
00240 
00241     ctl_rate.sleep();
00242   }
00243 
00244   //
00245   // Success.
00246   //
00247   if( ros::ok() )
00248   {
00249     ROS_INFO("%s: Follow trajectory succeeded.", action_name_.c_str());
00250     result_.error_code = FollowJointTrajectoryResult::SUCCESSFUL;
00251     as_.setSucceeded(result_);
00252   }
00253 
00254   //
00255   // Odd failure.
00256   //
00257   else
00258   {
00259     ROS_ERROR("%s: Follow trajectory failed.", action_name_.c_str());
00260     result_.error_code = FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
00261     as_.setAborted(result_);
00262   }
00263 }
00264 
00265 int ASFollowTrajectory::moveToWaypoint(JointTrajectory &jt, ssize_t iWaypoint)
00266 {
00267   HekRobot                &robot(node_.getRobot());  // hekateros
00268   HekJointTrajectoryPoint  pt;                       // hekateros joint point
00269   int                      j;                        // working index
00270 
00271   //
00272   // Load next trajectory point.
00273   //
00274   for(j=0; j<jt.joint_names.size(); ++j)
00275   {
00276     pt.append(jt.joint_names[j],
00277               jt.points[iWaypoint].positions[j], 
00278               velPerCent(jt.points[iWaypoint].velocities[j]));
00279     // RDK ROS_DEBUG
00280     ROS_INFO("%s: pos=%5.3f speed=%2.1f",
00281       jt.joint_names[j].c_str(), 
00282       jt.points[iWaypoint].positions[j], 
00283       velPerCent(jt.points[iWaypoint].velocities[j]));
00284   }
00285 
00286   //
00287   // Start moving the arm to the trajectory waypoint.
00288   //
00289   return robot.moveArm(pt);
00290 }
00291 
00292 void ASFollowTrajectory::monitorMove(JointTrajectory &jt, ssize_t iWaypoint)
00293 {
00294   HekRobot             &robot(node_.getRobot());  // hekateros
00295   HekJointStatePoint    jointCurState;            // current joint state point
00296   string                jointName;                // joint name
00297   double                jointWpPos;               // joint waypoint position
00298   double                jointWpVel;               // joint waypoint velocity
00299   double                jointCurPos;              // joint current position
00300   double                jointCurVel;              // joint current velocity
00301   double                jointDistPos;             // joint position distance
00302   double                jointDistVel;             // joint velocity distance
00303   double                waypointDist;             // current waypoint distance
00304   size_t                j;                        // working index
00305 
00306   robot.getJointState(jointCurState);
00307 
00308   m_fWaypointDist = 1000.0;    // make large
00309   waypointDist    = 0.0;        
00310 
00311   feedback_.joint_names.clear();
00312   feedback_.desired.positions.clear();
00313   feedback_.desired.velocities.clear();
00314   feedback_.desired.accelerations.clear();
00315   feedback_.actual.positions.clear();
00316   feedback_.actual.velocities.clear();
00317   feedback_.actual.accelerations.clear();
00318   feedback_.error.positions.clear();
00319   feedback_.error.velocities.clear();
00320   feedback_.error.accelerations.clear();
00321 
00322   // 
00323   // Calculate distance from current position to target waypoint.
00324   //
00325   // Distance metric: Linf of joint positions.
00326   // Alternerates:    L1 or L2 of joint positions, or zero point (end effector)
00327   //                  attachment point) Euclidean distance.
00328   //
00329   for(j=0; j<jt.joint_names.size(); ++j)
00330   {
00331     jointName   = jt.joint_names[j];
00332     jointWpPos  = jt.points[iWaypoint].positions[j]; 
00333     jointWpVel  = jt.points[iWaypoint].velocities[j]; 
00334 
00335     if( jointCurState.hasJoint(jointName) )
00336     {
00337       jointCurPos = jointCurState[jointName].m_fPosition;
00338       jointCurVel = jointCurState[jointName].m_fVelocity;
00339 
00340       jointDistPos = fabs(jointWpPos - jointCurPos);
00341       jointDistVel = jointWpVel - jointCurVel;
00342 
00343       if( jointDistPos > waypointDist )
00344       {
00345         waypointDist = jointDistPos;
00346       }
00347 
00348     }
00349     else
00350     {
00351       jointDistPos = 0.0;
00352       jointDistVel = 0.0;
00353     }
00354 
00355     // add point to feedback
00356     feedback_.joint_names.push_back(jointName);
00357     feedback_.desired.positions.push_back(jointWpPos);
00358     feedback_.desired.velocities.push_back(jointWpVel);
00359     feedback_.desired.accelerations.push_back(0.0);
00360     feedback_.actual.positions.push_back(jointCurPos);
00361     feedback_.actual.velocities.push_back(jointCurVel);
00362     feedback_.actual.accelerations.push_back(0.0);
00363     feedback_.error.positions.push_back(jointDistPos);
00364     feedback_.error.velocities.push_back(jointDistVel);
00365     feedback_.error.accelerations.push_back(0.0);
00366   }
00367 
00368   publishFeedback(iWaypoint);
00369 
00370   m_fWaypointDist = waypointDist;
00371 
00372   ++m_iterMonitor;
00373 
00374   // RDK ROS_DEBUG
00375   ROS_INFO("Monitoring waypoint %zd: iter=%d, dist=%lf\n",
00376       iWaypoint, m_iterMonitor, m_fWaypointDist);
00377 }
00378 
00379 bool ASFollowTrajectory::failedWaypoint(JointTrajectory &jt, ssize_t iWaypoint)
00380 {
00381   HekRobot           &robot(node_.getRobot());  // hekateros
00382 
00383   if( robot.isEStopped() || robot.isAlarmed() || !robot.areServosPowered() )
00384   {
00385     return true;
00386   }
00387 
00388   else if( atWaypoint(jt, iWaypoint) )
00389   {
00390     return false;
00391   }
00392 
00393   else if( (m_iterMonitor > MaxIters) && !robot.isInMotion() )
00394   {
00395     return true;
00396   }
00397 
00398   else
00399   {
00400     return false;
00401   }
00402 }
00403 
00404 void ASFollowTrajectory::publishFeedback(ssize_t iWaypoint)
00405 {
00406   stringstream ss;
00407 
00408   ss << iWaypoint;
00409 
00410   //
00411   // Feedback header.
00412   //
00413   feedback_.header.stamp    = ros::Time::now();
00414   feedback_.header.frame_id = ss.str();
00415   feedback_.header.seq++;
00416 
00417   as_.publishFeedback(feedback_);
00418 }
00419 
00420 void ASFollowTrajectory::preempt_cb()
00421 {
00422   ROS_INFO("%s: Preempt trajectory following.", action_name_.c_str());
00423   node_.getRobot().freeze();
00424 }


hekateros_control
Author(s): Robin Knight , Daniel Packard
autogenerated on Mon Oct 6 2014 00:36:42