spa_2d_conversion.cpp
Go to the documentation of this file.
00001 #include <pose_graph/spa_2d_conversion.h>
00002 #include <pose_graph/transforms.h>
00003 #include <pose_graph/exception.h>
00004 #include <pose_graph/utils.h>
00005 #include <tf/transform_datatypes.h>
00006 #include <boost/foreach.hpp>
00007 #include <boost/tuple/tuple.hpp>
00008 #include <set>
00009 
00010 namespace pose_graph
00011 {
00012 
00013 using std::set;
00014 using boost::tie;
00015 using sba::Node2d;
00016 using sba::SysSPA2d;
00017 using sba::Con2dP2;
00018 using geometry_msgs::Pose;
00019 
00020 
00021 typedef std::pair<NodeId, NodeId> NodePair;
00022 
00023 // Make a spa node given an initial pose estimate
00024 Node2d makeNode (const double x, const double y, const double theta)
00025 {
00026   Node2d n;
00027   n.trans(0) = x;
00028   n.trans(1) = y;
00029   n.trans(2) = 1.0;
00030   n.arot = theta;
00031   n.setTransform();
00032   n.setDr();
00033   return n;
00034 }
00035 
00036 
00037 // Get yaw from an eigen quaternion
00038 double getYaw (const Quaterniond& q)
00039 {
00040   const tf::Quaternion qt(q.x(), q.y(), q.z(), q.w());
00041   return tf::getYaw(qt);
00042 }
00043 
00044 Con2dP2 makeSpa2DConstraint (const PoseConstraint& constraint)
00045 {
00046   Con2dP2 spa;
00047   spa.tmean[0] = constraint.translation[0];
00048   spa.tmean[1] = constraint.translation[1];
00049   spa.amean = getYaw(constraint.rotation);
00050 
00051   spa.prec = Matrix3d::Zero();
00052   spa.prec(0,0) = constraint.precision(0,0);
00053   spa.prec(1,1) = constraint.precision(1,1);
00054 
00055   spa.prec(2,2) = constraint.precision(5,5)*4;
00056 
00057   return spa;
00058 }
00059 
00060 
00061 // Turn a pose graph into a pointer to a Spa2d
00062 Spa2DConversionResult poseGraphToSpa2D (const PoseGraph& pose_graph)
00063 {
00064   Spa2DPtr spa(new SysSPA2d());
00065   map<NodeId, unsigned> node_indices;
00066   unsigned next_index=0;
00067 
00068   // 1. Add the nodes
00069   BOOST_FOREACH (const NodeId n, pose_graph.allNodes()) {
00070     const Pose p = pose_graph.getInitialPoseEstimate(n);
00071     Eigen3::Vector3d pos(p.position.x, p.position.y, tf::getYaw(p.orientation));
00072     spa->addNode(pos, next_index);
00073     ROS_DEBUG_STREAM_NAMED ("pose_graph_spa", "Adding node " << next_index << " with mean " << pos);
00074     node_indices[n] = next_index++;
00075   }
00076 
00077   // 2. Add the edge constraints
00078   BOOST_FOREACH (const EdgeId e, pose_graph.allEdges()) {
00079 
00080     NodePair ids = pose_graph.incidentNodes(e);
00081     const pose_graph::PoseConstraint constraint = pose_graph.getConstraint(e);
00082     Eigen3::Vector3d mean(constraint.translation[0], constraint.translation[1], getYaw(constraint.rotation));
00083     Eigen3::Matrix3d prec = Eigen3::Matrix3d::Zero();
00084 
00085     // The 6x6 matrix constraint.precision is in order x,y,z of position and x,y,z of quaternion orientation. 
00086     // The line below would be correct for diagonal precision matrices, if theta = z/2, but in fact theta=sin(z/2)
00087     // so this isn't quite right for theta far from 0.  The precisions are heuristically set anyway though...
00088     prec(2, 2) = constraint.precision(5, 5)*4;
00089     prec(0, 0) = constraint.precision(0, 0);
00090     prec(1, 1) = constraint.precision(1, 1);
00091 
00092     const unsigned ind1 = node_indices[ids.first];
00093     const unsigned ind2 = node_indices[ids.second];
00094     spa->addConstraint(ind1, ind2, mean, prec);
00095     ROS_DEBUG_STREAM_NAMED ("pose_graph_spa", "Adding constraint from " << ind1 << " to " << ind2 <<
00096                             " with mean " << mean << " and precision " << prec);
00097   }
00098 
00099   
00100   return Spa2DConversionResult(spa, node_indices);
00101 }
00102 
00103 
00104 // Return the Pose corresponding to a spa node
00105 Pose getNodePose (const sba::Node2d& n)
00106 {
00107   Pose pose;
00108 
00109   pose.position.x = n.trans(0);
00110   pose.position.y = n.trans(1);
00111   pose.position.z = 0.0;
00112   
00113   pose.orientation = tf::createQuaternionMsgFromYaw(n.arot);
00114 
00115   return pose;
00116 }
00117 
00118 
00119 // Convert and optimize a graph
00120 NodePoseMap optimizeGraph2D (const PoseGraph& g)
00121 {
00122   /******************************
00123    * 1. Convert graph to SPA2D
00124    ******************************/
00125 
00126   ROS_DEBUG_NAMED ("pose_graph_spa", "Converting graph with %zu nodes to spa", g.allNodes().size());
00127   Spa2DConversionResult res=poseGraphToSpa2D(g);
00128 
00129   /******************************
00130    * 2. Optimize
00131    ******************************/
00132 
00133   const size_t num_nodes = res.spa->nodes.size();
00134   const size_t num_constraints = res.spa->p2cons.size();
00135 
00136   // Shouldn't need this check, but spa dies otherwise
00137   if (num_nodes>=2 && num_constraints>=1) { 
00138     ROS_DEBUG_NAMED ("pose_graph_spa", "Performing spa with %zu nodes and %zu constraints",
00139                      num_nodes, num_constraints);
00140     res.spa->doSPA(10, 1.0e-4, SBA_BLOCK_JACOBIAN_PCG);
00141     ROS_DEBUG_NAMED ("pose_graph_spa", "Graph optimization complete");
00142   }
00143   else 
00144     ROS_DEBUG_NAMED ("pose_graph_spa", "Not optimizing graph with %zu nodes and %zu constraints",
00145                      num_nodes, num_constraints);
00146   
00147   /******************************
00148    * 3. Return optimized poses
00149    ******************************/
00150 
00151   NodePoseMap optimized_poses;
00152   BOOST_FOREACH (const NodeIndexMap::value_type entry, res.node_index_map) {
00153     optimized_poses[entry.first] = getNodePose(res.spa->nodes[entry.second]);
00154     ROS_DEBUG_STREAM_NAMED ("spa_result", "  Node " << entry.first <<
00155                             "; pose " << optimized_poses[entry.first]);
00156   }
00157   return optimized_poses;
00158 }
00159 
00160 
00161 
00162 
00163 
00164 
00165 } // namespace


pose_graph
Author(s): Bhaskara Marthi
autogenerated on Tue Jan 7 2014 11:17:15