diff_drive_base.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *  Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2014-2017, Fetch Robotics Inc.
00005  *  Copyright (c) 2013, Unbounded Robotics 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 Unbounded Robotics 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 
00036 // Author: Michael Ferguson
00037 
00038 #include <angles/angles.h>
00039 #include <pluginlib/class_list_macros.h>
00040 #include <robot_controllers/diff_drive_base.h>
00041 
00042 PLUGINLIB_EXPORT_CLASS(robot_controllers::DiffDriveBaseController, robot_controllers::Controller)
00043 
00044 namespace robot_controllers
00045 {
00046 
00047 DiffDriveBaseController::DiffDriveBaseController() :
00048     initialized_(false),
00049     safety_scaling_(1.0)
00050 {
00051   theta_ = 0.0;
00052 
00053   odom_.pose.pose.orientation.z = 0.0;
00054   odom_.pose.pose.orientation.w = 1.0;
00055 
00056   last_sent_x_ = desired_x_ = 0.0;
00057   last_sent_r_ = desired_r_ = 0.0;
00058 
00059   left_last_timestamp_ = right_last_timestamp_ = 0.0;
00060   last_command_ = last_update_ = ros::Time(0.0);
00061 }
00062 
00063 int DiffDriveBaseController::init(ros::NodeHandle& nh, ControllerManager* manager)
00064 {
00065   // We absolutely need access to the controller manager
00066   if (!manager)
00067   {
00068     ROS_ERROR_NAMED("BaseController", "No controller manager available.");
00069     initialized_ = false;
00070     return -1;
00071   }
00072 
00073   Controller::init(nh, manager);
00074   manager_ = manager;
00075 
00076   // Initialize joints
00077   std::string l_name, r_name;
00078   nh.param<std::string>("l_wheel_joint", l_name, "l_wheel_joint");
00079   nh.param<std::string>("r_wheel_joint", r_name, "r_wheel_joint");
00080   left_ = manager_->getJointHandle(l_name);
00081   right_ = manager_->getJointHandle(r_name);
00082   if (left_ == NULL || right_ == NULL)
00083   {
00084     ROS_ERROR_NAMED("BaseController", "Cannot get wheel joints.");
00085     initialized_ = false;
00086     return -1;
00087   }
00088   left_last_position_ = left_->getPosition();
00089   right_last_position_ = right_->getPosition();
00090   last_update_ = ros::Time::now();
00091 
00092   // Get base parameters
00093   nh.param<double>("track_width", track_width_, 0.37476);
00094   nh.param<double>("radians_per_meter", radians_per_meter_, 16.5289);
00095 
00096   // If using an external correction (such as robot_pose_ekf or graft)
00097   // we should not publish the TF frame from base->odom
00098   nh.param<bool>("publish_tf", publish_tf_, true);
00099 
00100   // The pose in the odometry message is specified in terms of the odometry frame
00101   nh.param<std::string>("odometry_frame", odom_.header.frame_id, "odom");
00102 
00103   // The twist in the odometry message is specified in the coordinate frame of the base
00104   nh.param<std::string>("base_frame", odom_.child_frame_id, "base_link");
00105 
00106   // Get various thresholds below which we supress noise
00107   nh.param<double>("wheel_rotating_threshold", wheel_rotating_threshold_, 0.001);
00108   nh.param<double>("rotating_threshold", rotating_threshold_, 0.05);
00109   nh.param<double>("moving_threshold", moving_threshold_, 0.05);
00110 
00111   double t;
00112   nh.param<double>("timeout", t, 0.25);
00113   timeout_ = ros::Duration(t);
00114 
00115   // Get limits of base controller
00116   nh.param<double>("max_velocity_x", max_velocity_x_, 1.0);
00117   nh.param<double>("max_velocity_r", max_velocity_r_, 4.5);
00118   nh.param<double>("max_acceleration_x", max_acceleration_x_, 0.75);
00119   nh.param<double>("max_acceleration_r", max_acceleration_r_, 3.0);
00120 
00121   // Subscribe to base commands
00122   cmd_sub_ = nh.subscribe<geometry_msgs::Twist>("command", 1,
00123                 boost::bind(&DiffDriveBaseController::command, this, _1));
00124 
00125   // Publish odometry & tf
00126   ros::NodeHandle n;
00127   odom_pub_ = n.advertise<nav_msgs::Odometry>("odom", 10);
00128   if (publish_tf_)
00129     broadcaster_.reset(new tf::TransformBroadcaster());
00130 
00131   // Publish timer
00132   double publish_frequency;
00133   nh.param<double>("publish_frequency", publish_frequency, 100.0);
00134   odom_timer_ = n.createTimer(ros::Duration(1/publish_frequency),
00135                               &DiffDriveBaseController::publishCallback,
00136                               this);
00137 
00138   // Should we use the laser for safety limiting base velocity?
00139   nh.param<double>("laser_safety_dist", safety_scaling_distance_, 0.0);
00140   nh.param<double>("robot_safety_width", robot_width_, 0.7);
00141   if (safety_scaling_distance_ > 0.0)
00142   {
00143     scan_sub_ = n.subscribe<sensor_msgs::LaserScan>("base_scan", 1,
00144                   boost::bind(&DiffDriveBaseController::scanCallback, this, _1));
00145   }
00146 
00147   initialized_ = true;
00148 
00149   // Should we autostart?
00150   bool autostart;
00151   nh.param("autostart", autostart, false);
00152   if (autostart)
00153     manager->requestStart(getName());
00154 
00155   return 0;
00156 }
00157 
00158 void DiffDriveBaseController::command(const geometry_msgs::TwistConstPtr& msg)
00159 {
00160   if (!initialized_)
00161   {
00162     ROS_ERROR_NAMED("BaseController", "Unable to accept command, not initialized.");
00163     return;
00164   }
00165 
00166   if (std::isfinite(msg->linear.x) && std::isfinite(msg->angular.z))
00167   {
00168     boost::mutex::scoped_lock lock(command_mutex_);
00169     last_command_ = ros::Time::now();
00170     desired_x_ = msg->linear.x;
00171     desired_r_ = msg->angular.z;
00172   }
00173   else
00174   {
00175     ROS_ERROR_NAMED("BaseController", "Commanded velocities not finite!");
00176     return;
00177   }
00178 
00179   manager_->requestStart(getName());
00180 }
00181 
00182 bool DiffDriveBaseController::start()
00183 {
00184   if (!initialized_)
00185   {
00186     ROS_ERROR_NAMED("BaseController", "Unable to start, not initialized.");
00187     return false;
00188   }
00189 
00190   return true;
00191 }
00192 
00193 bool DiffDriveBaseController::stop(bool force)
00194 {
00195   // If we have timed out, certainly stop
00196   if (last_update_ - last_command_ >= timeout_)
00197     return true;
00198 
00199   // If not moving, stop
00200   if (last_sent_x_ == 0.0 && last_sent_r_ == 0.0)
00201     return true;
00202 
00203   // If forced, stop
00204   if (force)
00205     return true;
00206 
00207   return false;
00208 }
00209 
00210 bool DiffDriveBaseController::reset()
00211 {
00212   // Reset command
00213   last_command_ = ros::Time(0);
00214   return true;
00215 }
00216 
00217 void DiffDriveBaseController::update(const ros::Time& now, const ros::Duration& dt)
00218 {
00219   if (!initialized_)
00220     return;  // should never really hit this
00221 
00222   // See if we have timed out and need to stop
00223   if (now - last_command_ >= timeout_)
00224   {
00225     ROS_DEBUG_THROTTLE_NAMED(5, "BaseController", "Command timed out.");
00226     boost::mutex::scoped_lock lock(command_mutex_);
00227     desired_x_ = desired_r_ = 0.0;
00228   }
00229 
00230   // Make sure laser has not timed out
00231   if ((safety_scaling_distance_ > 0.0) &&
00232       (ros::Time::now() - last_laser_scan_ > ros::Duration(0.5)))
00233   {
00234     safety_scaling_ = 0.1;
00235   }
00236 
00237   // Do velocity acceleration/limiting
00238   double x, r;
00239   {
00240     boost::mutex::scoped_lock lock(command_mutex_);
00241     // Limit linear velocity based on obstacles
00242     x = std::max(-max_velocity_x_ * safety_scaling_, std::min(desired_x_, max_velocity_x_ * safety_scaling_));
00243     // Compute how much we actually scaled the linear velocity
00244     double actual_scaling = 1.0;
00245     if (desired_x_ != 0.0)
00246       actual_scaling = x/desired_x_;
00247     // Limit angular velocity
00248     // Scale same amount as linear velocity so that robot still follows the same "curvature"
00249     r = std::max(-max_velocity_r_, std::min(actual_scaling * desired_r_, max_velocity_r_));
00250   }
00251   if (x > last_sent_x_)
00252   {
00253     last_sent_x_ += max_acceleration_x_ * (now - last_update_).toSec();
00254     if (last_sent_x_ > x)
00255       last_sent_x_ = x;
00256   }
00257   else
00258   {
00259     last_sent_x_ -= max_acceleration_x_ * (now - last_update_).toSec();
00260     if (last_sent_x_ < x)
00261       last_sent_x_ = x;
00262   }
00263   if (r > last_sent_r_)
00264   {
00265     last_sent_r_ += max_acceleration_r_ * (now - last_update_).toSec();
00266     if (last_sent_r_ > r)
00267       last_sent_r_ = r;
00268   }
00269   else
00270   {
00271     last_sent_r_ -= max_acceleration_r_ * (now - last_update_).toSec();
00272     if (last_sent_r_ < r)
00273       last_sent_r_ = r;
00274   }
00275 
00276   double dx = 0.0;
00277   double dr = 0.0;
00278 
00279   double left_pos = left_->getPosition();
00280   double right_pos = right_->getPosition();
00281   double left_dx = angles::shortest_angular_distance(left_last_position_, left_pos)/radians_per_meter_;
00282   double right_dx = angles::shortest_angular_distance(right_last_position_, right_pos)/radians_per_meter_;
00283   double left_vel = static_cast<double>(left_->getVelocity())/radians_per_meter_;
00284   double right_vel = static_cast<double>(right_->getVelocity())/radians_per_meter_;
00285 
00286   // Threshold the odometry to avoid noise (especially in simulation)
00287   if (fabs(left_dx) > wheel_rotating_threshold_ ||
00288       fabs(right_dx) > wheel_rotating_threshold_ ||
00289       last_sent_x_ != 0.0 ||
00290       last_sent_r_ != 0.0)
00291   {
00292     // Above threshold, update last position
00293     left_last_position_ = left_pos;
00294     right_last_position_ = right_pos;
00295   }
00296   else
00297   {
00298     // Below threshold, zero delta/velocities
00299     left_dx = right_dx = 0.0;
00300     left_vel = right_vel = 0.0;
00301   }
00302 
00303   // Calculate forward and angular differences
00304   double d = (left_dx+right_dx)/2.0;
00305   double th = (right_dx-left_dx)/track_width_;
00306 
00307   // Calculate forward and angular velocities
00308   dx = (left_vel + right_vel)/2.0;
00309   dr = (right_vel - left_vel)/track_width_;
00310 
00311   // Actually set command
00312   if (fabs(dx) > moving_threshold_ ||
00313       fabs(dr) > rotating_threshold_ ||
00314       last_sent_x_ != 0.0 ||
00315       last_sent_r_ != 0.0)
00316   {
00317     setCommand(last_sent_x_ - (last_sent_r_/2.0 * track_width_),
00318                last_sent_x_ + (last_sent_r_/2.0 * track_width_));
00319   }
00320 
00321   // Lock mutex before updating
00322   boost::mutex::scoped_lock lock(odom_mutex_);
00323 
00324   // Update stored odometry pose...
00325   theta_ += th/2.0;
00326   odom_.pose.pose.position.x += d*cos(theta_);
00327   odom_.pose.pose.position.y += d*sin(theta_);
00328   theta_ += th/2.0;
00329   odom_.pose.pose.orientation.z = sin(theta_/2.0);
00330   odom_.pose.pose.orientation.w = cos(theta_/2.0);
00331   // ...and twist
00332   odom_.twist.twist.linear.x = dx;
00333   odom_.twist.twist.angular.z = dr;
00334 
00335   last_update_ = now;
00336 }
00337 
00338 std::vector<std::string> DiffDriveBaseController::getCommandedNames()
00339 {
00340   std::vector<std::string> names;
00341   if (left_)
00342     names.push_back(left_->getName());
00343   if (right_)
00344     names.push_back(right_->getName());
00345   return names;
00346 }
00347 
00348 std::vector<std::string> DiffDriveBaseController::getClaimedNames()
00349 {
00350   // Claimed == Commanded
00351   return getCommandedNames();
00352 }
00353 
00354 void DiffDriveBaseController::publishCallback(const ros::TimerEvent& event)
00355 {
00356   // Copy message under lock of mutex
00357   nav_msgs::Odometry msg;
00358   {
00359     boost::mutex::scoped_lock lock(odom_mutex_);
00360     msg = odom_;
00361   }
00362 
00363   // Publish or perish
00364   msg.header.stamp = ros::Time::now();
00365   odom_pub_.publish(msg);
00366 
00367   if (publish_tf_)
00368   {
00369     tf::Transform transform;
00370     transform.setOrigin(tf::Vector3(msg.pose.pose.position.x, msg.pose.pose.position.y, 0.0));
00371     transform.setRotation(tf::Quaternion(msg.pose.pose.orientation.x,
00372                                          msg.pose.pose.orientation.y,
00373                                          msg.pose.pose.orientation.z,
00374                                          msg.pose.pose.orientation.w) );
00375     /*
00376      * REP105 (http://ros.org/reps/rep-0105.html)
00377      *   says: map -> odom -> base_link
00378      */
00379     broadcaster_->sendTransform(tf::StampedTransform(transform, msg.header.stamp, msg.header.frame_id, msg.child_frame_id));
00380   }
00381 }
00382 
00383 void DiffDriveBaseController::scanCallback(
00384   const sensor_msgs::LaserScanConstPtr& scan)
00385 {
00386   double angle = scan->angle_min;
00387   double min_dist = safety_scaling_distance_;
00388 
00389   for (size_t i = 0; i < scan->ranges.size(); ++i, angle += scan->angle_increment)
00390   {
00391     if (std::isfinite(scan->ranges[i]) &&
00392         scan->ranges[i] > scan->range_min &&
00393         scan->ranges[i] < min_dist)
00394     {
00395       // Only test points in the forward 180 degrees
00396       if (angle < -1.5 || angle > 1.5)
00397         continue;
00398 
00399       // Check if point is inside the width of the robot
00400       double py = sin(angle) * scan->ranges[i];
00401       if (fabs(py) < (robot_width_/2.0))
00402         min_dist = scan->ranges[i];
00403     }
00404   }
00405 
00406   boost::mutex::scoped_lock lock(command_mutex_);
00407   safety_scaling_ = std::max(0.1, min_dist / safety_scaling_distance_);
00408   last_laser_scan_ = ros::Time::now();
00409 }
00410 
00411 void DiffDriveBaseController::setCommand(float left, float right)
00412 {
00413   // Convert meters/sec into radians/sec
00414   left_->setVelocity(left * radians_per_meter_, 0.0);
00415   right_->setVelocity(right * radians_per_meter_, 0.0);
00416 }
00417 
00418 }  // namespace robot_controllers


robot_controllers
Author(s): Michael Ferguson
autogenerated on Wed Jun 14 2017 04:09:10