gazebo_ros_kobuki_updates.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013, Yujin Robot.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of Yujin Robot nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
37 
38 
39 namespace gazebo {
40 
41 
43 {
44  /*
45  * Joint states
46  */
47  std::string baselink_frame = gazebo_ros_->resolveTF("base_link");
48  joint_state_.header.stamp = ros::Time::now();
49  joint_state_.header.frame_id = baselink_frame;
50 
51  #if GAZEBO_MAJOR_VERSION >= 9
52  joint_state_.position[LEFT] = joints_[LEFT]->Position(0);
53  joint_state_.position[RIGHT] = joints_[RIGHT]->Position(0);
54  #else
55  joint_state_.position[LEFT] = joints_[LEFT]->GetAngle(0).Radian();
56  joint_state_.position[RIGHT] = joints_[RIGHT]->GetAngle(0).Radian();
57  #endif
58 
59  joint_state_.velocity[LEFT] = joints_[LEFT]->GetVelocity(0);
60  joint_state_.velocity[RIGHT] = joints_[RIGHT]->GetVelocity(0);
61 
62 
64 }
65 
66 /*
67  * Odometry (encoders & IMU)
68  */
69 void GazeboRosKobuki::updateOdometry(common::Time& step_time)
70 {
71  std::string odom_frame = gazebo_ros_->resolveTF("odom");
72  std::string base_frame = gazebo_ros_->resolveTF("base_footprint");
73  odom_.header.stamp = joint_state_.header.stamp;
74  odom_.header.frame_id = odom_frame;
75  odom_.child_frame_id = base_frame;
76 
77  // Distance travelled by main wheels
78  double d1, d2;
79  double dr, da;
80  d1 = d2 = 0;
81  dr = da = 0;
82  d1 = step_time.Double() * (wheel_diam_ / 2) * joints_[LEFT]->GetVelocity(0);
83  d2 = step_time.Double() * (wheel_diam_ / 2) * joints_[RIGHT]->GetVelocity(0);
84  // Can see NaN values here, just zero them out if needed
85  if (std::isnan(d1))
86  {
87  ROS_WARN_STREAM_THROTTLE(0.1, "Gazebo ROS Kobuki plugin: NaN in d1. Step time: " << step_time.Double()
88  << ", WD: " << wheel_diam_ << ", velocity: " << joints_[LEFT]->GetVelocity(0));
89  d1 = 0;
90  }
91  if (std::isnan(d2))
92  {
93  ROS_WARN_STREAM_THROTTLE(0.1, "Gazebo ROS Kobuki plugin: NaN in d2. Step time: " << step_time.Double()
94  << ", WD: " << wheel_diam_ << ", velocity: " << joints_[RIGHT]->GetVelocity(0));
95  d2 = 0;
96  }
97  dr = (d1 + d2) / 2;
98  da = (d2 - d1) / wheel_sep_; // ignored
99 
100  // Just as in the Kobuki driver, the angular velocity is taken directly from the IMU
101  vel_angular_ = imu_->AngularVelocity();
102 
103  // Compute odometric pose
104  odom_pose_[0] += dr * cos( odom_pose_[2] );
105  odom_pose_[1] += dr * sin( odom_pose_[2] );
106 
107  #if GAZEBO_MAJOR_VERSION >= 9
108  odom_pose_[2] += vel_angular_.Z() * step_time.Double();
109  #else
110  odom_pose_[2] += vel_angular_.z * step_time.Double();
111  #endif
112 
113  // Compute odometric instantaneous velocity
114  odom_vel_[0] = dr / step_time.Double();
115  odom_vel_[1] = 0.0;
116 
117  #if GAZEBO_MAJOR_VERSION >= 9
118  odom_vel_[2] = vel_angular_.Z();
119 
120  #else
121  odom_vel_[2] = vel_angular_.z;
122  #endif
123 
124  odom_.pose.pose.position.x = odom_pose_[0];
125  odom_.pose.pose.position.y = odom_pose_[1];
126  odom_.pose.pose.position.z = 0;
127 
128  tf::Quaternion qt;
129  qt.setEuler(0,0,odom_pose_[2]);
130  odom_.pose.pose.orientation.x = qt.getX();
131  odom_.pose.pose.orientation.y = qt.getY();
132  odom_.pose.pose.orientation.z = qt.getZ();
133  odom_.pose.pose.orientation.w = qt.getW();
134 
135  odom_.pose.covariance[0] = 0.1;
136  odom_.pose.covariance[7] = 0.1;
137  odom_.pose.covariance[35] = 0.05;
138  odom_.pose.covariance[14] = 1e6;
139  odom_.pose.covariance[21] = 1e6;
140  odom_.pose.covariance[28] = 1e6;
141 
142  odom_.twist.twist.linear.x = odom_vel_[0];
143  odom_.twist.twist.linear.y = 0;
144  odom_.twist.twist.linear.z = 0;
145  odom_.twist.twist.angular.x = 0;
146  odom_.twist.twist.angular.y = 0;
147  odom_.twist.twist.angular.z = odom_vel_[2];
148  odom_pub_.publish(odom_); // publish odom message
149 
150  if (publish_tf_)
151  {
152  odom_tf_.header = odom_.header;
153  odom_tf_.child_frame_id = odom_.child_frame_id;
154  odom_tf_.transform.translation.x = odom_.pose.pose.position.x;
155  odom_tf_.transform.translation.y = odom_.pose.pose.position.y;
156  odom_tf_.transform.translation.z = odom_.pose.pose.position.z;
157  odom_tf_.transform.rotation = odom_.pose.pose.orientation;
159  }
160 }
161 
162 /*
163  * Publish IMU data
164  */
166 {
167  imu_msg_.header = joint_state_.header;
168 
169  #if GAZEBO_MAJOR_VERSION >= 9
170  ignition::math::Quaterniond quat = imu_->Orientation();
171  imu_msg_.orientation.x = quat.X();
172  imu_msg_.orientation.y = quat.Y();
173  imu_msg_.orientation.z = quat.Z();
174  imu_msg_.orientation.w = quat.W();
175  #else
176  math::Quaternion quat = imu_->Orientation();
177  imu_msg_.orientation.x = quat.x;
178  imu_msg_.orientation.y = quat.y;
179  imu_msg_.orientation.z = quat.z;
180  imu_msg_.orientation.w = quat.w;
181  #endif
182 
183 
184  imu_msg_.orientation_covariance[0] = 1e6;
185  imu_msg_.orientation_covariance[4] = 1e6;
186  imu_msg_.orientation_covariance[8] = 0.05;
187 
188  #if GAZEBO_MAJOR_VERSION >= 9
189  imu_msg_.angular_velocity.x = vel_angular_.X();
190  imu_msg_.angular_velocity.y = vel_angular_.Y();
191  imu_msg_.angular_velocity.z = vel_angular_.Z();
192  #else
193  imu_msg_.angular_velocity.x = vel_angular_.x;
194  imu_msg_.angular_velocity.y = vel_angular_.y;
195  imu_msg_.angular_velocity.z = vel_angular_.z;
196  #endif
197 
198 
199  imu_msg_.angular_velocity_covariance[0] = 1e6;
200  imu_msg_.angular_velocity_covariance[4] = 1e6;
201  imu_msg_.angular_velocity_covariance[8] = 0.05;
202 
203  #if GAZEBO_MAJOR_VERSION >= 9
204  ignition::math::Vector3d lin_acc = imu_->LinearAcceleration();
205  imu_msg_.linear_acceleration.x = lin_acc.X();
206  imu_msg_.linear_acceleration.y = lin_acc.Y();
207  imu_msg_.linear_acceleration.z = lin_acc.Z();
208  #else
209  math::Vector3 lin_acc = imu_->LinearAcceleration();
210  imu_msg_.linear_acceleration.x = lin_acc.x;
211  imu_msg_.linear_acceleration.y = lin_acc.y;
212  imu_msg_.linear_acceleration.z = lin_acc.z;
213  #endif
214 
215 
216  imu_pub_.publish(imu_msg_); // publish odom message
217 }
218 
219 /*
220  * Propagate velocity commands
221  * TODO: Check how to simulate disabled motors, e.g. set MaxForce to zero, but then damping is important!
222  */
224 {
226  {
227  wheel_speed_cmd_[LEFT] = 0.0;
228  wheel_speed_cmd_[RIGHT] = 0.0;
229  }
230  joints_[LEFT]->SetVelocity(0, wheel_speed_cmd_[LEFT] / (wheel_diam_ / 2.0));
231  joints_[RIGHT]->SetVelocity(0, wheel_speed_cmd_[RIGHT] / (wheel_diam_ / 2.0));
232 }
233 
234 /*
235  * Cliff sensors
236  * Check each sensor separately
237  */
239 {
240  // Left cliff sensor
241  if ((cliff_detected_left_ == false) &&
243  {
244  cliff_detected_left_ = true;
246  cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
247  // convert distance back to an AD reading
248  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_left_->Range(0)));
250  }
251  else if ((cliff_detected_left_ == true) &&
253  {
254  cliff_detected_left_ = false;
256  cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
257  // convert distance back to an AD reading
258  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_left_->Range(0)));
260  }
261  // Centre cliff sensor
262  if ((cliff_detected_center_ == false) &&
264  {
265  cliff_detected_center_ = true;
266  cliff_event_.sensor = kobuki_msgs::CliffEvent::CENTER;
267  cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
268  // convert distance back to an AD reading
269  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_center_->Range(0)));
271  }
272  else if ((cliff_detected_center_ == true) &&
274  {
275  cliff_detected_center_ = false;
276  cliff_event_.sensor = kobuki_msgs::CliffEvent::CENTER;
277  cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
278  // convert distance back to an AD reading
279  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_center_->Range(0)));
281  }
282  // Right cliff sensor
283  if ((cliff_detected_right_ == false) &&
285  {
286  cliff_detected_right_ = true;
288  cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
289  // convert distance back to an AD reading
290  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_right_->Range(0)));
292  }
293  else if ((cliff_detected_right_ == true) &&
295  {
296  cliff_detected_right_ = false;
298  cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
299  // convert distance back to an AD reading
300  cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_right_->Range(0)));
302  }
303 }
304 
305 /*
306  * Bumpers
307  */
308 // In order to simulate the three bumper sensors, a contact is assigned to one of the bumpers
309 // depending on its position. Each sensor covers a range of 60 degrees.
310 // +90 ... +30: left bumper
311 // +30 ... -30: centre bumper
312 // -30 ... -90: right bumper
314 {
315  // reset flags
316  bumper_left_is_pressed_ = false;
318  bumper_right_is_pressed_ = false;
319 
320  // parse contacts
321  msgs::Contacts contacts;
322  contacts = bumper_->Contacts();
323  double robot_heading;
324  #if GAZEBO_MAJOR_VERSION >= 9
325  ignition::math::Pose3d current_pose = model_->WorldPose();
326  robot_heading = current_pose.Rot().Yaw();
327  #else
328  math::Pose current_pose = model_->GetWorldPose();
329  robot_heading = current_pose.rot.GetYaw();
330  #endif
331 
332 
333  for (int i = 0; i < contacts.contact_size(); ++i)
334  {
335  double rel_contact_pos;
336  #if GAZEBO_MAJOR_VERSION >= 9
337  rel_contact_pos = contacts.contact(i).position(0).z() - current_pose.Pos().Z();
338  #else
339  rel_contact_pos = contacts.contact(i).position(0).z() - current_pose.pos.z;
340  #endif
341  // Actually, only contacts at the height of the bumper should be considered, but since we use a simplified collision model
342  // contacts further below and above need to be consider as well to identify "bumps" reliably.
343  if ((rel_contact_pos >= 0.01)
344  && (rel_contact_pos <= 0.13))
345  {
346  // using the force normals below, since the contact position is given in world coordinates
347  // also negating the normal, because it points from contact to robot centre
348  double global_contact_angle = std::atan2(-contacts.contact(i).normal(0).y(), -contacts.contact(i).normal(0).x());
349  double relative_contact_angle = global_contact_angle - robot_heading;
350 
351  if ((relative_contact_angle <= (M_PI/2)) && (relative_contact_angle > (M_PI/6)))
352  {
354  }
355  else if ((relative_contact_angle <= (M_PI/6)) && (relative_contact_angle >= (-M_PI/6)))
356  {
358  }
359  else if ((relative_contact_angle < (-M_PI/6)) && (relative_contact_angle >= (-M_PI/2)))
360  {
362  }
363  }
364  }
365 
366  // check for bumper state change
368  {
370  bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
373  }
375  {
376  bumper_left_was_pressed_ = false;
377  bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
380  }
382  {
384  bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
385  bumper_event_.bumper = kobuki_msgs::BumperEvent::CENTER;
387  }
389  {
391  bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
392  bumper_event_.bumper = kobuki_msgs::BumperEvent::CENTER;
394  }
396  {
398  bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
401  }
403  {
405  bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
408  }
409 }
410 }
bool cliff_detected_right_
Cliff flag for the right sensor.
double odom_pose_[3]
Vector for pose.
void publish(const boost::shared_ptr< M > &message) const
GazeboRosPtr gazebo_ros_
pointer to the gazebo ros node
float cliff_detection_threshold_
measured distance in meter for detecting a cliff
f
double wheel_sep_
Separation between the wheels.
ros::Publisher imu_pub_
ROS publisher for IMU data.
sensors::RaySensorPtr cliff_sensor_center_
Pointer to frontal cliff sensor.
bool bumper_center_was_pressed_
Flag for center bumper&#39;s last state.
bool cliff_detected_left_
Cliff flag for the left sensor.
sensors::ContactSensorPtr bumper_
Pointer to bumper sensor simulating Kobuki&#39;s left, centre and right bumper sensors.
physics::JointPtr joints_[2]
Pointers to Gazebo&#39;s joints.
TFSIMD_FORCE_INLINE const tfScalar & getW() const
bool bumper_right_was_pressed_
Flag for right bumper&#39;s last state.
sensors::ImuSensorPtr imu_
Pointer to IMU sensor model.
sensor_msgs::Imu imu_msg_
ROS message for publishing IMU data.
common::Time last_cmd_vel_time_
Simulation time of the last velocity command (used for time out)
ros::Publisher joint_state_pub_
ROS publisher for joint state messages.
bool bumper_left_is_pressed_
Flag for left bumper&#39;s current state.
double wheel_diam_
Diameter of the wheels.
bool motors_enabled_
Flag indicating if the motors are turned on or not.
kobuki_msgs::BumperEvent bumper_event_
Kobuki ROS message for bumper event.
ros::Publisher bumper_event_pub_
ROS publisher for bumper events.
math::Vector3 vel_angular_
Storage for the angular velocity reported by the IMU.
physics::ModelPtr model_
pointer to the model
double wheel_speed_cmd_[2]
Speeds of the wheels.
void sendTransform(const StampedTransform &transform)
bool bumper_center_is_pressed_
Flag for left bumper&#39;s current state.
void setEuler(const tfScalar &yaw, const tfScalar &pitch, const tfScalar &roll)
#define ROS_WARN_STREAM_THROTTLE(rate, args)
ros::Publisher odom_pub_
ROS publisher for odometry messages.
double cmd_vel_timeout_
Time out for velocity commands in seconds.
double odom_vel_[3]
Vector for velocity.
bool cliff_detected_center_
Cliff flag for the center sensor.
bool bumper_left_was_pressed_
Flag for left bumper&#39;s last state.
static Time now()
geometry_msgs::TransformStamped odom_tf_
TF transform for the odom frame.
kobuki_msgs::CliffEvent cliff_event_
Kobuki ROS message for cliff event.
void updateOdometry(common::Time &step_time)
sensors::RaySensorPtr cliff_sensor_left_
Pointer to left cliff sensor.
common::Time prev_update_time_
Simulation time on previous update.
ros::Publisher cliff_event_pub_
ROS publisher for cliff detection events.
bool bumper_right_is_pressed_
Flag for left bumper&#39;s current state.
bool publish_tf_
Flag for (not) publish tf transform for odom -> robot.
sensor_msgs::JointState joint_state_
ROS message for joint sates.
sensors::RaySensorPtr cliff_sensor_right_
Pointer to left right sensor.
nav_msgs::Odometry odom_
ROS message for odometry data.
tf::TransformBroadcaster tf_broadcaster_
TF transform publisher for the odom frame.


kobuki_gazebo_plugins
Author(s): Marcus Liebhardt
autogenerated on Mon Jun 10 2019 13:52:55