interactive_robot.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2013, Willow Garage, Inc.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of Willow Garage, Inc. nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Acorn Pooley
00036 *********************************************************************/
00037 
00038 // This code goes with the interactivity tutorial
00039 //    http://moveit.ros.org/wiki/index.php/Groovy/InteractiveRobot/InteractivityTutorial
00040 
00041 #include "interactive_robot.h"
00042 #include <eigen_conversions/eigen_msg.h>
00043 #include <moveit/robot_state/conversions.h>
00044 
00045 
00046 // default world object position is just in front and left of PR2 robot.
00047 const Eigen::Affine3d InteractiveRobot::DEFAULT_WORLD_OBJECT_POSE_(Eigen::Affine3d(Eigen::Translation3d(0.25, -0.5, 0.5)));
00048 
00049 // size of the world geometry cube
00050 const double InteractiveRobot::WORLD_BOX_SIZE_ = 0.15;
00051 
00052 // minimum delay between calls to callback function
00053 const ros::Duration InteractiveRobot::min_delay_(0.01);
00054 
00055 
00056 
00057 InteractiveRobot::InteractiveRobot(
00058       const std::string& robot_description,
00059       const std::string& robot_topic,
00060       const std::string& marker_topic,
00061       const std::string& imarker_topic) :
00062   // this node handle is used to create the publishers
00063   nh_(),
00064   // create publishers for markers and robot state
00065   robot_state_publisher_(nh_.advertise<moveit_msgs::DisplayRobotState>(robot_topic,1)),
00066   world_state_publisher_(nh_.advertise<visualization_msgs::Marker>(marker_topic,100)),
00067   // create an interactive marker server for displaying interactive markers
00068   interactive_marker_server_(imarker_topic),
00069   imarker_robot_(0),
00070   imarker_world_(0),
00071   // load the robot description
00072   rm_loader_(robot_description),
00073   group_(0),
00074   user_data_(0),
00075   user_callback_(0)
00076 {
00077   // get the RobotModel loaded from urdf and srdf files
00078   robot_model_ = rm_loader_.getModel();
00079   if (!robot_model_) {
00080     ROS_ERROR("Could not load robot description");
00081     throw RobotLoadException();
00082   }
00083 
00084   // create a RobotState to keep track of the current robot pose
00085   robot_state_.reset(new robot_state::RobotState(robot_model_));
00086   if (!robot_state_) {
00087     ROS_ERROR("Could not get RobotState from Model");
00088     throw RobotLoadException();
00089   }
00090   robot_state_->setToDefaultValues();
00091 
00092   // Prepare to move the "right_arm" group
00093   group_ = robot_state_->getJointStateGroup("right_arm");
00094   std::string end_link = group_->getJointModelGroup()->getLinkModelNames().back();
00095   desired_group_end_link_pose_ = robot_state_->getLinkState(end_link)->getGlobalLinkTransform();
00096 
00097   // Create a marker to control the "right_arm" group
00098   imarker_robot_ = new IMarker(
00099       interactive_marker_server_,
00100       "robot",
00101       desired_group_end_link_pose_,
00102       "/base_footprint",
00103       boost::bind(movedRobotMarkerCallback,this,_1),
00104       IMarker::BOTH),
00105 
00106   // create an interactive marker to control the world geometry (the yellow cube)
00107   desired_world_object_pose_ = DEFAULT_WORLD_OBJECT_POSE_;
00108   imarker_world_ = new IMarker(
00109       interactive_marker_server_,
00110       "world",
00111       desired_world_object_pose_,
00112       "/base_footprint",
00113       boost::bind(movedWorldMarkerCallback,this,_1),
00114       IMarker::POS),
00115 
00116   // start publishing timer.
00117   init_time_ = ros::Time::now();
00118   last_callback_time_ = init_time_;
00119   average_callback_duration_ = min_delay_;
00120   schedule_request_count_ = 0;
00121   publish_timer_ = nh_.createTimer(average_callback_duration_, &InteractiveRobot::updateCallback, this, true);
00122 
00123   // begin publishing robot state
00124   scheduleUpdate();
00125 }
00126 
00127 InteractiveRobot::~InteractiveRobot()
00128 {
00129   delete imarker_world_;
00130   delete imarker_robot_;
00131 }
00132 
00133 // callback called when marker moves.  Moves right hand to new marker pose.
00134 void InteractiveRobot::movedRobotMarkerCallback(
00135     InteractiveRobot *robot,
00136     const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback )
00137 {
00138   Eigen::Affine3d pose;
00139   tf::poseMsgToEigen(feedback->pose, pose);
00140   robot->setGroupPose(pose);
00141 }
00142 
00143 // callback called when marker moves.  Moves world object to new pose.
00144 void InteractiveRobot::movedWorldMarkerCallback(
00145     InteractiveRobot *robot,
00146     const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback )
00147 {
00148   Eigen::Affine3d pose;
00149   tf::poseMsgToEigen(feedback->pose, pose);
00150   robot->setWorldObjectPose(pose);
00151 }
00152 
00153 // set the callback timer to fire if needed.
00154 // Return true if callback should happen immediately
00155 bool InteractiveRobot::setCallbackTimer(bool new_update_request)
00156 {
00157   publish_timer_.stop();
00158 
00159   const ros::Time now = ros::Time::now();
00160   const ros::Duration desired_delay = std::max(min_delay_, average_callback_duration_ * 1.2);
00161   ros::Duration sec_since_last_callback = now - last_callback_time_;
00162   ros::Duration sec_til_next_callback = desired_delay - sec_since_last_callback;
00163 
00164   if (schedule_request_count_)
00165   {
00166     // need a callback desired_delay seconds after previous callback
00167     schedule_request_count_ += new_update_request ? 1 : 0;
00168     if (sec_til_next_callback <= ros::Duration(0.0001))
00169     {
00170       // just run the callback now
00171       return true;
00172     }
00173     publish_timer_.setPeriod(sec_til_next_callback);
00174     publish_timer_.start();
00175     return false;
00176   }
00177   else if (new_update_request)
00178   {
00179     if (sec_til_next_callback < min_delay_) {
00180       // been a while.  Use min_delay_.
00181       // Set last_callback_time_ to prevent firing too early
00182       sec_til_next_callback = min_delay_;
00183       sec_since_last_callback = desired_delay - sec_til_next_callback;
00184       last_callback_time_ = now - sec_since_last_callback;
00185     }
00186     publish_timer_.setPeriod(sec_til_next_callback);
00187     publish_timer_.start();
00188     return false;
00189   }
00190   else if (!init_time_.isZero())
00191   {
00192     // for the first few seconds after startup call the callback periodically
00193     // to ensure rviz gets the initial state.
00194     // Without this rviz does not show some state until markers are moved.
00195     if ((now - init_time_).sec >= 8)
00196     {
00197       init_time_ = ros::Time(0,0);
00198       return false;
00199     }
00200     else
00201     {
00202       publish_timer_.setPeriod(std::max(ros::Duration(1.0), average_callback_duration_*2));
00203       publish_timer_.start();
00204       return false;
00205     }
00206   }
00207   else
00208   {
00209     // nothing to do.  No callback needed.
00210     return false;
00211   }
00212 }
00213 
00214 // Indicate that the world or the robot has changed and
00215 // the new state needs to be updated and published to rviz
00216 void InteractiveRobot::scheduleUpdate()
00217 {
00218   // schedule an update callback for the future.
00219   // If the callback should run now, call it.
00220   if (setCallbackTimer(true))
00221     updateCallback(ros::TimerEvent());
00222 }
00223 
00224 
00225 /* callback called when it is time to publish */
00226 void InteractiveRobot::updateCallback(const ros::TimerEvent& e)
00227 {
00228   ros::Time tbegin = ros::Time::now();
00229   publish_timer_.stop();
00230 
00231   // do the actual calculations and publishing
00232   updateAll();
00233 
00234   // measure time spent in callback for rate limiting
00235   ros::Time tend = ros::Time::now();
00236   average_callback_duration_ = (average_callback_duration_ + (tend - tbegin)) * 0.5;
00237   last_callback_time_ = tend;
00238   schedule_request_count_ = 0;
00239 
00240   // schedule another callback if needed
00241   setCallbackTimer(false);
00242 }
00243 
00244 /* Calculate new positions and publish results to rviz */
00245 void InteractiveRobot::updateAll()
00246 {
00247   publishWorldState();
00248 
00249   if (group_->setFromIK(desired_group_end_link_pose_, 10, 0.1))
00250   {
00251     publishRobotState();
00252 
00253     if (user_callback_)
00254       user_callback_(*this);
00255   }
00256 }
00257 
00258 // change which group is being manipulated
00259 void InteractiveRobot::setGroup(const std::string& name)
00260 {
00261   robot_state::JointStateGroup* group = robot_state_->getJointStateGroup(name);
00262   if (!group)
00263   {
00264     ROS_ERROR_STREAM("No joint group named " << name);
00265     if (!group_)
00266       throw RobotLoadException();
00267   }
00268   group_ = group;
00269   std::string end_link = group_->getJointModelGroup()->getLinkModelNames().back();
00270   desired_group_end_link_pose_ = robot_state_->getLinkState(end_link)->getGlobalLinkTransform();
00271   if (imarker_robot_)
00272   {
00273     imarker_robot_->move(desired_group_end_link_pose_);
00274   }
00275 }
00276 
00277 // return current group name
00278 const std::string& InteractiveRobot::getGroupName() const
00279 {
00280   return group_->getName();
00281 }
00282 
00283 /* remember new desired robot pose and schedule an update */
00284 bool InteractiveRobot::setGroupPose(const Eigen::Affine3d& pose)
00285 {
00286   desired_group_end_link_pose_ = pose;
00287   scheduleUpdate();
00288 }
00289 
00290 /* publish robot pose to rviz */
00291 void InteractiveRobot::publishRobotState()
00292 {
00293   moveit_msgs::DisplayRobotState msg;
00294   robot_state::robotStateToRobotStateMsg(*robot_state_, msg.state);
00295   robot_state_publisher_.publish(msg);
00296 }
00297 
00298 
00299 /* remember new world object position and schedule an update */
00300 void InteractiveRobot::setWorldObjectPose(const Eigen::Affine3d& pose)
00301 {
00302   desired_world_object_pose_ = pose;
00303   scheduleUpdate();
00304 }
00305 
00306 /* publish world object position to rviz */
00307 void InteractiveRobot::publishWorldState()
00308 {
00309   visualization_msgs::Marker marker;
00310   marker.header.frame_id = "/base_footprint";
00311   marker.header.stamp = ros::Time::now();
00312   marker.ns = "world_box";
00313   marker.id = 0;
00314   marker.type = visualization_msgs::Marker::CUBE;
00315   marker.action = visualization_msgs::Marker::ADD;
00316   marker.scale.x = WORLD_BOX_SIZE_;
00317   marker.scale.y = WORLD_BOX_SIZE_;
00318   marker.scale.z = WORLD_BOX_SIZE_;
00319   marker.color.r = 1.0f;
00320   marker.color.g = 1.0f;
00321   marker.color.b = 0.0f;
00322   marker.color.a = 0.4f;
00323   marker.lifetime = ros::Duration();
00324   tf::poseEigenToMsg(desired_world_object_pose_, marker.pose);
00325   world_state_publisher_.publish(marker);
00326 }
00327 
00328 /* get world object pose and size */
00329 void InteractiveRobot::getWorldGeometry(Eigen::Affine3d& pose, double& size)
00330 {
00331   pose = desired_world_object_pose_;
00332   size = WORLD_BOX_SIZE_;
00333 }


pr2_moveit_tutorials
Author(s): Sachin Chitta
autogenerated on Mon Oct 6 2014 11:15:31