gazebo_ros_diffdrive_uos.cpp
Go to the documentation of this file.
2 #include <nav_msgs/Odometry.h>
3 #include <geometry_msgs/Twist.h>
4 
5 #include <ros/time.h>
6 
8 
9 using namespace gazebo;
10 
12 
14  wheel_speed_right_(0.0),
15  wheel_speed_left_(0.0)
16 {
17  this->spinner_thread_ = new boost::thread( boost::bind( &GazeboRosDiffdrive::spin, this) );
18 }
19 
21 {
22  rosnode_->shutdown();
23  this->spinner_thread_->join();
24  delete this->spinner_thread_;
25  delete rosnode_;
26 }
27 
28 void GazeboRosDiffdrive::Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf )
29 {
30  this->my_world_ = _parent->GetWorld();
31 
32  this->my_parent_ = _parent;
33  if (!this->my_parent_)
34  {
35  ROS_FATAL("Gazebo_ROS_Create controller requires a Model as its parent");
36  return;
37  }
38 
39 
40  // this MUST be called 'robotNamespace' for proper remapping to work
41  // TODO: could be 'node_namespace' now
42  this->node_namespace_ = "/";
43  if (_sdf->HasElement("robotNamespace"))
44  this->node_namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>() + "/";
45 
46 
47  cmd_vel_topic_name_ = "/cmd_vel";
48  if (_sdf->HasElement("cmd_vel_topic_name"))
49  cmd_vel_topic_name_ = _sdf->GetElement("cmd_vel_topic_name")->Get<std::string>();
50 
51  odom_topic_name_ = "/odom";
52  if (_sdf->HasElement("odom_topic_name"))
53  odom_topic_name_ = _sdf->GetElement("odom_topic_name")->Get<std::string>();
54 
55  joint_states_topic_name_ = "/joint_states";
56  if (_sdf->HasElement("joint_states_topic_name"))
57  joint_states_topic_name_ = _sdf->GetElement("joint_states_topic_name")->Get<std::string>();
58 
59  if (!ros::isInitialized())
60  {
61  int argc = 0;
62  char** argv = NULL;
63  ros::init(argc, argv, "gazebo_ros_diffdrive_uos", ros::init_options::NoSigintHandler|ros::init_options::AnonymousName);
64  }
65 
67 
68  cmd_vel_sub_ = rosnode_->subscribe(cmd_vel_topic_name_, 1, &GazeboRosDiffdrive::OnCmdVel, this);
69  odom_pub_ = rosnode_->advertise<nav_msgs::Odometry> (odom_topic_name_, 1);
70  joint_state_pub_ = rosnode_->advertise<sensor_msgs::JointState> (joint_states_topic_name_, 1);
71 
72  enum
73  {
74  LF = 0, // left front wheel
75  LM = 1, // left middle wheel
76  LR = 2, // left rear wheel
77  RF = 3, // right front wheel
78  RM = 4, // right middle wheel
79  RR = 5 // right rear wheel
80  };
81 
82  std::vector<std::string> joint_names(6);
83 
84  joint_names[LF] = "left_front_wheel_joint";
85  joint_names[LM] = "left_middle_wheel_joint";
86  joint_names[LR] = "left_rear_wheel_joint";
87  joint_names[RF] = "right_front_wheel_joint",
88  joint_names[RM] = "right_middle_wheel_joint";
89  joint_names[RR] = "right_rear_wheel_joint";
90 
91  std::vector<physics::JointPtr> joints_tmp(6);
92 
93  for(size_t i=0; i<joint_names.size(); i++)
94  {
95  // overwrite names, if configured in sdf
96  if(_sdf->HasElement(joint_names[i]))
97  {
98  joint_names[i] = _sdf->GetElement(joint_names[i])->Get<std::string>();
99  }
100 
101  // get all joint objects, some of them might not exist in the model
102  joints_tmp[i] = my_parent_->GetJoint(joint_names[i]);
103  }
104 
105  //check for availability
106 
107  std::vector<int> missing_joints;
108 
109  // left front, rear and right front and rear are required for a four wheel diffdrive robot
110  if(!joints_tmp[LF]) missing_joints.push_back(LF);
111  if(!joints_tmp[LR]) missing_joints.push_back(LR);
112  if(!joints_tmp[RF]) missing_joints.push_back(RF);
113  if(!joints_tmp[RR]) missing_joints.push_back(RR);
114 
115  // if one of the middle joints is available than the other should be available, too.
116  if( !joints_tmp[LM] != !joints_tmp[RM] )
117  {
118  missing_joints.push_back(LM);
119  missing_joints.push_back(RM);
120  }
121 
122  if(!missing_joints.empty())
123  {
124  // build helpful error message with the missing joints
125  std::string missing_err = "The controller couldn't get the joint(s): ";
126  for(size_t i=0; i<missing_joints.size(); i++)
127  {
128  missing_err += joint_names[missing_joints[i]] + (i != missing_joints.size()-1 ? ", " : "");
129  }
130  gzthrow("This plugin supports either a four or a six wheeled robot, due to that the middle wheels are optional. "
131  + missing_err);
132  }
133 
134  // Determine the number of joints, the type of robot, either four wheeled or six wheeled.
135  bool six_wheeled = joints_tmp[LM] && joints_tmp[RM];
136  num_joints_ = six_wheeled ? 6 : 4;
137 
138  int six_wheels[] = {LF, LM, LR, RF, RM, RR};
139  int four_wheels[] = {LF, LR, RF, RR};
140  int* wheels = six_wheeled ? six_wheels : four_wheels;
141 
142  if ( six_wheeled ) ROS_INFO_STREAM("The robot is six wheeled.");
143  else ROS_INFO_STREAM("The robot is four wheeled");
144 
145  left = six_wheeled ? 1 : 0;
146  right = six_wheeled ? 4 : 2;
147 
148  js_.name.resize(num_joints_);
149  js_.position.resize(num_joints_);
150  js_.velocity.resize(num_joints_);
151  js_.effort.resize(num_joints_);
152  joints_.resize(num_joints_);
153 
154  for (size_t i = 0; i < num_joints_; ++i)
155  {
156  js_.position[i] = 0;
157  js_.velocity[i] = 0;
158  js_.effort[i] = 0;
159  js_.name[i] = joint_names[wheels[i]];
160  joints_[i] = joints_tmp[wheels[i]];
161  }
162 
163  wheel_sep_ = 0.34;
164  if (_sdf->HasElement("wheel_separation"))
165  wheel_sep_ = _sdf->GetElement("wheel_separation")->Get<double>();
166 
167  turning_adaptation_ = 0.15;
168  if (_sdf->HasElement("turning_adaptation"))
169  turning_adaptation_ = _sdf->GetElement("turning_adaptation")->Get<double>();
170 
171  wheel_diam_ = 0.15;
172  if (_sdf->HasElement("wheel_diameter"))
173  wheel_diam_ = _sdf->GetElement("wheel_diameter")->Get<double>();
174 
175  torque_ = 4.0;
176  if (_sdf->HasElement("torque"))
177  torque_ = _sdf->GetElement("torque")->Get<double>();
178 
179  max_velocity_ = 4.0;
180  if (_sdf->HasElement("max_velocity"))
181  max_velocity_ = _sdf->GetElement("max_velocity")->Get<double>();
182 
183  //initialize time and odometry position
184 #if GAZEBO_MAJOR_VERSION > 8
185  prev_update_time_ = last_cmd_vel_time_ = this->my_world_->SimTime();
186 #else
187  prev_update_time_ = last_cmd_vel_time_ = this->my_world_->GetSimTime();
188 #endif
189  odom_pose_[0] = 0.0;
190  odom_pose_[1] = 0.0;
191  odom_pose_[2] = 0.0;
192 
193  // Get then name of the parent model
194  std::string modelName = _sdf->GetParent()->Get<std::string>("name");
195 
196  // Listen to the update event. This event is broadcast every
197  // simulation iteration.
198  this->updateConnection = event::Events::ConnectWorldUpdateBegin(
199  boost::bind(&GazeboRosDiffdrive::UpdateChild, this));
200  gzdbg << "plugin model name: " << modelName << "\n";
201 
202  ROS_INFO_STREAM("Plugin gazebo_ros_diffdrive_uos_uos initialized.");
203 }
204 
206 {
207 #if GAZEBO_MAJOR_VERSION > 8
208  common::Time time_now = this->my_world_->SimTime();
209 #else
210  common::Time time_now = this->my_world_->GetSimTime();
211 #endif
212  common::Time step_time = time_now - prev_update_time_;
213  prev_update_time_ = time_now;
214 
215  double wd, ws;
216  double d1, d2;
217  double dr, da;
218  double turning_adaptation;
219 
220  wd = wheel_diam_;
221  ws = wheel_sep_;
222  turning_adaptation = turning_adaptation_;
223 
224  d1 = d2 = 0;
225  dr = da = 0;
226 
227  // Distance travelled by middle (six wheeled robot) or by front wheels (four wheeled robot)
228  d1 = step_time.Double() * (wd / 2) * joints_[left]->GetVelocity(0);
229  d2 = step_time.Double() * (wd / 2) * joints_[right]->GetVelocity(0);
230 
231  // Can see NaN values here, just zero them out if needed
232  if (std::isnan(d1)) {
233  ROS_WARN_THROTTLE(0.1, "gazebo_ros_diffdrive_uos: NaN in d1. Step time: %.2f. WD: %.2f. Velocity: %.2f", step_time.Double(), wd, joints_[left]->GetVelocity(0));
234  d1 = 0;
235  }
236 
237  if (std::isnan(d2)) {
238  ROS_WARN_THROTTLE(0.1, "gazebo_ros_diffdrive_uos: NaN in d2. Step time: %.2f. WD: %.2f. Velocity: %.2f", step_time.Double(), wd, joints_[right]->GetVelocity(0));
239  d2 = 0;
240  }
241 
242  dr = (d1 + d2) / 2;
243  da = (d2 - d1) / ws * turning_adaptation;
244 
245  // Compute odometric pose
246  odom_pose_[0] += dr * cos(odom_pose_[2]);
247  odom_pose_[1] += dr * sin(odom_pose_[2]);
248  odom_pose_[2] += da;
249 
250  // Compute odometric instantaneous velocity
251  odom_vel_[0] = dr / step_time.Double();
252  odom_vel_[1] = 0.0;
253  odom_vel_[2] = da / step_time.Double();
254 
255 #if GAZEBO_MAJOR_VERSION > 8
256  if (this->my_world_->SimTime() > last_cmd_vel_time_ + common::Time(CMD_VEL_TIMEOUT))
257 #else
258  if (this->my_world_->GetSimTime() > last_cmd_vel_time_ + common::Time(CMD_VEL_TIMEOUT))
259 #endif
260  {
261 #if GAZEBO_MAJOR_VERSION > 8
262  ROS_DEBUG("gazebo_ros_diffdrive_uos: cmd_vel timeout - current: %f, last cmd_vel: %f, timeout: %f", this->my_world_->SimTime().Double(), last_cmd_vel_time_.Double(), common::Time(CMD_VEL_TIMEOUT).Double());
263 #else
264  ROS_DEBUG("gazebo_ros_diffdrive_uos: cmd_vel timeout - current: %f, last cmd_vel: %f, timeout: %f", this->my_world_->GetSimTime().Double(), last_cmd_vel_time_.Double(), common::Time(CMD_VEL_TIMEOUT).Double());
265 #endif
267  }
268 
269  ROS_DEBUG("gazebo_ros_diffdrive_uos: setting wheel speeds (left; %f, right: %f)", wheel_speed_left_ / (wd / 2.0), wheel_speed_right_ / (wd / 2.0));
270 
271  // turn left wheels
272  for (unsigned short i = 0; i < num_joints_/2; i++)
273  {
274  joints_[i]->SetVelocity(0, wheel_speed_left_ / (wd / 2.0));
275 #if GAZEBO_MAJOR_VERSION < 5
276  joints_[i]->SetMaxForce(0, torque_);
277 #endif
278  }
279 
280  // turn right wheels
281  for (unsigned short i = num_joints_/2; i < num_joints_; i++)
282  {
283  joints_[i]->SetVelocity(0, wheel_speed_right_ / (wd / 2.0));
284 #if GAZEBO_MAJOR_VERSION < 5
285  joints_[i]->SetMaxForce(0, torque_);
286 #endif
287  }
288 
289  nav_msgs::Odometry odom;
290  odom.header.stamp.sec = time_now.sec;
291  odom.header.stamp.nsec = time_now.nsec;
292  odom.header.frame_id = "odom_combined";
293  odom.child_frame_id = "base_footprint";
294  odom.pose.pose.position.x = odom_pose_[0];
295  odom.pose.pose.position.y = odom_pose_[1];
296  odom.pose.pose.position.z = 0;
297 
298  tf::Quaternion qt;
299  qt.setEuler(0, 0, odom_pose_[2]);
300 
301  odom.pose.pose.orientation.x = qt.getX();
302  odom.pose.pose.orientation.y = qt.getY();
303  odom.pose.pose.orientation.z = qt.getZ();
304  odom.pose.pose.orientation.w = qt.getW();
305 
306  double pose_cov[36] = { 1e-3, 0, 0, 0, 0, 0,
307  0, 1e-3, 0, 0, 0, 0,
308  0, 0, 1e6, 0, 0, 0,
309  0, 0, 0, 1e6, 0, 0,
310  0, 0, 0, 0, 1e6, 0,
311  0, 0, 0, 0, 0, 1e3};
312 
313  memcpy(&odom.pose.covariance[0], pose_cov, sizeof(double) * 36);
314  memcpy(&odom.twist.covariance[0], pose_cov, sizeof(double) * 36);
315 
316  odom.twist.twist.linear.x = odom_vel_[0];
317  odom.twist.twist.linear.y = 0;
318  odom.twist.twist.linear.z = 0;
319 
320  odom.twist.twist.angular.x = 0;
321  odom.twist.twist.angular.y = 0;
322  odom.twist.twist.angular.z = odom_vel_[2];
323 
324  odom_pub_.publish(odom);
325 
326  js_.header.stamp.sec = time_now.sec;
327  js_.header.stamp.nsec = time_now.nsec;
328 
329  for (size_t i = 0; i < num_joints_; ++i)
330  {
331 #if GAZEBO_MAJOR_VERSION > 8
332  js_.position[i] = joints_[i]->Position(0);
333 #else
334  js_.position[i] = joints_[i]->GetAngle(0).Radian();
335 #endif
336  js_.velocity[i] = joints_[i]->GetVelocity(0);
337  }
338 
340 }
341 
342 void GazeboRosDiffdrive::OnCmdVel(const geometry_msgs::TwistConstPtr &msg)
343 {
344 #if GAZEBO_MAJOR_VERSION > 8
345  last_cmd_vel_time_ = this->my_world_->SimTime();
346 #else
347  last_cmd_vel_time_ = this->my_world_->GetSimTime();
348 #endif
349  double vr, va;
350  vr = msg->linear.x;
351  va = msg->angular.z;
352 
353  wheel_speed_left_ = vr - va * wheel_sep_ / 2;
354  wheel_speed_right_ = vr + va * wheel_sep_ / 2;
355 
356  // limit wheel speed
357  if (fabs(wheel_speed_left_) > max_velocity_)
359  if (fabs(wheel_speed_right_) > max_velocity_)
361 }
362 
364 {
365  while(ros::ok()) ros::spinOnce();
366 }
367 
#define ROS_FATAL(...)
virtual void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
#define ROS_WARN_THROTTLE(rate,...)
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ROSCPP_DECL bool isInitialized()
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
float torque_
maximum torque applied to the wheels [Nm]
TFSIMD_FORCE_INLINE const tfScalar & getW() const
float wheel_speed_right_
Desired speeds of the wheels.
GZ_REGISTER_MODEL_PLUGIN(GazeboRosDiffdrive)
float wheel_sep_
Separation between the wheels.
event::ConnectionPtr updateConnection
float turning_adaptation_
Turning adaptation for odometry.
ROSCPP_DECL bool ok()
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void setEuler(const tfScalar &yaw, const tfScalar &pitch, const tfScalar &roll)
float wheel_diam_
Diameter of the wheels.
#define ROS_INFO_STREAM(args)
std::vector< physics::JointPtr > joints_
ROSCPP_DECL void spinOnce()
void OnCmdVel(const geometry_msgs::TwistConstPtr &msg)
float max_velocity_
maximum linear speed of the robot [m/s]
#define ROS_DEBUG(...)


diffdrive_gazebo_plugin
Author(s): Martin Guenther
autogenerated on Fri Oct 25 2019 03:36:16