cob_base_velocity_smoother.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *   http://www.apache.org/licenses/LICENSE-2.0
00009 
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 
00018 #include <cob_base_velocity_smoother.h>
00019 
00020 /****************************************************************
00021  * the ros navigation doesn't run very smoothly because acceleration is too high
00022  * --> cob has strong base motors and therefore reacts with shaking behavior
00023  * (PR2 has much more mechanical damping)
00024  * solution: the additional node cob_base_velocity_smoother smooths the velocities
00025  * comming from ROS-navigation or teleoperation, by calculating the mean values of a certain number
00026  * of past messages and limiting the acceleration under a given threshold.
00027  * cob_base_velocity_smoother subsribes (input) and publishes (output) geometry_msgs::Twist.
00028  ****************************************************************/
00029 
00030 // function for checking wether a new msg has been received, triggering publishers accordingly
00031 void cob_base_velocity_smoother::set_new_msg_received(bool received)
00032 {
00033   pthread_mutex_lock(&m_mutex);
00034   new_msg_received_ = received;
00035   pthread_mutex_unlock(&m_mutex);
00036 }
00037 
00038 bool cob_base_velocity_smoother::get_new_msg_received()
00039 {
00040   pthread_mutex_lock(&m_mutex);
00041   bool ret_val = new_msg_received_;
00042   pthread_mutex_unlock(&m_mutex);
00043   return ret_val;
00044 }
00045 
00046 // constructor
00047 cob_base_velocity_smoother::cob_base_velocity_smoother()
00048 {
00049   m_mutex = PTHREAD_MUTEX_INITIALIZER;
00050   new_msg_received_ = false;
00051 
00052   // create node handles
00053   nh_ = ros::NodeHandle();
00054   pnh_ = ros::NodeHandle("~");
00055 
00056   // publisher
00057   pub_ = nh_.advertise<geometry_msgs::Twist>("output", 1);
00058 
00059   // subscriber
00060   geometry_msgs_sub_ = nh_.subscribe<geometry_msgs::Twist>("input", 1, boost::bind(&cob_base_velocity_smoother::geometryCallback, this, _1));
00061 
00062   // get parameters from parameter server if possible or write default values to variables
00063   if( !pnh_.hasParam("circular_buffer_capacity") )
00064   {
00065      ROS_WARN("No parameter circular_buffer_capacity on parameter server. Using default [12]");
00066   }
00067   pnh_.param("circular_buffer_capacity", buffer_capacity_, 12);
00068 
00069   if( !pnh_.hasParam("maximal_time_delay") )
00070   {
00071     ROS_WARN("No parameter maximal_time_delay on parameter server. Using default [4 in s]");
00072   }
00073   pnh_.param("maximal_time_delay", store_delay_, 4.0);
00074 
00075   if( !pnh_.hasParam("maximal_time_delay_to_stop") )
00076   {
00077     ROS_WARN("No parameter maximal_time_delay_to_stop on parameter server. Using default [0.1 in s]");
00078   }
00079   pnh_.param("maximal_time_delay_to_stop", stop_delay_after_no_sub_, 0.1);
00080 
00081   if( !pnh_.hasParam("thresh_max_acc") )
00082   {
00083     ROS_WARN("No parameter thresh_max_acc on parameter server. Using default [0.3 in m/s]");
00084   }
00085   pnh_.param("thresh_max_acc", acc_limit_, 0.3);
00086 
00087   if( !pnh_.hasParam("loop_rate") )
00088   {
00089     ROS_WARN("No parameter loop_rate on parameter server. Using default [30 in Hz]");
00090   }
00091   pnh_.param("loop_rate", loop_rate_, 30.0);
00092 
00093   double min_input_rate;
00094   if( !pnh_.hasParam("min_input_rate") )
00095   {
00096     ROS_WARN("No parameter min_input_rate on parameter server. Using default [9 in Hz]");
00097   }
00098   pnh_.param("min_input_rate", min_input_rate, 9.0);
00099 
00100   if( min_input_rate > loop_rate_)
00101   {
00102     ROS_WARN("min_input_rate > loop_rate: Setting min_input_rate to loop_rate = %f", loop_rate_);
00103     min_input_rate = loop_rate_;
00104   }
00105   max_delay_between_commands_ = 1/min_input_rate;
00106 
00107   // set a geometry message containing zero-values
00108   zero_values_.linear.x = 0.0;
00109   zero_values_.linear.y = 0.0;
00110   zero_values_.linear.z = 0.0;
00111 
00112   zero_values_.angular.x = 0.0;
00113   zero_values_.angular.y = 0.0;
00114   zero_values_.angular.z = 0.0;
00115 
00116   // initialize circular buffers
00117   cb_.set_capacity(buffer_capacity_);
00118   cb_out_.set_capacity(buffer_capacity_);
00119   cb_time_.set_capacity(buffer_capacity_);
00120 
00121   // set actual ros::Time
00122   ros::Time now = ros::Time::now();
00123 
00124   // fill circular buffer with zero values
00125   while(cb_.full() == false)
00126   {
00127     cb_.push_front(zero_values_);
00128     cb_time_.push_front(now);
00129   }
00130 };
00131 
00132 // destructor
00133 cob_base_velocity_smoother::~cob_base_velocity_smoother(){}
00134 
00135 // callback function to subsribe to the geometry messages cmd_vel and save them in a member variable
00136 void cob_base_velocity_smoother::geometryCallback(const geometry_msgs::Twist::ConstPtr &cmd_vel)
00137 {
00138   sub_msg_ = *cmd_vel;
00139   set_new_msg_received(true);
00140 }
00141 
00142 // calculation function called periodically in main
00143 void cob_base_velocity_smoother::calculationStep()
00144 {
00145   // set current ros::Time
00146   ros::Time now = ros::Time::now();
00147   static ros::Time last = now;
00148 
00149   geometry_msgs::Twist result = geometry_msgs::Twist();
00150 
00151   // only publish command if we received a msg or the last message was actually zero
00152   if (get_new_msg_received())
00153   {
00154     // generate Output messages
00155     result = this->setOutput(now, sub_msg_);
00156     pub_.publish(result);
00157 
00158     last = now;
00159     set_new_msg_received(false);
00160   }
00161   // start writing in zeros if we did not receive a new msg within a certain amount of time.
00162   // Do not publish! Otherwise, the output of other nodes will be overwritten!
00163   else if ( fabs((last - now).toSec()) > max_delay_between_commands_)
00164   {
00165     result = this->setOutput(now, geometry_msgs::Twist());
00166   }
00167   // if last message was a zero msg, fill the buffer with zeros and publish again
00168   else if (IsZeroMsg(sub_msg_))
00169   {
00170     result = this->setOutput(now, sub_msg_);
00171     pub_.publish(result);
00172   }
00173 }
00174 
00175 // function for the actual computation
00176 // calls the reviseCircBuff and the meanValue-functions and limits the acceleration under thresh
00177 // returns the resulting geomtry message to be published to the base_controller
00178 geometry_msgs::Twist cob_base_velocity_smoother::setOutput(ros::Time now, geometry_msgs::Twist cmd_vel)
00179 {
00180   geometry_msgs::Twist result = zero_values_;
00181 
00182   // update the circular buffers
00183   this->reviseCircBuff(now, cmd_vel);
00184 
00185   // calculate the mean values for each direction
00186   result.linear.x = meanValueX();
00187   result.linear.y = meanValueY();
00188   result.angular.z = meanValueZ();
00189 
00190   // limit acceleration
00191   this->limitAcceleration(now, result);
00192 
00193   // insert the result-message into the circular-buffer storing the output
00194   cb_out_.push_front(result);
00195 
00196   return result;
00197 }
00198 
00199 // function that updates the circular buffer after receiving a new geometry message
00200 void cob_base_velocity_smoother::reviseCircBuff(ros::Time now, geometry_msgs::Twist cmd_vel)
00201 {
00202   if(this->circBuffOutOfDate(now) == true)
00203   {
00204     // the circular buffer is out of date, so clear and refill with zero messages before adding the new command
00205 
00206     // clear buffers
00207     cb_.clear();
00208     cb_time_.clear();
00209 
00210     // fill circular buffer with zero_values_ and time buffer with actual time-stamp
00211     while(cb_.full() == false)
00212     {
00213       cb_.push_front(zero_values_);
00214       cb_time_.push_front(now);
00215     }
00216 
00217     // add new command velocity message to circular buffer
00218     cb_.push_front(cmd_vel);
00219     // add new timestamp for subscribed command velocity message
00220     cb_time_.push_front(now);
00221   }
00222   else
00223   {
00224     // only some elements of the circular buffer are out of date, so only delete those
00225     double delay=(now.toSec() - cb_time_.back().toSec());
00226 
00227     while( delay >= store_delay_ )
00228     {
00229       // remove out-dated messages
00230       cb_.pop_back();
00231       cb_time_.pop_back();
00232 
00233       delay=(now.toSec() - cb_time_.back().toSec());
00234     }
00235     // if the circular buffer is empty now, refill with zero values
00236     if(cb_.empty() == true)
00237     {
00238       while(cb_.full() == false)
00239       {
00240         cb_.push_front(zero_values_);
00241         cb_time_.push_front(now);
00242       }
00243     }
00244     if(this->IsZeroMsg(cmd_vel))
00245     {
00246       // here we subscribed  a zero message, so we want to stop the robot
00247       long unsigned int size = floor( cb_.size() / 3 );
00248 
00249       // to stop the robot faster, fill the circular buffer with more than one, in fact floor (cb_.size() / 3 ), zero messages
00250       for(long unsigned int i=0; i< size; i++)
00251       {
00252         // add new command velocity message to circular buffer
00253         cb_.push_front(cmd_vel);
00254         // add new timestamp for subscribed command velocity message
00255         cb_time_.push_front(now);
00256       }
00257     }
00258     else
00259     {
00260       // add new command velocity message to circular buffer
00261       cb_.push_front(cmd_vel);
00262       // add new timestamp for subscribed command velocity message
00263       cb_time_.push_front(now);
00264     }
00265   }
00266 };
00267 
00268 // returns true if all messages in cb are out of date in consideration of store_delay
00269 bool cob_base_velocity_smoother::circBuffOutOfDate(ros::Time now)
00270 {
00271   bool result=true;
00272 
00273   long unsigned int count=0;
00274 
00275   while( (count < cb_.size()) && (result == true) )
00276   {
00277     double delay=(now.toSec() - cb_time_[count].toSec());
00278     if(delay < store_delay_)
00279     {
00280       result = false;
00281     }
00282     count++;
00283   }
00284 
00285   return result;
00286 };
00287 
00288 // returns true if the input msg cmd_vel equals zero_values_, false otherwise
00289 bool cob_base_velocity_smoother::IsZeroMsg(geometry_msgs::Twist cmd_vel)
00290 {
00291   bool result = true;
00292   if( (cmd_vel.linear.x) != 0 || (cmd_vel.linear.y != 0) || (cmd_vel.angular.z != 0) )
00293   {
00294     result = false;
00295   }
00296 
00297   return result;
00298 };
00299 
00300 int cob_base_velocity_smoother::signum(double var)
00301 {
00302   if(var < 0)
00303   {
00304     return -1;
00305   }
00306   else
00307   {
00308     return 1;
00309   }
00310 };
00311 
00312 // functions to calculate the mean values for linear/x
00313 double cob_base_velocity_smoother::meanValueX()
00314 {
00315   double result = 0;
00316   long unsigned int size = cb_.size();
00317 
00318   // calculate sum
00319   for(long unsigned int i=0; i<size; i++)
00320   {
00321     result += cb_[i].linear.x;
00322   }
00323   result /= size;
00324 
00325   if(size > 1)
00326   {
00327     double help_result = 0;
00328     double max = cb_[0].linear.x;
00329     long unsigned int max_ind = 0;
00330     for(long unsigned int i=0; i<size; i++)
00331     {
00332       if(abs(result-cb_[i].linear.x) > abs(result-max))
00333       {
00334         max = cb_[i].linear.x;
00335         max_ind = i;
00336       }
00337     }
00338 
00339     // calculate sum
00340     for(long unsigned int i=0; i<size; i++)
00341     {
00342       if(i != max_ind)
00343       {
00344         help_result += cb_[i].linear.x;
00345       }
00346     }
00347     result = help_result / (size - 1);
00348   }
00349 
00350   return result;
00351 };
00352 
00353 // functions to calculate the mean values for linear/y
00354 double cob_base_velocity_smoother::meanValueY()
00355 {
00356   double result = 0;
00357   long unsigned int size = cb_.size();
00358 
00359   // calculate sum
00360   for(long unsigned int i=0; i<size; i++)
00361   {
00362     result += cb_[i].linear.y;
00363   }
00364   result /= size;
00365 
00366   if(size > 1)
00367   {
00368     double help_result = 0;
00369     double max = cb_[0].linear.y;
00370     long unsigned int max_ind = 0;
00371     for(long unsigned int i=0; i<size; i++)
00372     {
00373       if(abs(result-cb_[i].linear.y) > abs(result-max))
00374       {
00375         max = cb_[i].linear.y;
00376         max_ind = i;
00377       }
00378     }
00379 
00380     // calculate sum
00381     for(long unsigned int i=0; i<size; i++)
00382     {
00383       if(i != max_ind)
00384       {
00385         help_result += cb_[i].linear.y;
00386       }
00387     }
00388     result = help_result / (size - 1);
00389   }
00390 
00391   return result;
00392 };
00393 
00394 // functions to calculate the mean values for angular/z
00395 double cob_base_velocity_smoother::meanValueZ()
00396 {
00397   double result = 0;
00398   long unsigned int size = cb_.size();
00399 
00400   // calculate sum
00401   for(long unsigned int i=0; i<size; i++)
00402   {
00403     result += cb_[i].angular.z;
00404   }
00405   result /= size;
00406 
00407   if(size > 1)
00408   {
00409     double help_result = 0;
00410     double max = cb_[0].angular.z;
00411     long unsigned int max_ind = 0;
00412     for(long unsigned int i = 0; i < size; i++)
00413     {
00414       if(abs(result-cb_[i].angular.z) > abs(result-max))
00415       {
00416         max = cb_[i].angular.z;
00417         max_ind = i;
00418       }
00419     }
00420 
00421     // calculate sum
00422     for(long unsigned int i = 0; i < size; i++)
00423     {
00424       if(i != max_ind)
00425       {
00426         help_result += cb_[i].angular.z;
00427       }
00428     }
00429     result = help_result / (size - 1);
00430   }
00431 
00432   return result;
00433 };
00434 
00435 // function to make the loop rate availabe outside the class
00436 double cob_base_velocity_smoother::getLoopRate()
00437 {
00438   return loop_rate_;
00439 }
00440 
00441 // function to compare two geometry messages
00442 bool cob_base_velocity_smoother::IsEqual(geometry_msgs::Twist msg1, geometry_msgs::Twist msg2)
00443 {
00444   if( (msg1.linear.x == msg2.linear.x) && (msg1.linear.y == msg2.linear.y) && (msg1.angular.z == msg2.angular.z))
00445   {
00446     return true;
00447   }
00448   else
00449   {
00450     return false;
00451   }
00452 }
00453 
00454 //function to limit the acceleration under the given threshhold thresh
00455 void cob_base_velocity_smoother::limitAcceleration(ros::Time now, geometry_msgs::Twist& result)
00456 {
00457   // limit the acceleration under thresh
00458   // only if cob_base_velocity_smoother has published a message yet
00459 
00460   double deltaTime = 0;
00461 
00462   if(cb_time_.size() > 1)
00463   {
00464     deltaTime = now.toSec() - cb_time_[2].toSec();
00465   }
00466 
00467   if( cb_out_.size() > 0)
00468   {
00469     if(deltaTime > 0)
00470     {
00471       // set delta velocity and acceleration values
00472       double deltaX = result.linear.x - cb_out_.front().linear.x;
00473 
00474       double deltaY = result.linear.y - cb_out_.front().linear.y;
00475 
00476       double deltaZ = result.angular.z - cb_out_.front().angular.z;
00477 
00478       if( abs(deltaX) > acc_limit_)
00479       {
00480         result.linear.x = cb_out_.front().linear.x + this->signum(deltaX) * acc_limit_;
00481       }
00482       if( abs(deltaY) > acc_limit_ )
00483       {
00484         result.linear.y = cb_out_.front().linear.y + this->signum(deltaY) * acc_limit_;
00485       }
00486       if( abs(deltaZ) > acc_limit_ )
00487       {
00488         result.angular.z = cb_out_.front().angular.z + this->signum(deltaZ) * acc_limit_;
00489       }
00490     }
00491   }
00492 };
00493 
00494 
00495 int main(int argc, char **argv)
00496 {
00497   // initialize ros and specifiy node name
00498   ros::init(argc, argv, "cob_base_velocity_smoother");
00499 
00500   // create Node Class
00501   cob_base_velocity_smoother my_velocity_smoother;
00502   // get loop rate from class member
00503   ros::Rate rate(my_velocity_smoother.getLoopRate());
00504   // actual calculation step with given frequency
00505   while(my_velocity_smoother.nh_.ok())
00506   {
00507     my_velocity_smoother.calculationStep();
00508 
00509     ros::spinOnce();
00510     rate.sleep();
00511   }
00512 
00513   return 0;
00514 }


cob_base_velocity_smoother
Author(s): Florian Mirus , Benjamin Maidel
autogenerated on Thu Jun 6 2019 21:18:55