estimator.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010, UC Regents
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the University of California nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 #include <algorithm>
00035 #include <ros/ros.h>
00036 #include <nodelet/nodelet.h>
00037 #include <pluginlib/class_list_macros.h>
00038 #include <diagnostic_updater/diagnostic_updater.h>
00039 #include <diagnostic_updater/update_functions.h>
00040 #include <string>
00041 #include <angles/angles.h>
00042 #include <tf/tf.h>
00043 #include <tf/transform_listener.h>
00044 #include <tf/transform_datatypes.h>
00045 #include <nav_msgs/Odometry.h>
00046 #include <Eigen/Eigen>
00047 
00048 using namespace std;
00049 
00050 namespace flyer_est
00051 {
00052 
00053 // Constants for velocity filter:
00054 const double VEL_FILT_A = 0.9;
00055 const double VEL_FILT_B = 0.1;
00056 
00057 class Estimator : public nodelet::Nodelet
00058 {
00059 private:
00060   ros::NodeHandle nh;
00061   ros::NodeHandle nh_priv;
00062   // Diagnostic Updater
00063   diagnostic_updater::Updater diag_updater;
00064   double min_freq;
00065   double max_freq;
00066   diagnostic_updater::FrequencyStatus freq_status;
00067   // Parameters
00068   string transform_topic;
00069   double freq;
00070   double xy_vel_filt_a;
00071   double xy_vel_filt_b;
00072   double z_vel_filt_a;
00073   double z_vel_filt_b;
00074   double ang_vel_filt_a;
00075   double ang_vel_filt_b;
00076   bool use_udp_for_transform_subscriber;
00077   // Publishers
00078   ros::Publisher output_pub;
00079   // Subscribers
00080   ros::Subscriber transform_sub;
00081   // TF
00082   tf::TransformListener tf_listener;
00083   // Timers
00084   ros::Timer main_timer;
00085   ros::Timer get_transforms_timer;
00086   // Members
00087   string flyer_imu_tf_node;
00088   string flyer_vicon_tf_node;
00089   bool got_first_transform;
00090   double last_transform_age;
00091   tf::StampedTransform last_transform;
00092   tf::StampedTransform T_flyer_vicon__flyer_imu; // Transform from flyer_vicon to flyer_imu, grabbed at startup
00093   tf::StampedTransform T_enu__ned; // Transform from /enu to /ned, grabbed at startup
00094   nav_msgs::Odometry state_msg;
00095   tf::Quaternion prev_quat; // store last orientation quaternion for angular velocity computations
00096   Eigen::Vector4d prev_quat_deriv; // store last quaternion derivative for angular velocity computations
00097   boost::mutex state_mutex;
00098   bool main_cb_first_time;
00099   double max_dt;
00100   bool got_new_measurement;
00101   int published_without_new_measurement_count;
00102   int max_published_without_new_measurement;
00103 
00104 public:
00105   EIGEN_MAKE_ALIGNED_OPERATOR_NEW Estimator() :
00106       diag_updater(), min_freq(40), max_freq(60), freq_status(
00107           diagnostic_updater::FrequencyStatusParam(&min_freq, &max_freq)), //
00108       transform_topic("vicon_recv_direct/output"), freq(50), //
00109       xy_vel_filt_a(0.9), xy_vel_filt_b(0.1), //
00110       z_vel_filt_a(0.9), z_vel_filt_b(0.1), //
00111       ang_vel_filt_a(0.9), ang_vel_filt_b(0.1), //
00112       use_udp_for_transform_subscriber(false), //
00113       got_first_transform(false), prev_quat_deriv(0, 0, 0, 0), main_cb_first_time(true), max_dt(0), //
00114       got_new_measurement(false), published_without_new_measurement_count(0), max_published_without_new_measurement(0)
00115   {
00116   }
00117 
00118   void onInit()
00119   {
00120     nh = getMTNodeHandle();
00121     nh_priv = getMTPrivateNodeHandle();
00122 
00123     // Diagnostics
00124     diag_updater.add("Estimator Status", this, &Estimator::diagnostics);
00125     diag_updater.add(freq_status);
00126     diag_updater.setHardwareID("none");
00127     diag_updater.force_update();
00128     // Parameters
00129     nh_priv.param("transform_topic", transform_topic, transform_topic);
00130     nh_priv.param("freq", freq, freq);
00131     nh_priv.param("xy_vel_filt_a", xy_vel_filt_a, xy_vel_filt_a);
00132     nh_priv.param("xy_vel_filt_b", xy_vel_filt_b, xy_vel_filt_b);
00133     nh_priv.param("z_vel_filt_a", z_vel_filt_a, z_vel_filt_a);
00134     nh_priv.param("z_vel_filt_b", z_vel_filt_b, z_vel_filt_b);
00135     nh_priv.param("ang_vel_filt_a", ang_vel_filt_a, ang_vel_filt_a);
00136     nh_priv.param("ang_vel_filt_b", ang_vel_filt_a, ang_vel_filt_a);
00137     nh_priv.param("use_udp_for_transform_subscriber", use_udp_for_transform_subscriber,
00138                   use_udp_for_transform_subscriber);
00139     min_freq = freq * 0.9;
00140     max_freq = freq * 1.1;
00141     // Publishers
00142     output_pub = nh_priv.advertise<nav_msgs::Odometry>("output", 10);
00143     // Get transform from flyer_vicon to flyer_imu
00144     flyer_imu_tf_node = ros::this_node::getNamespace() + "/flyer_imu";
00145     flyer_vicon_tf_node = ros::this_node::getNamespace() + "/flyer_vicon";
00146     // workaround annoying bug that appears to have crept in while ros #3617
00147     // was being fixed ..
00148     if (flyer_imu_tf_node.substr(0, 2) == "//")
00149       flyer_imu_tf_node = flyer_imu_tf_node.substr(1, flyer_imu_tf_node.size() - 1);
00150     if (flyer_vicon_tf_node.substr(0, 2) == "//")
00151       flyer_vicon_tf_node = flyer_vicon_tf_node.substr(1, flyer_vicon_tf_node.size() - 1);
00152     get_transforms_timer = nh.createTimer(ros::Duration(0.01), boost::bind(&Estimator::getTransforms, this, _1), true);
00153   }
00154 
00155   void getTransforms(const ros::TimerEvent& event)
00156   {
00157     ROS_INFO("Looking up flyer_vicon --> flyer_imu transform");
00158     ROS_INFO_STREAM(
00159         "flyer_imu_tf_node = '" << flyer_imu_tf_node <<"', flyer_vicon_tf_node = '" << flyer_vicon_tf_node << "'");
00160 
00161     bool gotit = false;
00162     ros::Duration waiter(0.5);
00163     while (!gotit)
00164     {
00165       try
00166       {
00167         tf_listener.lookupTransform(flyer_vicon_tf_node, flyer_imu_tf_node, ros::Time(0), T_flyer_vicon__flyer_imu);
00168         tf_listener.lookupTransform("/enu", "/ned", ros::Time(0), T_enu__ned);
00169         gotit = true;
00170       }
00171       catch (tf::TransformException& ex)
00172       {
00173         ROS_ERROR("Exception looking up transform: %s", ex.what());
00174       }
00175       waiter.sleep();
00176     }
00177     ROS_INFO_STREAM(
00178         "Got flyer_vicon --> flyer_imu transform: " << T_flyer_vicon__flyer_imu.getOrigin().getX() << " " << T_flyer_vicon__flyer_imu.getOrigin().getY() << " " << T_flyer_vicon__flyer_imu.getOrigin().getZ());
00179     ROS_INFO_STREAM(
00180         "Got /enu --> /ned transform: " << T_enu__ned.getOrigin().getX() << " " << T_enu__ned.getOrigin().getY() << " " << T_enu__ned.getOrigin().getZ());
00181     // Subscribers
00182     if (use_udp_for_transform_subscriber)
00183     {
00184       transform_sub = nh.subscribe(transform_topic, 1, &Estimator::transformCallback, this,
00185                                    ros::TransportHints().unreliable().tcpNoDelay());
00186     }
00187     else
00188     {
00189       transform_sub = nh.subscribe(transform_topic, 1, &Estimator::transformCallback, this,
00190                                    ros::TransportHints().tcpNoDelay());
00191     }
00192     // Timers
00193     main_timer = nh.createTimer(ros::Duration(1 / freq), &Estimator::mainCallback, this);
00194   }
00195 
00196 private:
00197   void transformCallback(const geometry_msgs::TransformStampedConstPtr& msg)
00198   {
00199     boost::mutex::scoped_lock(state_mutex);
00200     // Ok this is a bit convoluted. What is happening here:
00201     // - we receive a stamped transform providing the transform from /enu to flyer_vicon
00202     // - what we want is to output a nav_msgs/Odometry (position, orientation, velocity, ang vel)
00203     //   (though we'll leave ang vel zero for now, as well as the covariances) from /ned to flyer_imu
00204     // - and also to broadcast the position, orientation transform to /tf
00205     // - tf knows the transform between flyer_imu and flyer_vicon
00206     //  -- this doesn't change during a run -- so this is something we should just query at startup, store,
00207     //     and be done with it.
00208     // Steps:
00209     // - convert the received transform message to a tf::StampedTransform
00210     // - come up with the overall transform: /ned --> /enu --> flyer_vicon --> flyer_imu
00211     // - calculate filtered velocity
00212     // - combine transform and velocity into Odometry message, ready for publication at the next
00213     //   timer interval
00214     // TODO: somewhere, the /enu --> flyer_vicon relationship should be broadcast to TF so that visualization
00215     //       etc works.
00216     static ros::Time prev_stamp = ros::Time::now();
00217     tf::StampedTransform T_enu__flyer_vicon;
00218     tf::transformStampedMsgToTF(*msg, T_enu__flyer_vicon);
00219     tf::StampedTransform T_ned_imu;
00220     T_ned_imu.setData(T_enu__ned.inverse() * T_enu__flyer_vicon * T_flyer_vicon__flyer_imu);
00221     T_ned_imu.stamp_ = (*msg).header.stamp; // make sure the stamp is that of the data we just got
00222     //    ROS_INFO_STREAM("/ned --> /imu transform: " << T_ned_imu.getOrigin().getX()
00223     //        << " " << T_ned_imu.getOrigin().getY()
00224     //        << " " << T_ned_imu.getOrigin().getZ());
00225 
00226     // Now do velocity calculations
00227     last_transform_age = (T_ned_imu.stamp_ - ros::Time::now()).toSec() * 1000;
00228     double dt = (T_ned_imu.stamp_ - prev_stamp).toSec();
00229     got_new_measurement = true;
00230     max_dt = max(dt*1000, max_dt);
00231     if (dt > 0.001)
00232     {
00233       tf::Vector3 trans = T_ned_imu.getOrigin();
00234       state_msg.pose.pose.position.x = trans[0];
00235       state_msg.pose.pose.position.y = trans[1];
00236       state_msg.pose.pose.position.z = trans[2];
00237       tf::Quaternion quat = T_ned_imu.getRotation();
00238       state_msg.pose.pose.orientation.w = quat.getW();
00239       state_msg.pose.pose.orientation.x = quat.getX();
00240       state_msg.pose.pose.orientation.y = quat.getY();
00241       state_msg.pose.pose.orientation.z = quat.getZ();
00242       freq_status.tick();
00243       calcVelocity(state_msg.pose.pose.position.x, state_msg.pose.pose.position.y, state_msg.pose.pose.position.z, dt,
00244                    state_msg.twist.twist.linear.x, state_msg.twist.twist.linear.y, state_msg.twist.twist.linear.z);
00245       Eigen::Vector4d new_quat_deriv(0, 0, 0, 0);
00246       Eigen::Vector3d new_ang_vel(0, 0, 0);
00247       if (!got_first_transform)
00248         prev_quat = quat;
00249       calcAngularVelocity(quat, prev_quat, prev_quat_deriv, dt, new_quat_deriv, new_ang_vel);
00250       prev_quat = quat;
00251       prev_quat_deriv = new_quat_deriv;
00252       state_msg.twist.twist.angular.x = new_ang_vel[0];
00253       state_msg.twist.twist.angular.y = new_ang_vel[1];
00254       state_msg.twist.twist.angular.z = new_ang_vel[2];
00255       state_msg.header.stamp = T_ned_imu.stamp_;
00256     }
00257     else
00258     {
00259       ROS_WARN_STREAM("dt too small: " << dt);
00260     }
00261 
00262     prev_stamp = T_ned_imu.stamp_;
00263     if (!got_first_transform)
00264       got_first_transform = true;
00265   }
00266 
00267   void diagnostics(diagnostic_updater::DiagnosticStatusWrapper& stat)
00268   {
00269     stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "OK");
00270     stat.add("Got TF data", got_first_transform);
00271     stat.add("Last transform age [ms]", last_transform_age);
00272     stat.add("Maximum dt [ms]", max_dt);
00273     stat.add("Max # state msg published w/o new measurement", max_published_without_new_measurement);
00274   }
00275 
00276   // Calculate the angular velocity in the body frame based on finite differencing of the quaternions
00277   void calcAngularVelocity(const tf::Quaternion& cur_quat, const tf::Quaternion& prev_quat,
00278                            const Eigen::Vector4d& prev_quat_deriv, const double dt, //
00279                            Eigen::Vector4d& new_quat_deriv, Eigen::Vector3d& new_ang_vel)
00280   {
00281     // First, perform numerical differentiation on the quaternion:
00282     if (dt > 0.0001)
00283     {
00284       double mult = 1.0;
00285       if (cur_quat.w() * prev_quat.w() < 0)
00286       {
00287         NODELET_INFO("quat w's (cur, prev) = %f %f", cur_quat.w(), prev_quat.w());
00288         mult = -1.0;
00289       }
00290       tf::Quaternion temp = (cur_quat * mult - prev_quat) * ang_vel_filt_a / dt + prev_quat * ang_vel_filt_b;
00291       new_quat_deriv << temp.w(), temp.x(), temp.y(), temp.z();
00292     }
00293     else
00294     {
00295       ROS_WARN_STREAM("dt too small: " << dt);
00296       new_quat_deriv = prev_quat_deriv;
00297     }
00298 
00299     // Then, determine inertial angular rate vector using current quaternion and its derivative:
00300     // Following method from J. Diebel, "Representing attitude: Euler angles, unit quaternions, and rotation vectors," 2006
00301     Eigen::Matrix<double, 3, 4> W;
00302     W << -cur_quat.x(), cur_quat.w(), -cur_quat.z(), cur_quat.y(), //
00303     -cur_quat.y(), cur_quat.z(), cur_quat.w(), -cur_quat.x(), //
00304     -cur_quat.z(), -cur_quat.y(), cur_quat.x(), cur_quat.w();
00305     new_ang_vel = 2 * W * new_quat_deriv;
00306   }
00307 
00308   void calcVelocity(const double& current_x, const double& current_y, const double& current_z, const double dt,
00309                     double& new_vx, double& new_vy, double& new_vz)
00310   {
00311     static double prev_x, prev_y, prev_z;
00312     static double prev_vx = 0, prev_vy = 0, prev_vz = 0;
00313     static bool first = true;
00314     if (first)
00315     {
00316       new_vx = new_vy = new_vz = 0;
00317       first = false;
00318     }
00319     else
00320     {
00321       if (dt > 0.0001)
00322       {
00323         new_vx = xy_vel_filt_a * (current_x - prev_x) / dt + xy_vel_filt_b * prev_vx;
00324         new_vy = xy_vel_filt_a * (current_y - prev_y) / dt + xy_vel_filt_b * prev_vy;
00325         new_vz = z_vel_filt_a * (current_z - prev_z) / dt + z_vel_filt_b * prev_vz;
00326       }
00327       else
00328       {
00329         ROS_WARN_STREAM("dt too small: " << dt);
00330         new_vx = prev_vx;
00331         new_vy = prev_vy;
00332         new_vz = prev_vz;
00333       }
00334     }
00335     prev_x = current_x;
00336     prev_y = current_y;
00337     prev_z = current_z;
00338     prev_vx = new_vx;
00339     prev_vy = new_vy;
00340     prev_vz = new_vz;
00341 
00342   }
00343 
00344   void mainCallback(const ros::TimerEvent& e)
00345   {
00346     //static bool first = true;
00347     if (!got_first_transform)
00348     {
00349       if (main_cb_first_time)
00350       {
00351         ROS_INFO_STREAM(
00352             "Waiting for transforms from /ned to " << flyer_vicon_tf_node << " on topic " << transform_topic);
00353         main_cb_first_time = false;
00354       }
00355     }
00356     else
00357     {
00358       nav_msgs::OdometryPtr state_msg_ptr(new nav_msgs::Odometry);
00359       {
00360         boost::mutex::scoped_lock(state_mutex);
00361         *state_msg_ptr = state_msg;
00362         if (got_new_measurement)
00363         {
00364           got_new_measurement = false;
00365           published_without_new_measurement_count = 0;
00366         }
00367         else
00368         {
00369           published_without_new_measurement_count++;
00370           max_published_without_new_measurement = max(published_without_new_measurement_count,
00371                                                       max_published_without_new_measurement);
00372         }
00373       }
00374       output_pub.publish(state_msg_ptr);
00375     }
00376 
00377     diag_updater.update();
00378 
00379   }
00380 };
00381 PLUGINLIB_DECLARE_CLASS(flyer_est, Estimator, flyer_est::Estimator, nodelet::Nodelet);
00382 
00383 } // namespace
00384 
00385 //int main(int argc, char** argv)
00386 //{
00387 //  ros::init(argc, argv, "estimator");
00388 //  flyer_est::Estimator estimator;
00389 //  ros::spin();
00390 //  return 0;
00391 //}
00392 


flyer_est
Author(s): Patrick Bouffard
autogenerated on Sun Jan 5 2014 11:38:40