ros_filter.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2015, 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 "robot_localization/ros_filter.h"
00034 #include "robot_localization/filter_utilities.h"
00035 #include "robot_localization/ekf.h"
00036 #include "robot_localization/ukf.h"
00037 
00038 namespace RobotLocalization
00039 {
00040   template<typename T>
00041   RosFilter<T>::RosFilter(std::vector<double> args) :
00042       staticDiagErrorLevel_(diagnostic_msgs::DiagnosticStatus::OK),
00043       dynamicDiagErrorLevel_(diagnostic_msgs::DiagnosticStatus::OK),
00044       filter_(args),
00045       frequency_(30.0),
00046       lastSetPoseTime_(0),
00047       nhLocal_("~"),
00048       printDiagnostics_(true),
00049       twoDMode_(false)
00050   {
00051     stateVariableNames_.push_back("X");
00052     stateVariableNames_.push_back("Y");
00053     stateVariableNames_.push_back("Z");
00054     stateVariableNames_.push_back("ROLL");
00055     stateVariableNames_.push_back("PITCH");
00056     stateVariableNames_.push_back("YAW");
00057     stateVariableNames_.push_back("X_VELOCITY");
00058     stateVariableNames_.push_back("Y_VELOCITY");
00059     stateVariableNames_.push_back("Z_VELOCITY");
00060     stateVariableNames_.push_back("ROLL_VELOCITY");
00061     stateVariableNames_.push_back("PITCH_VELOCITY");
00062     stateVariableNames_.push_back("YAW_VELOCITY");
00063     stateVariableNames_.push_back("X_ACCELERATION");
00064     stateVariableNames_.push_back("Y_ACCELERATION");
00065     stateVariableNames_.push_back("Z_ACCELERATION");
00066 
00067     diagnosticUpdater_.setHardwareID("none");
00068   }
00069 
00070   template<typename T>
00071   RosFilter<T>::~RosFilter()
00072   {
00073     poseMessageFilters_.clear();
00074     twistMessageFilters_.clear();
00075     accelerationMessageFilters_.clear();
00076     poseTopicSubs_.clear();
00077     twistTopicSubs_.clear();
00078     imuTopicSubs_.clear();
00079     odomTopicSubs_.clear();
00080   }
00081 
00082   template<typename T>
00083   void RosFilter<T>::accelerationCallback(const sensor_msgs::Imu::ConstPtr &msg,
00084                                        const std::string &topicName,
00085                                        const std::string &targetFrame,
00086                                        const std::vector<int> &updateVector,
00087                                        const double mahalanobisThresh)
00088   {
00089     // If we've just reset the filter, then we want to ignore any messages
00090     // that arrive with an older timestamp
00091     if(msg->header.stamp <= lastSetPoseTime_)
00092     {
00093       return;
00094     }
00095 
00096     RF_DEBUG("------ RosFilter::accelerationCallback (" << topicName << ") ------\n"
00097              "Twist message:\n" << *msg);
00098 
00099     if (lastMessageTimes_.count(topicName) == 0)
00100     {
00101       lastMessageTimes_.insert(std::pair<std::string, ros::Time>(topicName, msg->header.stamp));
00102     }
00103 
00104     // Make sure this message is newer than the last one
00105     if (msg->header.stamp >= lastMessageTimes_[topicName])
00106     {
00107       RF_DEBUG("Update vector for " << topicName << " is:\n" << updateVector);
00108 
00109       Eigen::VectorXd measurement(STATE_SIZE);
00110       Eigen::MatrixXd measurementCovariance(STATE_SIZE, STATE_SIZE);
00111 
00112       measurement.setZero();
00113       measurementCovariance.setZero();
00114 
00115       // Make sure we're actually updating at least one of these variables
00116       if (updateVector[StateMemberAx] || updateVector[StateMemberAy] || updateVector[StateMemberAz])
00117       {
00118         std::vector<int> updateVectorCorrected = updateVector;
00119 
00120         // Prepare the twist data for inclusion in the filter
00121         if (prepareAcceleration(msg, topicName, targetFrame, updateVectorCorrected, measurement, measurementCovariance))
00122         {
00123           // Store the measurement. Add an "acceleration" suffix so we know what kind of measurement
00124           // we're dealing with when we debug the core filter logic.
00125           enqueueMeasurement(topicName, measurement, measurementCovariance, updateVectorCorrected, mahalanobisThresh, msg->header.stamp);
00126 
00127           RF_DEBUG("Enqueued new measurement for " << topicName << "_acceleration\n");
00128         }
00129         else
00130         {
00131           RF_DEBUG("Did *not* enqueue measurement for " << topicName << "_acceleration\n");
00132         }
00133       }
00134       else
00135       {
00136         RF_DEBUG("Update vector for " << topicName << " is such that none of its state variables will be updated\n");
00137       }
00138 
00139       lastMessageTimes_[topicName] = msg->header.stamp;
00140 
00141       RF_DEBUG("Last message time for " << topicName << " is now " << lastMessageTimes_[topicName] << "\n");
00142     }
00143     else
00144     {
00145       RF_DEBUG("Message is too old. Last message time for " << topicName <<
00146                " is " << lastMessageTimes_[topicName] << ", current message time is " <<
00147                msg->header.stamp << ".\n");
00148     }
00149 
00150     RF_DEBUG("\n----- /RosFilter::accelerationCallback (" << topicName << ") ------\n");
00151   }
00152 
00153   template<typename T>
00154   void RosFilter<T>::enqueueMeasurement(const std::string &topicName,
00155                                      const Eigen::VectorXd &measurement,
00156                                      const Eigen::MatrixXd &measurementCovariance,
00157                                      const std::vector<int> &updateVector,
00158                                      const double mahalanobisThresh,
00159                                      const ros::Time &time)
00160   {
00161     Measurement meas;
00162 
00163     meas.topicName_ = topicName;
00164     meas.measurement_ = measurement;
00165     meas.covariance_ = measurementCovariance;
00166     meas.updateVector_ = updateVector;
00167     meas.time_ = time.toSec();
00168     meas.mahalanobisThresh_ = mahalanobisThresh;
00169     measurementQueue_.push(meas);
00170   }
00171 
00172   template<typename T>
00173   void RosFilter<T>::forceTwoD(Eigen::VectorXd &measurement,
00174                                Eigen::MatrixXd &measurementCovariance,
00175                                std::vector<int> &updateVector)
00176   {
00177     measurement(StateMemberZ) = 0.0;
00178     measurement(StateMemberRoll) = 0.0;
00179     measurement(StateMemberPitch) = 0.0;
00180     measurement(StateMemberVz) = 0.0;
00181     measurement(StateMemberVroll) = 0.0;
00182     measurement(StateMemberVpitch) = 0.0;
00183     measurement(StateMemberAz) = 0.0;
00184 
00185     measurementCovariance(StateMemberZ, StateMemberZ) = 1e-6;
00186     measurementCovariance(StateMemberRoll, StateMemberRoll) = 1e-6;
00187     measurementCovariance(StateMemberPitch, StateMemberPitch) = 1e-6;
00188     measurementCovariance(StateMemberVz, StateMemberVz) = 1e-6;
00189     measurementCovariance(StateMemberVroll, StateMemberVroll) = 1e-6;
00190     measurementCovariance(StateMemberVpitch, StateMemberVpitch) = 1e-6;
00191     measurementCovariance(StateMemberAz, StateMemberAz) = 1e-6;
00192 
00193     updateVector[StateMemberZ] = 1;
00194     updateVector[StateMemberRoll] = 1;
00195     updateVector[StateMemberPitch] = 1;
00196     updateVector[StateMemberVz] = 1;
00197     updateVector[StateMemberVroll] = 1;
00198     updateVector[StateMemberVpitch] = 1;
00199     updateVector[StateMemberAz] = 1;
00200   }
00201 
00202   template<typename T>
00203   bool RosFilter<T>::getFilteredOdometryMessage(nav_msgs::Odometry &message)
00204   {
00205     // If the filter has received a measurement at some point...
00206     if (filter_.getInitializedStatus())
00207     {
00208       // Grab our current state and covariance estimates
00209       const Eigen::VectorXd &state = filter_.getState();
00210       const Eigen::MatrixXd &estimateErrorCovariance = filter_.getEstimateErrorCovariance();
00211 
00212       // Convert from roll, pitch, and yaw back to quaternion for
00213       // orientation values
00214       tf::Quaternion quat;
00215       quat.setRPY(state(StateMemberRoll), state(StateMemberPitch), state(StateMemberYaw));
00216 
00217       // Fill out the message
00218       message.pose.pose.position.x = state(StateMemberX);
00219       message.pose.pose.position.y = state(StateMemberY);
00220       message.pose.pose.position.z = state(StateMemberZ);
00221       message.pose.pose.orientation.x = quat.x();
00222       message.pose.pose.orientation.y = quat.y();
00223       message.pose.pose.orientation.z = quat.z();
00224       message.pose.pose.orientation.w = quat.w();
00225       message.twist.twist.linear.x = state(StateMemberVx);
00226       message.twist.twist.linear.y = state(StateMemberVy);
00227       message.twist.twist.linear.z = state(StateMemberVz);
00228       message.twist.twist.angular.x = state(StateMemberVroll);
00229       message.twist.twist.angular.y = state(StateMemberVpitch);
00230       message.twist.twist.angular.z = state(StateMemberVyaw);
00231 
00232       // Our covariance matrix layout doesn't quite match
00233       for (size_t i = 0; i < POSE_SIZE; i++)
00234       {
00235         for (size_t j = 0; j < POSE_SIZE; j++)
00236         {
00237           message.pose.covariance[POSE_SIZE * i + j] = estimateErrorCovariance(i, j);
00238         }
00239       }
00240 
00241       // POSE_SIZE and TWIST_SIZE are currently the same size, but we can spare a few
00242       // cycles to be meticulous and not index a twist covariance array on the size of
00243       // a pose covariance array
00244       for (size_t i = 0; i < TWIST_SIZE; i++)
00245       {
00246         for (size_t j = 0; j < TWIST_SIZE; j++)
00247         {
00248           message.twist.covariance[TWIST_SIZE * i + j] = estimateErrorCovariance(i + POSITION_V_OFFSET, j + POSITION_V_OFFSET);
00249         }
00250       }
00251 
00252       message.header.stamp = ros::Time::now();
00253       message.header.frame_id = worldFrameId_;
00254       message.child_frame_id = baseLinkFrameId_;
00255     }
00256 
00257     return filter_.getInitializedStatus();
00258   }
00259 
00260   template<typename T>
00261   void RosFilter<T>::imuCallback(const sensor_msgs::Imu::ConstPtr &msg,
00262                               const std::string &topicName)
00263   {
00264     // If we've just reset the filter, then we want to ignore any messages
00265     // that arrive with an older timestamp
00266     if(msg->header.stamp <= lastSetPoseTime_)
00267     {
00268       return;
00269     }
00270 
00271     RF_DEBUG("------ RosFilter::imuCallback (" << topicName << ") ------\n" <<
00272              "IMU message:\n" << *msg);
00273 
00274     // As with the odometry message, we can separate out the pose- and twist-related variables
00275     // in the IMU message and pass them to the pose and twist callbacks (filters)
00276 
00277     std::string imuPoseTopicName = topicName + "_pose";
00278     if(poseMessageFilters_.count(imuPoseTopicName) > 0)
00279     {
00280       // Extract the pose (orientation) data, pass it to its filter
00281       geometry_msgs::PoseWithCovarianceStamped *posPtr = new geometry_msgs::PoseWithCovarianceStamped();
00282       posPtr->header = msg->header;
00283       posPtr->pose.pose.orientation = msg->orientation;
00284 
00285       // Copy the covariance for roll, pitch, and yaw
00286       for (size_t i = 0; i < ORIENTATION_SIZE; i++)
00287       {
00288         for (size_t j = 0; j < ORIENTATION_SIZE; j++)
00289         {
00290           posPtr->pose.covariance[POSE_SIZE * (i + ORIENTATION_SIZE) + (j + ORIENTATION_SIZE)] = msg->orientation_covariance[ORIENTATION_SIZE * i + j];
00291         }
00292       }
00293 
00294       geometry_msgs::PoseWithCovarianceStampedConstPtr pptr(posPtr);
00295       poseMessageFilters_[imuPoseTopicName]->add(pptr);
00296     }
00297 
00298     std::string imuTwistTopicName = topicName + "_twist";
00299     if(twistMessageFilters_.count(imuTwistTopicName) > 0)
00300     {
00301       // Repeat for velocity
00302       geometry_msgs::TwistWithCovarianceStamped *twistPtr = new geometry_msgs::TwistWithCovarianceStamped();
00303       twistPtr->header = msg->header;
00304       twistPtr->twist.twist.angular = msg->angular_velocity;
00305 
00306       // Copy the covariance
00307       for (size_t i = 0; i < ORIENTATION_SIZE; i++)
00308       {
00309         for (size_t j = 0; j < ORIENTATION_SIZE; j++)
00310         {
00311           twistPtr->twist.covariance[TWIST_SIZE * (i + ORIENTATION_SIZE) + (j + ORIENTATION_SIZE)] = msg->angular_velocity_covariance[ORIENTATION_SIZE * i + j];
00312         }
00313       }
00314 
00315       geometry_msgs::TwistWithCovarianceStampedConstPtr tptr(twistPtr);
00316       twistMessageFilters_[imuTwistTopicName]->add(tptr);
00317     }
00318 
00319     std::string imuAccelTopicName = topicName + "_acceleration";
00320     if(accelerationMessageFilters_.count(imuAccelTopicName) > 0)
00321     {
00322       // We still need to handle the acceleration data, but we don't
00323       // actually have a good container message for it, so just pass
00324       // the IMU message on through a message filter.
00325       accelerationMessageFilters_[imuAccelTopicName]->add(msg);
00326     }
00327 
00328     RF_DEBUG("\n----- /RosFilter::imuCallback (" << topicName << ") ------\n");
00329   }
00330 
00331   template<typename T>
00332   void RosFilter<T>::integrateMeasurements(const double currentTime)
00333   {
00334     RF_DEBUG("------ RosFilter::integrateMeasurements ------\n\n"
00335              "Integration time is " << std::setprecision(20) << currentTime << "\n"
00336              << measurementQueue_.size() << " measurements in queue.\n");
00337 
00338     // If we have any measurements in the queue, process them
00339     if (!measurementQueue_.empty())
00340     {
00341       while (!measurementQueue_.empty())
00342       {
00343         Measurement measurement = measurementQueue_.top();
00344         measurementQueue_.pop();
00345 
00346         // This will call predict and, if necessary, correct
00347         filter_.processMeasurement(measurement);
00348       }
00349 
00350       filter_.setLastUpdateTime(currentTime);
00351     }
00352     else if (filter_.getInitializedStatus())
00353     {
00354       // In the event that we don't get any measurements for a long time,
00355       // we still need to continue to estimate our state. Therefore, we
00356       // should project the state forward here.
00357       double lastUpdateDelta = currentTime - filter_.getLastUpdateTime();
00358 
00359       // If we get a large delta, then continuously predict until
00360       if(lastUpdateDelta >= filter_.getSensorTimeout())
00361       {
00362         RF_DEBUG("Sensor timeout! Last update time was " << filter_.getLastUpdateTime() <<
00363                  ", current time is " << currentTime <<
00364                  ", delta is " << lastUpdateDelta << "\n");
00365 
00366         filter_.validateDelta(lastUpdateDelta);
00367         filter_.predict(lastUpdateDelta);
00368 
00369         // Update the last measurement time and last update time
00370         filter_.setLastMeasurementTime(filter_.getLastMeasurementTime() + lastUpdateDelta);
00371         filter_.setLastUpdateTime(filter_.getLastUpdateTime() + lastUpdateDelta);
00372       }
00373     }
00374     else
00375     {
00376       RF_DEBUG("Filter not yet initialized.\n");
00377     }
00378 
00379     RF_DEBUG("\n----- /RosFilter::integrateMeasurements ------\n");
00380   }
00381 
00382   template<typename T>
00383   void RosFilter<T>::loadParams()
00384   {
00385     /* For diagnostic purposes, collect information about how many different
00386      * sources are measuring each absolute pose variable and do not have
00387      * differential integration enabled.
00388      */
00389     std::map<StateMembers, int> absPoseVarCounts;
00390     absPoseVarCounts[StateMemberX] = 0;
00391     absPoseVarCounts[StateMemberY] = 0;
00392     absPoseVarCounts[StateMemberZ] = 0;
00393     absPoseVarCounts[StateMemberRoll] = 0;
00394     absPoseVarCounts[StateMemberPitch] = 0;
00395     absPoseVarCounts[StateMemberYaw] = 0;
00396 
00397     // Same for twist variables
00398     std::map<StateMembers, int> twistVarCounts;
00399     twistVarCounts[StateMemberVx] = 0;
00400     twistVarCounts[StateMemberVy] = 0;
00401     twistVarCounts[StateMemberVz] = 0;
00402     twistVarCounts[StateMemberVroll] = 0;
00403     twistVarCounts[StateMemberVpitch] = 0;
00404     twistVarCounts[StateMemberVyaw] = 0;
00405 
00406     // Determine if we'll be printing diagnostic information
00407     nhLocal_.param("print_diagnostics", printDiagnostics_, true);
00408 
00409     // Grab the debug param. If true, the node will produce a LOT of output.
00410     bool debug;
00411     nhLocal_.param("debug", debug, false);
00412 
00413     if (debug)
00414     {
00415       std::string debugOutFile;
00416 
00417       try
00418       {
00419         nhLocal_.param("debug_out_file", debugOutFile, std::string("robot_localization_debug.txt"));
00420         debugStream_.open(debugOutFile.c_str());
00421 
00422         // Make sure we succeeded
00423         if(debugStream_.is_open())
00424         {
00425           filter_.setDebug(debug, &debugStream_);
00426         }
00427         else
00428         {
00429           ROS_WARN_STREAM("RosFilter::loadParams() - unable to create debug output file " << debugOutFile);
00430         }
00431       }
00432       catch(const std::exception &e)
00433       {
00434         ROS_WARN_STREAM("RosFilter::loadParams() - unable to create debug output file" << debugOutFile
00435                         << ". Error was " << e.what() << "\n");
00436       }
00437     }
00438 
00439     // Try to resolve the tf_prefix
00440     nh_.param("/tf_prefix", tfPrefix_, std::string(""));
00441 
00442     if (tfPrefix_.empty())
00443     {
00444       nh_.param("tf_prefix", tfPrefix_, std::string(""));
00445     }
00446 
00447     // These params specify the name of the robot's body frame (typically
00448     // base_link) and odometry frame (typically odom)
00449     nhLocal_.param("map_frame", mapFrameId_, std::string("map"));
00450     nhLocal_.param("odom_frame", odomFrameId_, std::string("odom"));
00451     nhLocal_.param("base_link_frame", baseLinkFrameId_, std::string("base_link"));
00452 
00453     /*
00454      * These parameters are designed to enforce compliance with REP-105:
00455      * http://www.ros.org/reps/rep-0105.html
00456      * When fusing absolute position data from sensors such as GPS, the state
00457      * estimate can undergo discrete jumps. According to REP-105, we want three
00458      * coordinate frames: map, odom, and base_link. The map frame can have
00459      * discontinuities, but is the frame with the most accurate position estimate
00460      * for the robot and should not suffer from drift. The odom frame drifts over
00461      * time, but is guaranteed to be continuous and is accurate enough for local
00462      * planning and navigation. The base_link frame is affixed to the robot. The
00463      * intention is that some odometry source broadcasts the odom->base_link
00464      * transform. The localization software should broadcast map->base_link.
00465      * However, tf does not allow multiple parents for a coordinate frame, so
00466      * we must *compute* map->base_link, but then use the existing odom->base_link
00467      * transform to compute *and broadcast* map->odom.
00468      *
00469      * The state estimation nodes in robot_localization therefore have two "modes."
00470      * If your world_frame parameter value matches the odom_frame parameter value,
00471      * then robot_localization will assume someone else is broadcasting a transform
00472      * from odom_frame->base_link_frame, and it will compute the
00473      * map_frame->odom_frame transform. Otherwise, it will simply compute the
00474      * odom_frame->base_link_frame transform.
00475      *
00476      * The default is the latter behavior (broadcast of odom->base_link).
00477      */
00478     nhLocal_.param("world_frame", worldFrameId_, odomFrameId_);
00479 
00480     ROS_FATAL_COND(mapFrameId_ == odomFrameId_ ||
00481                    odomFrameId_ == baseLinkFrameId_ ||
00482                    mapFrameId_ == baseLinkFrameId_,
00483                    "Invalid frame configuration! The values for map_frame, odom_frame, and base_link_frame must be unique");
00484 
00485     // Append tf_prefix if it's specified
00486     if (!tfPrefix_.empty() && baseLinkFrameId_.at(0) != '/')
00487     {
00488       if(mapFrameId_.at(0) != '/')
00489       {
00490         mapFrameId_ = "/" + tfPrefix_ + "/" + mapFrameId_;
00491       }
00492 
00493       if(odomFrameId_.at(0) != '/')
00494       {
00495         odomFrameId_ = "/" + tfPrefix_ + "/" + odomFrameId_;
00496       }
00497 
00498       if(baseLinkFrameId_.at(0) != '/')
00499       {
00500         baseLinkFrameId_ = "/" + tfPrefix_ + "/" + baseLinkFrameId_;
00501       }
00502     }
00503 
00504     // Transform future (or past) dating
00505     double offsetTmp;
00506     nhLocal_.param("transform_time_offset", offsetTmp, 0.0);
00507     tfTimeOffset_.fromSec(offsetTmp);
00508 
00509     // Update frequency and sensor timeout
00510     double sensorTimeout;
00511     nhLocal_.param("frequency", frequency_, 30.0);
00512     nhLocal_.param("sensor_timeout", sensorTimeout, 1.0 / frequency_);
00513     filter_.setSensorTimeout(sensorTimeout);
00514 
00515     // Determine if we're in 2D mode
00516     nhLocal_.param("two_d_mode", twoDMode_, false);
00517 
00518     // Debugging writes to file
00519     RF_DEBUG("tf_prefix is " << tfPrefix_ <<
00520              "\nmap_frame is " << mapFrameId_ <<
00521              "\nodom_frame is " << odomFrameId_ <<
00522              "\nbase_link_frame is " << baseLinkFrameId_ <<
00523              "\nworld_frame is " << worldFrameId_ <<
00524              "\ntransform_time_offset is " << tfTimeOffset_.toSec() <<
00525              "\nfrequency is " << frequency_ <<
00526              "\nsensor_timeout is " << filter_.getSensorTimeout() <<
00527              "\ntwo_d_mode is " << (twoDMode_ ? "true" : "false") <<
00528              "\nprint_diagnostics is " << (printDiagnostics_ ? "true" : "false") << "\n");
00529 
00530     // Create a subscriber for manually setting/resetting pose
00531     setPoseSub_ = nh_.subscribe<geometry_msgs::PoseWithCovarianceStamped>("set_pose", 1, &RosFilter<T>::setPoseCallback, this);
00532 
00533     // Create a service for manually setting/resetting pose
00534     setPoseSrv_ = nh_.advertiseService("set_pose", &RosFilter<T>::setPoseSrvCallback, this);
00535 
00536     // Init the last last measurement time so we don't get a huge initial delta
00537     filter_.setLastMeasurementTime(ros::Time::now().toSec());
00538     filter_.setLastUpdateTime(ros::Time::now().toSec());
00539 
00540     // Now pull in each topic to which we want to subscribe.
00541     // Start with odom.
00542     size_t topicInd = 0;
00543     bool moreParams = false;
00544     do
00545     {
00546       // Build the string in the form of "odomX", where X is the odom topic number,
00547       // then check if we have any parameters with that value. Users need to make
00548       // sure they don't have gaps in their configs (e.g., odom0 and then odom2)
00549       std::stringstream ss;
00550       ss << "odom" << topicInd++;
00551       std::string odomTopicName = ss.str();
00552       moreParams = nhLocal_.hasParam(odomTopicName);
00553 
00554       if (moreParams)
00555       {
00556         // Determine if we want to integrate this sensor differentially
00557         bool differential;
00558         nhLocal_.param(odomTopicName + std::string("_differential"), differential, false);
00559 
00560         // Determine if we want to integrate this sensor relatively
00561         bool relative;
00562         nhLocal_.param(odomTopicName + std::string("_relative"), relative, false);
00563 
00564         if(relative && differential)
00565         {
00566           ROS_WARN_STREAM("Both " << odomTopicName << "_differential" << " and " << odomTopicName <<
00567                           "_relative were set to true. Using differential mode.");
00568 
00569           relative = false;
00570         }
00571 
00572         std::string odomTopic;
00573         nhLocal_.getParam(odomTopicName, odomTopic);
00574 
00575         // Check for pose rejection threshold
00576         double poseMahalanobisThresh;
00577         nhLocal_.param(odomTopicName + std::string("_pose_rejection_threshold"), poseMahalanobisThresh, std::numeric_limits<double>::max());
00578 
00579         // Check for twist rejection threshold
00580         double twistMahalanobisThresh;
00581         nhLocal_.param(odomTopicName + std::string("_twist_rejection_threshold"), twistMahalanobisThresh, std::numeric_limits<double>::max());
00582 
00583         // Now pull in its boolean update vector configuration. Create separate vectors for pose
00584         // and twist data, and then zero out the opposite values in each vector (no pose data in
00585         // the twist update vector and vice-versa).
00586         std::vector<int> updateVec = loadUpdateConfig(odomTopicName);
00587         std::vector<int> poseUpdateVec = updateVec;
00588         std::fill(poseUpdateVec.begin() + POSITION_V_OFFSET, poseUpdateVec.begin() + POSITION_V_OFFSET + TWIST_SIZE, 0);
00589         std::vector<int> twistUpdateVec = updateVec;
00590         std::fill(twistUpdateVec.begin() + POSITION_OFFSET, twistUpdateVec.begin() + POSITION_OFFSET + POSE_SIZE, 0);
00591 
00592         int poseUpdateSum = std::accumulate(poseUpdateVec.begin(), poseUpdateVec.end(), 0);
00593         int twistUpdateSum = std::accumulate(twistUpdateVec.begin(), twistUpdateVec.end(), 0);
00594 
00595         // Store the odometry topic subscribers so they dont go out of scope. Also,
00596         // odometry data has both pose and twist data, each with their own frame_id.
00597         // The odometry data gets broken up and passed into callbacks for pose and
00598         // twist data, so we need to create message filters for them, and then
00599         // manually add the pose and twist messages after we extract them from the
00600         // odometry message.
00601 
00602         int odomQueueSize = 1;
00603         nhLocal_.param(odomTopicName + "_queue_size", odomQueueSize, 1);
00604 
00605         if(poseUpdateSum + twistUpdateSum > 0)
00606         {
00607           odomTopicSubs_.push_back(
00608                 nh_.subscribe<nav_msgs::Odometry>(odomTopic, odomQueueSize,
00609                                                   boost::bind(&RosFilter<T>::odometryCallback, this, _1, odomTopicName)));
00610         }
00611         else
00612         {
00613           std::stringstream stream;
00614           stream << odomTopic << " is listed as an input topic, but all update variables are false";
00615 
00616           addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
00617                         odomTopic + "_configuration",
00618                         stream.str(),
00619                         true);
00620         }
00621 
00622         if(poseUpdateSum > 0)
00623         {
00624           poseMFPtr poseFilPtr(new tf::MessageFilter<geometry_msgs::PoseWithCovarianceStamped>(tfListener_, worldFrameId_, odomQueueSize));
00625           std::string odomPoseTopicName = odomTopicName + "_pose";
00626           poseFilPtr->registerCallback(boost::bind(&RosFilter<T>::poseCallback, this, _1, odomPoseTopicName, worldFrameId_, poseUpdateVec, differential, relative, false, poseMahalanobisThresh));
00627           poseFilPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformPoseFailureCallback, this, _1, _2, odomTopicName, worldFrameId_));
00628           poseMessageFilters_[odomPoseTopicName] = poseFilPtr;
00629 
00630           if(differential)
00631           {
00632             twistVarCounts[StateMemberVx] += poseUpdateVec[StateMemberX];
00633             twistVarCounts[StateMemberVy] += poseUpdateVec[StateMemberY];
00634             twistVarCounts[StateMemberVz] += poseUpdateVec[StateMemberZ];
00635             twistVarCounts[StateMemberVroll] += poseUpdateVec[StateMemberRoll];
00636             twistVarCounts[StateMemberVpitch] += poseUpdateVec[StateMemberPitch];
00637             twistVarCounts[StateMemberVyaw] += poseUpdateVec[StateMemberYaw];
00638           }
00639           else
00640           {
00641             absPoseVarCounts[StateMemberX] += poseUpdateVec[StateMemberX];
00642             absPoseVarCounts[StateMemberY] += poseUpdateVec[StateMemberY];
00643             absPoseVarCounts[StateMemberZ] += poseUpdateVec[StateMemberZ];
00644             absPoseVarCounts[StateMemberRoll] += poseUpdateVec[StateMemberRoll];
00645             absPoseVarCounts[StateMemberPitch] += poseUpdateVec[StateMemberPitch];
00646             absPoseVarCounts[StateMemberYaw] += poseUpdateVec[StateMemberYaw];
00647           }
00648         }
00649 
00650         if(twistUpdateSum > 0)
00651         {
00652           twistMFPtr twistFilPtr(new tf::MessageFilter<geometry_msgs::TwistWithCovarianceStamped>(tfListener_, baseLinkFrameId_, odomQueueSize));
00653           std::string odomTwistTopicName = odomTopicName + "_twist";
00654           twistFilPtr->registerCallback(boost::bind(&RosFilter<T>::twistCallback, this, _1, odomTwistTopicName, baseLinkFrameId_, twistUpdateVec, twistMahalanobisThresh));
00655           twistFilPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformTwistFailureCallback, this, _1, _2, odomTopicName, baseLinkFrameId_));
00656           twistMessageFilters_[odomTwistTopicName] = twistFilPtr;
00657 
00658           twistVarCounts[StateMemberVx] += twistUpdateVec[StateMemberVx];
00659           twistVarCounts[StateMemberVy] += twistUpdateVec[StateMemberVx];
00660           twistVarCounts[StateMemberVz] += twistUpdateVec[StateMemberVz];
00661           twistVarCounts[StateMemberVroll] += twistUpdateVec[StateMemberVroll];
00662           twistVarCounts[StateMemberVpitch] += twistUpdateVec[StateMemberVpitch];
00663           twistVarCounts[StateMemberVyaw] += twistUpdateVec[StateMemberVyaw];
00664         }
00665 
00666         RF_DEBUG("Subscribed to " << odomTopic << " (" << odomTopicName << ")\n\t" <<
00667                  odomTopicName << "_differential is " << (differential ? "true" : "false") << "\n\t" <<
00668                  odomTopicName << "_pose_rejection_threshold is " << poseMahalanobisThresh << "\n\t" <<
00669                  odomTopicName << "_twist_rejection_threshold is " << twistMahalanobisThresh << "\n\t" <<
00670                  odomTopicName << "_queue_size is " << odomQueueSize << "\n\t" <<
00671                  odomTopicName << " pose update vector is " << poseUpdateVec << "\t"<<
00672                  odomTopicName << " twist update vector is " << twistUpdateVec);
00673       }
00674     } while (moreParams);
00675 
00676     // Repeat for pose
00677     topicInd = 0;
00678     moreParams = false;
00679     do
00680     {
00681       std::stringstream ss;
00682       ss << "pose" << topicInd++;
00683       std::string poseTopicName = ss.str();
00684       moreParams = nhLocal_.hasParam(poseTopicName);
00685 
00686       if (moreParams)
00687       {
00688         bool differential;
00689         nhLocal_.param(poseTopicName + std::string("_differential"), differential, false);
00690 
00691         // Determine if we want to integrate this sensor relatively
00692         bool relative;
00693         nhLocal_.param(poseTopicName + std::string("_relative"), relative, false);
00694 
00695         if(relative && differential)
00696         {
00697           ROS_WARN_STREAM("Both " << poseTopicName << "_differential" << " and " << poseTopicName <<
00698                           "_relative were set to true. Using differential mode.");
00699 
00700           relative = false;
00701         }
00702 
00703         std::string poseTopic;
00704         nhLocal_.getParam(poseTopicName, poseTopic);
00705 
00706         // Check for pose rejection threshold
00707         double poseMahalanobisThresh;
00708         nhLocal_.param(poseTopicName + std::string("_rejection_threshold"), poseMahalanobisThresh, std::numeric_limits<double>::max());
00709 
00710         int poseQueueSize = 1;
00711         nhLocal_.param(poseTopicName + "_queue_size", poseQueueSize, 1);
00712 
00713         // Pull in the sensor's config, zero out values that are invalid for the pose type
00714         std::vector<int> poseUpdateVec = loadUpdateConfig(poseTopicName);
00715         std::fill(poseUpdateVec.begin() + POSITION_V_OFFSET, poseUpdateVec.begin() + POSITION_V_OFFSET + TWIST_SIZE, 0);
00716         std::fill(poseUpdateVec.begin() + POSITION_A_OFFSET, poseUpdateVec.begin() + POSITION_A_OFFSET + ACCELERATION_SIZE, 0);
00717 
00718         int poseUpdateSum = std::accumulate(poseUpdateVec.begin(), poseUpdateVec.end(), 0);
00719 
00720         if(poseUpdateSum > 0)
00721         {
00722           // Create and store message filter subscriber objects and message filters
00723           poseMFSubPtr subPtr(new message_filters::Subscriber<geometry_msgs::PoseWithCovarianceStamped>());
00724           subPtr->subscribe(nh_, poseTopic, poseQueueSize);
00725           poseMFPtr filPtr(new tf::MessageFilter<geometry_msgs::PoseWithCovarianceStamped>(*subPtr, tfListener_, worldFrameId_, poseQueueSize));
00726           filPtr->registerCallback(boost::bind(&RosFilter<T>::poseCallback, this, _1, poseTopicName, worldFrameId_, poseUpdateVec, differential, relative, false, poseMahalanobisThresh));
00727           filPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformPoseFailureCallback, this, _1, _2, poseTopicName, worldFrameId_));
00728           poseTopicSubs_.push_back(subPtr);
00729           poseMessageFilters_[poseTopicName] = filPtr;
00730 
00731           if(differential)
00732           {
00733             twistVarCounts[StateMemberVx] += poseUpdateVec[StateMemberX];
00734             twistVarCounts[StateMemberVy] += poseUpdateVec[StateMemberY];
00735             twistVarCounts[StateMemberVz] += poseUpdateVec[StateMemberZ];
00736             twistVarCounts[StateMemberVroll] += poseUpdateVec[StateMemberRoll];
00737             twistVarCounts[StateMemberVpitch] += poseUpdateVec[StateMemberPitch];
00738             twistVarCounts[StateMemberVyaw] += poseUpdateVec[StateMemberYaw];
00739           }
00740           else
00741           {
00742             absPoseVarCounts[StateMemberX] += poseUpdateVec[StateMemberX];
00743             absPoseVarCounts[StateMemberY] += poseUpdateVec[StateMemberY];
00744             absPoseVarCounts[StateMemberZ] += poseUpdateVec[StateMemberZ];
00745             absPoseVarCounts[StateMemberRoll] += poseUpdateVec[StateMemberRoll];
00746             absPoseVarCounts[StateMemberPitch] += poseUpdateVec[StateMemberPitch];
00747             absPoseVarCounts[StateMemberYaw] += poseUpdateVec[StateMemberYaw];
00748           }
00749         }
00750         else
00751         {
00752           ROS_WARN_STREAM("Warning: " << poseTopic << " is listed as an input topic, but all pose update variables are false");
00753         }
00754 
00755         RF_DEBUG("Subscribed to " << poseTopic << " (" << poseTopicName << ")\n\t" <<
00756                  poseTopicName << "_differential is " << (differential ? "true" : "false") << "\n\t" <<
00757                  poseTopicName << "_rejection_threshold is " << poseMahalanobisThresh << "\n\t" <<
00758                  poseTopicName << "_queue_size is " << poseQueueSize << "\n\t" <<
00759                  poseTopicName << " update vector is " << poseUpdateVec);
00760       }
00761     } while (moreParams);
00762 
00763     // Repeat for twist
00764     topicInd = 0;
00765     moreParams = false;
00766     do
00767     {
00768       std::stringstream ss;
00769       ss << "twist" << topicInd++;
00770       std::string twistTopicName = ss.str();
00771       moreParams = nhLocal_.hasParam(twistTopicName);
00772 
00773       if (moreParams)
00774       {
00775         std::string twistTopic;
00776         nhLocal_.getParam(twistTopicName, twistTopic);
00777 
00778         // Check for twist rejection threshold
00779         double twistMahalanobisThresh;
00780         nhLocal_.param(twistTopicName + std::string("_rejection_threshold"), twistMahalanobisThresh, std::numeric_limits<double>::max());
00781 
00782         int twistQueueSize = 1;
00783         nhLocal_.param(twistTopicName + "_queue_size", twistQueueSize, 1);
00784 
00785         // Pull in the sensor's config, zero out values that are invalid for the twist type
00786         std::vector<int> twistUpdateVec = loadUpdateConfig(twistTopicName);
00787         std::fill(twistUpdateVec.begin() + POSITION_OFFSET, twistUpdateVec.begin() + POSITION_OFFSET + POSE_SIZE, 0);
00788 
00789         int twistUpdateSum = std::accumulate(twistUpdateVec.begin(), twistUpdateVec.end(), 0);
00790 
00791         if(twistUpdateSum > 0)
00792         {
00793           // Create and store subscriptions and message filters
00794           twistMFSubPtr subPtr(new message_filters::Subscriber<geometry_msgs::TwistWithCovarianceStamped>());
00795           subPtr->subscribe(nh_, twistTopic, twistQueueSize);
00796           twistMFPtr filPtr(new tf::MessageFilter<geometry_msgs::TwistWithCovarianceStamped>(*subPtr, tfListener_, baseLinkFrameId_, twistQueueSize));
00797           filPtr->registerCallback(boost::bind(&RosFilter<T>::twistCallback, this, _1, twistTopicName, baseLinkFrameId_, twistUpdateVec, twistMahalanobisThresh));
00798           filPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformTwistFailureCallback, this, _1, _2, twistTopicName, baseLinkFrameId_));
00799           twistTopicSubs_.push_back(subPtr);
00800           twistMessageFilters_[twistTopicName] = filPtr;
00801 
00802           twistVarCounts[StateMemberVx] += twistUpdateVec[StateMemberVx];
00803           twistVarCounts[StateMemberVy] += twistUpdateVec[StateMemberVy];
00804           twistVarCounts[StateMemberVz] += twistUpdateVec[StateMemberVz];
00805           twistVarCounts[StateMemberVroll] += twistUpdateVec[StateMemberVroll];
00806           twistVarCounts[StateMemberVpitch] += twistUpdateVec[StateMemberVpitch];
00807           twistVarCounts[StateMemberVyaw] += twistUpdateVec[StateMemberVyaw];
00808         }
00809         else
00810         {
00811           ROS_WARN_STREAM("Warning: " << twistTopic << " is listed as an input topic, but all twist update variables are false");
00812         }
00813 
00814         RF_DEBUG("Subscribed to " << twistTopic << " (" << twistTopicName << ")\n\t" <<
00815                  twistTopicName << "_rejection_threshold is " << twistMahalanobisThresh << "\n\t" <<
00816                  twistTopicName << "_queue_size is " << twistQueueSize << "\n\t" <<
00817                  twistTopicName << " update vector is " << twistUpdateVec);
00818       }
00819     } while (moreParams);
00820 
00821     // Repeat for IMU
00822     topicInd = 0;
00823     moreParams = false;
00824     do
00825     {
00826       std::stringstream ss;
00827       ss << "imu" << topicInd++;
00828       std::string imuTopicName = ss.str();
00829       moreParams = nhLocal_.hasParam(imuTopicName);
00830 
00831       if (moreParams)
00832       {
00833         bool differential;
00834         nhLocal_.param(imuTopicName + std::string("_differential"), differential, false);
00835 
00836         // Determine if we want to integrate this sensor relatively
00837         bool relative;
00838         nhLocal_.param(imuTopicName + std::string("_relative"), relative, false);
00839 
00840         if(relative && differential)
00841         {
00842           ROS_WARN_STREAM("Both " << imuTopicName << "_differential" << " and " << imuTopicName <<
00843                           "_relative were set to true. Using differential mode.");
00844 
00845           relative = false;
00846         }
00847 
00848         std::string imuTopic;
00849         nhLocal_.getParam(imuTopicName, imuTopic);
00850 
00851         // Check for pose rejection threshold
00852         double poseMahalanobisThresh;
00853         nhLocal_.param(imuTopicName + std::string("_pose_rejection_threshold"), poseMahalanobisThresh, std::numeric_limits<double>::max());
00854 
00855         // Check for angular velocity rejection threshold. Handle deprecated name.
00856         double twistMahalanobisThresh;
00857         std::string correctImuTwistRejectionName = imuTopicName + std::string("_twist_rejection_threshold");
00858         std::string deprecatedImuTwistRejectionName = imuTopicName + std::string("_angular_velocity_rejection_threshold");
00859         if(nhLocal_.hasParam(correctImuTwistRejectionName))
00860         {
00861           nhLocal_.param(correctImuTwistRejectionName, twistMahalanobisThresh, std::numeric_limits<double>::max());
00862         }
00863         else if(nhLocal_.hasParam(deprecatedImuTwistRejectionName))
00864         {
00865           nhLocal_.param(deprecatedImuTwistRejectionName, twistMahalanobisThresh, std::numeric_limits<double>::max());
00866 
00867           ROS_WARN_STREAM("Detected deprecated parameter " << deprecatedImuTwistRejectionName << ". Please use " <<
00868                           correctImuTwistRejectionName << " intstead.");
00869         }
00870         else
00871         {
00872           twistMahalanobisThresh = std::numeric_limits<double>::max();
00873         }
00874 
00875         // Check for acceleration rejection threshold
00876         double accelMahalanobisThresh;
00877         nhLocal_.param(imuTopicName + std::string("_linear_acceleration_rejection_threshold"), accelMahalanobisThresh, std::numeric_limits<double>::max());
00878 
00879         bool removeGravAcc = false;
00880         if(!nhLocal_.getParam(imuTopicName + "_remove_gravitational_acceleration", removeGravAcc))
00881         {
00882           // Handle deprecated method
00883           nhLocal_.param("remove_gravitational_acceleration", removeGravAcc, false);
00884 
00885           ROS_WARN_STREAM("Detected deprecated parameter remove_gravitational_acceleration. Please specify this " <<
00886                           "parameter for each IMU, e.g., " << imuTopicName + "_remove_gravitational_acceleration");
00887         }
00888         removeGravitationalAcc_[imuTopicName + "_acceleration"] = removeGravAcc;
00889 
00890         // Now pull in its boolean update vector configuration and differential
00891         // update configuration (as this contains pose information)
00892         std::vector<int> updateVec = loadUpdateConfig(imuTopicName);
00893 
00894         std::vector<int> poseUpdateVec = updateVec;
00895         std::fill(poseUpdateVec.begin() + POSITION_V_OFFSET, poseUpdateVec.begin() + POSITION_V_OFFSET + TWIST_SIZE, 0);
00896         std::fill(poseUpdateVec.begin() + POSITION_A_OFFSET, poseUpdateVec.begin() + POSITION_A_OFFSET + ACCELERATION_SIZE, 0);
00897 
00898         std::vector<int> twistUpdateVec = updateVec;
00899         std::fill(twistUpdateVec.begin() + POSITION_OFFSET, twistUpdateVec.begin() + POSITION_OFFSET + POSE_SIZE, 0);
00900         std::fill(twistUpdateVec.begin() + POSITION_A_OFFSET, twistUpdateVec.begin() + POSITION_A_OFFSET + ACCELERATION_SIZE, 0);
00901 
00902         std::vector<int> accelUpdateVec = updateVec;
00903         std::fill(accelUpdateVec.begin() + POSITION_OFFSET, accelUpdateVec.begin() + POSITION_OFFSET + POSE_SIZE, 0);
00904         std::fill(accelUpdateVec.begin() + POSITION_V_OFFSET, accelUpdateVec.begin() + POSITION_V_OFFSET + TWIST_SIZE, 0);
00905 
00906         int poseUpdateSum = std::accumulate(poseUpdateVec.begin(), poseUpdateVec.end(), 0);
00907         int twistUpdateSum = std::accumulate(twistUpdateVec.begin(), twistUpdateVec.end(), 0);
00908         int accelUpdateSum = std::accumulate(accelUpdateVec.begin(), accelUpdateVec.end(), 0);
00909 
00910         int imuQueueSize = 1;
00911         nhLocal_.param(imuTopicName + "_queue_size", imuQueueSize, 1);
00912 
00913         if(poseUpdateSum + twistUpdateSum + accelUpdateSum > 0)
00914         {
00915           // Create and store subscriptions and message filters as with odometry data
00916           imuTopicSubs_.push_back(
00917                 nh_.subscribe<sensor_msgs::Imu>(imuTopic, imuQueueSize,
00918                                                 boost::bind(&RosFilter<T>::imuCallback, this, _1, imuTopicName)));
00919         }
00920         else
00921         {
00922           ROS_WARN_STREAM("Warning: " << imuTopic << " is listed as an input topic, but all its update variables are false");
00923         }
00924 
00925         if(poseUpdateSum > 0)
00926         {
00927           // @todo: There's a lot of ambiguity with IMU frames. Should allow a parameter that specifies a target IMU frame.
00928           poseMFPtr poseFilPtr(new tf::MessageFilter<geometry_msgs::PoseWithCovarianceStamped>(tfListener_, baseLinkFrameId_, imuQueueSize));
00929           std::string imuPoseTopicName = imuTopicName + "_pose";
00930           poseFilPtr->registerCallback(boost::bind(&RosFilter<T>::poseCallback, this, _1, imuPoseTopicName, baseLinkFrameId_, poseUpdateVec, differential, relative, true, poseMahalanobisThresh));
00931           poseFilPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformPoseFailureCallback, this, _1, _2, imuTopicName, baseLinkFrameId_));
00932           poseMessageFilters_[imuPoseTopicName] = poseFilPtr;
00933 
00934           if(differential)
00935           {
00936             twistVarCounts[StateMemberVroll] += poseUpdateVec[StateMemberRoll];
00937             twistVarCounts[StateMemberVpitch] += poseUpdateVec[StateMemberPitch];
00938             twistVarCounts[StateMemberVyaw] += poseUpdateVec[StateMemberYaw];
00939           }
00940           else
00941           {
00942             absPoseVarCounts[StateMemberRoll] += poseUpdateVec[StateMemberRoll];
00943             absPoseVarCounts[StateMemberPitch] += poseUpdateVec[StateMemberPitch];
00944             absPoseVarCounts[StateMemberYaw] += poseUpdateVec[StateMemberYaw];
00945           }
00946         }
00947 
00948         if(twistUpdateSum > 0)
00949         {
00950           twistMFPtr twistFilPtr(new tf::MessageFilter<geometry_msgs::TwistWithCovarianceStamped>(tfListener_, baseLinkFrameId_, imuQueueSize));
00951           std::string imuTwistTopicName = imuTopicName + "_twist";
00952           twistFilPtr->registerCallback(boost::bind(&RosFilter<T>::twistCallback, this, _1, imuTwistTopicName, baseLinkFrameId_, twistUpdateVec, twistMahalanobisThresh));
00953           twistFilPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformTwistFailureCallback, this, _1, _2, imuTopicName, baseLinkFrameId_));
00954           twistMessageFilters_[imuTwistTopicName] = twistFilPtr;
00955 
00956           twistVarCounts[StateMemberVroll] += twistUpdateVec[StateMemberVroll];
00957           twistVarCounts[StateMemberVpitch] += twistUpdateVec[StateMemberVpitch];
00958           twistVarCounts[StateMemberVyaw] += twistUpdateVec[StateMemberVyaw];
00959         }
00960 
00961         if(accelUpdateSum > 0)
00962         {
00963           imuMFPtr accelFilPtr(new tf::MessageFilter<sensor_msgs::Imu>(tfListener_, baseLinkFrameId_, imuQueueSize));
00964           std::string imuAccelTopicName = imuTopicName + "_acceleration";
00965           accelFilPtr->registerCallback(boost::bind(&RosFilter<T>::accelerationCallback, this, _1, imuAccelTopicName, baseLinkFrameId_, accelUpdateVec, accelMahalanobisThresh));
00966           accelFilPtr->registerFailureCallback(boost::bind(&RosFilter<T>::transformImuFailureCallback, this, _1, _2, imuTopicName, baseLinkFrameId_));
00967           accelerationMessageFilters_[imuAccelTopicName] = accelFilPtr;
00968         }
00969 
00970         RF_DEBUG("Subscribed to " << imuTopic << " (" << imuTopicName << ")\n\t" <<
00971                  imuTopicName << "_differential is " << (differential ? "true" : "false") << "\n\t" <<
00972                  imuTopicName << "_pose_rejection_threshold is " << poseMahalanobisThresh << "\n\t" <<
00973                  imuTopicName << "_twist_rejection_threshold is " << twistMahalanobisThresh << "\n\t" <<
00974                  imuTopicName << "_linear_acceleration_rejection_threshold is " << accelMahalanobisThresh << "\n\t" <<
00975                  imuTopicName << "_remove_gravitational_acceleration is " << (removeGravAcc ? "true" : "false") << "\n\t" <<
00976                  imuTopicName << "_queue_size is " << imuQueueSize << "\n\t" <<
00977                  imuTopicName << " pose update vector is " << poseUpdateVec << "\t"<<
00978                  imuTopicName << " twist update vector is " << twistUpdateVec << "\t" <<
00979                  imuTopicName << " acceleration update vector is " << accelUpdateVec);
00980       }
00981     } while (moreParams);
00982 
00983     /* Warn users about:
00984     *    1. Multiple non-differential input sources
00985     *    2. No absolute measurements of orientations variables
00986     *    3. No absolute *or* relative measurements of position variables
00987     */
00988     if(printDiagnostics_)
00989     {
00990       for(int stateVar = StateMemberX; stateVar <= StateMemberYaw; ++stateVar)
00991       {
00992         if(absPoseVarCounts[static_cast<StateMembers>(stateVar)] > 1)
00993         {
00994           std::stringstream stream;
00995           stream <<  absPoseVarCounts[static_cast<StateMembers>(stateVar - POSITION_OFFSET)] <<
00996               " absolute pose inputs detected for " << stateVariableNames_[stateVar] <<
00997               ". This may result in oscillations. Please ensure that your variances for each "
00998               "measured variable are set appropriately.";
00999 
01000           addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
01001                         stateVariableNames_[stateVar] + "_configuration",
01002                         stream.str(),
01003                         true);
01004         }
01005         else if(absPoseVarCounts[static_cast<StateMembers>(stateVar)] == 0)
01006         {
01007           if(twoDMode_ == false)
01008           {
01009             if(static_cast<StateMembers>(stateVar) == StateMemberRoll ||
01010                static_cast<StateMembers>(stateVar) == StateMemberPitch)
01011             {
01012               std::stringstream stream;
01013               stream << "two_d_mode is false, and " << stateVariableNames_[stateVar] << " is not being measured. "
01014                         "This will result in unbounded error growth and erratic filter behavior.";
01015 
01016               addDiagnostic(diagnostic_msgs::DiagnosticStatus::ERROR,
01017                             stateVariableNames_[stateVar] + "_configuration",
01018                             stream.str(),
01019                             true);
01020             }
01021           }
01022 
01023           if(static_cast<StateMembers>(stateVar) == StateMemberYaw)
01024           {
01025             std::stringstream stream;
01026             stream << stateVariableNames_[stateVar] << " is not being measured. This will result "
01027                       "in unbounded error growth and erratic filter behavior.";
01028 
01029             addDiagnostic(diagnostic_msgs::DiagnosticStatus::ERROR,
01030                           stateVariableNames_[stateVar] + "_configuration",
01031                           stream.str(),
01032                           true);
01033           }
01034 
01035           if((static_cast<StateMembers>(stateVar) == StateMemberX &&
01036               twistVarCounts[static_cast<StateMembers>(StateMemberVx)] == 0) ||
01037              (static_cast<StateMembers>(stateVar) == StateMemberY &&
01038               twistVarCounts[static_cast<StateMembers>(StateMemberVy)] == 0) ||
01039              (static_cast<StateMembers>(stateVar) == StateMemberZ &&
01040               twistVarCounts[static_cast<StateMembers>(StateMemberVz)] == 0 &&
01041               twoDMode_ == false))
01042           {
01043             std::stringstream stream;
01044             stream << "Neither " << stateVariableNames_[stateVar] << " nor its "
01045                       "velocity is being measured. This will result in unbounded "
01046                       "error growth and erratic filter behavior.";
01047 
01048             addDiagnostic(diagnostic_msgs::DiagnosticStatus::ERROR,
01049                           stateVariableNames_[stateVar] + "_configuration",
01050                           stream.str(),
01051                           true);
01052           }
01053 
01054         }
01055       }
01056     }
01057 
01058     // Load up the process noise covariance (from the launch file/parameter server)
01059     Eigen::MatrixXd processNoiseCovariance(STATE_SIZE, STATE_SIZE);
01060     processNoiseCovariance.setZero();
01061     XmlRpc::XmlRpcValue processNoiseCovarConfig;
01062 
01063     if (nhLocal_.hasParam("process_noise_covariance"))
01064     {
01065       try
01066       {
01067         nhLocal_.getParam("process_noise_covariance", processNoiseCovarConfig);
01068 
01069         ROS_ASSERT(processNoiseCovarConfig.getType() == XmlRpc::XmlRpcValue::TypeArray);
01070 
01071         int matSize = processNoiseCovariance.rows();
01072 
01074         if (processNoiseCovarConfig.size() != matSize * matSize)
01075         {
01076           if(::fabs(::sqrt(processNoiseCovarConfig.size()) - 12) < 0.1)
01077           {
01078             ROS_WARN_STREAM("Process_noise_covariance matrix should have " << matSize * matSize << " values.");
01079             matSize = 12;
01080           }
01081           else
01082           {
01083             ROS_FATAL_STREAM("Process_noise_covariance matrix must have " << matSize * matSize << " values.");
01084           }
01085         }
01087 
01088         for (int i = 0; i < matSize; i++)
01089         {
01090           for (int j = 0; j < matSize; j++)
01091           {
01092             try
01093             {
01094               // These matrices can cause problems if all the types
01095               // aren't specified with decimal points. Handle that
01096               // using string streams.
01097               std::ostringstream ostr;
01098               ostr << processNoiseCovarConfig[matSize * i + j];
01099               std::istringstream istr(ostr.str());
01100               istr >> processNoiseCovariance(i, j);
01101             }
01102             catch(XmlRpc::XmlRpcException &e)
01103             {
01104               throw e;
01105             }
01106             catch(...)
01107             {
01108               throw;
01109             }
01110           }
01111         }
01112 
01113         RF_DEBUG("Process noise covariance is:\n" << processNoiseCovariance << "\n");
01114       }
01115       catch (XmlRpc::XmlRpcException &e)
01116       {
01117         ROS_ERROR_STREAM(
01118           "ERROR reading sensor config: " << e.getMessage() << " for process_noise_covariance (type: " << processNoiseCovarConfig.getType() << ")");
01119       }
01120 
01121       filter_.setProcessNoiseCovariance(processNoiseCovariance);
01122     }
01123 
01124     // Load up the process noise covariance (from the launch file/parameter server)
01125     Eigen::MatrixXd initialEstimateErrorCovariance(STATE_SIZE, STATE_SIZE);
01126     initialEstimateErrorCovariance.setZero();
01127     XmlRpc::XmlRpcValue estimateErrorCovarConfig;
01128 
01129     if (nhLocal_.hasParam("initial_estimate_covariance"))
01130     {
01131       try
01132       {
01133         nhLocal_.getParam("initial_estimate_covariance", estimateErrorCovarConfig);
01134 
01135         ROS_ASSERT(estimateErrorCovarConfig.getType() == XmlRpc::XmlRpcValue::TypeArray);
01136 
01137         int matSize = initialEstimateErrorCovariance.rows();
01138 
01139         for (int i = 0; i < matSize; i++)
01140         {
01141           for (int j = 0; j < matSize; j++)
01142           {
01143             try
01144             {
01145               // These matrices can cause problems if all the types
01146               // aren't specified with decimal points. Handle that
01147               // using string streams.
01148               std::ostringstream ostr;
01149               ostr << estimateErrorCovarConfig[matSize * i + j];
01150               std::istringstream istr(ostr.str());
01151               istr >> initialEstimateErrorCovariance(i, j);
01152             }
01153             catch(XmlRpc::XmlRpcException &e)
01154             {
01155               throw e;
01156             }
01157             catch(...)
01158             {
01159               throw;
01160             }
01161           }
01162         }
01163 
01164         RF_DEBUG("Initial estimate error covariance is:\n" << initialEstimateErrorCovariance << "\n");
01165       }
01166       catch (XmlRpc::XmlRpcException &e)
01167       {
01168         ROS_ERROR_STREAM(
01169           "ERROR reading initial_estimate_covariance (type: " << estimateErrorCovarConfig.getType() << "): " << e.getMessage());
01170       }
01171       catch(...)
01172       {
01173         ROS_ERROR_STREAM(
01174           "ERROR reading initial_estimate_covariance (type: " << estimateErrorCovarConfig.getType() << ")");
01175       }
01176 
01177       filter_.setEstimateErrorCovariance(initialEstimateErrorCovariance);
01178     }
01179   }
01180 
01181   template<typename T>
01182   void RosFilter<T>::odometryCallback(const nav_msgs::Odometry::ConstPtr &msg,
01183                                    const std::string &topicName)
01184   {
01185     // If we've just reset the filter, then we want to ignore any messages
01186     // that arrive with an older timestamp
01187     if(msg->header.stamp <= lastSetPoseTime_)
01188     {
01189       return;
01190     }
01191 
01192     RF_DEBUG("------ RosFilter::odometryCallback (" << topicName << ") ------\n" <<
01193              "Odometry message:\n" << *msg);
01194 
01195     std::string odomPoseTopicName = topicName + "_pose";
01196     if(poseMessageFilters_.count(odomPoseTopicName) > 0)
01197     {
01198       // Grab the pose portion of the message and pass it to the poseCallback
01199       geometry_msgs::PoseWithCovarianceStamped *posPtr = new geometry_msgs::PoseWithCovarianceStamped();
01200       posPtr->header = msg->header;
01201       posPtr->pose = msg->pose; // Entire pose object, also copies covariance
01202 
01203       geometry_msgs::PoseWithCovarianceStampedConstPtr pptr(posPtr);
01204       poseMessageFilters_[odomPoseTopicName]->add(pptr);
01205     }
01206 
01207     std::string odomTwistTopicName = topicName + "_twist";
01208     if(twistMessageFilters_.count(odomTwistTopicName) > 0)
01209     {
01210       // Grab the twist portion of the message and pass it to the twistCallback
01211       geometry_msgs::TwistWithCovarianceStamped *twistPtr = new geometry_msgs::TwistWithCovarianceStamped();
01212       twistPtr->header = msg->header;
01213       twistPtr->header.frame_id = msg->child_frame_id;
01214       twistPtr->twist = msg->twist; // Entire twist object, also copies covariance
01215 
01216 
01217       geometry_msgs::TwistWithCovarianceStampedConstPtr tptr(twistPtr);
01218       twistMessageFilters_[odomTwistTopicName]->add(tptr);
01219     }
01220 
01221     RF_DEBUG("\n----- /RosFilter::odometryCallback (" << topicName << ") ------\n");
01222   }
01223 
01224   template<typename T>
01225   void RosFilter<T>::poseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg,
01226                                const std::string &topicName,
01227                                const std::string &targetFrame,
01228                                const std::vector<int> &updateVector,
01229                                const bool differential,
01230                                const bool relative,
01231                                const bool imuData,
01232                                const double mahalanobisThresh)
01233   {
01234     // If we've just reset the filter, then we want to ignore any messages
01235     // that arrive with an older timestamp
01236     if(msg->header.stamp <= lastSetPoseTime_)
01237     {
01238       return;
01239     }
01240 
01241     RF_DEBUG("------ RosFilter::poseCallback (" << topicName << ") ------\n" <<
01242              "Pose message:\n" << *msg);
01243 
01244     // Put the initial value in the lastMessagTimes_ for this variable if it's empty
01245     if (lastMessageTimes_.count(topicName) == 0)
01246     {
01247       lastMessageTimes_.insert(std::pair<std::string, ros::Time>(topicName, msg->header.stamp));
01248     }
01249 
01250     // Make sure this message is newer than the last one
01251     if (msg->header.stamp >= lastMessageTimes_[topicName])
01252     {
01253       RF_DEBUG("Update vector for " << topicName << " is:\n" << updateVector);
01254 
01255       Eigen::VectorXd measurement(STATE_SIZE);
01256       Eigen::MatrixXd measurementCovariance(STATE_SIZE, STATE_SIZE);
01257 
01258       measurement.setZero();
01259       measurementCovariance.setZero();
01260 
01261       // Make sure we're actually updating at least one of these variables
01262       if (updateVector[StateMemberX] || updateVector[StateMemberY] || updateVector[StateMemberZ] ||
01263           updateVector[StateMemberRoll] || updateVector[StateMemberPitch] || updateVector[StateMemberYaw])
01264       {
01265         std::vector<int> updateVectorCorrected = updateVector;
01266 
01267         // Prepare the pose data for inclusion in the filter
01268         if (preparePose(msg, topicName, targetFrame, differential, relative, imuData, updateVectorCorrected, measurement, measurementCovariance))
01269         {
01270           // Store the measurement. Add a "pose" suffix so we know what kind of measurement
01271           // we're dealing with when we debug the core filter logic.
01272           enqueueMeasurement(topicName, measurement, measurementCovariance, updateVectorCorrected, mahalanobisThresh, msg->header.stamp);
01273 
01274           RF_DEBUG("Enqueued new measurement for " << topicName << "\n");
01275         }
01276         else
01277         {
01278           RF_DEBUG("Did *not* enqueue measurement for " << topicName << "\n");
01279         }
01280       }
01281       else
01282       {
01283         RF_DEBUG("Update vector for " << topicName << " is such that none of its state variables will be updated\n");
01284       }
01285 
01286       lastMessageTimes_[topicName] = msg->header.stamp;
01287 
01288       RF_DEBUG("Last message time for " << topicName << " is now " << lastMessageTimes_[topicName] << "\n");
01289     }
01290     else
01291     {
01292       RF_DEBUG("Message is too old. Last message time for " << topicName << " is "
01293                << lastMessageTimes_[topicName] << ", current message time is "
01294                << msg->header.stamp << ".\n");
01295     }
01296 
01297     RF_DEBUG("\n----- /RosFilter::poseCallback (" << topicName << ") ------\n");
01298   }
01299 
01300   template<typename T>
01301   void RosFilter<T>::run()
01302   {
01303     ros::Time::init();
01304 
01305     loadParams();
01306 
01307     if(printDiagnostics_)
01308     {
01309       diagnosticUpdater_.add("Filter diagnostic updater", this, &RosFilter<T>::aggregateDiagnostics);
01310     }
01311 
01312     // Set up the frequency diagnostic
01313     double minFrequency = frequency_ - 2;
01314     double maxFrequency = frequency_ + 2;
01315     diagnostic_updater::HeaderlessTopicDiagnostic freqDiag("odometry/filtered",
01316                                                            diagnosticUpdater_,
01317                                                            diagnostic_updater::FrequencyStatusParam(&minFrequency,
01318                                                                                                     &maxFrequency,
01319                                                                                                     0.1, 10));
01320 
01321     // We may need to broadcast a different transform than
01322     // the one we've already calculated.
01323     tf::StampedTransform mapOdomTrans;
01324     tf::StampedTransform odomBaseLinkTrans;
01325     geometry_msgs::TransformStamped mapOdomTransMsg;
01326     ros::Time curTime;
01327     ros::Time lastDiagTime = ros::Time::now();
01328 
01329     // Clear out the transforms
01330     tf::transformTFToMsg(tf::Transform::getIdentity(), worldBaseLinkTransMsg_.transform);
01331     tf::transformTFToMsg(tf::Transform::getIdentity(), mapOdomTransMsg.transform);
01332 
01333     // Publisher
01334     ros::Publisher positionPub = nh_.advertise<nav_msgs::Odometry>("odometry/filtered", 20);
01335     tf::TransformBroadcaster worldTransformBroadcaster;
01336 
01337     ros::Rate loop_rate(frequency_);
01338 
01339     while (ros::ok())
01340     {
01341       // Get latest state and publish it
01342       nav_msgs::Odometry filteredPosition;
01343 
01344       if (getFilteredOdometryMessage(filteredPosition))
01345       {
01346         worldBaseLinkTransMsg_.header.stamp = filteredPosition.header.stamp + tfTimeOffset_;
01347         worldBaseLinkTransMsg_.header.frame_id = filteredPosition.header.frame_id;
01348         worldBaseLinkTransMsg_.child_frame_id = filteredPosition.child_frame_id;
01349 
01350         worldBaseLinkTransMsg_.transform.translation.x = filteredPosition.pose.pose.position.x;
01351         worldBaseLinkTransMsg_.transform.translation.y = filteredPosition.pose.pose.position.y;
01352         worldBaseLinkTransMsg_.transform.translation.z = filteredPosition.pose.pose.position.z;
01353         worldBaseLinkTransMsg_.transform.rotation = filteredPosition.pose.pose.orientation;
01354 
01355         // If the worldFrameId_ is the odomFrameId_ frame, then we can just send the transform. If the
01356         // worldFrameId_ is the mapFrameId_ frame, we'll have some work to do.
01357         if(filteredPosition.header.frame_id == odomFrameId_)
01358         {
01359           worldTransformBroadcaster.sendTransform(worldBaseLinkTransMsg_);
01360         }
01361         else if(filteredPosition.header.frame_id == mapFrameId_)
01362         {
01363           try
01364           {
01365             tf::StampedTransform worldBaseLinkTrans;
01366             tf::transformStampedMsgToTF(worldBaseLinkTransMsg_, worldBaseLinkTrans);
01367 
01368             tfListener_.lookupTransform(baseLinkFrameId_, odomFrameId_, ros::Time(0), odomBaseLinkTrans);
01369 
01370             /*
01371              * First, see these two references:
01372              * http://wiki.ros.org/tf/Overview/Using%20Published%20Transforms#lookupTransform
01373              * http://wiki.ros.org/geometry/CoordinateFrameConventions#Transform_Direction
01374              * We have a transform from mapFrameId_->baseLinkFrameId_, but it would actually transform
01375              * a given pose from baseLinkFrameId_->mapFrameId_. We then used lookupTransform, whose
01376              * first two arguments are target frame and source frame, to get a transform from
01377              * baseLinkFrameId_->odomFrameId_. However, this transform would actually transform data
01378              * from odomFrameId_->baseLinkFrameId_. Now imagine that we have a position in the
01379              * mapFrameId_ frame. First, we multiply it by the inverse of the
01380              * mapFrameId_->baseLinkFrameId, which will transform that data from mapFrameId_ to
01381              * baseLinkFrameId_. Now we want to go from baseLinkFrameId_->odomFrameId_, but the
01382              * transform we have takes data from odomFrameId_->baseLinkFrameId_, so we need its
01383              * inverse as well. We have now transformed our data from mapFrameId_ to odomFrameId_.
01384              * However, if we want other users to be able to do the same, we need to broadcast
01385              * the inverse of that entire transform.
01386             */
01387 
01388             mapOdomTrans.mult(worldBaseLinkTrans, odomBaseLinkTrans);
01389 
01390             tf::transformStampedTFToMsg(mapOdomTrans, mapOdomTransMsg);
01391             mapOdomTransMsg.header.stamp = filteredPosition.header.stamp + tfTimeOffset_;
01392             mapOdomTransMsg.header.frame_id = mapFrameId_;
01393             mapOdomTransMsg.child_frame_id = odomFrameId_;
01394 
01395             worldTransformBroadcaster.sendTransform(mapOdomTransMsg);
01396           }
01397           catch(...)
01398           {
01399             ROS_ERROR_STREAM("Could not obtain transform from " << odomFrameId_ << "->" << baseLinkFrameId_);
01400           }
01401         }
01402         else
01403         {
01404           ROS_ERROR_STREAM("Odometry message frame_id was " << filteredPosition.header.frame_id <<
01405                            ", expected " << mapFrameId_ << " or " << odomFrameId_);
01406         }
01407 
01408         // Fire off the position and the transform
01409         positionPub.publish(filteredPosition);
01410 
01411         if(printDiagnostics_)
01412         {
01413           freqDiag.tick();
01414         }
01415       }
01416 
01417       // The spin will call all the available callbacks and enqueue
01418       // their received measurements
01419       ros::spinOnce();
01420 
01421       // Now we'll integrate any measurements we've received
01422       ros::Time curTime = ros::Time::now();
01423       integrateMeasurements(ros::Time::now().toSec());
01424 
01425       /* Diagnostics can behave strangely when playing back from bag
01426        * files and using simulated time, so we have to check for
01427        * time suddenly moving backwards as well as the standard
01428        * timeout criterion before publishing. */
01429       double diagDuration = (curTime - lastDiagTime).toSec();
01430       if(printDiagnostics_ && (diagDuration >= diagnosticUpdater_.getPeriod() || diagDuration < 0.0))
01431       {
01432         diagnosticUpdater_.force_update();
01433         lastDiagTime = curTime;
01434       }
01435 
01436       if(!loop_rate.sleep())
01437       {
01438         ROS_WARN_STREAM("Failed to meet update rate! Try decreasing the rate, limiting sensor output frequency, or limiting the number of sensors.");
01439       }
01440     }
01441   }
01442 
01443   template<typename T>
01444   void RosFilter<T>::setPoseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg)
01445   {
01446     RF_DEBUG("------ RosFilter::setPoseCallback ------\nPose message:\n" << *msg);
01447 
01448     std::string topicName("setPose");
01449 
01450     // Get rid of any initial poses (pretend we've never had a measurement)
01451     initialMeasurements_.clear();
01452     previousMeasurements_.clear();
01453     previousMeasurementCovariances_.clear();
01454 
01455     // Clear out the measurement queue so that we don't immediately undo our
01456     // reset.
01457     while(!measurementQueue_.empty())
01458     {
01459       measurementQueue_.pop();
01460     }
01461 
01462     // Also set the last set pose time, so we ignore all messages
01463     // that occur before it
01464     lastSetPoseTime_ = msg->header.stamp;
01465 
01466     // Set the state vector to the reported pose
01467     Eigen::VectorXd measurement(STATE_SIZE);
01468     Eigen::MatrixXd measurementCovariance(STATE_SIZE, STATE_SIZE);
01469     std::vector<int> updateVector(STATE_SIZE, true);
01470 
01471     // We only measure pose variables, so initialize the vector to 0
01472     measurement.setZero();
01473 
01474     // Set this to the identity and let the message reset it
01475     measurementCovariance.setIdentity();
01476     measurementCovariance *= 1e-6;
01477 
01478     // Prepare the pose data (really just using this to transform it into the target frame).
01479     // Twist data is going to get zeroed out.
01480     preparePose(msg, topicName, worldFrameId_, false, false, false, updateVector, measurement, measurementCovariance);
01481 
01482     // For the state
01483     filter_.setState(measurement);
01484     filter_.setEstimateErrorCovariance(measurementCovariance);
01485 
01486     filter_.setLastMeasurementTime(ros::Time::now().toSec());
01487     filter_.setLastUpdateTime(ros::Time::now().toSec());
01488 
01489     RF_DEBUG("\n------ /RosFilter::setPoseCallback ------\n");
01490   }
01491 
01492   template<typename T>
01493   bool RosFilter<T>::setPoseSrvCallback(robot_localization::SetPose::Request& request,
01494                           robot_localization::SetPose::Response&)
01495   {
01496     geometry_msgs::PoseWithCovarianceStamped::Ptr msg;
01497     msg = boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>(request.pose);
01498     setPoseCallback(msg);
01499 
01500     return true;
01501   }
01502 
01503   template<typename T>
01504   std::string RosFilter<T>::tfFailureReasonString(const tf::FilterFailureReason reason)
01505   {
01506     std::string retVal;
01507 
01508     switch(reason)
01509     {
01510       case tf::filter_failure_reasons::OutTheBack:
01511         retVal = std::string("The timestamp on the message is more than the cache length earlier than the newest data in the transform cache");
01512         break;
01513       case tf::filter_failure_reasons::EmptyFrameID:
01514         retVal = std::string("The message frame_id is empty");
01515         break;
01516       case tf::filter_failure_reasons::Unknown:
01517       default:
01518         retVal = std::string("No transform exists from source to target frame");
01519         break;
01520     }
01521 
01522     return retVal;
01523   }
01524 
01525   template<typename T>
01526   void RosFilter<T>::transformImuFailureCallback(const sensor_msgs::Imu::ConstPtr &msg,
01527                                               const tf::FilterFailureReason reason,
01528                                               const std::string &topicName,
01529                                               const std::string &targetFrame)
01530   {
01531     std::stringstream stream;
01532     std::string warning;
01533 
01534     stream << "WARNING: failed to transform from " << msg->header.frame_id <<
01535               "->" << targetFrame << " for " << topicName << " message received at " <<
01536               msg->header.stamp << ". " << tfFailureReasonString(reason) << ".\n";
01537     warning = stream.str();
01538 
01539     ROS_WARN_STREAM_THROTTLE(2.0, warning);
01540     RF_DEBUG(warning);
01541   }
01542 
01543   template<typename T>
01544   void RosFilter<T>::transformPoseFailureCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg,
01545                                                const tf::FilterFailureReason reason,
01546                                                const std::string &topicName,
01547                                                const std::string &targetFrame)
01548   {
01549     std::stringstream stream;
01550     std::string warning;
01551 
01552     stream << "WARNING: failed to transform from " << msg->header.frame_id <<
01553               "->" << targetFrame << " for " << topicName << " message received at " <<
01554               msg->header.stamp << ". " << tfFailureReasonString(reason) << ".\n";
01555     warning = stream.str();
01556 
01557     ROS_WARN_STREAM_THROTTLE(2.0, warning);
01558     RF_DEBUG(warning);
01559   }
01560 
01561   template<typename T>
01562   void RosFilter<T>::transformTwistFailureCallback(const geometry_msgs::TwistWithCovarianceStamped::ConstPtr &msg,
01563                                                 const tf::FilterFailureReason reason,
01564                                                 const std::string &topicName,
01565                                                 const std::string &targetFrame)
01566   {
01567     std::stringstream stream;
01568     std::string warning;
01569 
01570     stream << "WARNING: failed to transform from " << msg->header.frame_id <<
01571               "->" << targetFrame << " for " << topicName << " message received at " <<
01572               msg->header.stamp << ". " << tfFailureReasonString(reason) << ".\n";
01573     warning = stream.str();
01574 
01575     ROS_WARN_STREAM_THROTTLE(2.0, warning);
01576     RF_DEBUG(warning);
01577   }
01578 
01579   template<typename T>
01580   void RosFilter<T>::twistCallback(const geometry_msgs::TwistWithCovarianceStamped::ConstPtr &msg,
01581                                 const std::string &topicName,
01582                                 const std::string &targetFrame,
01583                                 const std::vector<int> &updateVector,
01584                                 const double mahalanobisThresh)
01585   {
01586     // If we've just reset the filter, then we want to ignore any messages
01587     // that arrive with an older timestamp
01588     if(msg->header.stamp <= lastSetPoseTime_)
01589     {
01590       return;
01591     }
01592 
01593     RF_DEBUG("------ RosFilter::twistCallback (" << topicName << ") ------\n"
01594              "Twist message:\n" << *msg);
01595 
01596     if (lastMessageTimes_.count(topicName) == 0)
01597     {
01598       lastMessageTimes_.insert(std::pair<std::string, ros::Time>(topicName, msg->header.stamp));
01599     }
01600 
01601     // Make sure this message is newer than the last one
01602     if (msg->header.stamp >= lastMessageTimes_[topicName])
01603     {
01604       RF_DEBUG("Update vector for " << topicName << " is:\n" << updateVector);
01605 
01606       Eigen::VectorXd measurement(STATE_SIZE);
01607       Eigen::MatrixXd measurementCovariance(STATE_SIZE, STATE_SIZE);
01608 
01609       measurement.setZero();
01610       measurementCovariance.setZero();
01611 
01612       // Make sure we're actually updating at least one of these variables
01613       if (updateVector[StateMemberVx] || updateVector[StateMemberVy] || updateVector[StateMemberVz] ||
01614           updateVector[StateMemberVroll] || updateVector[StateMemberVpitch] || updateVector[StateMemberVyaw])
01615       {
01616         std::vector<int> updateVectorCorrected = updateVector;
01617 
01618         // Prepare the twist data for inclusion in the filter
01619         if (prepareTwist(msg, topicName, targetFrame, updateVectorCorrected, measurement, measurementCovariance))
01620         {
01621           // Store the measurement. Add a "twist" suffix so we know what kind of measurement
01622           // we're dealing with when we debug the core filter logic.
01623           enqueueMeasurement(topicName, measurement, measurementCovariance, updateVectorCorrected, mahalanobisThresh, msg->header.stamp);
01624 
01625           RF_DEBUG("Enqueued new measurement for " << topicName << "_twist\n");
01626         }
01627         else
01628         {
01629           RF_DEBUG("Did *not* enqueue measurement for " << topicName << "_twist\n");
01630         }
01631       }
01632       else
01633       {
01634         RF_DEBUG("Update vector for " << topicName << " is such that none of its state variables will be updated\n");
01635       }
01636 
01637       lastMessageTimes_[topicName] = msg->header.stamp;
01638 
01639       RF_DEBUG("Last message time for " << topicName << " is now " << lastMessageTimes_[topicName] << "\n");
01640     }
01641     else
01642     {
01643       RF_DEBUG("Message is too old. Last message time for " << topicName <<
01644                " is " << lastMessageTimes_[topicName] << ", current message time is " <<
01645                msg->header.stamp << ".\n");
01646     }
01647 
01648     RF_DEBUG("\n----- /RosFilter::twistCallback (" << topicName << ") ------\n");
01649   }
01650 
01651   template<typename T>
01652   void RosFilter<T>::addDiagnostic(const int errLevel,
01653                                    const std::string &topicAndClass,
01654                                    const std::string &message,
01655                                    const bool staticDiag)
01656   {
01657     if(staticDiag)
01658     {
01659       staticDiagnostics_[topicAndClass] = message;
01660       staticDiagErrorLevel_ = std::max(staticDiagErrorLevel_, errLevel);
01661     }
01662     else
01663     {
01664       dynamicDiagnostics_[topicAndClass] = message;
01665       dynamicDiagErrorLevel_ = std::max(dynamicDiagErrorLevel_, errLevel);
01666     }
01667   }
01668 
01669   template<typename T>
01670   void RosFilter<T>::aggregateDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &wrapper)
01671   {
01672     wrapper.clear();
01673     wrapper.clearSummary();
01674 
01675     int maxErrLevel = std::max(staticDiagErrorLevel_, dynamicDiagErrorLevel_);
01676 
01677     // Report the overall status
01678     switch(maxErrLevel)
01679     {
01680       case diagnostic_msgs::DiagnosticStatus::ERROR:
01681         wrapper.summary(maxErrLevel,
01682                         "Erroneous data or settings detected for a robot_localization state estimation node.");
01683         break;
01684       case diagnostic_msgs::DiagnosticStatus::WARN:
01685         wrapper.summary(maxErrLevel,
01686                         "Potentially erroneous data or settings detected for a robot_localization state estimation node.");
01687         break;
01688       case diagnostic_msgs::DiagnosticStatus::OK:
01689         wrapper.summary(maxErrLevel,
01690                         "The robot_localization state estimation node appears to be functioning properly.");
01691         break;
01692       default:
01693         break;
01694     }
01695 
01696     // Aggregate all the static messages
01697     for(std::map<std::string, std::string>::iterator diagIt = staticDiagnostics_.begin();
01698         diagIt != staticDiagnostics_.end();
01699         ++diagIt)
01700     {
01701       wrapper.add(diagIt->first, diagIt->second);
01702     }
01703 
01704     // Aggregate all the dynamic messages, then clear them
01705     for(std::map<std::string, std::string>::iterator diagIt = dynamicDiagnostics_.begin();
01706         diagIt != dynamicDiagnostics_.end();
01707         ++diagIt)
01708     {
01709       wrapper.add(diagIt->first, diagIt->second);
01710     }
01711     dynamicDiagnostics_.clear();
01712 
01713     // Reset the warning level for the dynamic diagnostic messages
01714     dynamicDiagErrorLevel_ = diagnostic_msgs::DiagnosticStatus::OK;
01715   }
01716 
01717   template<typename T>
01718   void RosFilter<T>::copyCovariance(const double *arr,
01719                                     Eigen::MatrixXd &covariance,
01720                                     const std::string &topicName,
01721                                     const std::vector<int> &updateVector,
01722                                     const size_t offset,
01723                                     const size_t dimension)
01724   {
01725     for (size_t i = 0; i < dimension; i++)
01726     {
01727       for (size_t j = 0; j < dimension; j++)
01728       {
01729         covariance(i, j) = arr[dimension * i + j];
01730 
01731         if(printDiagnostics_)
01732         {
01733           std::string iVar = stateVariableNames_[offset + i];
01734 
01735           if(covariance(i, j) > 1e3 && (updateVector[offset  + i] || updateVector[offset  + j]))
01736           {
01737             std::string jVar = stateVariableNames_[offset + j];
01738 
01739             std::stringstream stream;
01740             stream << "The covariance at position (" << dimension * i + j << "), which corresponds to " <<
01741                 (i == j ? iVar + " variance" : iVar + " and " + jVar + " covariance") <<
01742                 ", the value is extremely large (" << covariance(i, j) << "), but the update vector for " <<
01743                 (i == j ? iVar : iVar + " and/or " + jVar) << " is set to true. This may produce undesirable results.";
01744 
01745             addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
01746                           topicName + "_covariance",
01747                           stream.str(),
01748                           false);
01749           }
01750           else if(updateVector[i] && i == j && covariance(i, j) == 0)
01751           {
01752             std::stringstream stream;
01753             stream << "The covariance at position (" << dimension * i + j << "), which corresponds to " <<
01754                        iVar << " variance, was zero. This will be replaced with "
01755                        "a small value to maintain filter stability, but should be corrected at the message origin node.";
01756 
01757             addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
01758                           topicName + "_covariance",
01759                           stream.str(),
01760                           false);
01761           }
01762           else if(updateVector[i] && i == j && covariance(i, j) < 0)
01763           {
01764             std::stringstream stream;
01765             stream << "The covariance at position (" << dimension * i + j <<
01766                      "), which corresponds to " << iVar << " variance, was negative. This will be replaced with "
01767                       "a small positive value to maintain filter stability, but should be corrected at the message origin node.";
01768 
01769             addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
01770                           topicName + "_covariance",
01771                           stream.str(),
01772                           false);
01773           }
01774         }
01775       }
01776     }
01777   }
01778 
01779   template<typename T>
01780   void RosFilter<T>::copyCovariance(const Eigen::MatrixXd &covariance,
01781                                  double *arr,
01782                                  const size_t dimension)
01783   {
01784     for (size_t i = 0; i < dimension; i++)
01785     {
01786       for (size_t j = 0; j < dimension; j++)
01787       {
01788         arr[dimension * i + j] = covariance(i, j);
01789       }
01790     }
01791   }
01792 
01793   template<typename T>
01794   std::vector<int> RosFilter<T>::loadUpdateConfig(const std::string &topicName)
01795   {
01796     XmlRpc::XmlRpcValue topicConfig;
01797     std::vector<int> updateVector(STATE_SIZE, 0);
01798     std::string topicConfigName = topicName + "_config";
01799 
01800     try
01801     {
01802       nhLocal_.getParam(topicConfigName, topicConfig);
01803 
01804       ROS_ASSERT(topicConfig.getType() == XmlRpc::XmlRpcValue::TypeArray);
01805 
01806       if(topicConfig.size() != STATE_SIZE)
01807       {
01808         ROS_WARN_STREAM("Configuration vector for " << topicConfigName << " should have 15 entries.");
01809       }
01810 
01811       for (int i = 0; i < topicConfig.size(); i++)
01812       {
01813         // The double cast looks strange, but we'll get exceptions if we
01814         // remove the cast to bool. vector<bool> is discouraged, so updateVector
01815         // uses integers.
01816         updateVector[i] = static_cast<int>(static_cast<bool>(topicConfig[i]));
01817       }
01818     }
01819     catch (XmlRpc::XmlRpcException &e)
01820     {
01821       ROS_FATAL_STREAM("Could not read sensor update configuration for topic " << topicName <<
01822                        " (type: " << topicConfig.getType() << ", expected: " << XmlRpc::XmlRpcValue::TypeArray
01823                        << "). Error was " << e.getMessage() << "\n");
01824     }
01825 
01826     return updateVector;
01827   }
01828 
01829   template<typename T>
01830   bool RosFilter<T>::lookupTransformSafe(const std::string &targetFrame,
01831                                          const std::string &sourceFrame,
01832                                          const ros::Time &time,
01833                                          tf::StampedTransform &targetFrameTrans)
01834   {
01835     bool retVal = true;
01836 
01837     // First try to transform the data at the requested time
01838     try
01839     {
01840       tfListener_.lookupTransform(targetFrame, sourceFrame, time, targetFrameTrans);
01841     }
01842     catch (tf::TransformException &ex)
01843     {
01844       RF_DEBUG("WARNING: Could not obtain transform from " << sourceFrame <<
01845                " to " << targetFrame << ". Error was " << ex.what());
01846 
01847       // The issue might be that the transforms that are available are not close
01848       // enough temporally to be used. In that case, just use the latest available
01849       // transform and warn the user.
01850       try
01851       {
01852         tfListener_.lookupTransform(targetFrame, sourceFrame, ros::Time(0), targetFrameTrans);
01853 
01854         ROS_WARN_STREAM_THROTTLE(2.0, "Transform from " << sourceFrame << " to " << targetFrame <<
01855                                       " was unavailable for the time requested. Using latest instead.\n");
01856       }
01857       catch(tf::TransformException &ex)
01858       {
01859         ROS_WARN_STREAM_THROTTLE(2.0, "Could not obtain transform from " << sourceFrame <<
01860                                       " to " << targetFrame << ". Error was " << ex.what() << "\n");
01861 
01862         retVal = false;
01863       }
01864     }
01865 
01866     // Transforming from a frame id to itself can fail when the tf tree isn't
01867     // being broadcast (e.g., for some bag files). This is the only failure that
01868     // would throw an exception, so check for this situation before giving up.
01869     if(!retVal)
01870     {
01871       if(targetFrame == sourceFrame)
01872       {
01873         targetFrameTrans.setIdentity();
01874         retVal = true;
01875       }
01876     }
01877 
01878     return retVal;
01879   }
01880 
01881   template<typename T>
01882   bool RosFilter<T>::prepareAcceleration(const sensor_msgs::Imu::ConstPtr &msg,
01883                            const std::string &topicName,
01884                            const std::string &targetFrame,
01885                            std::vector<int> &updateVector,
01886                            Eigen::VectorXd &measurement,
01887                            Eigen::MatrixXd &measurementCovariance)
01888   {
01889     RF_DEBUG("------ RosFilter::prepareAcceleration (" << topicName << ") ------\n");
01890 
01891     // 1. Get the measurement into a vector
01892     tf::Vector3 accTmp(msg->linear_acceleration.x,
01893                        msg->linear_acceleration.y,
01894                        msg->linear_acceleration.z);
01895 
01896     // Set relevant header info
01897     std::string msgFrame = (msg->header.frame_id == "" ? baseLinkFrameId_ : msg->header.frame_id);
01898 
01899     // 2. robot_localization lets users configure which variables from the sensor should be
01900     //    fused with the filter. This is specified at the sensor level. However, the data
01901     //    may go through transforms before being fused with the state estimate. In that case,
01902     //    we need to know which of the transformed variables came from the pre-transformed
01903     //    "approved" variables (i.e., the ones that had "true" in their xxx_config parameter).
01904     //    To do this, we create a pose from the original upate vector, which contains only
01905     //    zeros and ones. This pose goes through the same transforms as the measurement. The
01906     //    non-zero values that result will be used to modify the updateVector.
01907     tf::Matrix3x3 maskAcc(updateVector[StateMemberAx], 0, 0,
01908                           0, updateVector[StateMemberAy], 0,
01909                           0, 0, updateVector[StateMemberAz]);
01910 
01911     // 3. We'll need to rotate the covariance as well
01912     Eigen::MatrixXd covarianceRotated(ACCELERATION_SIZE, ACCELERATION_SIZE);
01913     covarianceRotated.setZero();
01914 
01915     this->copyCovariance(&(msg->linear_acceleration_covariance[0]), covarianceRotated, topicName, updateVector, POSITION_A_OFFSET, ACCELERATION_SIZE);
01916 
01917     RF_DEBUG("Original measurement as tf object: " << accTmp <<
01918              "\nOriginal update vector:\n" << updateVector <<
01919              "\nOriginal covariance matrix:\n" << covarianceRotated << "\n");
01920 
01921     // 4. We need to transform this into the target frame (probably base_link)
01922     // It's unlikely that we'll get a velocity measurement in another frame, but
01923     // we have to handle the situation.
01924     tf::StampedTransform targetFrameTrans;
01925     bool canTransform = lookupTransformSafe(targetFrame, msgFrame, msg->header.stamp, targetFrameTrans);
01926 
01927     if(canTransform)
01928     {
01929       // We don't know if the user has already handled the removal
01930       // of normal forces, so we use a parameter
01931       if(removeGravitationalAcc_[topicName])
01932       {
01933         tf::Vector3 normAcc(0, 0, 9.80665);
01934         tf::Quaternion curAttitude;
01935         tf::Transform trans;
01936         tf::quaternionMsgToTF(msg->orientation, curAttitude);
01937         trans.setRotation(curAttitude);
01938         tf::Vector3 rotNorm = trans.getBasis().inverse() * normAcc;
01939         accTmp.setX(accTmp.getX() - rotNorm.getX());
01940         accTmp.setY(accTmp.getY() - rotNorm.getY());
01941         accTmp.setZ(accTmp.getZ() - rotNorm.getZ());
01942 
01943         RF_DEBUG("Orientation is " << curAttitude <<
01944                  "Acceleration due to gravity is " << rotNorm <<
01945                  "After removing acceleration due to gravity, acceleration is " << accTmp << "\n");
01946       }
01947 
01948       // Transform to correct frame
01949       // @todo: This needs to take into account offsets from the origin. Right now,
01950       // it assumes that if the sensor is placed at some non-zero offset from the
01951       // vehicle's center, that the vehicle turns with constant velocity. This needs
01952       // to be something like accTmp = targetFrameTrans.getBasis() * accTmp - targetFrameTrans.getOrigin().cross(rotation_acceleration);
01953       // We can get rotational acceleration by differentiating the rotational velocity (if it's available)
01954       accTmp = targetFrameTrans.getBasis() * accTmp;
01955       maskAcc = targetFrameTrans.getBasis() * maskAcc;
01956 
01957       // Now use the mask values to determinme which update vector values should true
01958       updateVector[StateMemberAx] = static_cast<int>(maskAcc.getRow(StateMemberAx - POSITION_A_OFFSET).length() >= 1e-6);
01959       updateVector[StateMemberAy] = static_cast<int>(maskAcc.getRow(StateMemberAy - POSITION_A_OFFSET).length() >= 1e-6);
01960       updateVector[StateMemberAz] = static_cast<int>(maskAcc.getRow(StateMemberAz - POSITION_A_OFFSET).length() >= 1e-6);
01961 
01962       RF_DEBUG(msg->header.frame_id << "->" << targetFrame << " transform:\n" << targetFrameTrans <<
01963                "\nAfter applying transform to " << targetFrame << ", update vector is:\n" << updateVector <<
01964                "\nAfter applying transform to " << targetFrame << ", measurement is:\n" << accTmp << "\n");
01965 
01966       // 5. Now rotate the covariance: create an augmented
01967       // matrix that contains a 3D rotation matrix in the
01968       // upper-left and lower-right quadrants, and zeros
01969       // elsewhere
01970       tf::Matrix3x3 rot(targetFrameTrans.getRotation());
01971       Eigen::MatrixXd rot3d(ACCELERATION_SIZE, ACCELERATION_SIZE);
01972       rot3d.setIdentity();
01973 
01974       for(size_t rInd = 0; rInd < ACCELERATION_SIZE; ++rInd)
01975       {
01976         rot3d(rInd, 0) = rot.getRow(rInd).getX();
01977         rot3d(rInd, 1) = rot.getRow(rInd).getY();
01978         rot3d(rInd, 2) = rot.getRow(rInd).getZ();
01979       }
01980 
01981       // Carry out the rotation
01982       covarianceRotated = rot3d * covarianceRotated.eval() * rot3d.transpose();
01983 
01984       RF_DEBUG("Transformed covariance is \n" << covarianceRotated << "\n");
01985 
01986       // 6. Store our corrected measurement and covariance
01987       measurement(StateMemberAx) = accTmp.getX();
01988       measurement(StateMemberAy) = accTmp.getY();
01989       measurement(StateMemberAz) = accTmp.getZ();
01990 
01991       // Copy the covariances
01992       measurementCovariance.block(POSITION_A_OFFSET, POSITION_A_OFFSET, ACCELERATION_SIZE, ACCELERATION_SIZE) = covarianceRotated.block(0, 0, ACCELERATION_SIZE, ACCELERATION_SIZE);
01993 
01994       // 7. Handle 2D mode
01995       if(twoDMode_)
01996       {
01997         forceTwoD(measurement, measurementCovariance, updateVector);
01998       }
01999     }
02000     else
02001     {
02002       RF_DEBUG("Could not transform measurement into " << targetFrame << ". Ignoring...\n");
02003     }
02004 
02005     RF_DEBUG("\n----- /RosFilter::prepareAcceleration(" << topicName << ") ------\n");
02006 
02007     return canTransform;
02008   }
02009 
02010   template<typename T>
02011   bool RosFilter<T>::preparePose(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg,
02012                                  const std::string &topicName,
02013                                  const std::string &targetFrame,
02014                                  const bool differential,
02015                                  const bool relative,
02016                                  const bool imuData,
02017                                  std::vector<int> &updateVector,
02018                                  Eigen::VectorXd &measurement,
02019                                  Eigen::MatrixXd &measurementCovariance)
02020   {
02021     bool retVal = false;
02022 
02023     RF_DEBUG("------ RosFilter::preparePose (" << topicName << ") ------\n");
02024 
02025     // 1. Get the measurement into a tf-friendly transform (pose) object
02026     tf::Stamped<tf::Pose> poseTmp;
02027 
02028     // We'll need this later for storing this measurement for differential integration
02029     tf::Transform curMeasurement;
02030 
02031     // Handle issues where frame_id data is not filled out properly
02032     // @todo: verify that this is necessary still. New IMU handling may
02033     // have rendered this obsolete.
02034     std::string finalTargetFrame;
02035     if(targetFrame == "" && msg->header.frame_id == "")
02036     {
02037       // Blank target and message frames mean we can just
02038       // use our world_frame
02039       finalTargetFrame = worldFrameId_;
02040     }
02041     else if(targetFrame == "")
02042     {
02043       // A blank target frame means we shouldn't bother
02044       // transforming the data
02045       finalTargetFrame = msg->header.frame_id;
02046     }
02047     else
02048     {
02049       // Otherwise, we should use our target frame
02050       finalTargetFrame = targetFrame;
02051     }
02052 
02053     RF_DEBUG("Final target frame for " << topicName << " is " << finalTargetFrame << "\n");
02054 
02055     poseTmp.frame_id_ = msg->header.frame_id;
02056     poseTmp.stamp_ = msg->header.stamp;
02057 
02058     // Fill out the position data
02059     poseTmp.setOrigin(tf::Vector3(msg->pose.pose.position.x,
02060                                   msg->pose.pose.position.y,
02061                                   msg->pose.pose.position.z));
02062 
02063     tf::Quaternion orientation;
02064 
02065     // Handle bad (empty) quaternions
02066     if(msg->pose.pose.orientation.x == 0 && msg->pose.pose.orientation.y == 0 &&
02067        msg->pose.pose.orientation.z == 0 && msg->pose.pose.orientation.w == 0)
02068     {
02069       orientation.setValue(0.0, 0.0, 0.0, 1.0);
02070 
02071       if(updateVector[StateMemberRoll] || updateVector[StateMemberPitch] || updateVector[StateMemberYaw])
02072       {
02073         std::stringstream stream;
02074         stream << "The " << topicName << " message contains an invalid orientation quaternion, " <<
02075                   "but its configuration is such that orientation data is being used. Correcting...";
02076 
02077         addDiagnostic(diagnostic_msgs::DiagnosticStatus::WARN,
02078                       topicName + "_orientation",
02079                       stream.str(),
02080                       false);
02081       }
02082     }
02083     else
02084     {
02085       tf::quaternionMsgToTF(msg->pose.pose.orientation, orientation);
02086     }
02087 
02088     // Fill out the orientation data
02089     poseTmp.setRotation(orientation);
02090 
02091     // 2. Get the target frame transformation
02092     tf::StampedTransform targetFrameTrans;
02093     bool canTransform = lookupTransformSafe(finalTargetFrame,
02094                                             poseTmp.frame_id_,
02095                                             poseTmp.stamp_,
02096                                             targetFrameTrans);
02097 
02098     // 3. Make sure we can work with this data before carrying on
02099     if(canTransform)
02100     {
02101       /* 4. robot_localization lets users configure which variables from the sensor should be
02102        *    fused with the filter. This is specified at the sensor level. However, the data
02103        *    may go through transforms before being fused with the state estimate. In that case,
02104        *    we need to know which of the transformed variables came from the pre-transformed
02105        *    "approved" variables (i.e., the ones that had "true" in their xxx_config parameter).
02106        *    To do this, we construct matrices using the update vector values on the diagonals,
02107        *    pass this matrix through the rotation, and use the length of each row to determine
02108        *    the transformed update vector. The process is slightly different for IMUs, as the
02109        *    coordinate frame transform is really the base_link->imu_frame transform, and not
02110        *    a transform from some other world-fixed frame (even though the IMU data itself *is*
02111        *    reported in a world fixed frame). */
02112       tf::Matrix3x3 maskPosition(updateVector[StateMemberX], 0, 0,
02113                                  0, updateVector[StateMemberY], 0,
02114                                  0, 0, updateVector[StateMemberZ]);
02115 
02116       tf::Matrix3x3 maskOrientation(updateVector[StateMemberRoll], 0, 0,
02117                                     0, updateVector[StateMemberPitch], 0,
02118                                     0, 0, updateVector[StateMemberYaw]);
02119 
02120       if(imuData)
02121       {
02122         /* We have to treat IMU orientation data differently. Even though we are dealing with pose
02123          * data when we work with orientations, for IMUs, the frame_id is the frame in which the
02124          * sensor is mounted, and not the coordinate frame of the IMU. Imagine an IMU that is mounted
02125          * facing sideways. The pitch in the IMU frame becomes roll for the vehicle. This means that
02126          * we need to rotate roll and pitch angles by the IMU's mounting yaw offset, and we must apply
02127          * similar treatment to its update mask and covariance. */
02128 
02129         double dummy, yaw;
02130         targetFrameTrans.getBasis().getRPY(dummy, dummy, yaw);
02131         tf::Matrix3x3 transTmp;
02132         transTmp.setRPY(0.0, 0.0, yaw);
02133 
02134         maskPosition = transTmp * maskPosition;
02135         maskOrientation = transTmp * maskOrientation;
02136       }
02137       else
02138       {
02139         maskPosition = targetFrameTrans.getBasis() * maskPosition;
02140         maskOrientation = targetFrameTrans.getBasis() * maskOrientation;
02141       }
02142 
02143       // Now copy the mask values back into the update vector: any row with a significant vector length
02144       // indicates that we want to set that variable to true in the update vector.
02145       updateVector[StateMemberX] = static_cast<int>(maskPosition.getRow(StateMemberX - POSITION_OFFSET).length() >= 1e-6);
02146       updateVector[StateMemberY] = static_cast<int>(maskPosition.getRow(StateMemberY - POSITION_OFFSET).length() >= 1e-6);
02147       updateVector[StateMemberZ] = static_cast<int>(maskPosition.getRow(StateMemberZ - POSITION_OFFSET).length() >= 1e-6);
02148       updateVector[StateMemberRoll] = static_cast<int>(maskOrientation.getRow(StateMemberRoll - ORIENTATION_OFFSET).length() >= 1e-6);
02149       updateVector[StateMemberPitch] = static_cast<int>(maskOrientation.getRow(StateMemberPitch - ORIENTATION_OFFSET).length() >= 1e-6);
02150       updateVector[StateMemberYaw] = static_cast<int>(maskOrientation.getRow(StateMemberYaw - ORIENTATION_OFFSET).length() >= 1e-6);
02151 
02152       // 5a. We'll need to rotate the covariance as well. Create a container and copy over the
02153       // covariance data
02154       Eigen::MatrixXd covariance(POSE_SIZE, POSE_SIZE);
02155       covariance.setZero();
02156       copyCovariance(&(msg->pose.covariance[0]), covariance, topicName, updateVector, POSITION_OFFSET, POSE_SIZE);
02157 
02158       // 5b. Now rotate the covariance: create an augmented matrix that
02159       // contains a 3D rotation matrix in the upper-left and lower-right
02160       // quadrants, with zeros elsewhere.
02161       tf::Matrix3x3 rot;
02162       Eigen::MatrixXd rot6d(POSE_SIZE, POSE_SIZE);
02163       rot6d.setIdentity();
02164       Eigen::MatrixXd covarianceRotated;
02165 
02166       if(imuData)
02167       {
02168         // Apply the same special logic to the IMU covariance rotation
02169         double dummy, yaw;
02170         targetFrameTrans.getBasis().getRPY(dummy, dummy, yaw);
02171         rot.setRPY(0.0, 0.0, yaw);
02172       }
02173       else
02174       {
02175         rot.setRotation(targetFrameTrans.getRotation());
02176       }
02177 
02178       for(size_t rInd = 0; rInd < POSITION_SIZE; ++rInd)
02179       {
02180         rot6d(rInd, 0) = rot.getRow(rInd).getX();
02181         rot6d(rInd, 1) = rot.getRow(rInd).getY();
02182         rot6d(rInd, 2) = rot.getRow(rInd).getZ();
02183         rot6d(rInd+POSITION_SIZE, 3) = rot.getRow(rInd).getX();
02184         rot6d(rInd+POSITION_SIZE, 4) = rot.getRow(rInd).getY();
02185         rot6d(rInd+POSITION_SIZE, 5) = rot.getRow(rInd).getZ();
02186       }
02187 
02188       // Now carry out the rotation
02189       covarianceRotated = rot6d * covariance * rot6d.transpose();
02190 
02191       RF_DEBUG("After rotating into the " << finalTargetFrame << " frame, covariance is \n" << covarianceRotated << "\n");
02192 
02193       /* 6a. For IMU data, the transform that we get is the transform from the body
02194        * frame of the robot (e.g., base_link) to the mounting frame of the robot. It
02195        * is *not* the coordinate frame in which the IMU orientation data is reported.
02196        * If the IMU is mounted in a non-neutral orientation, we need to remove those
02197        * offsets, and then we need to potentially "swap" roll and pitch.
02198        * Note that this transform does NOT handle NED->ENU conversions. Data is assumed
02199        * to be in the ENU frame when it is received.
02200        * */
02201       if(imuData)
02202       {
02203         // First, convert the transform and measurement rotation to RPY
02204         // @todo: There must be a way to handle this with quaternions. Need to look into it.
02205         double rollOffset = 0;
02206         double pitchOffset = 0;
02207         double yawOffset = 0;
02208         double roll = 0;
02209         double pitch = 0;
02210         double yaw = 0;
02211         RosFilterUtilities::quatToRPY(targetFrameTrans.getRotation(), rollOffset, pitchOffset, yawOffset);
02212         RosFilterUtilities::quatToRPY(poseTmp.getRotation(), roll, pitch, yaw);
02213 
02214         // 6b. Apply the offset (making sure to bound them), and throw them in a vector
02215         tf::Vector3 rpyAngles(FilterUtilities::clampRotation(roll - rollOffset),
02216                                FilterUtilities::clampRotation(pitch - pitchOffset),
02217                                FilterUtilities::clampRotation(yaw - yawOffset));
02218 
02219         // 6c. Now we need to rotate the roll and pitch by the yaw offset value.
02220         // Imagine a case where an IMU is mounted facing sideways. In that case
02221         // pitch for the IMU's world frame is roll for the robot.
02222         tf::Matrix3x3 mat;
02223         mat.setRPY(0.0, 0.0, yawOffset);
02224         rpyAngles = mat * rpyAngles;
02225         poseTmp.getBasis().setRPY(rpyAngles.getX(), rpyAngles.getY(), rpyAngles.getZ());
02226 
02227         // We will use this target transformation later on, but
02228         // we've already transformed this data as if the IMU
02229         // were mounted neutrall on the robot, so we can just
02230         // make the transform the identity.
02231         targetFrameTrans.setIdentity();
02232       }
02233 
02234       // 7. Two cases: if we're in differential mode, we need to generate a twist
02235       // message. Otherwise, we just transform it to the target frame.
02236       if(differential)
02237       {
02238         bool success = false;
02239 
02240         // We're going to be playing with poseTmp, so store it,
02241         // as we'll need to save its current value for the next
02242         // measurement.
02243         curMeasurement = poseTmp;
02244 
02245         // Make sure we have previous measurements to work with
02246         if(previousMeasurements_.count(topicName) > 0 && previousMeasurementCovariances_.count(topicName) > 0)
02247         {
02248           // 7a. If we are carrying out differential integration and
02249           // we have a previous measurement for this sensor,then we
02250           // need to apply the inverse of that measurement to this new
02251           // measurement to produce a "delta" measurement between the two.
02252           // Even if we're not using all of the variables from this sensor,
02253           // we need to use the whole measurement to determine the delta
02254           // to the new measurement
02255           tf::Transform prevMeasurement = previousMeasurements_[topicName];
02256           poseTmp.setData(prevMeasurement.inverseTimes(poseTmp));
02257 
02258           RF_DEBUG("Previous measurement:\n" << previousMeasurements_[topicName] <<
02259                    "\nAfter removing previous measurement, measurement delta is:\n" << poseTmp << "\n");
02260 
02261           // 7b. Now we we have a measurement delta in the frame_id of the
02262           // message, but we want that delta to be in the target frame, so
02263           // we need to apply the rotation of the target frame transform.
02264           targetFrameTrans.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
02265           poseTmp.mult(targetFrameTrans, poseTmp);
02266 
02267           RF_DEBUG("After rotating to the target frame, measurement delta is:\n" << poseTmp << "\n");
02268 
02269           // 7c. Now use the time difference from the last message to compute
02270           // translational and rotational velocities
02271           double dt = msg->header.stamp.toSec() - lastMessageTimes_[topicName].toSec();
02272           double xVel = poseTmp.getOrigin().getX() / dt;
02273           double yVel = poseTmp.getOrigin().getY() / dt;
02274           double zVel = poseTmp.getOrigin().getZ() / dt;
02275 
02276           double rollVel = 0;
02277           double pitchVel = 0;
02278           double yawVel = 0;
02279 
02280           RosFilterUtilities::quatToRPY(poseTmp.getRotation(), rollVel, pitchVel, yawVel);
02281           rollVel /= dt;
02282           pitchVel /= dt;
02283           yawVel /= dt;
02284 
02285           RF_DEBUG("Previous message time was " << lastMessageTimes_[topicName].toSec() <<
02286                    ", current message time is " << msg->header.stamp.toSec() << ", delta is " <<
02287                    dt << ", velocity is (vX, vY, vZ): (" << xVel << ", " << yVel << ", " << zVel <<
02288                    ")\n" << "(vRoll, vPitch, vYaw): (" << rollVel << ", " << pitchVel << ", " <<
02289                    yawVel << ")\n");
02290 
02291           // 7d. Fill out the velocity data in the message
02292           geometry_msgs::TwistWithCovarianceStamped *twistPtr = new geometry_msgs::TwistWithCovarianceStamped();
02293           twistPtr->header = msg->header;
02294           twistPtr->header.frame_id = baseLinkFrameId_;
02295           twistPtr->twist.twist.linear.x = xVel;
02296           twistPtr->twist.twist.linear.y = yVel;
02297           twistPtr->twist.twist.linear.z = zVel;
02298           twistPtr->twist.twist.angular.x = rollVel;
02299           twistPtr->twist.twist.angular.y = pitchVel;
02300           twistPtr->twist.twist.angular.z = yawVel;
02301           std::vector<int> twistUpdateVec(STATE_SIZE, false);
02302           std::copy(updateVector.begin() + POSITION_OFFSET, updateVector.begin() + POSE_SIZE, twistUpdateVec.begin() + POSITION_V_OFFSET);
02303           std::copy(twistUpdateVec.begin(), twistUpdateVec.end(), updateVector.begin());
02304           geometry_msgs::TwistWithCovarianceStampedConstPtr ptr(twistPtr);
02305 
02306           // 7e. Now rotate the previous covariance for this measurement to get it
02307           // into the target frame, and add the current measurement's rotated covariance
02308           // to the previous measurement's rotated covariance, and multiply by the time delta.
02309           Eigen::MatrixXd prevCovarRotated = rot6d * previousMeasurementCovariances_[topicName] * rot6d.transpose();
02310           covarianceRotated = (covarianceRotated.eval() + prevCovarRotated) * dt;
02311           copyCovariance(covarianceRotated, &(twistPtr->twist.covariance[0]), POSE_SIZE);
02312 
02313           RF_DEBUG("Previous measurement covariance:\n" << previousMeasurementCovariances_[topicName] <<
02314                    "\nPrevious measurement covariance rotated:\n" << prevCovarRotated <<
02315                    "\nFinal twist covariance:\n" << covarianceRotated << "\n");
02316 
02317           // Now pass this on to prepareTwist, which will convert it to the required frame
02318           success = prepareTwist(ptr, topicName + "_twist", twistPtr->header.frame_id, updateVector, measurement, measurementCovariance);
02319         }
02320 
02321         // 7f. Update the previous measurement and measurement covariance
02322         previousMeasurements_[topicName] = curMeasurement;
02323         previousMeasurementCovariances_[topicName] = covariance;
02324 
02325         retVal = success;
02326       }
02327       else
02328       {
02329         // 7g. If we're in relative mode, remove the initial measurement
02330         if(relative)
02331         {
02332           if(initialMeasurements_.count(topicName) == 0)
02333           {
02334             initialMeasurements_.insert(std::pair<std::string, tf::Transform>(topicName, poseTmp));
02335           }
02336 
02337           tf::Transform initialMeasurement = initialMeasurements_[topicName];
02338           poseTmp.setData(initialMeasurement.inverseTimes(poseTmp));
02339         }
02340 
02341         // 7h. Apply the target frame transformation to the pose object.
02342         poseTmp.mult(targetFrameTrans, poseTmp);
02343         poseTmp.frame_id_ = finalTargetFrame;
02344 
02345         // 7i. Finally, copy everything into our measurement and covariance objects
02346         measurement(StateMemberX) = poseTmp.getOrigin().x();
02347         measurement(StateMemberY) = poseTmp.getOrigin().y();
02348         measurement(StateMemberZ) = poseTmp.getOrigin().z();
02349 
02350         // The filter needs roll, pitch, and yaw values instead of quaternions
02351         double roll, pitch, yaw;
02352         RosFilterUtilities::quatToRPY(poseTmp.getRotation(), roll, pitch, yaw);
02353         measurement(StateMemberRoll) = roll;
02354         measurement(StateMemberPitch) = pitch;
02355         measurement(StateMemberYaw) = yaw;
02356 
02357         measurementCovariance.block(0, 0, POSE_SIZE, POSE_SIZE) = covarianceRotated.block(0, 0, POSE_SIZE, POSE_SIZE);
02358 
02359         // 8. Handle 2D mode
02360         if(twoDMode_)
02361         {
02362           forceTwoD(measurement, measurementCovariance, updateVector);
02363         }
02364 
02365         retVal = true;
02366       }
02367     }
02368     else
02369     {
02370       retVal = false;
02371 
02372       RF_DEBUG("Could not transform measurement into " << finalTargetFrame << ". Ignoring...");
02373     }
02374 
02375     RF_DEBUG("\n----- /RosFilter::preparePose (" << topicName << ") ------\n");
02376 
02377     return retVal;
02378   }
02379 
02380   template<typename T>
02381   bool RosFilter<T>::prepareTwist(const geometry_msgs::TwistWithCovarianceStamped::ConstPtr &msg,
02382                                const std::string &topicName,
02383                                const std::string &targetFrame,
02384                                std::vector<int> &updateVector,
02385                                Eigen::VectorXd &measurement,
02386                                Eigen::MatrixXd &measurementCovariance)
02387   {
02388     RF_DEBUG("------ RosFilter::prepareTwist (" << topicName << ") ------\n");
02389 
02390     // 1. Get the measurement into two separate vector objects.
02391     tf::Vector3 twistLin(msg->twist.twist.linear.x,
02392                          msg->twist.twist.linear.y,
02393                          msg->twist.twist.linear.z);
02394     tf::Vector3 measTwistRot(msg->twist.twist.angular.x,
02395                              msg->twist.twist.angular.y,
02396                              msg->twist.twist.angular.z);
02397 
02398     // 1a. This sensor may or may not measure rotational velocity. Regardless,
02399     // if it measures linear velocity, then later on, we'll need to remove "false"
02400     // linear velocity resulting from angular velocity and the translational offset
02401     // of the sensor from the vehicle origin.
02402     const Eigen::VectorXd &state = filter_.getState();
02403     tf::Vector3 stateTwistRot(state(StateMemberVroll),
02404                               state(StateMemberVpitch),
02405                               state(StateMemberVyaw));
02406 
02407     // Determine the frame_id of the data
02408     std::string msgFrame = (msg->header.frame_id == "" ? targetFrame : msg->header.frame_id);
02409 
02410     // 2. robot_localization lets users configure which variables from the sensor should be
02411     //    fused with the filter. This is specified at the sensor level. However, the data
02412     //    may go through transforms before being fused with the state estimate. In that case,
02413     //    we need to know which of the transformed variables came from the pre-transformed
02414     //    "approved" variables (i.e., the ones that had "true" in their xxx_config parameter).
02415     //    To do this, we construct matrices using the update vector values on the diagonals,
02416     //    pass this matrix through the rotation, and use the length of each row to determine
02417     //    the transformed update vector.
02418     tf::Matrix3x3 maskLin(updateVector[StateMemberVx], 0, 0,
02419                           0, updateVector[StateMemberVy], 0,
02420                           0, 0, updateVector[StateMemberVz]);
02421 
02422     tf::Matrix3x3 maskRot(updateVector[StateMemberVroll], 0, 0,
02423                           0, updateVector[StateMemberVpitch], 0,
02424                           0, 0, updateVector[StateMemberVyaw]);
02425 
02426     // 3. We'll need to rotate the covariance as well
02427     Eigen::MatrixXd covarianceRotated(TWIST_SIZE, TWIST_SIZE);
02428     covarianceRotated.setZero();
02429 
02430     copyCovariance(&(msg->twist.covariance[0]), covarianceRotated, topicName, updateVector, POSITION_V_OFFSET, TWIST_SIZE);
02431 
02432     RF_DEBUG("Original measurement as tf object:\nLinear: " << twistLin <<
02433              "Rotational: " << measTwistRot <<
02434              "\nOriginal update vector:\n" << updateVector <<
02435              "\nOriginal covariance matrix:\n" << covarianceRotated << "\n");
02436 
02437     // 4. We need to transform this into the target frame (probably base_link)
02438     tf::StampedTransform targetFrameTrans;
02439     bool canTransform = lookupTransformSafe(targetFrame, msgFrame, msg->header.stamp, targetFrameTrans);
02440 
02441     if(canTransform)
02442     {
02443       // Transform to correct frame. Note that we can get linear velocity
02444       // as a result of the sensor offset and rotational velocity
02445       measTwistRot = targetFrameTrans.getBasis() * measTwistRot;
02446       twistLin = targetFrameTrans.getBasis() * twistLin + targetFrameTrans.getOrigin().cross(stateTwistRot);
02447       maskLin = targetFrameTrans.getBasis() * maskLin;
02448       maskRot = targetFrameTrans.getBasis() * maskRot;
02449 
02450       // Now copy the mask values back into the update vector
02451       updateVector[StateMemberVx] = static_cast<int>(maskLin.getRow(StateMemberVx - POSITION_V_OFFSET).length() >= 1e-6);
02452       updateVector[StateMemberVy] = static_cast<int>(maskLin.getRow(StateMemberVy - POSITION_V_OFFSET).length() >= 1e-6);
02453       updateVector[StateMemberVz] = static_cast<int>(maskLin.getRow(StateMemberVz - POSITION_V_OFFSET).length() >= 1e-6);
02454       updateVector[StateMemberVroll] = static_cast<int>(maskRot.getRow(StateMemberVroll - ORIENTATION_V_OFFSET).length() >= 1e-6);
02455       updateVector[StateMemberVpitch] = static_cast<int>(maskRot.getRow(StateMemberVpitch - ORIENTATION_V_OFFSET).length() >= 1e-6);
02456       updateVector[StateMemberVyaw] = static_cast<int>(maskRot.getRow(StateMemberVyaw - ORIENTATION_V_OFFSET).length() >= 1e-6);
02457 
02458       RF_DEBUG(msg->header.frame_id << "->" << targetFrame << " transform:\n" << targetFrameTrans <<
02459                "\nAfter applying transform to " << targetFrame << ", update vector is:\n" << updateVector <<
02460                "\nAfter applying transform to " << targetFrame << ", measurement is:\n" <<
02461                "Linear: " << twistLin << "Rotational: " << measTwistRot << "\n");
02462 
02463       // 5. Now rotate the covariance: create an augmented
02464       // matrix that contains a 3D rotation matrix in the
02465       // upper-left and lower-right quadrants, and zeros
02466       // elsewhere
02467       tf::Matrix3x3 rot(targetFrameTrans.getRotation());
02468       Eigen::MatrixXd rot6d(TWIST_SIZE, TWIST_SIZE);
02469       rot6d.setIdentity();
02470 
02471       for(size_t rInd = 0; rInd < POSITION_SIZE; ++rInd)
02472       {
02473         rot6d(rInd, 0) = rot.getRow(rInd).getX();
02474         rot6d(rInd, 1) = rot.getRow(rInd).getY();
02475         rot6d(rInd, 2) = rot.getRow(rInd).getZ();
02476         rot6d(rInd+POSITION_SIZE, 3) = rot.getRow(rInd).getX();
02477         rot6d(rInd+POSITION_SIZE, 4) = rot.getRow(rInd).getY();
02478         rot6d(rInd+POSITION_SIZE, 5) = rot.getRow(rInd).getZ();
02479       }
02480 
02481       // Carry out the rotation
02482       covarianceRotated = rot6d * covarianceRotated.eval() * rot6d.transpose();
02483 
02484       RF_DEBUG("Transformed covariance is \n" << covarianceRotated << "\n");
02485 
02486       // 6. Store our corrected measurement and covariance
02487       measurement(StateMemberVx) = twistLin.getX();
02488       measurement(StateMemberVy) = twistLin.getY();
02489       measurement(StateMemberVz) = twistLin.getZ();
02490       measurement(StateMemberVroll) = measTwistRot.getX();
02491       measurement(StateMemberVpitch) = measTwistRot.getY();
02492       measurement(StateMemberVyaw) = measTwistRot.getZ();
02493 
02494       // Copy the covariances
02495       measurementCovariance.block(POSITION_V_OFFSET, POSITION_V_OFFSET, TWIST_SIZE, TWIST_SIZE) = covarianceRotated.block(0, 0, TWIST_SIZE, TWIST_SIZE);
02496 
02497       // 7. If in 2D mode, handle that.
02498       if(twoDMode_)
02499       {
02500         forceTwoD(measurement, measurementCovariance, updateVector);
02501       }
02502     }
02503     else
02504     {
02505       RF_DEBUG("Could not transform measurement into " << targetFrame << ". Ignoring...");
02506     }
02507 
02508     RF_DEBUG("\n----- /RosFilter::prepareTwist (" << topicName << ") ------\n");
02509 
02510     return canTransform;
02511   }
02512 }
02513 
02514 // Instantiations of classes is required when template class code
02515 // is placed in a .cpp file.
02516 template class RobotLocalization::RosFilter<RobotLocalization::Ekf>;
02517 template class RobotLocalization::RosFilter<RobotLocalization::Ukf>;
02518 


robot_localization
Author(s): Tom Moore
autogenerated on Fri Aug 28 2015 12:26:20