$search
00001 // SVN $HeadURL: http://alufr-ros-pkg.googlecode.com/svn/trunk/humanoid_stacks/humanoid_navigation/humanoid_localization/src/MotionModel.cpp $ 00002 // SVN $Id: MotionModel.cpp 3975 2013-02-26 10:38:51Z hornunga@informatik.uni-freiburg.de $ 00003 00004 /* 00005 * 6D localization for humanoid robots 00006 * 00007 * Copyright 2009-2012 Armin Hornung, University of Freiburg 00008 * http://www.ros.org/wiki/humanoid_localization 00009 * 00010 * 00011 * This program is free software: you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation, version 3. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU General Public License 00021 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00022 */ 00023 00024 #include <humanoid_localization/MotionModel.h> 00025 00026 using namespace tf; 00027 using namespace std; 00028 00029 namespace humanoid_localization{ 00030 00031 MotionModel::MotionModel(ros::NodeHandle* nh, EngineT* rngEngine, tf::TransformListener* tf, 00032 const std::string& odomFrameId, const std::string& baseFrameId) 00033 : m_tfListener(tf), 00034 m_rngNormal(*rngEngine, NormalDistributionT(0.0, 1.0)), 00035 m_rngUniform(*rngEngine, UniformDistributionT(0.0, 1.0)), 00036 m_odomFrameId(odomFrameId), m_baseFrameId(baseFrameId), 00037 m_firstOdometryReceived(false) 00038 00039 { 00040 00041 // motion model noise parameters: 00042 double xx, yy, zz, RR, PP, YY; 00043 nh->param("motion_noise/x", xx, 0.01); 00044 nh->param("motion_noise/y", yy, 0.01); 00045 nh->param("motion_noise/z", zz, 0.01); 00046 nh->param("motion_noise/roll", RR, 0.05); 00047 nh->param("motion_noise/pitch", PP, 0.1); 00048 nh->param("motion_noise/yaw", YY, 0.5); 00049 m_motionNoise(0) = xx; // x 00050 m_motionNoise(1) = yy; // y 00051 m_motionNoise(2) = zz; // z 00052 m_motionNoise(3) = RR; // roll 00053 m_motionNoise(4) = PP; // pitch 00054 m_motionNoise(5) = YY; // yaw 00055 00056 m_odomCalibration2D = Eigen::Matrix3d::Identity(); 00057 nh->param("motion_calib/xx", m_odomCalibration2D(0,0), 1.0); 00058 nh->param("motion_calib/xy", m_odomCalibration2D(0,1), 0.0); 00059 nh->param("motion_calib/xt", m_odomCalibration2D(0,2), 0.0); 00060 nh->param("motion_calib/yx", m_odomCalibration2D(1,0), 0.0); 00061 nh->param("motion_calib/yy", m_odomCalibration2D(1,1), 1.0); 00062 nh->param("motion_calib/yt", m_odomCalibration2D(1,2), 0.0); 00063 nh->param("motion_calib/tx", m_odomCalibration2D(2,0), 0.0); 00064 nh->param("motion_calib/ty", m_odomCalibration2D(2,1), 0.0); 00065 nh->param("motion_calib/tt", m_odomCalibration2D(2,2), 1.0); 00066 00067 // not used right now: 00068 // covariance matrix, contains squared std.devs on diagonal 00069 // Matrix6f motionNoise = Matrix6f::Zero(); 00070 // motionNoise.diagonal() = m_motionNoise.cwiseProduct( m_motionNoise); 00071 00072 // if (motionNoise.isZero()){ 00073 // m_motionNoiseL = Matrix6f::Zero(); 00074 // } else{ 00075 // m_motionNoiseL = motionNoise.llt().matrixL(); 00076 // } 00077 00078 reset(); 00079 00080 } 00081 00082 MotionModel::~MotionModel() { 00083 00084 } 00085 00086 void MotionModel::transformPose(tf::Pose& particlePose, const tf::Transform& odomTransform){ 00087 00088 00089 // apply transform to particle: 00090 particlePose *= odomTransform * odomTransformNoise(odomTransform); 00091 00092 // HACK for testing: fix roll & pitch at 0: 00093 // double roll, pitch, yaw; 00094 // tf::Matrix3x3 basis = particlePose.getBasis(); 00095 // basis.getRPY(roll, pitch, yaw); 00096 // basis.setRPY(0.0, 0.0, yaw); 00097 // particlePose.setBasis(basis); 00098 00099 00100 } 00101 00102 tf::Transform MotionModel::odomTransformNoise(const tf::Transform& odomTransform){ 00103 // absolute amount of translation, used to scale odom noise: 00104 // (about 1-2cm for each update step) 00105 const double d = odomTransform.getOrigin().length(); 00106 return tf::Transform(tf::createQuaternionFromRPY( 00107 d * m_motionNoise(3) * m_rngNormal(), 00108 d * m_motionNoise(4) * m_rngNormal(), 00109 d * m_motionNoise(5) * m_rngNormal()), 00110 tf::Vector3( 00111 d * m_motionNoise(0) * m_rngNormal(), 00112 d * m_motionNoise(1) * m_rngNormal(), 00113 d * m_motionNoise(2) * m_rngNormal())); 00114 } 00115 00116 void MotionModel::reset(){ 00117 m_firstOdometryReceived = false; 00118 } 00119 00120 void MotionModel::applyOdomTransform(Particles& particles, const tf::Transform& odomTransform){ 00121 tf::Transform calibratedOdomTransform = calibrateOdometry(odomTransform); 00122 00123 for (unsigned i=0; i < particles.size(); ++i){ 00124 transformPose(particles[i].pose, calibratedOdomTransform); 00125 } 00126 } 00127 00128 bool MotionModel::applyOdomTransformTemporal(Particles& particles,const ros::Time& t, double dt){ 00129 ros::WallTime startTime = ros::WallTime::now(); 00130 00131 // first see if default time is available 00132 tf::Transform odomTransform; 00133 if (!lookupOdomTransform(t, odomTransform)) 00134 return false; 00135 00136 00137 tf::Transform timeSampledTransform; 00138 ros::Time maxTime; 00139 std::string errorString; 00140 m_tfListener->getLatestCommonTime(m_odomFrameId, m_baseFrameId, maxTime, &errorString); 00141 ros::Duration maxDuration = maxTime - t; 00142 00143 for (unsigned i=0; i < particles.size(); ++i){ 00144 if (dt > 0.0){ 00145 ros::Duration duration(m_rngUniform()*dt -dt/2.0); 00146 // TODO: time t is time of first measurement in scan! 00147 if (duration > maxDuration) 00148 duration = maxDuration; 00149 if (lookupOdomTransform(t + duration, timeSampledTransform)) 00150 transformPose(particles[i].pose, timeSampledTransform); 00151 else{ 00152 ROS_WARN("Could not lookup temporal odomTransform"); 00153 transformPose(particles[i].pose, odomTransform); 00154 } 00155 } else{ 00156 transformPose(particles[i].pose, odomTransform); 00157 } 00158 } 00159 00160 double dwalltime = (ros::WallTime::now() - startTime).toSec(); 00161 ROS_INFO_STREAM("OdomTransformTemporal took " << dwalltime << "s "); 00162 00163 00164 return true; 00165 } 00166 00167 tf::Transform MotionModel::calibrateOdometry(const tf::Transform& odomTransform) const { 00168 Eigen::Vector3d odomPose2D; 00169 double roll, pitch; 00170 odomPose2D(0) = odomTransform.getOrigin().getX(); 00171 odomPose2D(1) = odomTransform.getOrigin().getY(); 00172 odomPose2D(2) = tf::getYaw(odomTransform.getRotation()); 00173 odomTransform.getBasis().getRPY(roll, pitch, odomPose2D(2)); 00174 00175 odomPose2D = m_odomCalibration2D * odomPose2D; 00176 00177 return tf::Transform(tf::createQuaternionFromRPY(roll, pitch, odomPose2D(2)), 00178 tf::Vector3(odomPose2D(0), odomPose2D(1), odomTransform.getOrigin().getZ())); 00179 00180 00181 } 00182 00183 bool MotionModel::lookupOdomTransform(const ros::Time& t, tf::Transform& odomTransform) const{ 00184 tf::Stamped<tf::Pose> odomPose; 00185 00186 if (t <= m_lastOdomPose.stamp_){ 00187 ROS_WARN("Looking up OdomTransform that is %f ms older than the last odomPose!", 00188 (m_lastOdomPose.stamp_ - t).toSec()/1000.0); 00189 } 00190 00191 if (!lookupOdomPose(t, odomPose)) 00192 return false; 00193 00194 odomTransform = computeOdomTransform(odomPose); 00195 return true; 00196 } 00197 00198 tf::Transform MotionModel::computeOdomTransform(const tf::Transform currentPose) const{ 00199 if (m_firstOdometryReceived){ 00200 return m_lastOdomPose.inverse() * currentPose; 00201 } else{ 00202 return tf::Transform(tf::createIdentityQuaternion(), tf::Vector3(0,0,0)); 00203 } 00204 00205 } 00206 00207 void MotionModel::storeOdomPose(const tf::Stamped<tf::Pose>& odomPose){ 00208 m_firstOdometryReceived = true; 00209 if (odomPose.stamp_ <= m_lastOdomPose.stamp_){ 00210 ROS_WARN("Trying to store an OdomPose that is older or equal than the current in the MotionModel, ignoring!"); 00211 } else { 00212 m_lastOdomPose = odomPose; 00213 } 00214 00215 } 00216 00217 00218 bool MotionModel::lookupOdomPose(const ros::Time& t, tf::Stamped<tf::Pose>& odomPose) const 00219 { 00220 tf::Stamped<tf::Pose> ident (tf::Transform(tf::createIdentityQuaternion(), 00221 tf::Vector3(0,0,0)), t, m_baseFrameId); 00222 00223 try 00224 { 00225 m_tfListener->transformPose(m_odomFrameId, ident, odomPose); 00226 } 00227 catch(tf::TransformException& e) 00228 { 00229 ROS_WARN("Failed to compute odom pose, skipping scan (%s)", e.what()); 00230 return false; 00231 } 00232 00233 return true; 00234 } 00235 00236 00237 bool MotionModel::lookupLocalTransform(const std::string& targetFrame, const ros::Time& t, 00238 tf::StampedTransform& localTransform) const 00239 { 00240 try 00241 { 00242 m_tfListener->lookupTransform(targetFrame, m_baseFrameId, t, localTransform); 00243 } 00244 catch(tf::TransformException& e) 00245 { 00246 ROS_WARN("Failed to lookup local transform (%s)", e.what()); 00247 return false; 00248 } 00249 00250 return true; 00251 } 00252 00253 bool MotionModel::getLastOdomPose(tf::Stamped<tf::Pose>& lastOdomPose) const{ 00254 if (m_firstOdometryReceived){ 00255 lastOdomPose = m_lastOdomPose; 00256 return true; 00257 } else{ 00258 return false; 00259 } 00260 00261 } 00262 00263 } 00264