gazebo_ros_kobuki.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2013, Yujin Robot.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of Yujin Robot nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00036 #include <cmath>
00037 #include <cstring>
00038 #include <boost/bind.hpp>
00039 #include <sensor_msgs/JointState.h>
00040 #include <tf/LinearMath/Quaternion.h>
00041 #include <gazebo/math/gzmath.hh>
00042 #include "kobuki_gazebo_plugins/gazebo_ros_kobuki.h"
00043 
00044 namespace gazebo
00045 {
00046 
00047 enum {LEFT= 0, RIGHT=1};
00048 
00049 GazeboRosKobuki::GazeboRosKobuki() : shutdown_requested_(false)
00050 {
00051   // Make sure the ROS node for Gazebo has already been initialized
00052   if (!ros::isInitialized())
00053   {
00054     ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
00055       << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
00056     return;
00057   }
00058 
00059   // Initialise variables
00060   wheel_speed_cmd_[LEFT] = 0.0;
00061   wheel_speed_cmd_[RIGHT] = 0.0;
00062   cliff_detected_left_ = false;
00063   cliff_detected_center_ = false;
00064   cliff_detected_right_ = false;
00065 }
00066 
00067 GazeboRosKobuki::~GazeboRosKobuki()
00068 {
00069 //  rosnode_->shutdown();
00070   shutdown_requested_ = true;
00071   // Wait for spinner thread to end
00072 //  ros_spinner_thread_->join();
00073 
00074   //  delete spinner_thread_;
00075 //  delete rosnode_;
00076 }
00077 
00078 void GazeboRosKobuki::Load(physics::ModelPtr parent, sdf::ElementPtr sdf)
00079 {
00080   model_ = parent;
00081   if (!model_)
00082   {
00083     ROS_ERROR_STREAM("Invalid model pointer! [" << node_name_ << "]");
00084     return;
00085   }
00086   // Get then name of the parent model and use it as node name
00087   std::string model_name = sdf->GetParent()->Get<std::string>("name");
00088   gzdbg << "Plugin model name: " << model_name << "\n";
00089   nh_ = ros::NodeHandle("");
00090   // creating a private name pace until Gazebo implements topic remappings
00091   nh_priv_ = ros::NodeHandle("/" + model_name);
00092   node_name_ = model_name;
00093 
00094   world_ = parent->GetWorld();
00095 
00096   /*
00097    * Prepare receiving motor power commands
00098    */
00099   motor_power_sub_ = nh_priv_.subscribe("commands/motor_power", 10, &GazeboRosKobuki::motorPowerCB, this);
00100   motors_enabled_ = true;
00101 
00102   /*
00103    * Prepare joint state publishing
00104    */
00105   if (sdf->HasElement("left_wheel_joint_name"))
00106   {
00107     left_wheel_joint_name_ = sdf->GetElement("left_wheel_joint_name")->Get<std::string>();
00108   }
00109   else
00110   {
00111     ROS_ERROR_STREAM("Couldn't find left wheel joint in the model description!"
00112                      << " Did you specify the correct joint name?" << " [" << node_name_ <<"]");
00113     return;
00114   }
00115   if (sdf->HasElement("right_wheel_joint_name"))
00116   {
00117     right_wheel_joint_name_ = sdf->GetElement("right_wheel_joint_name")->Get<std::string>();
00118   }
00119   else
00120   {
00121     ROS_ERROR_STREAM("Couldn't find right wheel joint in the model description!"
00122                      << " Did you specify the correct joint name?" << " [" << node_name_ <<"]");
00123     return;
00124   }
00125   joints_[LEFT] = parent->GetJoint(left_wheel_joint_name_);
00126   joints_[RIGHT] = parent->GetJoint(right_wheel_joint_name_);
00127   if (!joints_[LEFT] || !joints_[RIGHT])
00128   {
00129     ROS_ERROR_STREAM("Couldn't find specified wheel joints in the model! [" << node_name_ <<"]");
00130     return;
00131   }
00132   joint_state_.header.frame_id = "Joint States";
00133   joint_state_.name.push_back(left_wheel_joint_name_);
00134   joint_state_.position.push_back(0);
00135   joint_state_.velocity.push_back(0);
00136   joint_state_.effort.push_back(0);
00137   joint_state_.name.push_back(right_wheel_joint_name_);
00138   joint_state_.position.push_back(0);
00139   joint_state_.velocity.push_back(0);
00140   joint_state_.effort.push_back(0);
00141   joint_state_pub_ = nh_.advertise<sensor_msgs::JointState>("joint_states", 1);
00142 
00143   /*
00144    * Prepare publishing odometry data
00145    */
00146   if (sdf->HasElement("publish_tf"))
00147   {
00148     publish_tf_ = sdf->GetElement("publish_tf")->Get<bool>();
00149     if (publish_tf_)
00150     {
00151       ROS_INFO_STREAM("Will publish tf." << " [" << node_name_ <<"]");
00152     }
00153     else
00154     {
00155       ROS_INFO_STREAM("Won't publish tf." << " [" << node_name_ <<"]");
00156     }
00157   }
00158   else
00159   {
00160     publish_tf_ = false;
00161     ROS_INFO_STREAM("Couldn't find the 'publish tf' parameter in the model description."
00162                      << " Won't publish tf." << " [" << node_name_ <<"]");
00163     return;
00164   }
00165   if (sdf->HasElement("wheel_separation"))
00166   {
00167     wheel_sep_ = sdf->GetElement("wheel_separation")->Get<double>();
00168   }
00169   else
00170   {
00171     ROS_ERROR_STREAM("Couldn't find the wheel separation parameter in the model description!"
00172                      << " Did you specify it?" << " [" << node_name_ <<"]");
00173     return;
00174   }
00175   if (sdf->HasElement("wheel_diameter"))
00176   {
00177     wheel_diam_ = sdf->GetElement("wheel_diameter")->Get<double>();
00178   }
00179   else
00180   {
00181     ROS_ERROR_STREAM("Couldn't find the wheel diameter parameter in the model description!"
00182                      << " Did you specify it?" << " [" << node_name_ <<"]");
00183     return;
00184   }
00185   if (sdf->HasElement("torque"))
00186   {
00187     torque_ = sdf->GetElement("torque")->Get<double>();
00188   }
00189   else
00190   {
00191     ROS_ERROR_STREAM("Couldn't find the torque parameter in the model description!"
00192                      << " Did you specify it?" << " [" << node_name_ <<"]");
00193     return;
00194   }
00195   odom_pose_[0] = 0.0;
00196   odom_pose_[1] = 0.0;
00197   odom_pose_[2] = 0.0;
00198   odom_pub_ = nh_.advertise<nav_msgs::Odometry>("odom", 1);
00199   odom_reset_sub_ = nh_priv_.subscribe("commands/reset_odometry", 10, &GazeboRosKobuki::resetOdomCB, this);
00200 
00201   /*
00202    * Prepare receiving velocity commands
00203    */
00204   if (sdf->HasElement("velocity_command_timeout"))
00205   {
00206     cmd_vel_timeout_ = sdf->GetElement("velocity_command_timeout")->Get<double>();
00207   }
00208   else
00209   {
00210     ROS_ERROR_STREAM("Couldn't find the wheel separation parameter in the model description!"
00211                      << " Did you specify it?" << " [" << node_name_ <<"]");
00212     return;
00213   }
00214   last_cmd_vel_time_ = world_->GetSimTime();
00215   cmd_vel_sub_ = nh_priv_.subscribe("commands/velocity", 100, &GazeboRosKobuki::cmdVelCB, this);
00216 
00217   /*
00218    * Prepare cliff sensors
00219    */
00220   std::string cliff_sensor_left_name, cliff_sensor_center_name, cliff_sensor_right_name;
00221   if (sdf->HasElement("cliff_sensor_left_name"))
00222   {
00223     cliff_sensor_left_name = sdf->GetElement("cliff_sensor_left_name")->Get<std::string>();
00224   }
00225   else
00226   {
00227     ROS_ERROR_STREAM("Couldn't find the name of left cliff sensor in the model description!"
00228                      << " Did you specify it?" << " [" << node_name_ <<"]");
00229     return;
00230   }
00231   if (sdf->HasElement("cliff_sensor_center_name"))
00232   {
00233     cliff_sensor_center_name = sdf->GetElement("cliff_sensor_center_name")->Get<std::string>();
00234   }
00235   else
00236   {
00237     ROS_ERROR_STREAM("Couldn't find the name of frontal cliff sensor in the model description!"
00238                      << " Did you specify it?" << " [" << node_name_ <<"]");
00239     return;
00240   }
00241   if (sdf->HasElement("cliff_sensor_right_name"))
00242   {
00243     cliff_sensor_right_name = sdf->GetElement("cliff_sensor_right_name")->Get<std::string>();
00244   }
00245   else
00246   {
00247     ROS_ERROR_STREAM("Couldn't find the name of right cliff sensor in the model description!"
00248                      << " Did you specify it?" << " [" << node_name_ <<"]");
00249     return;
00250   }
00251   cliff_sensor_left_ = boost::dynamic_pointer_cast<sensors::RaySensor>(
00252                        sensors::SensorManager::Instance()->GetSensor(cliff_sensor_left_name));
00253   cliff_sensor_center_ = boost::dynamic_pointer_cast<sensors::RaySensor>(
00254                         sensors::SensorManager::Instance()->GetSensor(cliff_sensor_center_name));
00255   cliff_sensor_right_ = boost::dynamic_pointer_cast<sensors::RaySensor>(
00256                         sensors::SensorManager::Instance()->GetSensor(cliff_sensor_right_name));
00257   if (!cliff_sensor_left_)
00258   {
00259     ROS_ERROR_STREAM("Couldn't find the left cliff sensor in the model! [" << node_name_ <<"]");
00260     return;
00261   }
00262   if (!cliff_sensor_center_)
00263   {
00264     ROS_ERROR_STREAM("Couldn't find the center cliff sensor in the model! [" << node_name_ <<"]");
00265     return;
00266   }
00267   if (!cliff_sensor_right_)
00268   {
00269     ROS_ERROR_STREAM("Couldn't find the right cliff sensor in the model! [" << node_name_ <<"]");
00270     return;
00271   }
00272   if (sdf->HasElement("cliff_detection_threshold"))
00273   {
00274     cliff_detection_threshold_ = sdf->GetElement("cliff_detection_threshold")->Get<double>();
00275   }
00276   else
00277   {
00278     ROS_ERROR_STREAM("Couldn't find the cliff detection threshold parameter in the model description!"
00279                      << " Did you specify it?" << " [" << node_name_ <<"]");
00280     return;
00281   }
00282   cliff_sensor_left_->SetActive(true);
00283   cliff_sensor_center_->SetActive(true);
00284   cliff_sensor_right_->SetActive(true);
00285   cliff_event_pub_ = nh_priv_.advertise<kobuki_msgs::CliffEvent>("events/cliff", 1);
00286 
00287   /*
00288    * Prepare bumper
00289    */
00290   std::string bumper_name;
00291   if (sdf->HasElement("bumper_name"))
00292   {
00293     bumper_name = sdf->GetElement("bumper_name")->Get<std::string>();
00294   }
00295   else
00296   {
00297     ROS_ERROR_STREAM("Couldn't find the name of bumper sensor in the model description!"
00298                      << " Did you specify it?" << " [" << node_name_ <<"]");
00299     return;
00300   }
00301   bumper_ = boost::dynamic_pointer_cast<sensors::ContactSensor>(
00302             sensors::SensorManager::Instance()->GetSensor(bumper_name));
00303   if (!bumper_)
00304   {
00305     ROS_ERROR_STREAM("Couldn't find the bumpers in the model! [" << node_name_ <<"]");
00306     return;
00307   }
00308   bumper_->SetActive(true);
00309   bumper_event_pub_ = nh_priv_.advertise<kobuki_msgs::BumperEvent>("events/bumper", 1);
00310 
00311   /*
00312    * Prepare IMU
00313    */
00314   std::string imu_name;
00315   if (sdf->HasElement("imu_name"))
00316   {
00317   imu_name = sdf->GetElement("imu_name")->Get<std::string>();
00318   }
00319   else
00320   {
00321     ROS_ERROR_STREAM("Couldn't find the name of IMU sensor in the model description!"
00322                      << " Did you specify it?" << " [" << node_name_ <<"]");
00323     return;
00324   }
00325   imu_ = boost::dynamic_pointer_cast<sensors::ImuSensor>(
00326             sensors::SensorManager::Instance()->GetSensor(imu_name));
00327   if (!imu_)
00328   {
00329     ROS_ERROR_STREAM("Couldn't find the IMU in the model! [" << node_name_ <<"]");
00330     return;
00331   }
00332   imu_->SetActive(true);
00333   imu_pub_ = nh_priv_.advertise<sensor_msgs::Imu>("sensors/imu_data", 1);
00334 
00335   prev_update_time_ = world_->GetSimTime();
00336   ROS_INFO_STREAM("GazeboRosKobuki plugin ready to go! [" << node_name_ << "]");
00337   update_connection_ = event::Events::ConnectWorldUpdateBegin(boost::bind(&GazeboRosKobuki::OnUpdate, this));
00338 }
00339 
00340 void GazeboRosKobuki::motorPowerCB(const kobuki_msgs::MotorPowerPtr &msg)
00341 {
00342   if ((msg->state == kobuki_msgs::MotorPower::ON) && (!motors_enabled_))
00343   {
00344     motors_enabled_ = true;
00345     ROS_INFO_STREAM("Motors fired up. [" << node_name_ << "]");
00346   }
00347   else if ((msg->state == kobuki_msgs::MotorPower::OFF) && (motors_enabled_))
00348   {
00349     motors_enabled_ = false;
00350     ROS_INFO_STREAM("Motors taking a rest. [" << node_name_ << "]");
00351   }
00352 }
00353 
00354 void GazeboRosKobuki::cmdVelCB(const geometry_msgs::TwistConstPtr &msg)
00355 {
00356   last_cmd_vel_time_ = world_->GetSimTime();
00357   wheel_speed_cmd_[LEFT] = msg->linear.x - msg->angular.z * (wheel_sep_) / 2;
00358   wheel_speed_cmd_[RIGHT] = msg->linear.x + msg->angular.z * (wheel_sep_) / 2;
00359 }
00360 
00361 void GazeboRosKobuki::resetOdomCB(const std_msgs::EmptyConstPtr &msg)
00362 {
00363   odom_pose_[0] = 0.0;
00364   odom_pose_[1] = 0.0;
00365   odom_pose_[2] = 0.0;
00366 }
00367 
00368 void GazeboRosKobuki::OnUpdate()
00369 {
00370   /*
00371    * First process ROS callbacks
00372    */
00373   ros::spinOnce();
00374 
00375   /*
00376    * Update current time and time step
00377    */
00378   common::Time time_now = world_->GetSimTime();
00379   common::Time step_time = time_now - prev_update_time_;
00380   prev_update_time_ = time_now;
00381 
00382   /*
00383    * Joint states
00384    */
00385   joint_state_.header.stamp = ros::Time::now();
00386   joint_state_.header.frame_id = "base_link";
00387   joint_state_.position[LEFT] = joints_[LEFT]->GetAngle(0).Radian();
00388   joint_state_.velocity[LEFT] = joints_[LEFT]->GetVelocity(0);
00389   joint_state_.position[RIGHT] = joints_[RIGHT]->GetAngle(0).Radian();
00390   joint_state_.velocity[RIGHT] = joints_[RIGHT]->GetVelocity(0);
00391   joint_state_pub_.publish(joint_state_);
00392 
00393   /*
00394    * Odometry (encoders & IMU)
00395    */
00396   odom_.header.stamp = joint_state_.header.stamp;
00397   odom_.header.frame_id = "odom";
00398   odom_.child_frame_id = "base_footprint";
00399 
00400   // Distance travelled by main wheels
00401   double d1, d2;
00402   double dr, da;
00403   d1 = d2 = 0;
00404   dr = da = 0;
00405   d1 = step_time.Double() * (wheel_diam_ / 2) * joints_[LEFT]->GetVelocity(0);
00406   d2 = step_time.Double() * (wheel_diam_ / 2) * joints_[RIGHT]->GetVelocity(0);
00407   // Can see NaN values here, just zero them out if needed
00408   if (isnan(d1))
00409   {
00410     ROS_WARN_STREAM_THROTTLE(0.1, "Gazebo ROS Kobuki plugin: NaN in d1. Step time: " << step_time.Double()
00411                              << ", WD: " << wheel_diam_ << ", velocity: " << joints_[LEFT]->GetVelocity(0));
00412     d1 = 0;
00413   }
00414   if (isnan(d2))
00415   {
00416     ROS_WARN_STREAM_THROTTLE(0.1, "Gazebo ROS Kobuki plugin: NaN in d2. Step time: " << step_time.Double()
00417                              << ", WD: " << wheel_diam_ << ", velocity: " << joints_[RIGHT]->GetVelocity(0));
00418     d2 = 0;
00419   }
00420   dr = (d1 + d2) / 2;
00421   da = (d2 - d1) / wheel_sep_; // ignored
00422 
00423   // Just as in the Kobuki driver, the angular velocity is taken directly from the IMU
00424   vel_angular_ = imu_->GetAngularVelocity();
00425 
00426   // Compute odometric pose
00427   odom_pose_[0] += dr * cos( odom_pose_[2] );
00428   odom_pose_[1] += dr * sin( odom_pose_[2] );
00429   odom_pose_[2] += vel_angular_.z * step_time.Double();
00430   // Compute odometric instantaneous velocity
00431   odom_vel_[0] = dr / step_time.Double();
00432   odom_vel_[1] = 0.0;
00433   odom_vel_[2] = vel_angular_.z;
00434 
00435   odom_.pose.pose.position.x = odom_pose_[0];
00436   odom_.pose.pose.position.y = odom_pose_[1];
00437   odom_.pose.pose.position.z = 0;
00438 
00439   tf::Quaternion qt;
00440   qt.setEuler(0,0,odom_pose_[2]);
00441   odom_.pose.pose.orientation.x = qt.getX();
00442   odom_.pose.pose.orientation.y = qt.getY();
00443   odom_.pose.pose.orientation.z = qt.getZ();
00444   odom_.pose.pose.orientation.w = qt.getW();
00445 
00446   odom_.pose.covariance[0]  = 0.1;
00447   odom_.pose.covariance[7]  = 0.1;
00448   odom_.pose.covariance[35] = 0.05;
00449   odom_.pose.covariance[14] = 1e6;
00450   odom_.pose.covariance[21] = 1e6;
00451   odom_.pose.covariance[28] = 1e6;
00452 
00453   odom_.twist.twist.linear.x = odom_vel_[0];
00454   odom_.twist.twist.linear.y = 0;
00455   odom_.twist.twist.linear.z = 0;
00456   odom_.twist.twist.angular.x = 0;
00457   odom_.twist.twist.angular.y = 0;
00458   odom_.twist.twist.angular.z = odom_vel_[2];
00459   odom_pub_.publish(odom_); // publish odom message
00460 
00461   if (publish_tf_)
00462   {
00463     odom_tf_.header = odom_.header;
00464     odom_tf_.child_frame_id = odom_.child_frame_id;
00465     odom_tf_.transform.translation.x = odom_.pose.pose.position.x;
00466     odom_tf_.transform.translation.y = odom_.pose.pose.position.y;
00467     odom_tf_.transform.translation.z = odom_.pose.pose.position.z;
00468     odom_tf_.transform.rotation = odom_.pose.pose.orientation;
00469     tf_broadcaster_.sendTransform(odom_tf_);
00470   }
00471 
00472   /*
00473    * Publish IMU data
00474    */
00475   imu_msg_.header = joint_state_.header;
00476   math::Quaternion quat = imu_->GetOrientation();
00477   imu_msg_.orientation.x = quat.x;
00478   imu_msg_.orientation.y = quat.y;
00479   imu_msg_.orientation.z = quat.z;
00480   imu_msg_.orientation.w = quat.w;
00481   imu_msg_.orientation_covariance[0] = 1e6;
00482   imu_msg_.orientation_covariance[4] = 1e6;
00483   imu_msg_.orientation_covariance[8] = 0.05;
00484   imu_msg_.angular_velocity.x = vel_angular_.x;
00485   imu_msg_.angular_velocity.y = vel_angular_.y;
00486   imu_msg_.angular_velocity.z = vel_angular_.z;
00487   imu_msg_.angular_velocity_covariance[0] = 1e6;
00488   imu_msg_.angular_velocity_covariance[4] = 1e6;
00489   imu_msg_.angular_velocity_covariance[8] = 0.05;
00490   math::Vector3 lin_acc = imu_->GetLinearAcceleration();
00491   imu_msg_.linear_acceleration.x = lin_acc.x;
00492   imu_msg_.linear_acceleration.y = lin_acc.y;
00493   imu_msg_.linear_acceleration.z = lin_acc.z;
00494   imu_pub_.publish(imu_msg_); // publish odom message
00495 
00496   /*
00497    * Propagate velocity commands
00498    * TODO: Check how to simulate disabled motors, e.g. set MaxForce to zero, but then damping is important!
00499    */
00500   if (((time_now - last_cmd_vel_time_).Double() > cmd_vel_timeout_) || !motors_enabled_)
00501   {
00502     wheel_speed_cmd_[LEFT] = 0.0;
00503     wheel_speed_cmd_[RIGHT] = 0.0;
00504   }
00505   joints_[LEFT]->SetVelocity(0, wheel_speed_cmd_[LEFT] / (wheel_diam_ / 2.0));
00506   joints_[RIGHT]->SetVelocity(0, wheel_speed_cmd_[RIGHT] / (wheel_diam_ / 2.0));
00507   joints_[LEFT]->SetMaxForce(0, torque_);
00508   joints_[RIGHT]->SetMaxForce(0, torque_);
00509 
00510   /*
00511    * Cliff sensors
00512    * Check each sensor separately
00513    */
00514   // Left cliff sensor
00515   if ((cliff_detected_left_ == false) &&
00516       (cliff_sensor_left_->GetRange(0) >= cliff_detection_threshold_))
00517   {
00518     cliff_detected_left_ = true;
00519     cliff_event_.sensor = kobuki_msgs::CliffEvent::LEFT;
00520     cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
00521     // convert distance back to an AD reading
00522     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_left_->GetRange(0)));
00523     cliff_event_pub_.publish(cliff_event_);
00524   }
00525   else if ((cliff_detected_left_ == true) &&
00526             (cliff_sensor_left_->GetRange(0) < cliff_detection_threshold_))
00527   {
00528     cliff_detected_left_ = false;
00529     cliff_event_.sensor = kobuki_msgs::CliffEvent::LEFT;
00530     cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
00531     // convert distance back to an AD reading
00532     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_left_->GetRange(0)));
00533     cliff_event_pub_.publish(cliff_event_);
00534   }
00535   // Centre cliff sensor
00536   if ((cliff_detected_center_ == false) &&
00537       (cliff_sensor_center_->GetRange(0) >= cliff_detection_threshold_))
00538   {
00539     cliff_detected_center_ = true;
00540     cliff_event_.sensor = kobuki_msgs::CliffEvent::CENTER;
00541     cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
00542     // convert distance back to an AD reading
00543     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_center_->GetRange(0)));
00544     cliff_event_pub_.publish(cliff_event_);
00545   }
00546   else if ((cliff_detected_center_ == true) &&
00547             (cliff_sensor_center_->GetRange(0) < cliff_detection_threshold_))
00548   {
00549     cliff_detected_center_ = false;
00550     cliff_event_.sensor = kobuki_msgs::CliffEvent::CENTER;
00551     cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
00552     // convert distance back to an AD reading
00553     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_center_->GetRange(0)));
00554     cliff_event_pub_.publish(cliff_event_);
00555   }
00556   // Right cliff sensor
00557   if ((cliff_detected_right_ == false) &&
00558       (cliff_sensor_right_->GetRange(0) >= cliff_detection_threshold_))
00559   {
00560     cliff_detected_right_ = true;
00561     cliff_event_.sensor = kobuki_msgs::CliffEvent::RIGHT;
00562     cliff_event_.state = kobuki_msgs::CliffEvent::CLIFF;
00563     // convert distance back to an AD reading
00564     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_right_->GetRange(0)));
00565     cliff_event_pub_.publish(cliff_event_);
00566   }
00567   else if ((cliff_detected_right_ == true) &&
00568             (cliff_sensor_right_->GetRange(0) < cliff_detection_threshold_))
00569   {
00570     cliff_detected_right_ = false;
00571     cliff_event_.sensor = kobuki_msgs::CliffEvent::RIGHT;
00572     cliff_event_.state = kobuki_msgs::CliffEvent::FLOOR;
00573     // convert distance back to an AD reading
00574     cliff_event_.bottom = (int)(76123.0f * atan2(0.995f, cliff_sensor_right_->GetRange(0)));
00575     cliff_event_pub_.publish(cliff_event_);
00576   }
00577 
00578   /*
00579    * Bumpers
00580    */
00581   // In order to simulate the three bumper sensors, a contact is assigned to one of the bumpers
00582   // depending on its position. Each sensor covers a range of 60 degrees.
00583   // +90 ... +30: left bumper
00584   // +30 ... -30: centre bumper
00585   // -30 ... -90: right bumper
00586 
00587   // reset flags
00588   bumper_left_is_pressed_ = false;
00589   bumper_center_is_pressed_ = false;
00590   bumper_right_is_pressed_ = false;
00591 
00592   // parse contacts
00593   msgs::Contacts contacts;
00594   contacts = bumper_->GetContacts();
00595   math::Pose current_pose = model_->GetWorldPose();
00596   double robot_heading = current_pose.rot.GetYaw();
00597 
00598   for (int i = 0; i < contacts.contact_size(); ++i)
00599   {
00600     double rel_contact_pos =  contacts.contact(i).position(0).z() - current_pose.pos.z;
00601     if ((rel_contact_pos >= 0.015)
00602         && (rel_contact_pos <= 0.085)) // only consider contacts at the height of the bumper
00603     {
00604       // using the force normals below, since the contact position is given in world coordinates
00605       // also negating the normal, because it points from contact to robot centre
00606       double global_contact_angle = std::atan2(-contacts.contact(i).normal(0).y(), -contacts.contact(i).normal(0).x());
00607       double relative_contact_angle = global_contact_angle - robot_heading;
00608 
00609       if ((relative_contact_angle <= (M_PI/2)) && (relative_contact_angle > (M_PI/6)))
00610       {
00611         bumper_left_is_pressed_ = true;
00612       }
00613       else if ((relative_contact_angle <= (M_PI/6)) && (relative_contact_angle >= (-M_PI/6)))
00614       {
00615         bumper_center_is_pressed_ = true;
00616       }
00617       else if ((relative_contact_angle < (-M_PI/6)) && (relative_contact_angle >= (-M_PI/2)))
00618       {
00619         bumper_right_is_pressed_ = true;
00620       }
00621     }
00622   }
00623 
00624   // check for bumper state change
00625   if (bumper_left_is_pressed_ && !bumper_left_was_pressed_)
00626   {
00627     bumper_left_was_pressed_ = true;
00628     bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
00629     bumper_event_.bumper = kobuki_msgs::BumperEvent::LEFT;
00630     bumper_event_pub_.publish(bumper_event_);
00631   }
00632   else if (!bumper_left_is_pressed_ && bumper_left_was_pressed_)
00633   {
00634     bumper_left_was_pressed_ = false;
00635     bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
00636     bumper_event_.bumper = kobuki_msgs::BumperEvent::LEFT;
00637     bumper_event_pub_.publish(bumper_event_);
00638   }
00639   if (bumper_center_is_pressed_ && !bumper_center_was_pressed_)
00640   {
00641     bumper_center_was_pressed_ = true;
00642     bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
00643     bumper_event_.bumper = kobuki_msgs::BumperEvent::CENTER;
00644     bumper_event_pub_.publish(bumper_event_);
00645   }
00646   else if (!bumper_center_is_pressed_ && bumper_center_was_pressed_)
00647   {
00648     bumper_center_was_pressed_ = false;
00649     bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
00650     bumper_event_.bumper = kobuki_msgs::BumperEvent::CENTER;
00651     bumper_event_pub_.publish(bumper_event_);
00652   }
00653   if (bumper_right_is_pressed_ && !bumper_right_was_pressed_)
00654   {
00655     bumper_right_was_pressed_ = true;
00656     bumper_event_.state = kobuki_msgs::BumperEvent::PRESSED;
00657     bumper_event_.bumper = kobuki_msgs::BumperEvent::RIGHT;
00658     bumper_event_pub_.publish(bumper_event_);
00659   }
00660   else if (!bumper_right_is_pressed_ && bumper_right_was_pressed_)
00661   {
00662     bumper_right_was_pressed_ = false;
00663     bumper_event_.state = kobuki_msgs::BumperEvent::RELEASED;
00664     bumper_event_.bumper = kobuki_msgs::BumperEvent::RIGHT;
00665     bumper_event_pub_.publish(bumper_event_);
00666   }
00667 }
00668 
00669 void GazeboRosKobuki::spin()
00670 {
00671   while(ros::ok() && !shutdown_requested_)
00672   {
00673     ros::spinOnce();
00674   }
00675 }
00676 
00677 // Register this plugin with the simulator
00678 GZ_REGISTER_MODEL_PLUGIN(GazeboRosKobuki);
00679 
00680 } // namespace gazebo


kobuki_gazebo_plugins
Author(s): Marcus Liebhardt
autogenerated on Wed Aug 26 2015 12:18:28