double_diff_drive_controller.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, PAL Robotics, S.L.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the PAL Robotics nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /*
36  * Author: Bence Magyar
37  */
38 
39 #include <tf/transform_datatypes.h>
40 
41 #include <urdf_parser/urdf_parser.h>
42 
43 #include <boost/assign.hpp>
44 
46 
47 
49 {
50 
53  : open_loop_(false)
54  , command_struct_()
55  , wheel_radius_(0)
56  , wheel_separation_(0)
57  , drive_motor_gear_ratio_(0)
58  , steer_motor_gear_ratio_(0)
59  , cmd_vel_timeout_(0.5)
60  , base_frame_id_("base_link")
61  , enable_odom_tf_(true)
62  , wheel_joints_size_(0)
63 {
64 }
65 
68  ros::NodeHandle& root_nh,
69  ros::NodeHandle& controller_nh)
70 {
71  const std::string complete_ns = controller_nh.getNamespace();
72  std::size_t id = complete_ns.find_last_of("/");
73  name_ = complete_ns.substr(id + 1);
74 
75  // Get joint names from the parameter server
76  std::string drive_motor_name;
77  controller_nh.param("drive_motor_joint", drive_motor_name, drive_motor_name);
78  ROS_INFO_STREAM_NAMED(name_, "Drive motor joint (drive_motor) is : " << drive_motor_name);
79 
80  std::string steer_motor_name;
81  controller_nh.param("steer_motor_joint", steer_motor_name, steer_motor_name);
82  ROS_INFO_STREAM_NAMED(name_, "Steer motor joint (steer_motor) is : " << steer_motor_name);
83 
84  // Odometry related:
85  double publish_rate;
86  controller_nh.param("publish_rate", publish_rate, 20.0);
87  ROS_INFO_STREAM_NAMED(name_, "Controller state will be published at "
88  << publish_rate << "Hz.");
89  publish_period_ = ros::Duration(1.0 / publish_rate);
90 
91  controller_nh.param("open_loop", open_loop_, open_loop_);
92 
93  // Twist command related:
94  controller_nh.param("cmd_vel_timeout", cmd_vel_timeout_, cmd_vel_timeout_);
95  ROS_INFO_STREAM_NAMED(name_, "Velocity commands will be considered old if they are older than "
96  << cmd_vel_timeout_ << "s.");
97 
98  controller_nh.param("base_frame_id", base_frame_id_, base_frame_id_);
99  ROS_INFO_STREAM_NAMED(name_, "Base frame_id set to " << base_frame_id_);
100 
101  controller_nh.param("enable_odom_tf", enable_odom_tf_, enable_odom_tf_);
102  ROS_INFO_STREAM_NAMED(name_, "Publishing to tf is " << (enable_odom_tf_?"enabled":"disabled"));
103 
104  // Velocity and acceleration limits:
105  controller_nh.param("linear/x/has_velocity_limits" , limiter_lin_.has_velocity_limits , limiter_lin_.has_velocity_limits );
106  controller_nh.param("linear/x/has_acceleration_limits", limiter_lin_.has_acceleration_limits, limiter_lin_.has_acceleration_limits);
107  controller_nh.param("linear/x/max_velocity" , limiter_lin_.max_velocity , limiter_lin_.max_velocity );
108  controller_nh.param("linear/x/min_velocity" , limiter_lin_.min_velocity , -limiter_lin_.max_velocity );
109  controller_nh.param("linear/x/max_acceleration" , limiter_lin_.max_acceleration , limiter_lin_.max_acceleration );
110  controller_nh.param("linear/x/min_acceleration" , limiter_lin_.min_acceleration , -limiter_lin_.max_acceleration );
111 
112  controller_nh.param("angular/z/has_velocity_limits" , limiter_ang_.has_velocity_limits , limiter_ang_.has_velocity_limits );
113  controller_nh.param("angular/z/has_acceleration_limits", limiter_ang_.has_acceleration_limits, limiter_ang_.has_acceleration_limits);
114  controller_nh.param("angular/z/max_velocity" , limiter_ang_.max_velocity , limiter_ang_.max_velocity );
115  controller_nh.param("angular/z/min_velocity" , limiter_ang_.min_velocity , -limiter_ang_.max_velocity );
116  controller_nh.param("angular/z/max_acceleration" , limiter_ang_.max_acceleration , limiter_ang_.max_acceleration );
117  controller_nh.param("angular/z/min_acceleration" , limiter_ang_.min_acceleration , -limiter_ang_.max_acceleration );
118 
119  // Get the joint objects to use in the realtime loop
120  drive_motor_input_ = hw->getHandle(drive_motor_name); // throws on failure
121  steer_motor_input_ = hw->getHandle(steer_motor_name); // throws on failure
122 
123  // Pass params through and setup publishers and subscribers
124  if (!setWheelParamsFromUrdf(root_nh, controller_nh, drive_motor_name, steer_motor_name))
125  {
126  return false;
127  }
128 
129  setupRtPublishersMsg(root_nh, controller_nh);
130 
131  sub_command_ = controller_nh.subscribe("cmd_vel", 1, &DoubleDiffDriveController::cmdVelCallback, this);
132 
133  return true;
134 }
135 
136 
139 {
140  // COMPUTE AND PUBLISH ODOMETRY
141  if (open_loop_)
142  {
144  }
145  else
146  {
147  double drive_vel = drive_motor_input_.getVelocity();
148  double steer_vel = steer_motor_input_.getVelocity();
149  if (std::isnan(drive_vel) || std::isnan(steer_vel))
150  return;
151 
152  // Estimate twist (using joint information) and integrate
153  odometry_.update(drive_vel, steer_vel, time);
154  }
155 
156  // Publish odometry message
158  {
160  // Compute and store orientation info
161  const geometry_msgs::Quaternion orientation(
163 
164  // Populate odom message and publish
165  if(odom_pub_->trylock())
166  {
167  odom_pub_->msg_.header.stamp = time;
168  odom_pub_->msg_.pose.pose.position.x = odometry_.getX();
169  odom_pub_->msg_.pose.pose.position.y = odometry_.getY();
170  odom_pub_->msg_.pose.pose.orientation = orientation;
171  odom_pub_->msg_.twist.twist.linear.x = odometry_.getLinear();
172  odom_pub_->msg_.twist.twist.angular.z = odometry_.getAngular();
173  odom_pub_->unlockAndPublish();
174  }
175 
176  // Publish tf /odom frame
177  if (enable_odom_tf_ && tf_odom_pub_->trylock())
178  {
179  geometry_msgs::TransformStamped& odom_frame = tf_odom_pub_->msg_.transforms[0];
180  odom_frame.header.stamp = time;
181  odom_frame.transform.translation.x = odometry_.getX();
182  odom_frame.transform.translation.y = odometry_.getY();
183  odom_frame.transform.rotation = orientation;
184  tf_odom_pub_->unlockAndPublish();
185  }
186  }
187 
188  // MOVE ROBOT
189  // Retrieve current velocity command and time step:
190  Commands curr_cmd = *(command_.readFromRT());
191  const double dt = (time - curr_cmd.stamp).toSec();
192 
193  // Brake if cmd_vel has timeout:
194  if (dt > cmd_vel_timeout_)
195  {
196  curr_cmd.lin = 0.0;
197  curr_cmd.ang = 0.0;
198  }
199 
200  // Limit velocities and accelerations:
201  const double cmd_dt(period.toSec());
202  limiter_lin_.limit(curr_cmd.lin, last_cmd_.lin, cmd_dt);
203  limiter_ang_.limit(curr_cmd.ang, last_cmd_.ang, cmd_dt);
204  last_cmd_ = curr_cmd;
205 
206  // Compute wheels velocities (this is the actual ik):
207  // NOTE: the input desired twist (from topic /cmd_vel) is a body twist.
208  const double drive_vel = (1.0 / wheel_radius_) * (curr_cmd.lin * drive_motor_gear_ratio_);
209  const double steer_vel = (1.0 / wheel_radius_) * (2.0 / wheel_separation_)
210  * (curr_cmd.ang * steer_motor_gear_ratio_);
211 
212  // Set wheels velocities:
213  drive_motor_input_.setCommand(drive_vel);
214  steer_motor_input_.setCommand(steer_vel);
215 }
216 
219 {
220  brake();
221 
222  // Register starting time used to keep fixed rate
224 
225  odometry_.init(time);
226 }
227 
230 {
231  brake();
232 }
233 
236 {
239 }
240 
242 void DoubleDiffDriveController::cmdVelCallback(const geometry_msgs::Twist& command)
243 {
244  if(isRunning())
245  {
246  command_struct_.ang = command.angular.z;
247  command_struct_.lin = command.linear.x;
249  command_.writeFromNonRT (command_struct_);
251  "Added values to command. "
252  << "Ang: " << command_struct_.ang << ", "
253  << "Lin: " << command_struct_.lin << ", "
254  << "Stamp: " << command_struct_.stamp);
255  }
256  else
257  {
258  ROS_ERROR_NAMED(name_, "Can't accept new commands. Controller is not running.");
259  }
260 }
261 
264  ros::NodeHandle &controller_nh,
265  const std::string& drive_motor_name,
266  const std::string& steer_motor_name)
267 {
268  bool has_wheel_separation = controller_nh.getParam("wheel_separation", wheel_separation_);
269  if (!has_wheel_separation)
270  {
271  ROS_ERROR_STREAM_NAMED(name_, " wheel_separation couldn't be retrieved");
272  return false;
273  }
274 
275  bool has_wheel_radius = controller_nh.getParam("wheel_radius", wheel_radius_);
276  if (!has_wheel_radius)
277  {
278  ROS_ERROR_STREAM_NAMED(name_, " wheel_radius couldn't be retrieved");
279  return false;
280  }
281 
282  bool has_drive_motor_gear_ratio = controller_nh.getParam("drive_motor_gear_ratio", drive_motor_gear_ratio_);
283  if (!has_drive_motor_gear_ratio)
284  {
285  ROS_ERROR_STREAM_NAMED(name_, " drive_motor_gear_ratio couldn't be retrieved");
286  return false;
287  }
288 
289  bool has_steer_motor_gear_ratio = controller_nh.getParam("steer_motor_gear_ratio", steer_motor_gear_ratio_);
290  if (!has_steer_motor_gear_ratio)
291  {
292  ROS_ERROR_STREAM_NAMED(name_, " steer_motor_gear_ratio couldn't be retrieved");
293  return false;
294  }
295 
296  ROS_INFO_STREAM("Wheel radius: " << wheel_radius_);
297  ROS_INFO_STREAM("Wheel seperation: " << wheel_separation_);
298  ROS_INFO_STREAM("Drive motor gear ratio: " << drive_motor_gear_ratio_);
299  ROS_INFO_STREAM("Steer motor gear ratio: " << steer_motor_gear_ratio_);
300 
301  // Set wheel params for the odometry computation
304 
305  return true;
306 }
307 
310 {
311  // Get covariance parameters for odometry.
312  XmlRpc::XmlRpcValue pose_cov_list;
313  controller_nh.getParam("pose_covariance_diagonal", pose_cov_list);
315  ROS_ASSERT(pose_cov_list.size() == 6);
316  for (int i = 0; i < pose_cov_list.size(); ++i)
317  ROS_ASSERT(pose_cov_list[i].getType() == XmlRpc::XmlRpcValue::TypeDouble);
318 
319  XmlRpc::XmlRpcValue twist_cov_list;
320  controller_nh.getParam("twist_covariance_diagonal", twist_cov_list);
322  ROS_ASSERT(twist_cov_list.size() == 6);
323  for (int i = 0; i < twist_cov_list.size(); ++i)
324  ROS_ASSERT(twist_cov_list[i].getType() == XmlRpc::XmlRpcValue::TypeDouble);
325 
326  // Setup odometry msg.
327  odom_pub_.reset(new realtime_tools::RealtimePublisher<nav_msgs::Odometry>(controller_nh, "odom", 100));
328  odom_pub_->msg_.header.frame_id = "odom";
329  odom_pub_->msg_.child_frame_id = base_frame_id_;
330  odom_pub_->msg_.pose.pose.position.z = 0;
331  odom_pub_->msg_.pose.covariance = boost::assign::list_of
332  (static_cast<double>(pose_cov_list[0])) (0) (0) (0) (0) (0)
333  (0) (static_cast<double>(pose_cov_list[1])) (0) (0) (0) (0)
334  (0) (0) (static_cast<double>(pose_cov_list[2])) (0) (0) (0)
335  (0) (0) (0) (static_cast<double>(pose_cov_list[3])) (0) (0)
336  (0) (0) (0) (0) (static_cast<double>(pose_cov_list[4])) (0)
337  (0) (0) (0) (0) (0) (static_cast<double>(pose_cov_list[5]));
338  odom_pub_->msg_.twist.twist.linear.y = 0;
339  odom_pub_->msg_.twist.twist.linear.z = 0;
340  odom_pub_->msg_.twist.twist.angular.x = 0;
341  odom_pub_->msg_.twist.twist.angular.y = 0;
342  odom_pub_->msg_.twist.covariance = boost::assign::list_of
343  (static_cast<double>(twist_cov_list[0])) (0) (0) (0) (0) (0)
344  (0) (static_cast<double>(twist_cov_list[1])) (0) (0) (0) (0)
345  (0) (0) (static_cast<double>(twist_cov_list[2])) (0) (0) (0)
346  (0) (0) (0) (static_cast<double>(twist_cov_list[3])) (0) (0)
347  (0) (0) (0) (0) (static_cast<double>(twist_cov_list[4])) (0)
348  (0) (0) (0) (0) (0) (static_cast<double>(twist_cov_list[5]));
349 
350  // Setup tf msg.
351  tf_odom_pub_.reset(new realtime_tools::RealtimePublisher<tf::tfMessage>(root_nh, "/tf", 100));
352  tf_odom_pub_->msg_.transforms.resize(1);
353  tf_odom_pub_->msg_.transforms[0].transform.translation.z = 0.0;
354  tf_odom_pub_->msg_.transforms[0].child_frame_id = base_frame_id_;
355  tf_odom_pub_->msg_.transforms[0].header.frame_id = "odom";
356 }
357 
358 } // namespace double_diff_drive_controller
double getAngular() const
angular velocity getter
Definition: odometry.h:138
boost::shared_ptr< realtime_tools::RealtimePublisher< nav_msgs::Odometry > > odom_pub_
Odometry related:
void limit(double &v, double v0, double dt)
Limit the velocity and acceleration.
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_ERROR_STREAM_NAMED(name, args)
hardware_interface::JointHandle drive_motor_input_
Hardware handles:
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
int size() const
void setGearRatios(double drive_motor_gear_ratio, double steer_motor_gear_ratio)
Sets the gear ratio parameters: gear ratio.
Definition: odometry.cpp:121
void cmdVelCallback(const geometry_msgs::Twist &command)
Velocity command callback.
void update(const ros::Time &time, const ros::Duration &period)
Updates controller, i.e. computes the odometry and sets the new velocity commands.
#define ROS_INFO_STREAM_NAMED(name, args)
Type const & getType() const
bool init(hardware_interface::VelocityJointInterface *hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
Initialize controller.
double getHeading() const
heading getter
Definition: odometry.h:102
double wheel_radius_
Wheel radius (assuming it&#39;s the same for the left and right wheels):
bool enable_odom_tf_
Whether to publish odometry to tf or not:
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void updateOpenLoop(double linear, double angular, const ros::Time &time)
Updates the odometry class with latest velocity command.
Definition: odometry.cpp:101
const std::string & getNamespace() const
double getY() const
y position getter
Definition: odometry.h:120
std::string base_frame_id_
Frame to use for the robot base:
JointHandle getHandle(const std::string &name)
void setCommand(double command)
static geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw)
boost::shared_ptr< realtime_tools::RealtimePublisher< tf::tfMessage > > tf_odom_pub_
void init(const ros::Time &time)
Initialize the odometry.
Definition: odometry.cpp:71
#define ROS_INFO_STREAM(args)
void brake()
Brakes the wheels, i.e. sets the velocity to 0.
void setupRtPublishersMsg(ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
Sets the odometry publishing fields.
bool getParam(const std::string &key, std::string &s) const
#define ROS_ERROR_NAMED(name,...)
double wheel_separation_
Wheel separation, wrt the midpoint of the wheel width:
static Time now()
void setWheelsParams(double wheel_radius, double wheel_separation)
Sets the wheels parameters: radius and seperation.
Definition: odometry.cpp:114
bool update(double drive_motor_vel, double steer_motor_vel, const ros::Time &time)
Updates the odometry class with latest wheels position.
Definition: odometry.cpp:82
#define ROS_ASSERT(cond)
double getLinear() const
linear velocity getter
Definition: odometry.h:129
bool setWheelParamsFromUrdf(ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh, const std::string &drive_motor_name, const std::string &steer_motor_name)
Sets odometry parameters from the URDF, i.e. the wheel radius and separation.
double cmd_vel_timeout_
Timeout to consider cmd_vel commands old:
double getX() const
x position getter
Definition: odometry.h:111


moose_control
Author(s): Tony Baltovski
autogenerated on Wed Mar 10 2021 03:43:55