steered_wheel_base_controller.cpp
Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 
00058 
00059 
00060 
00061 
00062 
00063 
00064 
00065 
00066 
00067 
00068 
00069 
00070 
00071 
00072 
00073 
00074 
00075 
00076 
00077 
00078 
00079 
00080 
00081 
00082 
00083 
00084 
00085 
00086 
00087 
00088 
00089 
00090 
00091 
00092 
00093 
00094 
00095 
00096 
00097 
00098 
00099 
00100 
00101 
00102 
00103 
00104 
00105 
00106 
00107 
00108 
00109 
00110 
00111 
00112 
00113 
00114 
00115 
00116 
00117 
00118 
00119 
00120 
00121 
00122 
00123 
00124 
00125 
00126 
00127 
00128 
00129 
00130 
00131 
00132 
00133 
00134 
00135 
00136 
00137 
00138 
00139 
00140 
00141 
00142 
00143 
00144 
00145 
00146 
00147 
00148 
00149 
00150 
00151 //
00152 // Copyright (c) 2013-2015 Wunderkammer Laboratory
00153 //
00154 // Licensed under the Apache License, Version 2.0 (the "License");
00155 // you may not use this file except in compliance with the License.
00156 // You may obtain a copy of the License at
00157 //
00158 //   http://www.apache.org/licenses/LICENSE-2.0
00159 //
00160 // Unless required by applicable law or agreed to in writing, software
00161 // distributed under the License is distributed on an "AS IS" BASIS,
00162 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00163 // See the License for the specific language governing permissions and
00164 // limitations under the License.
00165 
00166 #include <algorithm>
00167 #include <exception>
00168 #include <set>
00169 #include <stdexcept>
00170 #include <string>
00171 #include <vector>
00172 
00173 #include <angles/angles.h>
00174 
00175 #include <boost/foreach.hpp>
00176 #include <boost/math/special_functions/sign.hpp>
00177 #include <boost/shared_ptr.hpp>
00178 
00179 #include <control_toolbox/pid.h>
00180 #include <controller_interface/controller_base.h>
00181 
00182 #include <Eigen/Core>
00183 #include <Eigen/Geometry>
00184 #include <Eigen/SVD>
00185 
00186 #include <geometry_msgs/Twist.h>
00187 #include <hardware_interface/joint_command_interface.h>
00188 
00189 #include <kdl/chainfksolverpos_recursive.hpp>
00190 #include <kdl_parser/kdl_parser.hpp>
00191 
00192 #include <nav_msgs/Odometry.h>
00193 #include <pluginlib/class_list_macros.h>
00194 #include <realtime_tools/realtime_publisher.h>
00195 
00196 #include <tf/tfMessage.h>
00197 #include <tf/transform_datatypes.h>
00198 
00199 #include <urdf/model.h>
00200 
00201 using std::runtime_error;
00202 using std::set;
00203 using std::string;
00204 
00205 using boost::math::sign;
00206 using boost::shared_ptr;
00207 
00208 using Eigen::Affine2d;
00209 using Eigen::Matrix2d;
00210 using Eigen::Vector2d;
00211 
00212 using geometry_msgs::TwistConstPtr;
00213 
00214 using hardware_interface::JointHandle;
00215 using hardware_interface::RobotHW;
00216 
00217 using hardware_interface::EffortJointInterface;
00218 using hardware_interface::PositionJointInterface;
00219 using hardware_interface::VelocityJointInterface;
00220 
00221 using realtime_tools::RealtimeBuffer;
00222 using realtime_tools::RealtimePublisher;
00223 
00224 using ros::Duration;
00225 using ros::NodeHandle;
00226 using ros::Time;
00227 
00228 using XmlRpc::XmlRpcValue;
00229 
00230 namespace
00231 {
00232 
00233 void addClaimedResources(hardware_interface::HardwareInterface *const hw_iface,
00234                          set<string>& claimed_resources)
00235 {
00236   if (hw_iface == NULL)
00237     return;
00238   const set<string> claims = hw_iface->getClaims();
00239   claimed_resources.insert(claims.begin(), claims.end());
00240   hw_iface->clearClaims();
00241 }
00242 
00243 double clamp(const double val, const double min_val, const double max_val)
00244 {
00245   return std::min(std::max(val, min_val), max_val);
00246 }
00247 
00248 double hermite(const double t)
00249 {
00250   if (t <= 0)
00251     return 0;
00252   if (t >= 1)
00253     return 1;
00254   return (-2 * t + 3) * t * t;  // -2t^3 + 3t^2
00255 }
00256 
00257 class Joint
00258 {
00259 public:
00260   virtual ~Joint() {}
00261   virtual void init() = 0;
00262   virtual void stop() = 0;
00263 
00264   bool isValidPos(const double pos) const;
00265   double getPos() const {return handle_.getPosition();}
00266   virtual void setPos(const double pos, const Duration& period) {}
00267 
00268   virtual void setVel(const double vel, const Duration& period) = 0;
00269 
00270 protected:
00271   Joint(const JointHandle& handle,
00272         const shared_ptr<const urdf::Joint> urdf_joint);
00273 
00274   JointHandle handle_;
00275   const bool is_revolute_;
00276   double lower_limit_, upper_limit_;  // Unit: radian
00277 };
00278 
00279 // Joint with a position interface
00280 class PosJoint : public Joint
00281 {
00282 public:
00283   PosJoint(const JointHandle& handle,
00284            const shared_ptr<const urdf::Joint> urdf_joint) :
00285     Joint(handle, urdf_joint) {}
00286 
00287   virtual void init();
00288   virtual void stop();
00289   virtual void setPos(const double pos, const Duration& period);
00290   virtual void setVel(const double vel, const Duration& period);
00291 
00292 private:
00293   double pos_;
00294 };
00295 
00296 // Joint with a velocity interface. VelJoint is used for axles only.
00297 class VelJoint : public Joint
00298 {
00299 public:
00300   VelJoint(const JointHandle& handle,
00301            const shared_ptr<const urdf::Joint> urdf_joint) :
00302     Joint(handle, urdf_joint) {}
00303 
00304   virtual void init() {stop();}
00305   virtual void stop();
00306   virtual void setVel(const double vel, const Duration& period);
00307 };
00308 
00309 // An object of class PIDJoint is a joint controlled by a PID controller.
00310 class PIDJoint : public Joint
00311 {
00312 public:
00313   PIDJoint(const JointHandle& handle,
00314            const shared_ptr<const urdf::Joint> urdf_joint,
00315            const NodeHandle& ctrlr_nh);
00316 
00317   virtual void init();
00318   virtual void stop();
00319   virtual void setPos(const double pos, const Duration& period);
00320   virtual void setVel(const double vel, const Duration& period);
00321 
00322 private:
00323   control_toolbox::Pid pid_ctrlr_;
00324 };
00325 
00326 // An object of class Wheel is a steered wheel.
00327 class Wheel
00328 {
00329 public:
00330   Wheel(const KDL::Tree& tree,
00331         const string& base_link, const string& steer_link,
00332         const shared_ptr<Joint> steer_joint,
00333         const shared_ptr<Joint> axle_joint,
00334         const double circ);
00335 
00336   const Vector2d& pos() const {return pos_;}
00337   Vector2d getDeltaPos();
00338 
00339   void initJoints();
00340   void stop() const;
00341   double ctrlSteering(const Duration& period, const double hermite_scale,
00342                       const double hermite_offset);
00343   double ctrlSteering(const double theta_desired, const Duration& period,
00344                       const double hermite_scale, const double hermite_offset);
00345   void ctrlAxle(const double lin_speed, const Duration& period) const;
00346 
00347 private:
00348   void initPos(const KDL::Tree& tree, const string& base_link);
00349 
00350   string steer_link_; // Steering link
00351   Vector2d pos_;      // Wheel's position in the base link's frame
00352 
00353   shared_ptr<Joint> steer_joint_;   // Steering joint
00354   shared_ptr<Joint> axle_joint_;
00355   double theta_steer_;              // Steering joint position
00356   double last_theta_steer_desired_; // Last desired steering joint position
00357   double last_theta_axle_;          // Last axle joint position
00358 
00359   double radius_;         // Unit: meter.
00360   double inv_radius_;     // Inverse of radius_
00361   double axle_vel_gain_;  // Axle velocity gain
00362 };
00363 
00364 Joint::Joint(const JointHandle& handle,
00365              const shared_ptr<const urdf::Joint> urdf_joint) :
00366   handle_(handle), is_revolute_(urdf_joint->type == urdf::Joint::REVOLUTE)
00367 {
00368   if (!is_revolute_)
00369     return;
00370 
00371   if (urdf_joint->limits == NULL)
00372   {
00373     ROS_WARN_STREAM("SteeredWheelBaseController: No limits were specified "
00374                     "for joint \"" << urdf_joint->name << "\". The lower "
00375                     "limit will be set to -pi and the upper limit will be "
00376                     "set to pi.");
00377     lower_limit_ = -M_PI;
00378     upper_limit_ = M_PI;
00379   }
00380   else if (urdf_joint->limits->lower >= urdf_joint->limits->upper)
00381   {
00382     throw runtime_error("The lower limit of joint \"" + urdf_joint->name +
00383                         "\" must be less than the upper limit.");
00384   }
00385   else
00386   {
00387     lower_limit_ = urdf_joint->limits->lower;
00388     upper_limit_ = urdf_joint->limits->upper;
00389   }
00390 }
00391 
00392 bool Joint::isValidPos(const double pos) const
00393 {
00394   if (!is_revolute_)
00395     return true;
00396   return pos >= lower_limit_ && pos <= upper_limit_;
00397 }
00398 
00399 // Initialize this joint.
00400 void PosJoint::init()
00401 {
00402   pos_ = getPos();
00403   stop();
00404 }
00405 
00406 // Stop this joint's motion.
00407 void PosJoint::stop()
00408 {
00409   handle_.setCommand(getPos());
00410 }
00411 
00412 // Specify this joint's position.
00413 void PosJoint::setPos(const double pos, const Duration& /* period */)
00414 {
00415   pos_ = pos;
00416   handle_.setCommand(pos_);
00417 }
00418 
00419 // Specify this joint's velocity.
00420 void PosJoint::setVel(const double vel, const Duration& period)
00421 {
00422   pos_ += vel * period.toSec();
00423   handle_.setCommand(pos_);
00424 }
00425 
00426 // Stop this joint's motion.
00427 void VelJoint::stop()
00428 {
00429   handle_.setCommand(0);
00430 }
00431 
00432 // Specify this joint's velocity.
00433 void VelJoint::setVel(const double vel, const Duration& /* period */)
00434 {
00435   handle_.setCommand(vel);
00436 }
00437 
00438 PIDJoint::PIDJoint(const JointHandle& handle,
00439                    const shared_ptr<const urdf::Joint> urdf_joint,
00440                    const NodeHandle& ctrlr_nh) : Joint(handle, urdf_joint)
00441 {
00442   const NodeHandle nh(ctrlr_nh, "pid_gains/" + handle.getName());
00443   if (!pid_ctrlr_.init(nh))
00444   {
00445     throw runtime_error("No PID gain values for \"" + handle.getName() +
00446                         "\" were found.");
00447   }
00448 }
00449 
00450 // Initialize this joint.
00451 void PIDJoint::init()
00452 {
00453   pid_ctrlr_.reset();
00454   stop();
00455 }
00456 
00457 // Stop this joint's motion.
00458 void PIDJoint::stop()
00459 {
00460   // The command passed to setCommand() might be an effort value or a
00461   // velocity. In either case, the correct command to pass here is zero.
00462   handle_.setCommand(0);
00463 }
00464 
00465 // Specify this joint's position.
00466 void PIDJoint::setPos(const double pos, const Duration& period)
00467 {
00468   const double curr_pos = getPos();
00469 
00470   double error;
00471   if (is_revolute_)
00472   {
00473     angles::shortest_angular_distance_with_limits(curr_pos, pos,
00474                                                   lower_limit_, upper_limit_,
00475                                                   error);
00476   }
00477   else
00478   {
00479     error = angles::shortest_angular_distance(curr_pos, pos);
00480   }
00481 
00482   handle_.setCommand(pid_ctrlr_.computeCommand(error, period));
00483 }
00484 
00485 // Specify this joint's velocity.
00486 void PIDJoint::setVel(const double vel, const Duration& period)
00487 {
00488   const double error = vel - handle_.getVelocity();
00489   handle_.setCommand(pid_ctrlr_.computeCommand(error, period));
00490 }
00491 
00492 Wheel::Wheel(const KDL::Tree& tree,
00493              const string& base_link, const string& steer_link,
00494              const shared_ptr<Joint> steer_joint,
00495              const shared_ptr<Joint> axle_joint,
00496              const double circ)
00497 {
00498   steer_link_ = steer_link;
00499   initPos(tree, base_link);
00500 
00501   steer_joint_ = steer_joint;
00502   axle_joint_ = axle_joint;
00503   theta_steer_ = steer_joint_->getPos();
00504   last_theta_steer_desired_ = theta_steer_;
00505   last_theta_axle_ = axle_joint_->getPos();
00506 
00507   radius_ = circ / (2 * M_PI);
00508   inv_radius_ = 1 / radius_;
00509   axle_vel_gain_ = 0;
00510 }
00511 
00512 // Return the difference between this wheel's current position and its
00513 // position when getDeltaPos() was last called. The returned vector is defined
00514 // in the base link's frame.
00515 Vector2d Wheel::getDeltaPos()
00516 {
00517   const double theta_axle = axle_joint_->getPos();
00518   const double delta_theta_axle = theta_axle - last_theta_axle_;
00519   last_theta_axle_ = theta_axle;
00520   const double vec_mag = delta_theta_axle * radius_;
00521   return Vector2d(cos(theta_steer_), sin(theta_steer_)) * vec_mag;
00522 }
00523 
00524 // Initialize this wheel's steering and axle joints.
00525 void Wheel::initJoints()
00526 {
00527   steer_joint_->init();
00528   axle_joint_->init();
00529 }
00530 
00531 // Stop this wheel's motion.
00532 void Wheel::stop() const
00533 {
00534   steer_joint_->stop();
00535   axle_joint_->stop();
00536 }
00537 
00538 // Maintain the position of this wheel's steering joint. Return a linear speed
00539 // gain value based on the difference between the desired steering angle and
00540 // the actual steering angle.
00541 double Wheel::ctrlSteering(const Duration& period, const double hermite_scale,
00542                            const double hermite_offset)
00543 {
00544   return ctrlSteering(last_theta_steer_desired_, period, hermite_scale,
00545                       hermite_offset);
00546 }
00547 
00548 // Control this wheel's steering joint. theta_desired range: [-pi, pi].
00549 // Return a linear speed gain value based on the difference between the
00550 // desired steering angle and the actual steering angle.
00551 double Wheel::ctrlSteering(const double theta_desired, const Duration& period,
00552                            const double hermite_scale,
00553                            const double hermite_offset)
00554 {
00555   last_theta_steer_desired_ = theta_desired;
00556 
00557   // Find the minimum rotation that will align the wheel with theta_desired.
00558   theta_steer_ = steer_joint_->getPos();
00559   const double theta_diff = fabs(theta_desired - theta_steer_);
00560   double theta;
00561   if (theta_diff > M_PI_2)
00562   {
00563     theta = theta_desired - copysign(M_PI, theta_desired);
00564     axle_vel_gain_ = -1;
00565   }
00566   else
00567   {
00568     theta = theta_desired;
00569     axle_vel_gain_ = 1;
00570   }
00571 
00572   // Keep theta within its valid range.
00573   if (!steer_joint_->isValidPos(theta))
00574   {
00575     theta -= copysign(M_PI, theta);
00576     axle_vel_gain_ = -axle_vel_gain_;
00577   }
00578 
00579   steer_joint_->setPos(theta, period);
00580   return 1 - hermite(hermite_scale * (fabs(theta - theta_steer_) -
00581                                       hermite_offset));
00582 }
00583 
00584 // Control this wheel's axle joint.
00585 void Wheel::ctrlAxle(const double lin_speed, const Duration& period) const
00586 {
00587   const double ang_vel = axle_vel_gain_ * inv_radius_ * lin_speed;
00588   axle_joint_->setVel(ang_vel, period);
00589 }
00590 
00591 // Initialize pos_.
00592 void Wheel::initPos(const KDL::Tree& tree, const string& base_link)
00593 {
00594   KDL::Chain chain;
00595   if (!tree.getChain(base_link, steer_link_, chain))
00596   {
00597     throw runtime_error("No kinematic chain was found from \"" + base_link +
00598                         "\" to \"" + steer_link_ + "\".");
00599   }
00600 
00601   const unsigned int num_joints = chain.getNrOfJoints();
00602   KDL::JntArray joint_positions(num_joints);
00603   for (unsigned int i = 0; i < num_joints; i++)
00604     joint_positions(i) = 0;
00605 
00606   KDL::ChainFkSolverPos_recursive solver(chain);
00607   KDL::Frame frame;
00608   if (solver.JntToCart(joint_positions, frame) < 0)
00609   {
00610     throw runtime_error("The position of steering link \"" + steer_link_ +
00611                         "\" in base link frame \"" + base_link +
00612                         "\" was not found.");
00613   }
00614   pos_ = Vector2d(frame.p.x(), frame.p.y());
00615 }
00616 
00617 bool hasNonzeroPosLimits(const shared_ptr<const urdf::Joint> urdf_joint)
00618 {
00619   return urdf_joint->limits != NULL &&
00620     (urdf_joint->limits->lower != 0 || urdf_joint->limits->upper != 0);
00621 }
00622 
00623 // Return the URDF joint specified by joint_name.
00624 const shared_ptr<const urdf::Joint> getURDFJoint(const string& joint_name,
00625                                                  const bool is_steer_joint,
00626                                                  const urdf::Model& urdf_model)
00627 {
00628   const shared_ptr<const urdf::Joint> urdf_joint =
00629     urdf_model.getJoint(joint_name);
00630   if (urdf_joint == NULL)
00631   {
00632     throw runtime_error("\"" + joint_name +
00633                         "\" was not found in the URDF data.");
00634   }
00635 
00636   if (is_steer_joint)
00637   {
00638     if (urdf_joint->type == urdf::Joint::CONTINUOUS)
00639     {
00640       if (hasNonzeroPosLimits(urdf_joint))
00641       {
00642         ROS_WARN_STREAM("SteeredWheelBaseController: A nonzero lower or "
00643                         "upper limit was specified for continuous "
00644                         "steering joint \"" << joint_name << "\". "
00645                         "Position limits for continuous steering joints "
00646                         "are ignored.");
00647       }
00648     }
00649     else if (urdf_joint->type == urdf::Joint::REVOLUTE)
00650     {
00651       if (urdf_joint->limits == NULL)
00652       {
00653         throw runtime_error("Revolute steering joint \"" + joint_name +
00654                             "\" must have lower and upper limits.");
00655       }
00656       if (urdf_joint->limits->lower >= urdf_joint->limits->upper)
00657       {
00658         throw runtime_error("The lower limit of revolute steering "
00659                             "joint \"" + joint_name + "\" must be less "
00660                             "than the upper limit.");
00661       }
00662     }
00663     else
00664     {
00665       throw runtime_error("Steering joint \"" + joint_name + "\" must be "
00666                           "continuous or revolute.");
00667     }
00668   }
00669   else  // This is an axle joint.
00670   {
00671     if (urdf_joint->type != urdf::Joint::CONTINUOUS)
00672     {
00673       throw runtime_error("Axle joint \"" + joint_name + "\" must be "
00674                           "continuous.");
00675     }
00676     if (hasNonzeroPosLimits(urdf_joint))
00677     {
00678       ROS_WARN_STREAM("SteeredWheelBaseController: A nonzero lower or "
00679                       "upper limit was specified for axle joint \"" <<
00680                       joint_name << "\". Position limits for axle joints "
00681                       "are ignored.");
00682     }
00683   }
00684 
00685   return urdf_joint;
00686 }
00687 
00688 // Create an object of class Joint that corresponds to the URDF joint specified
00689 // by joint_name.
00690 shared_ptr<Joint> getJoint(const string& joint_name, const bool is_steer_joint,
00691                            const NodeHandle& ctrlr_nh,
00692                            const urdf::Model& urdf_model,
00693                            EffortJointInterface *const eff_joint_iface,
00694                            PositionJointInterface *const pos_joint_iface,
00695                            VelocityJointInterface *const vel_joint_iface)
00696 {
00697   // Effort interface
00698   if (eff_joint_iface != NULL)
00699   {
00700     JointHandle handle;
00701     bool handle_found;
00702     try
00703     {
00704       handle = eff_joint_iface->getHandle(joint_name);
00705       handle_found = true;
00706     }
00707     catch (...)
00708     {
00709       handle_found = false;
00710     }
00711 
00712     if (handle_found)
00713     {
00714       const shared_ptr<const urdf::Joint> urdf_joint =
00715         getURDFJoint(joint_name, is_steer_joint, urdf_model);
00716       shared_ptr<Joint> joint(new PIDJoint(handle, urdf_joint, ctrlr_nh));
00717       return joint;
00718     }
00719   }
00720 
00721   // Position interface
00722   if (pos_joint_iface != NULL)
00723   {
00724     JointHandle handle;
00725     bool handle_found;
00726     try
00727     {
00728       handle = pos_joint_iface->getHandle(joint_name);
00729       handle_found = true;
00730     }
00731     catch (...)
00732     {
00733       handle_found = false;
00734     }
00735 
00736     if (handle_found)
00737     {
00738       const shared_ptr<const urdf::Joint> urdf_joint =
00739         getURDFJoint(joint_name, is_steer_joint, urdf_model);
00740       shared_ptr<Joint> joint(new PosJoint(handle, urdf_joint));
00741       return joint;
00742     }
00743   }
00744 
00745   // Velocity interface
00746   if (vel_joint_iface != NULL)
00747   {
00748     JointHandle handle;
00749     bool handle_found;
00750     try
00751     {
00752       handle = vel_joint_iface->getHandle(joint_name);
00753       handle_found = true;
00754     }
00755     catch (...)
00756     {
00757       handle_found = false;
00758     }
00759 
00760     if (handle_found)
00761     {
00762       const shared_ptr<const urdf::Joint> urdf_joint =
00763         getURDFJoint(joint_name, is_steer_joint, urdf_model);
00764 
00765       if (is_steer_joint)
00766       {
00767         shared_ptr<Joint> joint(new PIDJoint(handle, urdf_joint, ctrlr_nh));
00768         return joint;
00769       }
00770       shared_ptr<Joint> joint(new VelJoint(handle, urdf_joint));
00771       return joint;
00772     }
00773   }
00774 
00775   throw runtime_error("No handle for \"" + joint_name + "\" was found.");
00776 }
00777 
00778 } // namespace
00779 
00780 namespace steered_wheel_base_controller
00781 {
00782 
00783 // Steered-wheel base controller
00784 class SteeredWheelBaseController : public controller_interface::ControllerBase
00785 {
00786 public:
00787   SteeredWheelBaseController();
00788 
00789   // These are not real-time safe.
00790   virtual bool initRequest(RobotHW *const robot_hw,
00791                            NodeHandle& root_nh, NodeHandle& ctrlr_nh,
00792                            set<string>& claimed_resources);
00793   virtual string getHardwareInterfaceType() const;
00794 
00795   // These are real-time safe.
00796   virtual void starting(const Time& time);
00797   virtual void update(const Time& time, const Duration& period);
00798   virtual void stopping(const Time& time);
00799 
00800 private:
00801   struct VelCmd     // Velocity command
00802   {
00803     double x_vel;   // X velocity component. Unit: m/s.
00804     double y_vel;   // Y velocity component. Unit: m/s.
00805     double yaw_vel; // Yaw velocity. Unit: rad/s.
00806 
00807     // last_vel_cmd_time is the time at which the most recent velocity command
00808     // was received.
00809     Time last_vel_cmd_time;
00810   };
00811 
00812   static const string DEF_ROBOT_DESC_NAME;
00813   static const string DEF_BASE_LINK;
00814   static const double DEF_CMD_VEL_TIMEOUT;
00815 
00816   static const double DEF_LIN_SPEED_LIMIT;
00817   static const double DEF_LIN_ACCEL_LIMIT;
00818   static const double DEF_LIN_DECEL_LIMIT;
00819 
00820   static const double DEF_YAW_SPEED_LIMIT;
00821   static const double DEF_YAW_ACCEL_LIMIT;
00822   static const double DEF_YAW_DECEL_LIMIT;
00823 
00824   static const double DEF_FULL_AXLE_SPEED_ANG;
00825   static const double DEF_ZERO_AXLE_SPEED_ANG;
00826 
00827   static const double DEF_WHEEL_DIA_SCALE;
00828 
00829   static const double DEF_ODOM_PUB_FREQ;
00830   static const bool DEF_PUB_ODOM_TO_BASE;
00831   static const string DEF_ODOM_FRAME;
00832   static const string DEF_BASE_FRAME;
00833   static const double DEF_INIT_X;
00834   static const double DEF_INIT_Y;
00835   static const double DEF_INIT_YAW;
00836   static const double DEF_SD;
00837 
00838   static const Vector2d X_DIR;
00839 
00840   void init(EffortJointInterface *const eff_joint_iface,
00841             PositionJointInterface *const pos_joint_iface,
00842             VelocityJointInterface *const vel_joint_iface,
00843             NodeHandle& ctrlr_nh);
00844   void velCmdCB(const TwistConstPtr& vel_cmd);
00845 
00846   Vector2d enforceLinLimits(const Vector2d& desired_vel,
00847                             const double delta_t, const double inv_delta_t);
00848   double enforceYawLimits(const double desired_vel,
00849                           const double delta_t, const double inv_delta_t);
00850   void ctrlWheels(const Vector2d& lin_vel, const double yaw_vel,
00851                   const Duration& period);
00852   void compOdometry(const Time& time, const double inv_delta_t);
00853 
00854   std::vector<Wheel> wheels_;
00855 
00856   // Linear motion limits
00857   bool has_lin_speed_limit_;
00858   double lin_speed_limit_;
00859   bool has_lin_accel_limit_;
00860   double lin_accel_limit_;
00861   bool has_lin_decel_limit_;
00862   double lin_decel_limit_;
00863 
00864   // Yaw limits
00865   bool has_yaw_speed_limit_;
00866   double yaw_speed_limit_;
00867   bool has_yaw_accel_limit_;
00868   double yaw_accel_limit_;
00869   bool has_yaw_decel_limit_;
00870   double yaw_decel_limit_;
00871 
00872   double hermite_scale_, hermite_offset_;
00873 
00874   Vector2d last_lin_vel_; // Last linear velocity. Unit: m/s.
00875   double last_yaw_vel_;   // Last yaw velocity. Unit: rad/s.
00876 
00877   // Velocity command member variables
00878   VelCmd vel_cmd_;
00879   RealtimeBuffer<VelCmd> vel_cmd_buf_;
00880   bool vel_cmd_timeout_enabled_;
00881   Duration vel_cmd_timeout_;
00882   ros::Subscriber vel_cmd_sub_;
00883 
00884   // Odometry
00885   bool comp_odom_;              // Compute odometry
00886   bool pub_odom_to_base_;       // Publish the odometry to base frame transform
00887   Duration odom_pub_period_;    // Odometry publishing period
00888   Affine2d init_odom_to_base_;  // Initial odometry to base frame transform
00889   Affine2d odom_to_base_;       // Odometry to base frame transform
00890   Affine2d odom_rigid_transf_;
00891   // wheel_pos_ contains the positions of the wheel's steering axles.
00892   // The positions are relative to the centroid of the steering axle positions
00893   // in the base link's frame. neg_wheel_centroid is the negative version of
00894   // that centroid.
00895   Eigen::Matrix2Xd wheel_pos_;
00896   Vector2d neg_wheel_centroid_;
00897   Eigen::MatrixX2d new_wheel_pos_;
00898   RealtimePublisher<nav_msgs::Odometry> odom_pub_;
00899   RealtimePublisher<tf::tfMessage> odom_tf_pub_;
00900   Time last_odom_pub_time_, last_odom_tf_pub_time_;
00901 };
00902 
00903 const string SteeredWheelBaseController::DEF_ROBOT_DESC_NAME =
00904   "robot_description";
00905 const string SteeredWheelBaseController::DEF_BASE_LINK = "base_link";
00906 const double SteeredWheelBaseController::DEF_CMD_VEL_TIMEOUT = 0.5;
00907 
00908 const double SteeredWheelBaseController::DEF_LIN_SPEED_LIMIT = 1;
00909 const double SteeredWheelBaseController::DEF_LIN_ACCEL_LIMIT = 1;
00910 const double SteeredWheelBaseController::DEF_LIN_DECEL_LIMIT = -1;
00911 
00912 const double SteeredWheelBaseController::DEF_YAW_SPEED_LIMIT = 1;
00913 const double SteeredWheelBaseController::DEF_YAW_ACCEL_LIMIT = 1;
00914 const double SteeredWheelBaseController::DEF_YAW_DECEL_LIMIT = -1;
00915 
00916 const double SteeredWheelBaseController::DEF_FULL_AXLE_SPEED_ANG = 0.7854;
00917 const double SteeredWheelBaseController::DEF_ZERO_AXLE_SPEED_ANG = 1.5708;
00918 
00919 const double SteeredWheelBaseController::DEF_WHEEL_DIA_SCALE = 1;
00920 
00921 const double SteeredWheelBaseController::DEF_ODOM_PUB_FREQ = 30;
00922 const bool SteeredWheelBaseController::DEF_PUB_ODOM_TO_BASE = true;
00923 const string SteeredWheelBaseController::DEF_ODOM_FRAME = "odom";
00924 const string SteeredWheelBaseController::DEF_BASE_FRAME = "base_link";
00925 const double SteeredWheelBaseController::DEF_INIT_X = 0;
00926 const double SteeredWheelBaseController::DEF_INIT_Y = 0;
00927 const double SteeredWheelBaseController::DEF_INIT_YAW = 0;
00928 const double SteeredWheelBaseController::DEF_SD = 0;
00929 
00930 // X direction
00931 const Vector2d SteeredWheelBaseController::X_DIR = Vector2d::UnitX();
00932 
00933 SteeredWheelBaseController::SteeredWheelBaseController()
00934 {
00935   state_ = CONSTRUCTED;
00936 }
00937 
00938 bool SteeredWheelBaseController::initRequest(RobotHW *const robot_hw,
00939                                              NodeHandle& /* root_nh */,
00940                                              NodeHandle& ctrlr_nh,
00941                                              set<string>& claimed_resources)
00942 {
00943   if (state_ != CONSTRUCTED)
00944   {
00945     ROS_ERROR("SteeredWheelBaseController: The steered-wheel base controller "
00946               "could not be created.");
00947     return false;
00948   }
00949 
00950   EffortJointInterface *const eff_joint_iface =
00951     robot_hw->get<EffortJointInterface>();
00952   PositionJointInterface *const pos_joint_iface =
00953     robot_hw->get<PositionJointInterface>();
00954   VelocityJointInterface *const vel_joint_iface =
00955     robot_hw->get<VelocityJointInterface>();
00956 
00957   if (eff_joint_iface != NULL)
00958     eff_joint_iface->clearClaims();
00959   if (pos_joint_iface != NULL)
00960     pos_joint_iface->clearClaims();
00961   if (vel_joint_iface != NULL)
00962     vel_joint_iface->clearClaims();
00963 
00964   try
00965   {
00966     init(eff_joint_iface, pos_joint_iface, vel_joint_iface, ctrlr_nh);
00967   }
00968   catch (const std::exception& ex)
00969   {
00970     ROS_ERROR_STREAM("SteeredWheelBaseController: " << ex.what());
00971     return false;
00972   }
00973 
00974   claimed_resources.clear();
00975   addClaimedResources(eff_joint_iface, claimed_resources);
00976   addClaimedResources(pos_joint_iface, claimed_resources);
00977   addClaimedResources(vel_joint_iface, claimed_resources);
00978 
00979   state_ = INITIALIZED;
00980   return true;
00981 }
00982 
00983 string SteeredWheelBaseController::getHardwareInterfaceType() const
00984 {
00985   return "";
00986 }
00987 
00988 void SteeredWheelBaseController::starting(const Time& time)
00989 {
00990   BOOST_FOREACH(Wheel& wheel, wheels_)
00991     wheel.initJoints();
00992 
00993   last_lin_vel_ = Vector2d(0, 0);
00994   last_yaw_vel_ = 0;
00995 
00996   if (comp_odom_)
00997   {
00998     last_odom_pub_time_ = time;
00999     last_odom_tf_pub_time_ = time;
01000   }
01001 
01002   vel_cmd_.x_vel = 0;
01003   vel_cmd_.y_vel = 0;
01004   vel_cmd_.yaw_vel = 0;
01005   vel_cmd_.last_vel_cmd_time = time;
01006   vel_cmd_buf_.initRT(vel_cmd_);
01007 }
01008 
01009 void SteeredWheelBaseController::update(const Time& time,
01010                                         const Duration& period)
01011 {
01012   const double delta_t = period.toSec();
01013   if (delta_t <= 0)
01014     return;
01015 
01016   vel_cmd_ = *(vel_cmd_buf_.readFromRT());
01017   Vector2d desired_lin_vel;
01018   double desired_yaw_vel;
01019   if (!vel_cmd_timeout_enabled_ ||
01020       time - vel_cmd_.last_vel_cmd_time <= vel_cmd_timeout_)
01021   {
01022     desired_lin_vel = Vector2d(vel_cmd_.x_vel, vel_cmd_.y_vel);
01023     desired_yaw_vel = vel_cmd_.yaw_vel;
01024   }
01025   else
01026   {
01027     // Too much time has elapsed since the last velocity command was received.
01028     // Stop the robot.
01029     desired_lin_vel.setZero();
01030     desired_yaw_vel = 0;
01031   }
01032   const double inv_delta_t = 1 / delta_t;
01033 
01034   const Vector2d lin_vel = enforceLinLimits(desired_lin_vel,
01035                                             delta_t, inv_delta_t);
01036   const double yaw_vel = enforceYawLimits(desired_yaw_vel,
01037                                           delta_t, inv_delta_t);
01038   ctrlWheels(lin_vel, yaw_vel, period);
01039   if (comp_odom_)
01040     compOdometry(time, inv_delta_t);
01041 }
01042 
01043 void SteeredWheelBaseController::stopping(const Time& time)
01044 {
01045   BOOST_FOREACH(Wheel& wheel, wheels_)
01046     wheel.stop();
01047 }
01048 
01049 // Initialize this steered-wheel base controller.
01050 void SteeredWheelBaseController::
01051 init(EffortJointInterface *const eff_joint_iface,
01052      PositionJointInterface *const pos_joint_iface,
01053      VelocityJointInterface *const vel_joint_iface,
01054      NodeHandle& ctrlr_nh)
01055 {
01056   string robot_desc_name;
01057   ctrlr_nh.param("robot_description_name", robot_desc_name,
01058                  DEF_ROBOT_DESC_NAME);
01059   urdf::Model urdf_model;
01060   if (!urdf_model.initParam(robot_desc_name))
01061     throw runtime_error("The URDF data was not found.");
01062   KDL::Tree model_tree;
01063   if (!kdl_parser::treeFromUrdfModel(urdf_model, model_tree))
01064     throw runtime_error("The kinematic tree could not be created.");
01065 
01066   string base_link;
01067   ctrlr_nh.param("base_link", base_link, DEF_BASE_LINK);
01068   double timeout;
01069   ctrlr_nh.param("cmd_vel_timeout", timeout, DEF_CMD_VEL_TIMEOUT);
01070   vel_cmd_timeout_enabled_ = timeout > 0;
01071   if (vel_cmd_timeout_enabled_)
01072     vel_cmd_timeout_.fromSec(timeout);
01073 
01074   ctrlr_nh.param("linear_speed_limit", lin_speed_limit_, DEF_LIN_SPEED_LIMIT);
01075   has_lin_speed_limit_ = lin_speed_limit_ >= 0;
01076   ctrlr_nh.param("linear_acceleration_limit", lin_accel_limit_,
01077                  DEF_LIN_ACCEL_LIMIT);
01078   has_lin_accel_limit_ = lin_accel_limit_ >= 0;
01079   // For safety, a valid deceleration limit must be greater than zero.
01080   ctrlr_nh.param("linear_deceleration_limit", lin_decel_limit_,
01081                  DEF_LIN_DECEL_LIMIT);
01082   has_lin_decel_limit_ = lin_decel_limit_ > 0;
01083 
01084   ctrlr_nh.param("yaw_speed_limit", yaw_speed_limit_, DEF_YAW_SPEED_LIMIT);
01085   has_yaw_speed_limit_ = yaw_speed_limit_ >= 0;
01086   ctrlr_nh.param("yaw_acceleration_limit", yaw_accel_limit_,
01087                  DEF_YAW_ACCEL_LIMIT);
01088   has_yaw_accel_limit_ = yaw_accel_limit_ >= 0;
01089   // For safety, a valid deceleration limit must be greater than zero.
01090   ctrlr_nh.param("yaw_deceleration_limit", yaw_decel_limit_,
01091                  DEF_YAW_DECEL_LIMIT);
01092   has_yaw_decel_limit_ = yaw_decel_limit_ > 0;
01093 
01094   ctrlr_nh.param("full_axle_speed_angle", hermite_offset_,
01095                  DEF_FULL_AXLE_SPEED_ANG);
01096   if (hermite_offset_ < 0 || hermite_offset_ > M_PI)
01097     throw runtime_error("full_axle_speed_angle must be in the range [0, pi].");
01098   double zero_axle_speed_ang;
01099   ctrlr_nh.param("zero_axle_speed_angle", zero_axle_speed_ang,
01100                  DEF_ZERO_AXLE_SPEED_ANG);
01101   if (zero_axle_speed_ang < 0 || zero_axle_speed_ang > M_PI)
01102     throw runtime_error("zero_axle_speed_angle must be in the range [0, pi].");
01103   if (hermite_offset_ >= zero_axle_speed_ang)
01104   {
01105     throw runtime_error("full_axle_speed_angle must be less than "
01106                         "zero_axle_speed_angle.");
01107   }
01108   hermite_scale_ = 1 / (zero_axle_speed_ang - hermite_offset_);
01109 
01110   // Wheels
01111 
01112   XmlRpcValue wheel_param_list;
01113   if (!ctrlr_nh.getParam("wheels", wheel_param_list))
01114     throw runtime_error("No wheels were specified.");
01115   if (wheel_param_list.getType() != XmlRpcValue::TypeArray)
01116     throw runtime_error("The specified list of wheels is invalid.");
01117   if (wheel_param_list.size() < 2)
01118     throw runtime_error("At least two wheels must be specified.");
01119 
01120   double wheel_dia_scale;
01121   ctrlr_nh.param("wheel_diameter_scale", wheel_dia_scale, DEF_WHEEL_DIA_SCALE);
01122   if (wheel_dia_scale <= 0)
01123   {
01124     throw runtime_error("The specified wheel diameter scale is less than or "
01125                         "equal to zero.");
01126   }
01127 
01128   for (int i = 0; i < wheel_param_list.size(); i++)
01129   {
01130     XmlRpcValue& wheel_params = wheel_param_list[i];
01131     if (wheel_params.getType() != XmlRpcValue::TypeStruct)
01132       throw runtime_error("The specified list of wheels is invalid.");
01133 
01134     if (!wheel_params.hasMember("steering_joint"))
01135       throw runtime_error("A steering joint was not specified.");
01136     XmlRpcValue& xml_steer_joint = wheel_params["steering_joint"];
01137     if (!xml_steer_joint.valid() ||
01138         xml_steer_joint.getType() != XmlRpcValue::TypeString)
01139     {
01140       throw runtime_error("An invalid steering joint was specified.");
01141     }
01142     const string steer_joint_name = xml_steer_joint;
01143     const shared_ptr<const urdf::Joint> steer_joint =
01144       urdf_model.getJoint(steer_joint_name);
01145     if (steer_joint == NULL)
01146     {
01147       throw runtime_error("Steering joint \"" + steer_joint_name +
01148                           "\" was not found in the URDF data.");
01149     }
01150     const string steer_link = steer_joint->child_link_name;
01151 
01152     if (!wheel_params.hasMember("axle_joint"))
01153       throw runtime_error("An axle joint was not specified.");
01154     XmlRpcValue& xml_axle_joint = wheel_params["axle_joint"];
01155     if (!xml_axle_joint.valid() ||
01156         xml_axle_joint.getType() != XmlRpcValue::TypeString)
01157     {
01158       throw runtime_error("An invalid axle joint was specified.");
01159     }
01160     const string axle_joint_name = xml_axle_joint;
01161 
01162     if (!wheel_params.hasMember("diameter"))
01163       throw runtime_error("A wheel diameter was not specified.");
01164     XmlRpcValue& xml_dia = wheel_params["diameter"];
01165     if (!xml_dia.valid())
01166       throw runtime_error("An invalid wheel diameter was specified.");
01167     double dia;
01168     switch (xml_dia.getType())
01169     {
01170       case XmlRpcValue::TypeInt:
01171         {
01172           const int tmp = xml_dia;
01173           dia = tmp;
01174         }
01175         break;
01176       case XmlRpcValue::TypeDouble:
01177         dia = xml_dia;
01178         break;
01179       default:
01180         throw runtime_error("An invalid wheel diameter was specified.");
01181     }
01182     if (dia <= 0)
01183     {
01184       throw runtime_error("A specified wheel diameter is less than or "
01185                           "equal to zero.");
01186     }
01187     // Circumference
01188     const double circ = (2 * M_PI) * (wheel_dia_scale * dia) / 2;
01189 
01190     wheels_.push_back(Wheel(model_tree, base_link, steer_link,
01191                             getJoint(steer_joint_name, true,
01192                                      ctrlr_nh, urdf_model,
01193                                      eff_joint_iface, pos_joint_iface,
01194                                      vel_joint_iface),
01195                             getJoint(axle_joint_name, false,
01196                                      ctrlr_nh, urdf_model,
01197                                      eff_joint_iface, pos_joint_iface,
01198                                      vel_joint_iface), circ));
01199   }
01200 
01201   // Odometry
01202   double odom_pub_freq;
01203   ctrlr_nh.param("odometry_publishing_frequency", odom_pub_freq,
01204                  DEF_ODOM_PUB_FREQ);
01205   comp_odom_ = odom_pub_freq > 0;
01206   if (comp_odom_)
01207   {
01208     odom_pub_period_ = Duration(1 / odom_pub_freq);
01209     ctrlr_nh.param("publish_odometry_to_base_transform", pub_odom_to_base_,
01210                    DEF_PUB_ODOM_TO_BASE);
01211 
01212     double init_x, init_y, init_yaw;
01213     ctrlr_nh.param("initial_x", init_x, DEF_INIT_X);
01214     ctrlr_nh.param("initial_y", init_y, DEF_INIT_Y);
01215     ctrlr_nh.param("initial_yaw", init_yaw, DEF_INIT_YAW);
01216     double x_sd, y_sd, yaw_sd;
01217     ctrlr_nh.param("x_sd", x_sd, DEF_SD);
01218     ctrlr_nh.param("y_sd", y_sd, DEF_SD);
01219     ctrlr_nh.param("yaw_sd", yaw_sd, DEF_SD);
01220     double x_speed_sd, y_speed_sd, yaw_speed_sd;
01221     ctrlr_nh.param("x_speed_sd", x_speed_sd, DEF_SD);
01222     ctrlr_nh.param("y_speed_sd", y_speed_sd, DEF_SD);
01223     ctrlr_nh.param("yaw_speed_sd", yaw_speed_sd, DEF_SD);
01224 
01225     init_odom_to_base_.setIdentity();
01226     init_odom_to_base_.rotate(clamp(init_yaw, -M_PI, M_PI));
01227     init_odom_to_base_.translation() = Vector2d(init_x, init_y);
01228     odom_to_base_ = init_odom_to_base_;
01229     odom_rigid_transf_.setIdentity();
01230 
01231     wheel_pos_.resize(2, wheels_.size());
01232     for (size_t col = 0; col < wheels_.size(); col++)
01233       wheel_pos_.col(col) = wheels_[col].pos();
01234     const Vector2d centroid = wheel_pos_.rowwise().mean();
01235     wheel_pos_.colwise() -= centroid;
01236     neg_wheel_centroid_ = -centroid;
01237     new_wheel_pos_.resize(wheels_.size(), 2);
01238 
01239     string odom_frame, base_frame;
01240     ctrlr_nh.param("odometry_frame", odom_frame, DEF_ODOM_FRAME);
01241     ctrlr_nh.param("base_frame", base_frame, DEF_BASE_FRAME);
01242 
01243     odom_pub_.msg_.header.frame_id = odom_frame;
01244     odom_pub_.msg_.child_frame_id = base_frame;
01245 
01246     odom_pub_.msg_.pose.pose.position.z = 0;
01247 
01248     odom_pub_.msg_.pose.covariance.assign(0);
01249     odom_pub_.msg_.pose.covariance[0] = x_sd * x_sd;
01250     odom_pub_.msg_.pose.covariance[7] = y_sd * y_sd;
01251     odom_pub_.msg_.pose.covariance[35] = yaw_sd * yaw_sd;
01252 
01253     odom_pub_.msg_.twist.twist.linear.z = 0;
01254     odom_pub_.msg_.twist.twist.angular.x = 0;
01255     odom_pub_.msg_.twist.twist.angular.y = 0;
01256 
01257     odom_pub_.msg_.twist.covariance.assign(0);
01258     odom_pub_.msg_.twist.covariance[0] = x_speed_sd * x_speed_sd;
01259     odom_pub_.msg_.twist.covariance[7] = y_speed_sd * y_speed_sd;
01260     odom_pub_.msg_.twist.covariance[35] = yaw_speed_sd * yaw_speed_sd;
01261 
01262     odom_pub_.init(ctrlr_nh, "odom", 1);
01263 
01264     if (pub_odom_to_base_)
01265     {
01266       odom_tf_pub_.msg_.transforms.resize(1);
01267       geometry_msgs::TransformStamped& odom_tf_trans =
01268         odom_tf_pub_.msg_.transforms[0];
01269       odom_tf_trans.header.frame_id = odom_pub_.msg_.header.frame_id;
01270       odom_tf_trans.child_frame_id = odom_pub_.msg_.child_frame_id;
01271       odom_tf_trans.transform.translation.z = 0;
01272       odom_tf_pub_.init(ctrlr_nh, "/tf", 1);
01273     }
01274   }
01275 
01276   vel_cmd_sub_ = ctrlr_nh.subscribe("cmd_vel", 1,
01277                                     &SteeredWheelBaseController::velCmdCB,
01278                                     this);
01279 }
01280 
01281 // Velocity command callback
01282 void SteeredWheelBaseController::velCmdCB(const TwistConstPtr& vel_cmd)
01283 {
01284   vel_cmd_.x_vel = vel_cmd->linear.x;
01285   vel_cmd_.y_vel = vel_cmd->linear.y;
01286   vel_cmd_.yaw_vel = vel_cmd->angular.z;
01287   vel_cmd_.last_vel_cmd_time = Time::now();
01288   vel_cmd_buf_.writeFromNonRT(vel_cmd_);
01289 }
01290 
01291 // Enforce linear motion limits.
01292 Vector2d SteeredWheelBaseController::
01293 enforceLinLimits(const Vector2d& desired_vel,
01294                  const double delta_t, const double inv_delta_t)
01295 {
01296   Vector2d vel = desired_vel;
01297   if (has_lin_speed_limit_)
01298   {
01299     const double vel_mag = vel.norm();
01300     if (vel_mag > lin_speed_limit_)
01301       vel = (vel / vel_mag) * lin_speed_limit_;
01302   }
01303 
01304   Vector2d accel = (vel - last_lin_vel_) * inv_delta_t;
01305 
01306   if (accel.dot(last_lin_vel_) >= 0)
01307   {
01308     // Acceleration
01309 
01310     if (has_lin_accel_limit_)
01311     {
01312       const double accel_mag = accel.norm();
01313       if (accel_mag > lin_accel_limit_)
01314       {
01315         accel = (accel / accel_mag) * lin_accel_limit_;
01316         vel = last_lin_vel_ + accel * delta_t;
01317       }
01318     }
01319   }
01320   else
01321   {
01322     // Deceleration
01323 
01324     if (has_lin_decel_limit_)
01325     {
01326       const double accel_mag = accel.norm();
01327       if (accel_mag > lin_decel_limit_)
01328       {
01329         accel = (accel / accel_mag) * lin_decel_limit_;
01330         vel = last_lin_vel_ + accel * delta_t;
01331       }
01332     }
01333     if (vel.dot(last_lin_vel_) < 0)
01334       vel = Vector2d(0, 0);
01335   }
01336 
01337   last_lin_vel_ = vel;
01338   return vel;
01339 }
01340 
01341 double SteeredWheelBaseController::enforceYawLimits(const double desired_vel,
01342                                                     const double delta_t,
01343                                                     const double inv_delta_t)
01344 {
01345   double vel = desired_vel;
01346   if (has_yaw_speed_limit_)
01347     vel = clamp(vel, -yaw_speed_limit_, yaw_speed_limit_);
01348 
01349   double accel = (vel - last_yaw_vel_) * inv_delta_t;
01350 
01351   const double accel_sign = sign(accel);
01352   const double last_yaw_vel_sign = sign(last_yaw_vel_);
01353   if (accel_sign == last_yaw_vel_sign || last_yaw_vel_sign == 0)
01354   {
01355     // Acceleration
01356 
01357     if (has_yaw_accel_limit_ && fabs(accel) > yaw_accel_limit_)
01358     {
01359       accel = accel_sign * yaw_accel_limit_;
01360       vel = last_yaw_vel_ + accel * delta_t;
01361     }
01362   }
01363   else
01364   {
01365     // Deceleration
01366 
01367     if (has_yaw_decel_limit_ && fabs(accel) > yaw_decel_limit_)
01368     {
01369       accel = accel_sign * yaw_decel_limit_;
01370       vel = last_yaw_vel_ + accel * delta_t;
01371     }
01372     if (sign(vel) != last_yaw_vel_sign)
01373       vel = 0;
01374   }
01375 
01376   last_yaw_vel_ = vel;
01377   return vel;
01378 }
01379 
01380 // Control the wheels.
01381 void SteeredWheelBaseController::ctrlWheels(const Vector2d& lin_vel,
01382                                             const double yaw_vel,
01383                                             const Duration& period)
01384 {
01385   const double lin_speed = lin_vel.norm();
01386 
01387   if (yaw_vel == 0)
01388   {
01389     if (lin_speed > 0)
01390     {
01391       // Point the wheels in the same direction.
01392 
01393       const Vector2d dir = lin_vel / lin_speed;
01394       const double theta =
01395         copysign(acos(dir.dot(SteeredWheelBaseController::X_DIR)), dir.y());
01396 
01397       double min_speed_gain = 1;
01398       BOOST_FOREACH(Wheel& wheel, wheels_)
01399       {
01400         const double speed_gain =
01401           wheel.ctrlSteering(theta, period, hermite_scale_, hermite_offset_);
01402         if (speed_gain < min_speed_gain)
01403           min_speed_gain = speed_gain;
01404       }
01405       const double lin_speed_2 = min_speed_gain * lin_speed;
01406       BOOST_FOREACH(Wheel& wheel, wheels_)
01407       {
01408         wheel.ctrlAxle(lin_speed_2, period);
01409       }
01410     }
01411     else
01412     {
01413       // Stop wheel rotation.
01414       BOOST_FOREACH(Wheel& wheel, wheels_)
01415       {
01416         wheel.ctrlSteering(period, hermite_scale_, hermite_offset_);
01417         wheel.ctrlAxle(0, period);
01418       }
01419     }
01420   }
01421   else  // The yaw velocity is nonzero.
01422   {
01423     // Align the wheels so that they are tangent to circles centered
01424     // at "center".
01425 
01426     Vector2d center;
01427     if (lin_speed > 0)
01428     {
01429       const Vector2d dir = lin_vel / lin_speed;
01430       center = Vector2d(-dir.y(), dir.x()) * lin_speed / yaw_vel;
01431     }
01432     else
01433     {
01434       center.setZero();
01435     }
01436 
01437     std::vector<double> radii;
01438     double min_speed_gain = 1;
01439     BOOST_FOREACH(Wheel& wheel, wheels_)
01440     {
01441       Vector2d vec = wheel.pos();
01442       vec -= center;
01443       const double radius = vec.norm();
01444       radii.push_back(radius);
01445       double theta;
01446       if (radius > 0)
01447       {
01448         vec /= radius;
01449         theta =
01450           copysign(acos(vec.dot(SteeredWheelBaseController::X_DIR)), vec.y()) +
01451           M_PI_2;
01452       }
01453       else
01454       {
01455         theta = 0;
01456       }
01457                         
01458       const double speed_gain =
01459         wheel.ctrlSteering(theta, period, hermite_scale_, hermite_offset_);
01460       if (speed_gain < min_speed_gain)
01461         min_speed_gain = speed_gain;
01462     }
01463 
01464     const double lin_speed_gain = min_speed_gain * yaw_vel;
01465     size_t i = 0;
01466     BOOST_FOREACH(Wheel& wheel, wheels_)
01467     {
01468       wheel.ctrlAxle(lin_speed_gain * radii[i++], period);
01469     }
01470   }
01471 }
01472 
01473 // Compute odometry.
01474 void SteeredWheelBaseController::compOdometry(const Time& time,
01475                                               const double inv_delta_t)
01476 {
01477   // Compute the rigid transform from wheel_pos_ to new_wheel_pos_.
01478 
01479   for (size_t row = 0; row < wheels_.size(); row++)
01480     new_wheel_pos_.row(row) = wheels_[row].pos() + wheels_[row].getDeltaPos();
01481   const Eigen::RowVector2d new_wheel_centroid =
01482     new_wheel_pos_.colwise().mean();
01483   new_wheel_pos_.rowwise() -= new_wheel_centroid;
01484 
01485   const Matrix2d h = wheel_pos_ * new_wheel_pos_;
01486   const Eigen::JacobiSVD<Matrix2d> svd(h, Eigen::ComputeFullU |
01487                                        Eigen::ComputeFullV);
01488   Matrix2d rot = svd.matrixV() * svd.matrixU().transpose();
01489   if (rot.determinant() < 0)
01490     rot.col(1) *= -1;
01491 
01492   odom_rigid_transf_.matrix().block(0, 0, 2, 2) = rot;
01493   odom_rigid_transf_.translation() =
01494     rot * neg_wheel_centroid_ + new_wheel_centroid.transpose();
01495   odom_to_base_ = odom_to_base_ * odom_rigid_transf_;
01496 
01497   const double odom_x = odom_to_base_.translation().x();
01498   const double odom_y = odom_to_base_.translation().y();
01499   const double odom_yaw = atan2(odom_to_base_(1, 0), odom_to_base_(0, 0));
01500 
01501   // Publish the odometry.
01502 
01503   geometry_msgs::Quaternion orientation;
01504   bool orientation_comped = false;
01505 
01506   // tf
01507   if (pub_odom_to_base_ && time - last_odom_tf_pub_time_ >= odom_pub_period_ &&
01508       odom_tf_pub_.trylock())
01509   {
01510     orientation = tf::createQuaternionMsgFromYaw(odom_yaw);
01511     orientation_comped = true;
01512 
01513     geometry_msgs::TransformStamped& odom_tf_trans =
01514       odom_tf_pub_.msg_.transforms[0];
01515     odom_tf_trans.header.stamp = time;
01516     odom_tf_trans.transform.translation.x = odom_x;
01517     odom_tf_trans.transform.translation.y = odom_y;
01518     odom_tf_trans.transform.rotation = orientation;
01519 
01520     odom_tf_pub_.unlockAndPublish();
01521     last_odom_tf_pub_time_ = time;
01522   }
01523 
01524   // odom
01525   if (time - last_odom_pub_time_ >= odom_pub_period_ && odom_pub_.trylock())
01526   {
01527     if (!orientation_comped)
01528       orientation = tf::createQuaternionMsgFromYaw(odom_yaw);
01529 
01530     odom_pub_.msg_.header.stamp = time;
01531     odom_pub_.msg_.pose.pose.position.x = odom_x;
01532     odom_pub_.msg_.pose.pose.position.y = odom_y;
01533     odom_pub_.msg_.pose.pose.orientation = orientation;
01534 
01535     odom_pub_.msg_.twist.twist.linear.x =
01536       odom_rigid_transf_.translation().x() * inv_delta_t;
01537     odom_pub_.msg_.twist.twist.linear.y =
01538       odom_rigid_transf_.translation().y() * inv_delta_t;
01539     odom_pub_.msg_.twist.twist.angular.z =
01540       atan2(odom_rigid_transf_(1, 0), odom_rigid_transf_(0, 0)) * inv_delta_t;
01541 
01542     odom_pub_.unlockAndPublish();
01543     last_odom_pub_time_ = time;
01544   }
01545 }
01546 
01547 } // namespace steered_wheel_base_controller
01548 
01549 PLUGINLIB_EXPORT_CLASS(steered_wheel_base_controller::\
01550 SteeredWheelBaseController, controller_interface::ControllerBase)


steered_wheel_base_controller
Author(s): Jim Rothrock
autogenerated on Mon Nov 16 2015 13:44:46