00001
00009
00010
00011
00012
00013 #include <ros/ros.h>
00014 #include <nodelet/nodelet.h>
00015 #include <pluginlib/class_list_macros.h>
00016
00017 #include <dynamic_reconfigure/server.h>
00018 #include <yocs_velocity_smoother/paramsConfig.h>
00019
00020 #include <ecl/threads/thread.hpp>
00021
00022 #include "yocs_velocity_smoother/velocity_smoother_nodelet.hpp"
00023
00024
00025
00026
00027
00028 #define PERIOD_RECORD_SIZE 5
00029 #define ZERO_VEL_COMMAND geometry_msgs::Twist();
00030 #define IS_ZERO_VEOCITY(a) ((a.linear.x == 0.0) && (a.angular.z == 0.0))
00031
00032
00033
00034
00035
00036 namespace yocs_velocity_smoother {
00037
00038
00039
00040
00041
00042 void VelocitySmoother::reconfigCB(yocs_velocity_smoother::paramsConfig &config, uint32_t level)
00043 {
00044 ROS_INFO("Reconfigure request : %f %f %f %f %f",
00045 config.speed_lim_v, config.speed_lim_w, config.accel_lim_v, config.accel_lim_w, config.decel_factor);
00046
00047 speed_lim_v = config.speed_lim_v;
00048 speed_lim_w = config.speed_lim_w;
00049 accel_lim_v = config.accel_lim_v;
00050 accel_lim_w = config.accel_lim_w;
00051 decel_factor = config.decel_factor;
00052 decel_lim_v = decel_factor*accel_lim_v;
00053 decel_lim_w = decel_factor*accel_lim_w;
00054 }
00055
00056 void VelocitySmoother::velocityCB(const geometry_msgs::Twist::ConstPtr& msg)
00057 {
00058
00059
00060 if (period_record.size() < PERIOD_RECORD_SIZE)
00061 {
00062 period_record.push_back((ros::Time::now() - last_cb_time).toSec());
00063 }
00064 else
00065 {
00066 period_record[pr_next] = (ros::Time::now() - last_cb_time).toSec();
00067 }
00068
00069 pr_next++;
00070 pr_next %= period_record.size();
00071 last_cb_time = ros::Time::now();
00072
00073 if (period_record.size() <= PERIOD_RECORD_SIZE/2)
00074 {
00075
00076 cb_avg_time = 0.1;
00077 }
00078 else
00079 {
00080
00081 cb_avg_time = median(period_record);
00082 }
00083
00084 input_active = true;
00085
00086
00087 target_vel.linear.x =
00088 msg->linear.x > 0.0 ? std::min(msg->linear.x, speed_lim_v) : std::max(msg->linear.x, -speed_lim_v);
00089 target_vel.angular.z =
00090 msg->angular.z > 0.0 ? std::min(msg->angular.z, speed_lim_w) : std::max(msg->angular.z, -speed_lim_w);
00091 }
00092
00093 void VelocitySmoother::odometryCB(const nav_msgs::Odometry::ConstPtr& msg)
00094 {
00095 if (robot_feedback == ODOMETRY)
00096 current_vel = msg->twist.twist;
00097
00098
00099 }
00100
00101 void VelocitySmoother::robotVelCB(const geometry_msgs::Twist::ConstPtr& msg)
00102 {
00103 if (robot_feedback == COMMANDS)
00104 current_vel = *msg;
00105
00106
00107 }
00108
00109 void VelocitySmoother::spin()
00110 {
00111 double period = 1.0/frequency;
00112 ros::Rate spin_rate(frequency);
00113
00114 while (! shutdown_req && ros::ok())
00115 {
00116 if ((input_active == true) && (cb_avg_time > 0.0) &&
00117 ((ros::Time::now() - last_cb_time).toSec() > std::min(3.0*cb_avg_time, 0.5)))
00118 {
00119
00120
00121
00122
00123
00124 input_active = false;
00125 if (IS_ZERO_VEOCITY(target_vel) == false)
00126 {
00127 ROS_WARN_STREAM("Velocity Smoother : input got inactive leaving us a non-zero target velocity ("
00128 << target_vel.linear.x << ", " << target_vel.angular.z << "), zeroing...[" << name << "]");
00129 target_vel = ZERO_VEL_COMMAND;
00130 }
00131 }
00132
00133 if ((robot_feedback != NONE) && (input_active == true) && (cb_avg_time > 0.0) &&
00134 (((ros::Time::now() - last_cb_time).toSec() > 5.0*cb_avg_time) ||
00135 (std::abs(current_vel.linear.x - last_cmd_vel.linear.x) > 0.2) ||
00136 (std::abs(current_vel.angular.z - last_cmd_vel.angular.z) > 2.0)))
00137 {
00138
00139
00140
00141
00142
00143
00144
00145 ROS_WARN("Using robot velocity feedback (%s) instead of last command: %f, %f, %f",
00146 robot_feedback == ODOMETRY ? "odometry" : "end commands",
00147 (ros::Time::now() - last_cb_time).toSec(),
00148 current_vel.linear.x - last_cmd_vel.linear.x,
00149 current_vel.angular.z - last_cmd_vel.angular.z);
00150 last_cmd_vel = current_vel;
00151 }
00152
00153 geometry_msgs::TwistPtr cmd_vel;
00154
00155 if ((target_vel.linear.x != last_cmd_vel.linear.x) ||
00156 (target_vel.angular.z != last_cmd_vel.angular.z))
00157 {
00158
00159 cmd_vel.reset(new geometry_msgs::Twist(target_vel));
00160
00161 double v_inc, w_inc, max_v_inc, max_w_inc;
00162
00163 v_inc = target_vel.linear.x - last_cmd_vel.linear.x;
00164 if ((robot_feedback == ODOMETRY) && (current_vel.linear.x*target_vel.linear.x < 0.0))
00165 {
00166
00167 max_v_inc = decel_lim_v*period;
00168 }
00169 else
00170 {
00171 max_v_inc = ((v_inc*target_vel.linear.x > 0.0)?accel_lim_v:decel_lim_v)*period;
00172 }
00173
00174 w_inc = target_vel.angular.z - last_cmd_vel.angular.z;
00175 if ((robot_feedback == ODOMETRY) && (current_vel.angular.z*target_vel.angular.z < 0.0))
00176 {
00177
00178 max_w_inc = decel_lim_w*period;
00179 }
00180 else
00181 {
00182 max_w_inc = ((w_inc*target_vel.angular.z > 0.0)?accel_lim_w:decel_lim_w)*period;
00183 }
00184
00185
00186
00187
00188 double MA = sqrt( v_inc * v_inc + w_inc * w_inc);
00189 double MB = sqrt(max_v_inc * max_v_inc + max_w_inc * max_w_inc);
00190
00191 double Av = std::abs(v_inc) / MA;
00192 double Aw = std::abs(w_inc) / MA;
00193 double Bv = max_v_inc / MB;
00194 double Bw = max_w_inc / MB;
00195 double theta = atan2(Bw, Bv) - atan2(Aw, Av);
00196
00197 if (theta < 0)
00198 {
00199
00200 max_v_inc = (max_w_inc*std::abs(v_inc))/std::abs(w_inc);
00201 }
00202 else
00203 {
00204
00205 max_w_inc = (max_v_inc*std::abs(w_inc))/std::abs(v_inc);
00206 }
00207
00208 if (std::abs(v_inc) > max_v_inc)
00209 {
00210
00211 cmd_vel->linear.x = last_cmd_vel.linear.x + sign(v_inc)*max_v_inc;
00212 }
00213
00214 if (std::abs(w_inc) > max_w_inc)
00215 {
00216
00217 cmd_vel->angular.z = last_cmd_vel.angular.z + sign(w_inc)*max_w_inc;
00218 }
00219
00220 smooth_vel_pub.publish(cmd_vel);
00221 last_cmd_vel = *cmd_vel;
00222 }
00223 else if (input_active == true)
00224 {
00225
00226 cmd_vel.reset(new geometry_msgs::Twist(last_cmd_vel));
00227 smooth_vel_pub.publish(cmd_vel);
00228 }
00229
00230 spin_rate.sleep();
00231 }
00232 }
00233
00239 bool VelocitySmoother::init(ros::NodeHandle& nh)
00240 {
00241
00242 dynamic_reconfigure_callback = boost::bind(&VelocitySmoother::reconfigCB, this, _1, _2);
00243
00244 dynamic_reconfigure_server = new dynamic_reconfigure::Server<yocs_velocity_smoother::paramsConfig>(nh);
00245 dynamic_reconfigure_server->setCallback(dynamic_reconfigure_callback);
00246
00247
00248 int feedback;
00249 nh.param("frequency", frequency, 20.0);
00250 nh.param("decel_factor", decel_factor, 1.0);
00251 nh.param("robot_feedback", feedback, (int)NONE);
00252
00253 if ((int(feedback) < NONE) || (int(feedback) > COMMANDS))
00254 {
00255 ROS_WARN("Invalid robot feedback type (%d). Valid options are 0 (NONE, default), 1 (ODOMETRY) and 2 (COMMANDS)",
00256 feedback);
00257 feedback = NONE;
00258 }
00259
00260 robot_feedback = static_cast<RobotFeedbackType>(feedback);
00261
00262
00263 if ((nh.getParam("speed_lim_v", speed_lim_v) == false) ||
00264 (nh.getParam("speed_lim_w", speed_lim_w) == false))
00265 {
00266 ROS_ERROR("Missing velocity limit parameter(s)");
00267 return false;
00268 }
00269
00270 if ((nh.getParam("accel_lim_v", accel_lim_v) == false) ||
00271 (nh.getParam("accel_lim_w", accel_lim_w) == false))
00272 {
00273 ROS_ERROR("Missing acceleration limit parameter(s)");
00274 return false;
00275 }
00276
00277
00278 decel_lim_v = decel_factor*accel_lim_v;
00279 decel_lim_w = decel_factor*accel_lim_w;
00280
00281
00282 odometry_sub = nh.subscribe("odometry", 1, &VelocitySmoother::odometryCB, this);
00283 current_vel_sub = nh.subscribe("robot_cmd_vel", 1, &VelocitySmoother::robotVelCB, this);
00284 raw_in_vel_sub = nh.subscribe("raw_cmd_vel", 1, &VelocitySmoother::velocityCB, this);
00285 smooth_vel_pub = nh.advertise <geometry_msgs::Twist> ("smooth_cmd_vel", 1);
00286
00287 return true;
00288 }
00289
00290
00291
00292
00293
00294
00295 class VelocitySmootherNodelet : public nodelet::Nodelet
00296 {
00297 public:
00298 VelocitySmootherNodelet() { }
00299 ~VelocitySmootherNodelet()
00300 {
00301 NODELET_DEBUG("Velocity Smoother : waiting for worker thread to finish...");
00302 vel_smoother_->shutdown();
00303 worker_thread_.join();
00304 }
00305
00306 std::string unresolvedName(const std::string &name) const {
00307 size_t pos = name.find_last_of('/');
00308 return name.substr(pos + 1);
00309 }
00310
00311
00312 virtual void onInit()
00313 {
00314 ros::NodeHandle ph = getPrivateNodeHandle();
00315 std::string resolved_name = ph.getUnresolvedNamespace();
00316 std::string name = unresolvedName(resolved_name);
00317 NODELET_DEBUG_STREAM("Velocity Smoother : initialising nodelet...[" << name << "]");
00318 vel_smoother_.reset(new VelocitySmoother(name));
00319 if (vel_smoother_->init(ph))
00320 {
00321 NODELET_DEBUG_STREAM("Velocity Smoother : nodelet initialised [" << name << "]");
00322 worker_thread_.start(&VelocitySmoother::spin, *vel_smoother_);
00323 }
00324 else
00325 {
00326 NODELET_ERROR_STREAM("Velocity Smoother : nodelet initialisation failed [" << name << "]");
00327 }
00328 }
00329
00330 private:
00331 boost::shared_ptr<VelocitySmoother> vel_smoother_;
00332 ecl::Thread worker_thread_;
00333 };
00334
00335 }
00336
00337 PLUGINLIB_EXPORT_CLASS(yocs_velocity_smoother::VelocitySmootherNodelet, nodelet::Nodelet);