utm_transform_node.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014, Charles River Analytics, Inc.
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
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  * notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above
00012  * copyright notice, this list of conditions and the following
00013  * disclaimer in the documentation and/or other materials provided
00014  * with the distribution.
00015  * 3. Neither the name of the copyright holder nor the names of its
00016  * contributors may be used to endorse or promote products derived
00017  * from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00027  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00028  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00029  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE.
00031  */
00032 
00033 #include <ros/ros.h>
00034 
00035 #include <nav_msgs/Odometry.h>
00036 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00037 #include <sensor_msgs/Imu.h>
00038 #include <sensor_msgs/NavSatFix.h>
00039 
00040 #include <tf/transform_broadcaster.h>
00041 #include <tf/transform_listener.h>
00042 
00044 nav_msgs::Odometry *latestUtmMsg_;
00045 nav_msgs::Odometry *latestOdomMessage_;
00046 sensor_msgs::Imu *latestImuMsg_;
00047 
00049 tf::TransformListener *tfListener_;
00050 
00052 tf::StampedTransform *odomUtmTransform_;
00053 
00056 tf::Pose originalOdomPose_;
00057 
00060 tf::Pose originalUtmPose_;
00061 
00064 double magneticDeclination_;
00065 
00067 double utmOdomTfRoll_;
00068 
00070 double utmOdomTfPitch_;
00071 
00073 double utmOdomTfYaw_;
00074 
00076 bool hasFix_;
00077 
00079 bool transformGood_;
00080 
00083 double rollOffset_;
00084 
00087 double pitchOffset_;
00088 
00092 double yawOffset_;
00093 
00097 bool zeroAltitude_;
00098 
00107 void computeOdomUtmTransform()
00108 {
00109   // Only do this if:
00110   // 1. We haven't computed the odom_frame->utm_frame transform before
00111   // 2. We've received the messages we need already
00112   // 3. We have good GPS data
00113   if(!transformGood_ &&
00114      latestUtmMsg_ != NULL &&
00115      latestImuMsg_ != NULL &&
00116      latestOdomMessage_ != NULL &&
00117      hasFix_)
00118   {
00119     if(!std::isnan(latestUtmMsg_->pose.pose.position.x) &&
00120        !std::isnan(latestUtmMsg_->pose.pose.position.y) &&
00121        !std::isnan(latestUtmMsg_->pose.pose.position.z))
00122     {
00123       ROS_INFO_STREAM("Computing initial " << latestOdomMessage_->header.frame_id << "->" << latestUtmMsg_->header.frame_id << " transform");
00124 
00125       // Now that we have what we need, create the transform so it will be broadcast
00126       if(odomUtmTransform_ == NULL)
00127       {
00128         odomUtmTransform_ = new tf::StampedTransform();
00129       }
00130 
00131       // Get the IMU's current RPY values. Need the raw values (for yaw, anyway).
00132       tf::Matrix3x3 mat(tf::Quaternion(latestImuMsg_->orientation.x,
00133                                        latestImuMsg_->orientation.y,
00134                                        latestImuMsg_->orientation.z,
00135                                        latestImuMsg_->orientation.w));
00136 
00137       // Convert to RPY
00138       mat.getRPY(utmOdomTfRoll_, utmOdomTfPitch_, utmOdomTfYaw_);
00139 
00140       ROS_INFO_STREAM("Latest IMU orientation was: (" << std::fixed << utmOdomTfRoll_ << ", " << utmOdomTfPitch_ << ", " << utmOdomTfYaw_ << ")");
00141 
00142       // Compute the final yaw value that corrects for the difference between the
00143       // IMU's heading and the UTM grid's belief of where 0 heading should be (i.e.,
00144       // along the x-axis)
00145       utmOdomTfYaw_ += (magneticDeclination_ + yawOffset_ + (M_PI / 2.0));
00146       utmOdomTfPitch_ += pitchOffset_;
00147       utmOdomTfRoll_ += rollOffset_;
00148 
00149       ROS_INFO_STREAM("Corrected for magnetic declination of " << std::fixed << magneticDeclination_ <<
00150                       ", user-specified offset of " << yawOffset_ << ", and fixed offset of " << (M_PI / 2.0) <<
00151                       ". Transform heading factor is now " << utmOdomTfYaw_);
00152 
00153       // Convert to tf-friendly structures
00154       tf::Quaternion quat;
00155       quat.setRPY(utmOdomTfRoll_, utmOdomTfPitch_, utmOdomTfYaw_);
00156       tf::Vector3 pos;
00157       tf::pointMsgToTF(latestUtmMsg_->pose.pose.position, pos);
00158 
00159       // Put the transform together
00160       odomUtmTransform_->frame_id_ = latestOdomMessage_->header.frame_id;
00161       odomUtmTransform_->child_frame_id_ = latestUtmMsg_->header.frame_id;
00162       odomUtmTransform_->setRotation(quat);
00163       odomUtmTransform_->setOrigin(pos);
00164 
00165       ROS_INFO_STREAM("Before correcttion, " << odomUtmTransform_->frame_id_  << "->" <<
00166                       odomUtmTransform_->child_frame_id_ << " transform is: " << std::fixed <<
00167                       "\nPosition: (" << odomUtmTransform_->getOrigin().getX() << ", " <<
00168                                          odomUtmTransform_->getOrigin().getY() << ", " <<
00169                                          odomUtmTransform_->getOrigin().getZ() << ")" <<
00170                       "\nOrientation: (" << utmOdomTfRoll_ << ", " <<
00171                                             utmOdomTfPitch_ << ", " <<
00172                                             utmOdomTfYaw_ << ")");
00173 
00174       // If we started in a location without GPS (or with poor GPS), then we could be at some non-zero
00175       // (x, y) location in the odomFrame_ frame. For that reason, we need to move this transform to the
00176       // origin. To do that, we need to figure out what our odometry origin (the inverse
00177       // of our position) is in the UTM frame.
00178 
00179       // Convert the pose to a tf object
00180       tf::Pose odomPose;
00181       tf::poseMsgToTF(latestOdomMessage_->pose.pose, odomPose);
00182 
00183       // The transform order will be orig_odom_pos * orig_utm_pos_inverse * cur_utm_pos
00184       // Store these values so we can change and re-compose the transform later.
00185       originalOdomPose_ = odomPose;
00186       originalUtmPose_ = *odomUtmTransform_;
00187       odomUtmTransform_->mult(originalOdomPose_, originalUtmPose_.inverse());
00188 
00189       double odomRoll;
00190       double odomPitch;
00191       double odomYaw;
00192       double utmRoll;
00193       double utmPitch;
00194       double utmYaw;
00195 
00196       mat.setRotation(odomPose.getRotation());
00197       mat.getRPY(odomRoll, odomPitch, odomYaw);
00198 
00199       ROS_INFO_STREAM("Latest " << latestOdomMessage_->header.frame_id << " pose is: " << std::fixed <<
00200                       "\nPosition: (" << odomPose.getOrigin().getX() << ", " <<
00201                                          odomPose.getOrigin().getY() << ", " <<
00202                                          odomPose.getOrigin().getZ() << ")" <<
00203                       "\nOrientation: (" << odomRoll << ", " <<
00204                                             odomPitch << ", " <<
00205                                             odomYaw << ")");
00206 
00207       mat.setRotation(odomUtmTransform_->getRotation());
00208       mat.getRPY(utmRoll, utmPitch, utmYaw);
00209 
00210       ROS_INFO_STREAM(odomUtmTransform_->frame_id_  << "->" << odomUtmTransform_->child_frame_id_ <<
00211                        " transform is now: " << std::fixed <<
00212                        "\nPosition: (" << odomUtmTransform_->getOrigin().getX() << ", " <<
00213                                           odomUtmTransform_->getOrigin().getY() << ", " <<
00214                                           odomUtmTransform_->getOrigin().getZ() << ")" <<
00215                        "\nOrientation: (" << utmRoll << ", " <<
00216                                              utmPitch << ", " <<
00217                                              utmYaw << ")");
00218 
00219       transformGood_ = true;
00220     }
00221   }
00222 }
00223 
00226 void utmCallback(const nav_msgs::OdometryConstPtr& msg)
00227 {
00228   if(latestUtmMsg_ == NULL)
00229   {
00230     ROS_INFO("Received initial UTM message");
00231 
00232     latestUtmMsg_ = new nav_msgs::Odometry();
00233   }
00234 
00235   *latestUtmMsg_ = *msg;
00236 
00237   if(zeroAltitude_ && odomUtmTransform_ != NULL)
00238   {
00239     // Grab the original transforms, update the z position
00240     // in the original UTM transform, and then put it back
00241     // together.
00242     tf::Vector3 origin = originalUtmPose_.getOrigin();
00243     origin.setZ(msg->pose.pose.position.z);
00244     originalUtmPose_.setOrigin(origin);
00245 
00246     odomUtmTransform_->mult(originalOdomPose_, originalUtmPose_.inverse());
00247   }
00248 }
00249 
00252 void imuCallback(const sensor_msgs::ImuConstPtr& msg)
00253 {
00254   if(latestImuMsg_ == NULL)
00255   {
00256     ROS_INFO("Received initial IMU message");
00257 
00258     latestImuMsg_ = new sensor_msgs::Imu();
00259   }
00260 
00261   *latestImuMsg_ = *msg;
00262 }
00263 
00266 void odomCallback(const nav_msgs::OdometryConstPtr& msg)
00267 {
00268   if(latestOdomMessage_ == NULL)
00269   {
00270     ROS_INFO("Received initial relay odometry message");
00271 
00272     latestOdomMessage_ = new nav_msgs::Odometry();
00273   }
00274 
00275   *latestOdomMessage_ = *msg;
00276 }
00277 
00280 void gpsFixCallback(const sensor_msgs::NavSatFixConstPtr& msg)
00281 {
00282   hasFix_ = (msg->status.status != sensor_msgs::NavSatStatus::STATUS_NO_FIX &&
00283              !std::isnan(msg->altitude) &&
00284              !std::isnan(msg->latitude) &&
00285              !std::isnan(msg->longitude));
00286 }
00287 
00291 int main(int argc, char **argv)
00292 {
00293   ros::init(argc, argv, "utm_transform_node");
00294 
00295   ros::Time::init();
00296 
00297   ros::NodeHandle nh;
00298   ros::NodeHandle nhPriv("~");
00299 
00300   tf::TransformBroadcaster broadcaster;
00301 
00302   latestUtmMsg_ = NULL;
00303   latestImuMsg_ = NULL;
00304   latestOdomMessage_ = NULL;
00305   odomUtmTransform_ = NULL;
00306   tfListener_ = new tf::TransformListener();
00307 
00308   magneticDeclination_ = 0;
00309   utmOdomTfYaw_ = 0;
00310   rollOffset_ = 0;
00311   pitchOffset_ = 0;
00312   yawOffset_ = 0;
00313   hasFix_ = false;
00314   transformGood_ = false;
00315   double frequency = 0;
00316 
00317   // Subscribe to the messages we need
00318   ros::Subscriber utmSub = nh.subscribe("gps/gps_utm", 10, &utmCallback);
00319   ros::Subscriber imuSub = nh.subscribe("imu/data", 10, &imuCallback);
00320   ros::Subscriber odomSub = nh.subscribe("odometry/filtered", 10, &odomCallback);
00321   ros::Subscriber gpsFixSub = nh.subscribe("gps/fix", 10, &gpsFixCallback);
00322 
00323   // Load the parameters we need
00324   nhPriv.getParam("magnetic_declination_radians", magneticDeclination_);
00325   nhPriv.getParam("roll_offset", rollOffset_);
00326   nhPriv.getParam("pitch_offset", pitchOffset_);
00327   nhPriv.getParam("yaw_offset", yawOffset_);
00328   nhPriv.param("zero_altitude", zeroAltitude_, false);
00329   nhPriv.param("frequency", frequency, 50.0);
00330 
00331   ros::Rate rate(frequency);
00332   while(ros::ok())
00333   {
00334     ros::spinOnce();
00335 
00336     if(!transformGood_)
00337     {
00338       computeOdomUtmTransform();
00339     }
00340     else
00341     {
00342       odomUtmTransform_->stamp_ = ros::Time::now();
00343       broadcaster.sendTransform(*odomUtmTransform_);
00344     }
00345 
00346     rate.sleep();
00347   }
00348 
00349   delete latestUtmMsg_;
00350   delete latestImuMsg_;
00351   delete latestOdomMessage_;
00352   delete odomUtmTransform_;
00353 
00354   return 0;
00355 }
00356 


robot_localization
Author(s): Tom Moore
autogenerated on Mon Oct 6 2014 04:11:15