00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 #include <hector_gazebo_plugins/gazebo_ros_imu.h>
00058 #include "common/Events.hh"
00059 #include "physics/physics.hh"
00060 
00061 #include <ros/console.h>
00062 
00063 namespace gazebo
00064 {
00065 
00066 
00067 #ifdef DEBUG_OUTPUT
00068   #include <geometry_msgs/PoseStamped.h>
00069   static ros::Publisher debugPublisher;
00070 #endif // DEBUG_OUTPUT
00071 
00073 
00074 GazeboRosIMU::GazeboRosIMU()
00075 {
00076 }
00077 
00079 
00080 GazeboRosIMU::~GazeboRosIMU()
00081 {
00082   updateTimer.Disconnect(updateConnection);
00083 
00084   node_handle_->shutdown();
00085 #ifdef USE_CBQ
00086   callback_queue_thread_.join();
00087 #endif
00088   delete node_handle_;
00089 }
00090 
00092 
00093 void GazeboRosIMU::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
00094 {
00095   
00096   world = _model->GetWorld();
00097 
00098   
00099   if (!_sdf->HasElement("robotNamespace"))
00100     robotNamespace.clear();
00101   else
00102     robotNamespace = _sdf->GetElement("robotNamespace")->GetValueString() + "/";
00103 
00104   if (!_sdf->HasElement("bodyName"))
00105   {
00106     link = _model->GetLink();
00107     linkName = link->GetName();
00108   }
00109   else {
00110     linkName = _sdf->GetElement("bodyName")->GetValueString();
00111     link = _model->GetLink(linkName);
00112   }
00113 
00114   
00115   if (!link)
00116   {
00117     ROS_FATAL("GazeboRosIMU plugin error: bodyName: %s does not exist\n", linkName.c_str());
00118     return;
00119   }
00120 
00121   if (!_sdf->HasElement("frameId"))
00122     frameId = linkName;
00123   else
00124     frameId = _sdf->GetElement("frameId")->GetValueString();
00125 
00126   if (!_sdf->HasElement("topicName"))
00127     topicName = "imu";
00128   else
00129     topicName = _sdf->GetElement("topicName")->GetValueString();
00130 
00131   if (!_sdf->HasElement("serviceName"))
00132     serviceName = topicName + "/calibrate";
00133   else
00134     serviceName = _sdf->GetElement("serviceName")->GetValueString();
00135 
00136   accelModel.Load(_sdf, "accel");
00137   rateModel.Load(_sdf, "rate");
00138   headingModel.Load(_sdf, "heading");
00139 
00140   
00141   if (_sdf->HasElement("gaussianNoise")) {
00142     double gaussianNoise = _sdf->GetElement("gaussianNoise")->GetValueDouble();
00143     if (gaussianNoise != 0.0) {
00144       accelModel.gaussian_noise = gaussianNoise;
00145       rateModel.gaussian_noise  = gaussianNoise;
00146     }
00147   }
00148 
00149   if (_sdf->HasElement("rpyOffset")) {
00150     math::Vector3 rpyOffset = _sdf->GetElement("rpyOffset")->GetValueVector3();
00151     if (accelModel.offset.y == 0.0 && rpyOffset.x != 0.0) accelModel.offset.y = -rpyOffset.x * 9.8065;
00152     if (accelModel.offset.x == 0.0 && rpyOffset.y != 0.0) accelModel.offset.x =  rpyOffset.y * 9.8065;
00153     if (headingModel.offset == 0.0 && rpyOffset.z != 0.0) headingModel.offset =  rpyOffset.z;
00154   }
00155 
00156   
00157   imuMsg.angular_velocity_covariance[0] = rateModel.gaussian_noise.x*rateModel.gaussian_noise.x;
00158   imuMsg.angular_velocity_covariance[4] = rateModel.gaussian_noise.y*rateModel.gaussian_noise.y;
00159   imuMsg.angular_velocity_covariance[8] = rateModel.gaussian_noise.z*rateModel.gaussian_noise.z;
00160   imuMsg.linear_acceleration_covariance[0] = accelModel.gaussian_noise.x*accelModel.gaussian_noise.x;
00161   imuMsg.linear_acceleration_covariance[4] = accelModel.gaussian_noise.y*accelModel.gaussian_noise.y;
00162   imuMsg.linear_acceleration_covariance[8] = accelModel.gaussian_noise.z*accelModel.gaussian_noise.z;
00163 
00164   
00165   if (!ros::isInitialized())
00166   {
00167     int argc = 0;
00168     char** argv = NULL;
00169     ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName);
00170   }
00171 
00172   node_handle_ = new ros::NodeHandle(robotNamespace);
00173 
00174   
00175   if (!topicName.empty())
00176     pub_ = node_handle_->advertise<sensor_msgs::Imu>(topicName, 10);
00177 
00178 #ifdef DEBUG_OUTPUT
00179   debugPublisher = rosnode_->advertise<geometry_msgs::PoseStamped>(topicName + "/pose", 10);
00180 #endif // DEBUG_OUTPUT
00181 
00182   
00183   if (!serviceName.empty())
00184     srv_ = node_handle_->advertiseService(serviceName, &GazeboRosIMU::ServiceCallback, this);
00185 
00186   accelBiasService = node_handle_->advertiseService(topicName + "/set_accel_bias", &GazeboRosIMU::SetAccelBiasCallback, this);
00187   rateBiasService  = node_handle_->advertiseService(topicName + "/set_rate_bias", &GazeboRosIMU::SetRateBiasCallback, this);
00188 
00189 #ifdef USE_CBQ
00190   
00191   callback_queue_thread_ = boost::thread( boost::bind( &GazeboRosIMU::CallbackQueueThread,this ) );
00192 #endif
00193 
00194   Reset();
00195 
00196   
00197   updateTimer.Load(world, _sdf);
00198   updateConnection = updateTimer.Connect(boost::bind(&GazeboRosIMU::Update, this));
00199 }
00200 
00201 void GazeboRosIMU::Reset()
00202 {
00203   updateTimer.Reset();
00204 
00205   orientation = math::Quaternion();
00206   velocity = 0.0;
00207   accel = 0.0;
00208 
00209   accelModel.reset();
00210   rateModel.reset();
00211   headingModel.reset();
00212 }
00213 
00215 
00216 bool GazeboRosIMU::ServiceCallback(std_srvs::Empty::Request &req,
00217                                         std_srvs::Empty::Response &res)
00218 {
00219   boost::mutex::scoped_lock scoped_lock(lock);
00220   rateModel.reset();
00221   return true;
00222 }
00223 
00224 bool GazeboRosIMU::SetAccelBiasCallback(hector_gazebo_plugins::SetBias::Request &req, hector_gazebo_plugins::SetBias::Response &res)
00225 {
00226   boost::mutex::scoped_lock scoped_lock(lock);
00227   accelModel.reset(math::Vector3(req.bias.x, req.bias.y, req.bias.z));
00228   return true;
00229 }
00230 
00231 bool GazeboRosIMU::SetRateBiasCallback(hector_gazebo_plugins::SetBias::Request &req, hector_gazebo_plugins::SetBias::Response &res)
00232 {
00233   boost::mutex::scoped_lock scoped_lock(lock);
00234   rateModel.reset(math::Vector3(req.bias.x, req.bias.y, req.bias.z));
00235   return true;
00236 }
00237 
00239 
00240 void GazeboRosIMU::Update()
00241 {
00242   
00243   common::Time cur_time = world->GetSimTime();
00244   double dt = updateTimer.getTimeSinceLastUpdate().Double();
00245   boost::mutex::scoped_lock scoped_lock(lock);
00246 
00247   
00248   math::Pose pose = link->GetWorldPose();
00249 
00250   
00251   
00252   
00253   math::Vector3 temp = link->GetWorldLinearVel(); 
00254   if (dt > 0.0) accel = pose.rot.RotateVectorReverse((temp - velocity) / dt);
00255   velocity = temp;
00256 
00257   
00258   
00259   math::Quaternion delta = pose.rot - orientation;
00260   orientation = pose.rot;
00261   if (dt > 0.0) {
00262     rate.x = 2.0 * (-orientation.x * delta.w + orientation.w * delta.x + orientation.z * delta.y - orientation.y * delta.z) / dt;
00263     rate.y = 2.0 * (-orientation.y * delta.w - orientation.z * delta.x + orientation.w * delta.y + orientation.x * delta.z) / dt;
00264     rate.z = 2.0 * (-orientation.z * delta.w + orientation.y * delta.x - orientation.x * delta.y + orientation.w * delta.z) / dt;
00265   }
00266 
00267   
00268   gravity       = world->GetPhysicsEngine()->GetGravity();
00269   gravity_body  = orientation.RotateVectorReverse(gravity);
00270   double gravity_length = gravity.GetLength();
00271   ROS_DEBUG_NAMED("gazebo_ros_imu", "gravity_world = [%g %g %g]", gravity.x, gravity.y, gravity.z);
00272 
00273   
00274   accel = accel - gravity_body;
00275 
00276   
00277   accel = accel + accelModel.update(dt);
00278   rate  = rate  + rateModel.update(dt);
00279   headingModel.update(dt);
00280   ROS_DEBUG_NAMED("gazebo_ros_imu", "Current errors: accel = [%g %g %g], rate = [%g %g %g], heading = %g",
00281                  accelModel.getCurrentError().x, accelModel.getCurrentError().y, accelModel.getCurrentError().z,
00282                  rateModel.getCurrentError().x, rateModel.getCurrentError().y, rateModel.getCurrentError().z,
00283                  headingModel.getCurrentError());
00284 
00285   
00286   double normalization_constant = (gravity_body + accelModel.getCurrentError()).GetLength() * gravity_body.GetLength();
00287   double cos_alpha = (gravity_body + accelModel.getCurrentError()).GetDotProd(gravity_body)/normalization_constant;
00288   math::Vector3 normal_vector(gravity_body.GetCrossProd(accelModel.getCurrentError()));
00289   normal_vector *= sqrt((1 - cos_alpha)/2)/normalization_constant;
00290   math::Quaternion attitudeError(sqrt((1 + cos_alpha)/2), normal_vector.x, normal_vector.y, normal_vector.z);
00291   math::Quaternion headingError(cos(headingModel.getCurrentError()/2),0,0,sin(headingModel.getCurrentError()/2));
00292   pose.rot = attitudeError * pose.rot * headingError;
00293 
00294   
00295   imuMsg.header.frame_id = frameId;
00296   imuMsg.header.stamp.sec = cur_time.sec;
00297   imuMsg.header.stamp.nsec = cur_time.nsec;
00298 
00299   
00300   imuMsg.orientation.x = pose.rot.x;
00301   imuMsg.orientation.y = pose.rot.y;
00302   imuMsg.orientation.z = pose.rot.z;
00303   imuMsg.orientation.w = pose.rot.w;
00304 
00305   
00306   imuMsg.angular_velocity.x    = rate.x;
00307   imuMsg.angular_velocity.y    = rate.y;
00308   imuMsg.angular_velocity.z    = rate.z;
00309 
00310   
00311   imuMsg.linear_acceleration.x    = accel.x;
00312   imuMsg.linear_acceleration.y    = accel.y;
00313   imuMsg.linear_acceleration.z    = accel.z;
00314 
00315   
00316   imuMsg.orientation_covariance[8] = headingModel.gaussian_noise*headingModel.gaussian_noise;
00317   if (gravity_length > 0.0) {
00318     imuMsg.orientation_covariance[0] = accelModel.gaussian_noise.x*accelModel.gaussian_noise.x/(gravity_length*gravity_length);
00319     imuMsg.orientation_covariance[4] = accelModel.gaussian_noise.y*accelModel.gaussian_noise.y/(gravity_length*gravity_length);
00320   } else {
00321     imuMsg.orientation_covariance[0] = -1;
00322     imuMsg.orientation_covariance[4] = -1;
00323   }
00324 
00325   
00326   pub_.publish(imuMsg);
00327   ROS_DEBUG_NAMED("gazebo_ros_imu", "Publishing IMU data at t = %f", cur_time.Double());
00328 
00329   
00330 #ifdef DEBUG_OUTPUT
00331   if (debugPublisher) {
00332     geometry_msgs::PoseStamped debugPose;
00333     debugPose.header = imuMsg.header;
00334     debugPose.header.frame_id = "/map";
00335     debugPose.pose.orientation.w = imuMsg.orientation.w;
00336     debugPose.pose.orientation.x = imuMsg.orientation.x;
00337     debugPose.pose.orientation.y = imuMsg.orientation.y;
00338     debugPose.pose.orientation.z = imuMsg.orientation.z;
00339     math::Pose pose = link->GetWorldPose();
00340     debugPose.pose.position.x = pose.pos.x;
00341     debugPose.pose.position.y = pose.pos.y;
00342     debugPose.pose.position.z = pose.pos.z;
00343     debugPublisher.publish(debugPose);
00344   }
00345 #endif // DEBUG_OUTPUT
00346 }
00347 
00348 #ifdef USE_CBQ
00349 void GazeboRosIMU::CallbackQueueThread()
00350 {
00351   static const double timeout = 0.01;
00352 
00353   while (rosnode_->ok())
00354   {
00355     callback_queue_.callAvailable(ros::WallDuration(timeout));
00356   }
00357 }
00358 #endif
00359 
00360 
00361 GZ_REGISTER_MODEL_PLUGIN(GazeboRosIMU)
00362 
00363 }