ackermann_controller.cpp
Go to the documentation of this file.
1 
6 #include <cmath>
8 #include <urdf_parser/urdf_parser.h>
9 #include <boost/assign.hpp>
10 #include <stdexcept>
11 #include <string>
13 
17 
18 namespace ackermann_controller {
19 
21  : open_loop_(false)
22  , command_struct_()
23  , steering_angle_instead_of_angular_speed_(true)
24  , wheelbase_(1.0)
25  , cmd_vel_timeout_(0.5)
26  , velocity_rolling_window_size_(10)
27  , odom_frame_id_("odom")
28  , base_frame_id_("base_link")
29  , base_link_("base_link")
30  , enable_odom_tf_(true)
31 {
32 }
33 
36  ros::NodeHandle& root_nh,
37  ros::NodeHandle& controller_nh)
38 {
42 
43  const std::string complete_ns = controller_nh.getNamespace();
44  std::size_t id = complete_ns.find_last_of("/");
45  name_ = complete_ns.substr(id + 1);
46 
47  if (!initParams(controller_nh))
48  {
49  return false;
50  }
51 
52  try
53  {
55 
56  std::vector<std::string>::const_iterator it;
57  std::vector<std::string> spinning_joint_names = getJointNames(controller_nh, "spinning_joints");
58  std::vector<std::string> odometry_joint_names = getJointNames(controller_nh, "odometry_joints");
59  std::vector<std::string> steering_joint_names = getJointNames(controller_nh, "steering_joints");
60 
61  for (it = spinning_joint_names.begin(); it != spinning_joint_names.end(); ++it)
62  {
63  ActuatedWheel wheel(*it, base_link_, urdf_model, vhw->getHandle(*it));
65  "Found spinning joint " << wheel.name_
66  << " with lateral deviation " << wheel.lateral_deviation_
67  << " and radius " << wheel.radius_);
68  spinning_joints_.push_back(wheel);
69  }
70 
71  for (it = odometry_joint_names.begin(); it != odometry_joint_names.end(); ++it)
72  {
73  Wheel wheel(*it, base_link_, urdf_model, shw->getHandle(*it));
75  "Found odometry joint " << wheel.name_
76  << " with lateral deviation " << wheel.lateral_deviation_
77  << " and radius " << wheel.radius_);
78  odometry_joints_.push_back(wheel);
79  }
80 
81  for (it = steering_joint_names.begin(); it != steering_joint_names.end(); ++it)
82  {
83  ActuatedJoint steering_joint(*it, base_link_, urdf_model, phw->getHandle(*it));
85  "Found steering joint " << steering_joint.name_
86  << " with lateral deviation " << steering_joint.lateral_deviation_);
87  steering_joints_.push_back(steering_joint);
88  }
89  }
90  catch(const std::runtime_error& e)
91  {
92  ROS_ERROR_STREAM_NAMED(name_, e.what());
93  return false;
94  }
95 
98 
99  setOdomPubFields(root_nh, controller_nh);
100 
101  sub_command_ = controller_nh.subscribe("cmd_vel", 1, &AckermannController::cmdVelCallback, this);
102 
103  return true;
104 }
105 
106 void AckermannController::update(const ros::Time& time, const ros::Duration& period)
107 {
108  updateOdometry(time, period);
109  moveRobot(time, period);
110 }
111 
113 {
114  if (open_loop_)
115  {
117  }
118  else
119  {
121  }
122 
124  {
126 
127  const geometry_msgs::Quaternion orientation(
129 
130  if (odom_pub_->trylock())
131  {
132  odom_pub_->msg_.header.stamp = time;
133  odom_pub_->msg_.pose.pose.position.x = odometry_.getX();
134  odom_pub_->msg_.pose.pose.position.y = odometry_.getY();
135  odom_pub_->msg_.pose.pose.orientation = orientation;
136  odom_pub_->msg_.twist.twist.linear.x = odometry_.getLinear();
137  odom_pub_->msg_.twist.twist.angular.z = odometry_.getAngular();
138  odom_pub_->unlockAndPublish();
139  }
140 
141  if (enable_odom_tf_ && tf_odom_pub_->trylock())
142  {
143  geometry_msgs::TransformStamped& odom_frame = tf_odom_pub_->msg_.transforms[0];
144  odom_frame.header.stamp = time;
145  odom_frame.transform.translation.x = odometry_.getX();
146  odom_frame.transform.translation.y = odometry_.getY();
147  odom_frame.transform.rotation = orientation;
148  tf_odom_pub_->unlockAndPublish();
149  }
150  }
151 }
152 
154 {
155  // Retrieve current velocity command and time step:
156  Commands curr_cmd = *(command_.readFromRT());
157  const double dt = (time - curr_cmd.stamp).toSec();
158 
159  // Brake if cmd_vel has timeout:
160  if (dt > cmd_vel_timeout_)
161  {
162  curr_cmd.lin = 0.0;
163  curr_cmd.ang = 0.0;
164  }
165 
166  // Limit velocities and accelerations:
167  const double cmd_dt(period.toSec());
168 
169  limiter_.limit(curr_cmd.lin, last0_cmd_.lin, last1_cmd_.lin, cmd_dt);
170 
172  last0_cmd_ = curr_cmd;
173 
174  double angle = 0.0;
175 
177  {
178  angle = curr_cmd.ang;
179  }
180  else
181  {
182  angle = curr_cmd.lin * tan(curr_cmd.ang) / wheelbase_;
183  }
184 
185  // Compute steering angles
186  for (std::vector<ActuatedJoint>::iterator it = steering_joints_.begin(); it != steering_joints_.end(); ++it)
187  {
188  double steering_angle = std::atan(wheelbase_ * std::tan(angle)/std::abs(wheelbase_ + it->lateral_deviation_ * std::tan(angle)));
189  it->setCommand(steering_angle);
190  }
191 
192  // Compute wheels velocities
193  for (std::vector<ActuatedWheel>::iterator it = spinning_joints_.begin(); it != spinning_joints_.end(); ++it)
194  {
195  double velocity = (curr_cmd.lin * (1.0 + 2.0 * it->lateral_deviation_ * std::tan(curr_cmd.ang) / (2.0 * wheelbase_))) / it->radius_;
196  it->setCommand(velocity);
197  }
198 }
199 
201 {
202  brake();
203 
204  // Register starting time used to keep fixed rate
206 
207  odometry_.init(time);
208 }
209 
211 {
212  brake();
213 }
214 
216 {
217  const double velocity = 0.0;
218 
219  std::vector<ActuatedWheel>::iterator it;
220  for (it = spinning_joints_.begin(); it != spinning_joints_.end(); ++it)
221  {
222  it->setCommand(velocity);
223  }
224 }
225 
226 void AckermannController::cmdVelCallback(const geometry_msgs::Twist& command)
227 {
228  if (isRunning())
229  {
230  command_struct_.ang = command.angular.z;
231  command_struct_.lin = command.linear.x;
233  command_.writeFromNonRT(command_struct_);
234 
236  name_,
237  "Added values to command. "
238  << "Ang: " << command_struct_.ang << ", "
239  << "Lin: " << command_struct_.lin << ", "
240  << "Stamp: " << command_struct_.stamp);
241  }
242  else
243  {
244  ROS_ERROR_NAMED(name_, "Can't accept new commands. Controller is not running.");
245  }
246 }
247 
249 {
250  // Odometry related:
251  double publish_rate;
252  controller_nh.param("publish_rate", publish_rate, 50.0);
253  ROS_INFO_STREAM_NAMED(name_, "Controller state will be published at "
254  << publish_rate << "Hz.");
255  publish_period_ = ros::Duration(1.0 / publish_rate);
256 
257  controller_nh.param("open_loop", open_loop_, open_loop_);
258  ROS_INFO_STREAM_NAMED(name_, "Open loop is " << (open_loop_?"enabled":"disabled"));
259 
260  controller_nh.param("wheelbase", wheelbase_, wheelbase_);
261  ROS_INFO_STREAM_NAMED(name_, "Wheelbase set to " << wheelbase_);
262 
263  controller_nh.param("velocity_rolling_window_size", velocity_rolling_window_size_, velocity_rolling_window_size_);
265  name_, "Velocity rolling window size of " << velocity_rolling_window_size_ << ".");
266 
267  // Twist command related:
268  controller_nh.param("cmd_vel_timeout", cmd_vel_timeout_, cmd_vel_timeout_);
269  ROS_INFO_STREAM_NAMED(name_, "Velocity commands will be considered old if they are older than "
270  << cmd_vel_timeout_ << "s.");
271 
272  controller_nh.param("odom_frame_id", odom_frame_id_, odom_frame_id_);
273  ROS_INFO_STREAM_NAMED(name_, "Odom frame_id set to " << odom_frame_id_);
274 
275  controller_nh.param("base_frame_id", base_frame_id_, base_frame_id_);
276  ROS_INFO_STREAM_NAMED(name_, "Base frame_id set to " << base_frame_id_);
277 
278  controller_nh.param("base_model_link", base_link_, base_link_);
279  ROS_INFO_STREAM_NAMED(name_, "Base base_model_link set to " << base_link_);
280 
281  controller_nh.param("enable_odom_tf", enable_odom_tf_, enable_odom_tf_);
282  ROS_INFO_STREAM_NAMED(name_, "Publishing to tf is " << (enable_odom_tf_?"enabled":"disabled"));
283 
284  controller_nh.param("steering_angle_instead_of_angular_speed", steering_angle_instead_of_angular_speed_, steering_angle_instead_of_angular_speed_);
285  ROS_INFO_STREAM_NAMED(name_, "Steering angle instead of angular speed is " << (steering_angle_instead_of_angular_speed_?"enabled":"disabled"));
286 
287  // Velocity and acceleration limits:
288  controller_nh.param("has_velocity_limits" , limiter_.has_velocity_limits , limiter_.has_velocity_limits );
289  controller_nh.param("has_acceleration_limits", limiter_.has_acceleration_limits, limiter_.has_acceleration_limits);
290  controller_nh.param("has_deceleration_limits", limiter_.has_deceleration_limits, limiter_.has_deceleration_limits);
291  controller_nh.param("has_jerk_limits" , limiter_.has_jerk_limits , limiter_.has_jerk_limits );
292  controller_nh.param("max_velocity" , limiter_.max_velocity , limiter_.max_velocity );
293  controller_nh.param("min_velocity" , limiter_.min_velocity , -limiter_.max_velocity );
294  controller_nh.param("max_acceleration" , limiter_.max_acceleration , limiter_.max_acceleration );
295  controller_nh.param("min_acceleration" , limiter_.min_acceleration , -limiter_.max_acceleration );
296  controller_nh.param("max_deceleration" , limiter_.max_deceleration , limiter_.max_deceleration );
297  controller_nh.param("min_deceleration" , limiter_.min_deceleration , -limiter_.max_deceleration );
298  controller_nh.param("max_jerk" , limiter_.max_jerk , limiter_.max_jerk );
299  controller_nh.param("min_jerk" , limiter_.min_jerk , -limiter_.max_jerk );
300 
301  return true;
302 }
303 
305 {
306  const std::string model_param_name = "robot_description";
307  std::string robot_model_str = "";
308 
309  if (!nh.hasParam(model_param_name) || !nh.getParam(model_param_name, robot_model_str))
310  {
311  throw std::runtime_error("Robot description couldn't be retrieved from param server.");
312  }
313 
314  boost::shared_ptr<urdf::ModelInterface> model(urdf::parseURDF(robot_model_str));
315 
316  return model;
317 }
318 
319 std::vector<std::string> AckermannController::getJointNames(
320  ros::NodeHandle& controller_nh,
321  const std::string& param)
322 {
323  std::vector<std::string> names;
324 
325  XmlRpc::XmlRpcValue wheel_list;
326  if (!controller_nh.getParam(param, wheel_list))
327  {
328  throw std::runtime_error(std::string("Couldn't retrieve param '") + param + "'.");
329  }
330 
331  if (wheel_list.getType() == XmlRpc::XmlRpcValue::TypeArray)
332  {
333  if (wheel_list.size() == 0)
334  {
335  throw std::runtime_error(param + " is an empty list");
336  }
337 
338  for (int i = 0; i < wheel_list.size(); ++i)
339  {
340  if (wheel_list[i].getType() != XmlRpc::XmlRpcValue::TypeString)
341  {
342  throw std::runtime_error(param + " child isn't a string.");
343  }
344  }
345 
346  names.resize(wheel_list.size());
347  for (int i = 0; i < wheel_list.size(); ++i)
348  {
349  names[i] = static_cast<std::string>(wheel_list[i]);
350  }
351  }
352  else if (wheel_list.getType() == XmlRpc::XmlRpcValue::TypeString)
353  {
354  names.push_back(wheel_list);
355  }
356  else
357  {
358  throw std::runtime_error(param + " is neither a list of strings nor a string.");
359  }
360 
361  return names;
362 }
363 
365 {
366  // Get and check params for covariances
367  XmlRpc::XmlRpcValue pose_cov_list;
368  controller_nh.getParam("pose_covariance_diagonal", pose_cov_list);
370  ROS_ASSERT(pose_cov_list.size() == 6);
371  for (int i = 0; i < pose_cov_list.size(); ++i)
372  ROS_ASSERT(pose_cov_list[i].getType() == XmlRpc::XmlRpcValue::TypeDouble);
373 
374  XmlRpc::XmlRpcValue twist_cov_list;
375  controller_nh.getParam("twist_covariance_diagonal", twist_cov_list);
377  ROS_ASSERT(twist_cov_list.size() == 6);
378  for (int i = 0; i < twist_cov_list.size(); ++i)
379  ROS_ASSERT(twist_cov_list[i].getType() == XmlRpc::XmlRpcValue::TypeDouble);
380 
381  // Setup odometry realtime publisher + odom message constant fields
382  odom_pub_.reset(new realtime_tools::RealtimePublisher<nav_msgs::Odometry>(controller_nh, "odom", 100));
383  odom_pub_->msg_.header.frame_id = odom_frame_id_;
384  odom_pub_->msg_.child_frame_id = base_frame_id_;
385  odom_pub_->msg_.pose.pose.position.z = 0;
386  odom_pub_->msg_.pose.covariance = boost::assign::list_of
387  (static_cast<double>(pose_cov_list[0])) (0) (0) (0) (0) (0)
388  (0) (static_cast<double>(pose_cov_list[1])) (0) (0) (0) (0)
389  (0) (0) (static_cast<double>(pose_cov_list[2])) (0) (0) (0)
390  (0) (0) (0) (static_cast<double>(pose_cov_list[3])) (0) (0)
391  (0) (0) (0) (0) (static_cast<double>(pose_cov_list[4])) (0)
392  (0) (0) (0) (0) (0) (static_cast<double>(pose_cov_list[5]));
393  odom_pub_->msg_.twist.twist.linear.y = 0;
394  odom_pub_->msg_.twist.twist.linear.z = 0;
395  odom_pub_->msg_.twist.twist.angular.x = 0;
396  odom_pub_->msg_.twist.twist.angular.y = 0;
397  odom_pub_->msg_.twist.covariance = boost::assign::list_of
398  (static_cast<double>(twist_cov_list[0])) (0) (0) (0) (0) (0)
399  (0) (static_cast<double>(twist_cov_list[1])) (0) (0) (0) (0)
400  (0) (0) (static_cast<double>(twist_cov_list[2])) (0) (0) (0)
401  (0) (0) (0) (static_cast<double>(twist_cov_list[3])) (0) (0)
402  (0) (0) (0) (0) (static_cast<double>(twist_cov_list[4])) (0)
403  (0) (0) (0) (0) (0) (static_cast<double>(twist_cov_list[5]));
404  tf_odom_pub_.reset(new realtime_tools::RealtimePublisher<tf::tfMessage>(root_nh, "/tf", 100));
405  tf_odom_pub_->msg_.transforms.resize(1);
406  tf_odom_pub_->msg_.transforms[0].transform.translation.z = 0.0;
407  tf_odom_pub_->msg_.transforms[0].child_frame_id = base_frame_id_;
408  tf_odom_pub_->msg_.transforms[0].header.frame_id = odom_frame_id_;
409 }
410 }
411 
double getY() const
y position getter
Definition: odometry.h:88
void stopping(const ros::Time &)
Stops controller.
bool update(const std::vector< ActuatedJoint > &steering_joints, const std::vector< Wheel > &odometry_joints, const ros::Time &time)
Updates the odometry class with latest wheels position.
Definition: odometry.cpp:39
#define ROS_DEBUG_STREAM_NAMED(name, args)
void updateOdometry(const ros::Time &time, const ros::Duration &period)
#define ROS_ERROR_STREAM_NAMED(name, args)
void setWheelbase(double wheelbase)
Definition: odometry.h:111
std::vector< ActuatedJoint > steering_joints_
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
std::vector< ActuatedWheel > spinning_joints_
boost::shared_ptr< realtime_tools::RealtimePublisher< nav_msgs::Odometry > > odom_pub_
int size() const
boost::shared_ptr< realtime_tools::RealtimePublisher< tf::tfMessage > > tf_odom_pub_
void cmdVelCallback(const geometry_msgs::Twist &command)
#define ROS_INFO_STREAM_NAMED(name, args)
bool initParams(ros::NodeHandle &controller_nh)
void setOdomPubFields(ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
Type const & getType() const
void starting(const ros::Time &time)
Starts controller.
void update(const ros::Time &time, const ros::Duration &period)
Updates controller, i.e. computes the odometry and sets the new velocity commands.
ackermann_controller::SpeedLimiter limiter_
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
void setVelocityRollingWindowSize(size_t velocity_rolling_window_size)
Velocity rolling window size setter.
Definition: odometry.cpp:110
double limit(double &v, double v0, double v1, double dt)
Limit the velocity and acceleration.
boost::shared_ptr< urdf::ModelInterface > getURDFModel(const ros::NodeHandle &nh)
bool init(hardware_interface::RobotHW *hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
Initialize controller.
double getHeading() const
heading getter
Definition: odometry.h:70
bool param(const std::string &param_name, T &param_val, const T &default_val) const
const std::string & getNamespace() const
void updateOpenLoop(double linear, double angular, const ros::Time &time)
Updates the odometry class with latest velocity command.
Definition: odometry.cpp:98
realtime_tools::RealtimeBuffer< Commands > command_
void moveRobot(const ros::Time &time, const ros::Duration &period)
double getLinear() const
linear velocity getter
Definition: odometry.h:97
JointHandle getHandle(const std::string &name)
static geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw)
ackermann_controller::Odometry odometry_
bool getParam(const std::string &key, std::string &s) const
double getX() const
x position getter
Definition: odometry.h:79
#define ROS_ERROR_NAMED(name,...)
static Time now()
void init(const ros::Time &time)
Initialize the odometry.
Definition: odometry.cpp:32
bool hasParam(const std::string &key) const
#define ROS_ASSERT(cond)
std::vector< std::string > getJointNames(ros::NodeHandle &controller_nh, const std::string &param)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
double getAngular() const
angular velocity getter
Definition: odometry.h:106


ackermann_controller
Author(s): GĂ©rald Lelong
autogenerated on Mon Jun 10 2019 12:44:48