navsat_transform_new.cpp
Go to the documentation of this file.
00001 #include <summit_xl_localization/navsat_transform_new.h>
00002 
00003 #include <robot_localization/filter_common.h>
00004 #include <robot_localization/filter_utilities.h>
00005 #include <robot_localization/navsat_conversions.h>
00006 #include <robot_localization/ros_filter_utilities.h>
00007 
00008 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
00009 
00010 #include <XmlRpcException.h>
00011 
00012 namespace RobotLocalization
00013 {
00014 NavSatTransformNew::NavSatTransformNew() :
00015     magneticDeclination_(0.0),
00016     utmOdomTfYaw_(0.0),
00017     yawOffset_(0.0),
00018     broadcastUtmTransform_(false),
00019     hasTransformOdom_(false),
00020     hasTransformGps_(false),
00021     hasTransformImu_(false),
00022     transformGood_(false),
00023     gpsUpdated_(false),
00024     odomUpdated_(false),
00025     publishGps_(false),
00026     zeroAltitude_(false),
00027     worldFrameId_("odom"),
00028     baseLinkFrameId_("base_link"),
00029     utmZone_(""),
00030     tfListener_(tfBuffer_),  
00031     travelledDistance_(-1.0),
00032     localOdomTopic_("odometry/filtered"),
00033     hasToPublish_(false),
00034     previous_x_(0.0),
00035     previous_y_(0.0)
00036 {
00037   latestUtmCovariance_.resize(POSE_SIZE, POSE_SIZE);
00038 }
00039 
00040   NavSatTransformNew::~NavSatTransformNew() {}
00041 
00042   void NavSatTransformNew::localOdomCallback(const nav_msgs::OdometryConstPtr& msg)
00043   {
00044     double dist_x=fabs(msg->pose.pose.position.x-previous_x_);
00045     double dist_y=fabs(msg->pose.pose.position.y-previous_y_);
00046 
00047     double distance=sqrt(dist_x*dist_x+dist_y*dist_y);
00048 
00049     if(distance>travelledDistance_)
00050       {
00051         hasToPublish_ = true;
00052         previous_x_=msg->pose.pose.position.x;
00053         previous_y_=msg->pose.pose.position.y;
00054       }
00055   }
00056 
00057   void NavSatTransformNew::odomCallback(const nav_msgs::OdometryConstPtr& msg)
00058   {
00059     if(!transformGood_)
00060     {
00061       setTransformOdometry(msg);
00062     }
00063 
00064     tf2::fromMsg(msg->pose.pose, latestWorldPose_);
00065     odomUpdateTime_ = msg->header.stamp;
00066     odomUpdated_ = true;
00067   }
00068 
00069 void NavSatTransformNew::gpsFixCallback(const sensor_msgs::NavSatFixConstPtr& msg)
00070 {
00071 
00072     // Make sure the GPS data is usable
00073     bool goodGps = (msg->status.status != sensor_msgs::NavSatStatus::STATUS_NO_FIX &&
00074                     !std::isnan(msg->altitude) &&
00075                     !std::isnan(msg->latitude) &&
00076                     !std::isnan(msg->longitude));
00077 
00078     if(goodGps)
00079     {
00080       // If we haven't computed the transform yet, then
00081       // store this message as the initial GPS data to use
00082       if(!transformGood_)
00083       {
00084         setTransformGps(msg);
00085       }
00086 
00087       double utmX = 0;
00088       double utmY = 0;
00089       std::string utmZoneTmp;
00090       NavsatConversions::LLtoUTM(msg->latitude, msg->longitude, utmY, utmX, utmZoneTmp);
00091       latestUtmPose_.setOrigin(tf2::Vector3(utmX, utmY, msg->altitude));
00092       latestUtmCovariance_.setZero();
00093 
00094       // Copy the measurement's covariance matrix so that we can rotate it later
00095       for (size_t i = 0; i < POSITION_SIZE; i++)
00096       {
00097         for (size_t j = 0; j < POSITION_SIZE; j++)
00098         {
00099           latestUtmCovariance_(i, j) = msg->position_covariance[POSITION_SIZE * i + j];
00100         }
00101       }
00102 
00103       gpsUpdateTime_ = msg->header.stamp;
00104       gpsUpdated_ = true;
00105     }
00106   }
00107 
00108 void NavSatTransformNew::setTransformGps(const sensor_msgs::NavSatFixConstPtr& msg)
00109 {
00110   double utmX = 0;
00111   double utmY = 0;
00112   NavsatConversions::LLtoUTM(msg->latitude, msg->longitude, utmY, utmX, utmZone_);
00113   
00114   ROS_INFO_STREAM("Datum (latitude, longitude, altitude) is (" << std::fixed << msg->latitude << ", " << msg->longitude << ", " << msg->altitude << ")");
00115   ROS_INFO_STREAM("Datum UTM coordinate is (" << std::fixed << utmX << ", " << utmY << ")");
00116   
00117   transformUtmPose_.setOrigin(tf2::Vector3(utmX, utmY, msg->altitude));
00118   transformUtmPose_.setRotation(tf2::Quaternion::getIdentity());
00119   hasTransformGps_ = true;
00120 }
00121 
00122 void NavSatTransformNew::setTransformOdometry(const nav_msgs::OdometryConstPtr& msg)
00123 {
00124   tf2::fromMsg(msg->pose.pose, transformWorldPose_);
00125   worldFrameId_ = msg->header.frame_id;
00126   baseLinkFrameId_ = msg->child_frame_id;
00127   hasTransformOdom_ = true;
00128 
00129   ROS_INFO_STREAM("Initial odometry position is (" << std::fixed << 
00130                   transformWorldPose_.getOrigin().getX() << ", " << 
00131                   transformWorldPose_.getOrigin().getY() << ", " << 
00132                   transformWorldPose_.getOrigin().getZ() << ")");
00133 }
00134 
00135 void NavSatTransformNew::imuCallback(const sensor_msgs::ImuConstPtr& msg)
00136 {
00137   // We need the baseLinkFrameId_ from the odometry message, so
00138   // we need to wait until we receive it.
00139   if(hasTransformOdom_)
00140     {
00141       /* This method only gets called if we don't yet have the
00142        * IMU data (the subscriber gets shut down once we compute
00143        * the transform), so we can assumed that every IMU message
00144        * that comes here is meant to be used for that purpose. */
00145       tf2::fromMsg(msg->orientation, transformOrientation_);
00146       
00147       // Correct for the IMU's orientation w.r.t. base_link
00148       tf2::Transform targetFrameTrans;
00149       bool canTransform = RosFilterUtilities::lookupTransformSafe(tfBuffer_,
00150                                                                   baseLinkFrameId_,
00151                                                                   msg->header.frame_id,
00152                                                                   msg->header.stamp,
00153                                                                   targetFrameTrans);
00154 
00155       if(canTransform)
00156       {
00157         double rollOffset = 0;
00158         double pitchOffset = 0;
00159         double yawOffset = 0;
00160         double roll = 0;
00161         double pitch = 0;
00162         double yaw = 0;
00163         RosFilterUtilities::quatToRPY(targetFrameTrans.getRotation(), rollOffset, pitchOffset, yawOffset);
00164         RosFilterUtilities::quatToRPY(transformOrientation_, roll, pitch, yaw);
00165 
00166         ROS_DEBUG_STREAM("Initial orientation roll, pitch, yaw is (" <<
00167                          roll << ", " << pitch << ", " << yaw << ")");
00168 
00169         // Apply the offset (making sure to bound them), and throw them in a vector
00170         tf2::Vector3 rpyAngles(FilterUtilities::clampRotation(roll - rollOffset),
00171                                FilterUtilities::clampRotation(pitch - pitchOffset),
00172                                FilterUtilities::clampRotation(yaw - yawOffset));
00173 
00174         // Now we need to rotate the roll and pitch by the yaw offset value.
00175         // Imagine a case where an IMU is mounted facing sideways. In that case
00176         // pitch for the IMU's world frame is roll for the robot.
00177         tf2::Matrix3x3 mat;
00178         mat.setRPY(0.0, 0.0, yawOffset);
00179         rpyAngles = mat * rpyAngles;
00180         transformOrientation_.setRPY(rpyAngles.getX(), rpyAngles.getY(), rpyAngles.getZ());
00181 
00182         ROS_DEBUG_STREAM("Initial corrected orientation roll, pitch, yaw is (" <<
00183                          rpyAngles.getX() << ", " << rpyAngles.getY() << ", " << rpyAngles.getZ() << ")");
00184 
00185         hasTransformImu_ = true;
00186       }
00187     }
00188 }
00189 
00190 void NavSatTransformNew::computeTransform()
00191 {
00192     // Only do this if:
00193     // 1. We haven't computed the odom_frame->utm_frame transform before
00194     // 2. We've received the data we need
00195     if(!transformGood_ && hasTransformOdom_ && hasTransformGps_ && hasTransformImu_)
00196     {
00197       // Get the IMU's current RPY values. Need the raw values (for yaw, anyway).
00198       tf2::Matrix3x3 mat(transformOrientation_);
00199 
00200       // Convert to RPY
00201       double imuRoll;
00202       double imuPitch;
00203       double imuYaw;
00204       mat.getRPY(imuRoll, imuPitch, imuYaw);
00205 
00206       /* The IMU's heading was likely originally reported w.r.t. magnetic north.
00207        * However, all the nodes in robot_localization assume that orientation data,
00208        * including that reported by IMUs, is reported in an ENU frame, with a 0 yaw
00209        * value being reported when facing east and increasing counter-clockwise (i.e.,
00210        * towards north). Conveniently, this aligns with the UTM grid, where X is east
00211        * and Y is north. However, we have two additional considerations:
00212        *   1. The IMU may have its non-ENU frame data transformed to ENU, but there's
00213        *      a possibility that its data has not been corrected for magnetic
00214        *      declination. We need to account for this. A positive magnetic
00215        *      declination is counter-clockwise in an ENU frame. Therefore, if
00216        *      we have a magnetic declination of N radians, then when the sensor
00217        *      is facing a heading of N, it reports 0. Therefore, we need to add
00218        *      the declination angle.
00219        *   2. To account for any other offsets that may not be accounted for by the
00220        *      IMU driver or any interim processing node, we expose a yaw offset that
00221        *      lets users work with navsat_transform_node.
00222        */
00223       imuYaw += (magneticDeclination_ + yawOffset_);
00224 
00225       ROS_INFO_STREAM("Corrected for magnetic declination of " << std::fixed << magneticDeclination_ <<
00226                       " and user-specified offset of " << yawOffset_ << ". Transform heading factor is now " << imuYaw);
00227 
00228       // Convert to tf-friendly structures
00229       tf2::Quaternion imuQuat;
00230       imuQuat.setRPY(0.0, 0.0, imuYaw);
00231 
00232       // The transform order will be orig_odom_pos * orig_utm_pos_inverse * cur_utm_pos.
00233       // Doing it this way will allow us to cope with having non-zero odometry position
00234       // when we get our first GPS message.
00235       tf2::Transform utmPoseWithOrientation;
00236       utmPoseWithOrientation.setOrigin(transformUtmPose_.getOrigin());
00237       utmPoseWithOrientation.setRotation(imuQuat);
00238       utmWorldTransform_.mult(transformWorldPose_, utmPoseWithOrientation.inverse());
00239 
00240       utmWorldTransInverse_ = utmWorldTransform_.inverse();
00241 
00242       double roll = 0;
00243       double pitch = 0;
00244       double yaw = 0;
00245       mat.setRotation(latestWorldPose_.getRotation());
00246       mat.getRPY(roll, pitch, yaw);
00247 
00248       ROS_INFO_STREAM("Transform world frame pose is: " << std::fixed <<
00249                       "\nPosition: (" << transformWorldPose_.getOrigin().getX() << ", " <<
00250                                          transformWorldPose_.getOrigin().getY() << ", " <<
00251                                          transformWorldPose_.getOrigin().getZ() << ")" <<
00252                       "\nOrientation: (" << roll << ", " <<
00253                                             pitch << ", " <<
00254                                             yaw << ")");
00255 
00256       mat.setRotation(utmWorldTransform_.getRotation());
00257       mat.getRPY(roll, pitch, yaw);
00258 
00259       ROS_INFO_STREAM("World frame->utm transform is " << std::fixed <<
00260                        "\nPosition: (" << utmWorldTransform_.getOrigin().getX() << ", " <<
00261                                           utmWorldTransform_.getOrigin().getY() << ", " <<
00262                                           utmWorldTransform_.getOrigin().getZ() << ")" <<
00263                        "\nOrientation: (" << roll << ", " <<
00264                                              pitch << ", " <<
00265                                              yaw << ")");
00266 
00267       transformGood_ = true;
00268 
00269       // Send out the (static) UTM transform in case anyone else would like to use it.
00270       if(broadcastUtmTransform_)
00271       {
00272         geometry_msgs::TransformStamped utmTransformStamped;
00273         utmTransformStamped.header.stamp = ros::Time::now();
00274         utmTransformStamped.header.frame_id = worldFrameId_;
00275         utmTransformStamped.child_frame_id = "utm";
00276         utmTransformStamped.transform = tf2::toMsg(utmWorldTransform_);
00277         utmBroadcaster_.sendTransform(utmTransformStamped);
00278       }
00279     }
00280 }
00281 
00282 bool NavSatTransformNew::prepareGpsOdometry(nav_msgs::Odometry &gpsOdom)
00283 {
00284   bool newData = false;
00285 
00286   if(transformGood_ && gpsUpdated_)
00287     {
00288       tf2::Transform transformedUtm;
00289 
00290       transformedUtm.mult(utmWorldTransform_, latestUtmPose_);
00291       transformedUtm.setRotation(tf2::Quaternion::getIdentity());
00292 
00293       // Rotate the covariance as well
00294       tf2::Matrix3x3 rot(utmWorldTransform_.getRotation());
00295       Eigen::MatrixXd rot6d(POSE_SIZE, POSE_SIZE);
00296       rot6d.setIdentity();
00297 
00298       for(size_t rInd = 0; rInd < POSITION_SIZE; ++rInd)
00299       {
00300         rot6d(rInd, 0) = rot.getRow(rInd).getX();
00301         rot6d(rInd, 1) = rot.getRow(rInd).getY();
00302         rot6d(rInd, 2) = rot.getRow(rInd).getZ();
00303         rot6d(rInd+POSITION_SIZE, 3) = rot.getRow(rInd).getX();
00304         rot6d(rInd+POSITION_SIZE, 4) = rot.getRow(rInd).getY();
00305         rot6d(rInd+POSITION_SIZE, 5) = rot.getRow(rInd).getZ();
00306       }
00307 
00308       // Rotate the covariance
00309       latestUtmCovariance_ = rot6d * latestUtmCovariance_.eval() * rot6d.transpose();
00310 
00311       // Now fill out the message. Set the orientation to the identity.
00312       tf2::toMsg(transformedUtm, gpsOdom.pose.pose);
00313       gpsOdom.pose.pose.position.z = (zeroAltitude_ ? 0.0 : gpsOdom.pose.pose.position.z);
00314 
00315       // Copy the measurement's covariance matrix so that we can rotate it later
00316       for (size_t i = 0; i < POSE_SIZE; i++)
00317       {
00318         for (size_t j = 0; j < POSE_SIZE; j++)
00319         {
00320           gpsOdom.pose.covariance[POSE_SIZE * i + j] = latestUtmCovariance_(i, j);
00321         }
00322       }
00323 
00324       gpsOdom.header.frame_id = worldFrameId_;
00325       gpsOdom.header.stamp = gpsUpdateTime_;
00326 
00327       // Mark this GPS as used
00328       gpsUpdated_ = false;
00329       newData = true;
00330     }
00331 
00332     return newData;
00333 }
00334  
00335 void NavSatTransformNew::run()
00336 {
00337   ros::Time::init();
00338 
00339     double frequency = 10.0;
00340     double delay = 0.0;
00341 
00342     ros::NodeHandle nh;
00343     ros::NodeHandle nhPriv("~");
00344    // Load the parameters we need
00345     nhPriv.getParam("magnetic_declination_radians", magneticDeclination_);
00346     nhPriv.param("yaw_offset", yawOffset_, 0.0);
00347     nhPriv.param("broadcast_utm_transform", broadcastUtmTransform_, false);
00348     nhPriv.param("zero_altitude", zeroAltitude_, false);
00349     nhPriv.param("publish_filtered_gps", publishGps_, false);
00350     nhPriv.param("frequency", frequency, 10.0);
00351     nhPriv.param("delay", delay, 0.0);
00352     
00353     //NEW PARAMETERS NEEDED
00354     nhPriv.param("travelled_distance",travelledDistance_,-1.0);
00355     nhPriv.param<std::string>("localOdomTopic",localOdomTopic_,"");
00356 
00357     ros::Subscriber odomSub = nh.subscribe("odometry/filtered", 1, &NavSatTransformNew::odomCallback, this);
00358     ros::Subscriber gpsSub = nh.subscribe("gps/fix", 1, &NavSatTransformNew::gpsFixCallback, this);
00359     ros::Subscriber imuSub = nh.subscribe<sensor_msgs::Imu>("imu/data", 1, &NavSatTransformNew::imuCallback, this);
00360 
00361     ros::Subscriber localOdomSub = nh.subscribe(localOdomTopic_, 1,&NavSatTransformNew::localOdomCallback, this);
00362     
00363     ros::Publisher gpsOdomPub = nh.advertise<nav_msgs::Odometry>("odometry/gps", 10);
00364     ros::Publisher filteredGpsPub;
00365     
00366     // Sleep for the parameterized amount of time, to give
00367     // other nodes time to start up (not always necessary)
00368     ros::Duration startDelay(delay);
00369     startDelay.sleep();
00370 
00371     ros::Rate rate(frequency);
00372     while(ros::ok())
00373     {
00374       ros::spinOnce();
00375 
00376       if(!transformGood_)
00377       {
00378         computeTransform();
00379 
00380         if(transformGood_)
00381         {
00382           // Once we have the transform, we don't need the IMU
00383           imuSub.shutdown();
00384         }
00385       }
00386       else
00387       {
00388         if(hasToPublish_)
00389           {
00390             nav_msgs::Odometry gpsOdom;
00391             if(prepareGpsOdometry(gpsOdom))
00392               {
00393                 gpsOdomPub.publish(gpsOdom);
00394                 ROS_INFO("Publish GPS odometry");
00395               }
00396             hasToPublish_=false;
00397           }
00398       }
00399       rate.sleep();
00400     }
00401   }
00402   
00403 }


summit_xl_localization
Author(s):
autogenerated on Thu Jun 6 2019 21:18:15