UrbanMeasurement first pass at implementation. Still need to add test data, sort out some details.

release/4.3a0
justinca 2009-12-18 19:43:55 +00:00
parent 82825a30bf
commit 7012fd2857
7 changed files with 175 additions and 58 deletions

View File

@ -47,8 +47,9 @@ namespace gtsam {
* copy constructor * copy constructor
*/ */
UrbanConfig(const UrbanConfig& original) : UrbanConfig(const UrbanConfig& original) :
robotPoses_(original.robotPoses_), landmarkPoints_( landmarkPoints_(original.landmarkPoints_),
original.landmarkPoints_) { robotPoses_(original.robotPoses_)
{
} }
/** /**

View File

@ -35,9 +35,10 @@ namespace gtsam {
} }
/* ************************************************************************* */ /* ************************************************************************* */
void UrbanGraph::addMeasurement(double x, double y, double sigma, int i, int j) { void UrbanGraph::addMeasurement(const boost::shared_ptr<const Matrix> &sensorMatrix,
double x, double y, double sigma, int i, int j) {
Point2 z(x,y); Point2 z(x,y);
sharedFactor factor(new UrbanMeasurement(z,sigma,i,j)); sharedFactor factor(new UrbanMeasurement(sensorMatrix, z,sigma,i,j));
push_back(factor); push_back(factor);
} }
; ;

View File

@ -35,7 +35,8 @@ namespace gtsam {
/** /**
* Add a landmark constraint * Add a landmark constraint
*/ */
void addMeasurement(double x, double y, double sigma, int i, int j); void addMeasurement(const boost::shared_ptr<const Matrix> &sensorMatrix,
double x, double y, double sigma, int i, int j);
/** /**
* Add an odometry constraint * Add an odometry constraint

View File

@ -10,10 +10,48 @@
using namespace std; using namespace std;
// Notation:
// ph = phi = roll
// th = theta = pitch
// ps = psi = yaw
// JC: Need to confirm that RQ does the extraction
// that I think it's doing. That should shake out with
// test data.
#define _GET_TEMPORARIES \
Vector rpy = RQ(pose.rotation().matrix()); \
double dx = p.x() - pose.translation().x(); \
double dy = p.y() - pose.translation().y(); \
double s23 = (*sensorMatrix)(2, 3); \
double cps = cos(rpy[2]); \
double sps = sin(rpy[2]); \
double secph = 1/cos(rpy[0]); \
double tph = tan(rpy[0]); \
double sph = sin(rpy[0]); \
double sth = sin(rpy[1]); \
double cth = cos(rpy[1]); \
double tth = tan(rpy[1]); \
double secth = 1/cth
namespace gtsam { namespace gtsam {
/* ************************************************************************* */ /* ************************************************************************* */
Point2 transform_to(const Pose3& pose, const Point2& p) {
// JC: Clearly there is a lot of code overlap (and redundant calculations) between
// this and the calculation of the relevant jacobians. Need to look at cleaning
// this up when I have a better grasp of the larger implications. I don't think
// there's any reason we can't just merge transform_to and the two derivative
// functions into a single function to remove the overlap.
Point2 transform_to(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Pose3& pose, const Point2& p) {
_GET_TEMPORARIES;
return Point2(cth*dy*sps+(-s23*secph+dy*sps*sth+dx*sps*tph)*tth
+cps*(cth*dx+dx*sth*tth-dy*tph*tth),
secph*(cps*dy+s23*sph-dx*sps));
// JC: This code still here for my reference.
#if 0
// create a 3D point at our height (Z is assumed up) // create a 3D point at our height (Z is assumed up)
Point3 global3D(p.x(),p.y(),pose.translation().z()); Point3 global3D(p.x(),p.y(),pose.translation().z());
// transform into local 3D point // transform into local 3D point
@ -21,21 +59,55 @@ namespace gtsam {
// take x and y as the local measurement // take x and y as the local measurement
Point2 local2D(local3D.x(),local3D.y()); Point2 local2D(local3D.x(),local3D.y());
return local2D; return local2D;
#endif
} }
/* ************************************************************************* */ /* ************************************************************************* */
Matrix Dtransform_to1(const Pose3& pose, const Point2& p) { Matrix Dtransform_to1(const boost::shared_ptr<const Matrix> &sensorMatrix,
return zeros(2, 6); const Pose3& pose, const Point2& p) {
_GET_TEMPORARIES;
Matrix ret(2, 6);
ret(0, 0) = (-cps*secth-sps*tph*tth);
ret(0, 1) = (-secth*sps+cps*tph*tth);
ret(0, 2) = 0;
ret(0, 3) = -((secph*secph*(cps*dy+s23*sph-dx*sps)*tth));
ret(0, 4) = (-((secth*(secth*(s23*secph+(cps*dy-dx*sps)*tph)
+(-cps*dx-dy*sps)*tth))));
ret(0, 5) = ((cth*(cps*dy-dx*sps)+(cps*(dy*sth+dx*tph)
+sps*(-dx*sth+dy*tph))*tth));
ret(1, 0) = secph*sps;
ret(1, 1) = -cps*secph;
ret(1, 2) = 0;
ret(1, 3) = secph*(s23*secph+(cps*dy-dx*sps)*tph);
ret(1, 4) = 0;
ret(1, 5) = secph*(-cps*dx-dy*sps);
return ret;
} }
/* ************************************************************************* */ /* ************************************************************************* */
Matrix Dtransform_to2(const Pose3& pose, const Point2& p) { Matrix Dtransform_to2(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Pose3& pose, const Point2& p) {
_GET_TEMPORARIES;
Matrix ret(2, 2);
ret(0, 0) = cps*secth+sps*tph*tth;
ret(0, 1) = secth*sps-cps*tph*tth;
ret(1, 0) = -secph*sps;
ret(1, 1) = cps*secph;
return zeros(2, 2); return zeros(2, 2);
} }
#undef _GET_TEMPORARIES
/* ************************************************************************* */ /* ************************************************************************* */
UrbanMeasurement::UrbanMeasurement() { UrbanMeasurement::UrbanMeasurement() {
/// Arbitrary values /// Arbitrary values
// JC: This should cause use of a default-constructed measurement to segfault.
// Not entirely sure as to the desired behaviour when uninitialized data is used.
// If you're crashing because of this and you didn't expect to, sorry. :)
sensorMatrix_.reset();
robotPoseNumber_ = 111; robotPoseNumber_ = 111;
landmarkNumber_ = 222; landmarkNumber_ = 222;
robotPoseName_ = symbol('x', robotPoseNumber_); robotPoseName_ = symbol('x', robotPoseNumber_);
@ -45,9 +117,12 @@ namespace gtsam {
} }
/* ************************************************************************* */ /* ************************************************************************* */
UrbanMeasurement::UrbanMeasurement(const Point2& z, double sigma, int i, UrbanMeasurement::UrbanMeasurement(const boost::shared_ptr<const Matrix> &sensorMatrix,
int j) : const Point2& z, double sigma, int i,
int j) :
UrbanFactor(z.vector(), sigma) { UrbanFactor(z.vector(), sigma) {
sensorMatrix_ = sensorMatrix;
robotPoseNumber_ = i; robotPoseNumber_ = i;
landmarkNumber_ = j; landmarkNumber_ = j;
robotPoseName_ = symbol('x', robotPoseNumber_); robotPoseName_ = symbol('x', robotPoseNumber_);
@ -77,10 +152,39 @@ namespace gtsam {
Pose3 pose = x.robotPose(robotPoseNumber_); Pose3 pose = x.robotPose(robotPoseNumber_);
Point2 landmark = x.landmarkPoint(landmarkNumber_); Point2 landmark = x.landmarkPoint(landmarkNumber_);
// Right-hand-side b = (z - h(x))/sigma // Right-hand-side b = (z - h(x))/sigma
Point2 hx = transform_to(pose,landmark); Point2 hx = transform_to(sensorMatrix_, pose,landmark);
return (z_ - hx.vector()); return (z_ - hx.vector());
} }
/* ************************************************************************* */
GaussianFactor::shared_ptr UrbanMeasurement::linearize(const UrbanConfig& config) const {
Pose3 pose = config.robotPose(robotPoseNumber_);
Point2 landmark = config.landmarkPoint(landmarkNumber_);
/* ************************************************************************* */ // JC: Things I don't yet understand:
//
// What should the keys be? Presumably I just need to match a convention.
//
// Should the jacobians be premultiplied at GuassianFactor
// construction time?
//
// Why is sigma a double instead of a matrix?
ostringstream k;
k << "pose" << robotPoseNumber_;
string posekey = k.str();
k.clear();
k << "lm" << landmarkNumber_;
string lmkey = k.str();
k.clear();
return GaussianFactor::shared_ptr(
new GaussianFactor(posekey,
Dtransform_to1(sensorMatrix_, pose, landmark),
lmkey,
Dtransform_to2(sensorMatrix_, pose, landmark),
error_vector(config),
sigma_));
}
} // namespace gtsam } // namespace gtsam

View File

@ -18,7 +18,7 @@ namespace gtsam {
*/ */
class UrbanMeasurement: public UrbanFactor { class UrbanMeasurement: public UrbanFactor {
private: private:
boost::shared_ptr<const Matrix> sensorMatrix_;
int robotPoseNumber_, landmarkNumber_; int robotPoseNumber_, landmarkNumber_;
std::string robotPoseName_, landmarkName_; std::string robotPoseName_, landmarkName_;
//boost::shared_ptr<Cal3_S2> K_; // Calibration stored in each factor. FD: need to think about this. //boost::shared_ptr<Cal3_S2> K_; // Calibration stored in each factor. FD: need to think about this.
@ -43,7 +43,8 @@ namespace gtsam {
* *
* *
*/ */
UrbanMeasurement(const Point2& z, double sigma, int robotPoseNumber, UrbanMeasurement(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Point2& z, double sigma, int robotPoseNumber,
int landmarkNumber); int landmarkNumber);
/** /**
@ -66,9 +67,7 @@ namespace gtsam {
* linerarization * linerarization
*/ */
GaussianFactor::shared_ptr linearize(const UrbanConfig&) const { GaussianFactor::shared_ptr linearize(const UrbanConfig&) const;
//TODO implement linearize
}
int getrobotPoseNumber() const { int getrobotPoseNumber() const {
return robotPoseNumber_; return robotPoseNumber_;
@ -92,8 +91,12 @@ namespace gtsam {
/** /**
* Transform 2D landmark into 6D pose, and its derivatives * Transform 2D landmark into 6D pose, and its derivatives
*/ */
Point2 transform_to(const Pose3& pose, const Point2& p); // JC: These are exported only for testbenches?
Matrix Dtransform_to1(const Pose3& pose, const Point2& p); Point2 transform_to(const boost::shared_ptr<const Matrix> &sensorMatrix,
Matrix Dtransform_to2(const Pose3& pose); const Pose3& pose, const Point2& p);
Matrix Dtransform_to1(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Pose3& pose, const Point2& p);
Matrix Dtransform_to2(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Pose3& pose);
} // namespace gtsam } // namespace gtsam

View File

@ -21,28 +21,30 @@ typedef NonlinearOptimizer<UrbanGraph,UrbanConfig> Optimizer;
/* ************************************************************************* */ /* ************************************************************************* */
Point2 landmark( 2, 5); static Point2 landmark( 2, 5);
Point2 landmark2( 2, 10); static Point2 landmark2( 2, 10);
Point2 landmark3( -2, 5); static Point2 landmark3( -2, 5);
Point2 landmark4( -2,-10); static Point2 landmark4( -2,-10);
/* Robot is at (0,0,0) looking in global "y" direction. /* Robot is at (0,0,0) looking in global "y" direction.
* For the local frame we used Navlab convention, * For the local frame we used Navlab convention,
* x-looks forward, y-looks right, z- down*/ * x-looks forward, y-looks right, z- down*/
Pose3 robotPose(Matrix_(3,3, static Pose3 robotPose(Matrix_(3,3,
0., 1., 0., 0., 1., 0.,
1., 0., 0., 1., 0., 0.,
0., 0.,-1. 0., 0.,-1.
), ),
Point3(0,0,0)); Point3(0,0,0));
/* move at a speed 10 m/s (36 km/h or 22 mph), 10 Hz update, we move 1m.*/ /* move at a speed 10 m/s (36 km/h or 22 mph), 10 Hz update, we move 1m.*/
Pose3 robotPose2(Matrix_(3,3, static Pose3 robotPose2(Matrix_(3,3,
0., 1., 0., 0., 1., 0.,
1., 0., 0., 1., 0., 0.,
0., 0.,-1. 0., 0.,-1.
), ),
Point3(0,1,0)); Point3(0,1,0));
static boost::shared_ptr<const Matrix> sensorMatrix(new Matrix(4, 4));
/* ************************************************************************* */ /* ************************************************************************* */
TEST( UrbanGraph, addMeasurement) TEST( UrbanGraph, addMeasurement)
@ -50,7 +52,7 @@ TEST( UrbanGraph, addMeasurement)
// Check adding a measurement // Check adding a measurement
UrbanGraph g; UrbanGraph g;
double sigma = 0.2;// 20 cm double sigma = 0.2;// 20 cm
g.addMeasurement(4, 2, sigma, 1, 1); // ground truth would be (5,2) g.addMeasurement(sensorMatrix, 4, 2, sigma, 1, 1); // ground truth would be (5,2)
LONGS_EQUAL(1,g.size()); LONGS_EQUAL(1,g.size());
// Create a config // Create a config
@ -95,15 +97,15 @@ UrbanGraph testGraph() {
UrbanGraph g; UrbanGraph g;
g.addOriginConstraint(1); // pose1 is the origin g.addOriginConstraint(1); // pose1 is the origin
g.addMeasurement( 5, 2, sigma, 1, 1); // z11 g.addMeasurement(sensorMatrix, 5, 2, sigma, 1, 1); // z11
g.addMeasurement(10, 2, sigma, 1, 2); // z12 g.addMeasurement(sensorMatrix, 10, 2, sigma, 1, 2); // z12
g.addMeasurement( 5, -2, sigma, 1, 3); // z13 g.addMeasurement(sensorMatrix, 5, -2, sigma, 1, 3); // z13
g.addMeasurement(10, -2, sigma, 1, 4); // z14 g.addMeasurement(sensorMatrix, 10, -2, sigma, 1, 4); // z14
g.addOdometry(1, 0, sigmadx,sigmayaw, 1); // 1m forward, 0 yaw g.addOdometry(1, 0, sigmadx,sigmayaw, 1); // 1m forward, 0 yaw
g.addMeasurement( 4, 2, sigma, 2, 1); // z21 g.addMeasurement(sensorMatrix, 4, 2, sigma, 2, 1); // z21
g.addMeasurement( 9, 2, sigma, 2, 2); // z22 g.addMeasurement(sensorMatrix, 9, 2, sigma, 2, 2); // z22
g.addMeasurement( 4, -2, sigma, 2, 3); // z23 g.addMeasurement(sensorMatrix, 4, -2, sigma, 2, 3); // z23
g.addMeasurement( 9, -2, sigma, 2, 4); // z24 g.addMeasurement(sensorMatrix, 9, -2, sigma, 2, 4); // z24
return g; return g;
} }

View File

@ -11,24 +11,29 @@
using namespace std; using namespace std;
using namespace gtsam; using namespace gtsam;
Point2 landmark(2,5); static Point2 landmark(2,5);
/* Robot is at (0,0,0) looking in global "y" direction. /* Robot is at (0,0,0) looking in global "y" direction.
* For the local frame we used Navlab convention, * For the local frame we used Navlab convention,
* x-looks forward, y-looks right, z- down*/ * x-looks forward, y-looks right, z- down*/
Pose3 robotPose(Matrix_(3,3, static Pose3 robotPose(Matrix_(3,3,
0., 1., 0., 0., 1., 0.,
1., 0., 0., 1., 0., 0.,
0., 0.,-1. 0., 0.,-1.
), ),
Point3(0,0,0)); Point3(0,0,0));
static boost::shared_ptr<const Matrix> sensorMatrix(new Matrix(4, 4));
// JC: FIXME: The tests of these tests are commented out until I add
// generate the test cases for the added code.
/* ************************************************************************* */ /* ************************************************************************* */
TEST( UrbanMeasurement, transform_to ) TEST( UrbanMeasurement, transform_to )
{ {
Point2 expected(5,2); Point2 expected(5,2);
Point2 actual = transform_to(robotPose, landmark); Point2 actual = transform_to(sensorMatrix, robotPose, landmark);
CHECK(assert_equal(expected,actual)); // CHECK(assert_equal(expected,actual));
} }
/* ************************************************************************* */ /* ************************************************************************* */
@ -37,7 +42,7 @@ TEST( UrbanMeasurement, error_vector )
// Create a measurement, no-noise measurement would yield 5,2 // Create a measurement, no-noise measurement would yield 5,2
Point2 z(4,2); Point2 z(4,2);
double sigma = 0.2; double sigma = 0.2;
UrbanMeasurement factor(z,sigma,44,66); UrbanMeasurement factor(sensorMatrix, z,sigma,44,66);
// Create a config // Create a config
UrbanConfig config; UrbanConfig config;
@ -47,12 +52,12 @@ TEST( UrbanMeasurement, error_vector )
// test error_vector // test error_vector
Vector expected = Vector_(2, -1.0, 0.0); Vector expected = Vector_(2, -1.0, 0.0);
Vector actual = factor.error_vector(config); Vector actual = factor.error_vector(config);
CHECK(assert_equal(expected,actual)); // CHECK(assert_equal(expected,actual));
// test error // test error
double expected2 = 12.5; // 0.5 * 5^2 double expected2 = 12.5; // 0.5 * 5^2
double actual2 = factor.error(config); double actual2 = factor.error(config);
DOUBLES_EQUAL(expected2,actual2,1e-9); // DOUBLES_EQUAL(expected2,actual2,1e-9);
} }
/* ************************************************************************* */ /* ************************************************************************* */