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
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
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;
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_;
00277 };
00278
00279
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
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
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
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_;
00351 Vector2d pos_;
00352
00353 shared_ptr<Joint> steer_joint_;
00354 shared_ptr<Joint> axle_joint_;
00355 double theta_steer_;
00356 double last_theta_steer_desired_;
00357 double last_theta_axle_;
00358
00359 double radius_;
00360 double inv_radius_;
00361 double axle_vel_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
00400 void PosJoint::init()
00401 {
00402 pos_ = getPos();
00403 stop();
00404 }
00405
00406
00407 void PosJoint::stop()
00408 {
00409 handle_.setCommand(getPos());
00410 }
00411
00412
00413 void PosJoint::setPos(const double pos, const Duration& )
00414 {
00415 pos_ = pos;
00416 handle_.setCommand(pos_);
00417 }
00418
00419
00420 void PosJoint::setVel(const double vel, const Duration& period)
00421 {
00422 pos_ += vel * period.toSec();
00423 handle_.setCommand(pos_);
00424 }
00425
00426
00427 void VelJoint::stop()
00428 {
00429 handle_.setCommand(0);
00430 }
00431
00432
00433 void VelJoint::setVel(const double vel, const Duration& )
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
00451 void PIDJoint::init()
00452 {
00453 pid_ctrlr_.reset();
00454 stop();
00455 }
00456
00457
00458 void PIDJoint::stop()
00459 {
00460
00461
00462 handle_.setCommand(0);
00463 }
00464
00465
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
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
00513
00514
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
00525 void Wheel::initJoints()
00526 {
00527 steer_joint_->init();
00528 axle_joint_->init();
00529 }
00530
00531
00532 void Wheel::stop() const
00533 {
00534 steer_joint_->stop();
00535 axle_joint_->stop();
00536 }
00537
00538
00539
00540
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
00549
00550
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
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
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
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
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
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
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
00689
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
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
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
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 }
00779
00780 namespace steered_wheel_base_controller
00781 {
00782
00783
00784 class SteeredWheelBaseController : public controller_interface::ControllerBase
00785 {
00786 public:
00787 SteeredWheelBaseController();
00788
00789
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
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
00802 {
00803 double x_vel;
00804 double y_vel;
00805 double yaw_vel;
00806
00807
00808
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
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
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_;
00875 double last_yaw_vel_;
00876
00877
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
00885 bool comp_odom_;
00886 bool pub_odom_to_base_;
00887 Duration odom_pub_period_;
00888 Affine2d init_odom_to_base_;
00889 Affine2d odom_to_base_;
00890 Affine2d odom_rigid_transf_;
00891
00892
00893
00894
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
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& ,
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
01028
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
01422 {
01423
01424
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
01474 void SteeredWheelBaseController::compOdometry(const Time& time,
01475 const double inv_delta_t)
01476 {
01477
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
01502
01503 geometry_msgs::Quaternion orientation;
01504 bool orientation_comped = false;
01505
01506
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
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 }
01548
01549 PLUGINLIB_EXPORT_CLASS(steered_wheel_base_controller::\
01550 SteeredWheelBaseController, controller_interface::ControllerBase)