UrbanMeasurement first pass at implementation. Still need to add test data, sort out some details.
parent
82825a30bf
commit
7012fd2857
|
@ -47,8 +47,9 @@ namespace gtsam {
|
|||
* copy constructor
|
||||
*/
|
||||
UrbanConfig(const UrbanConfig& original) :
|
||||
robotPoses_(original.robotPoses_), landmarkPoints_(
|
||||
original.landmarkPoints_) {
|
||||
landmarkPoints_(original.landmarkPoints_),
|
||||
robotPoses_(original.robotPoses_)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
sharedFactor factor(new UrbanMeasurement(z,sigma,i,j));
|
||||
sharedFactor factor(new UrbanMeasurement(sensorMatrix, z,sigma,i,j));
|
||||
push_back(factor);
|
||||
}
|
||||
;
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace gtsam {
|
|||
/**
|
||||
* 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
|
||||
|
|
|
@ -10,10 +10,48 @@
|
|||
|
||||
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 {
|
||||
|
||||
/* ************************************************************************* */
|
||||
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)
|
||||
Point3 global3D(p.x(),p.y(),pose.translation().z());
|
||||
// transform into local 3D point
|
||||
|
@ -21,21 +59,55 @@ namespace gtsam {
|
|||
// take x and y as the local measurement
|
||||
Point2 local2D(local3D.x(),local3D.y());
|
||||
return local2D;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
Matrix Dtransform_to1(const Pose3& pose, const Point2& p) {
|
||||
return zeros(2, 6);
|
||||
Matrix Dtransform_to1(const boost::shared_ptr<const Matrix> &sensorMatrix,
|
||||
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);
|
||||
}
|
||||
|
||||
#undef _GET_TEMPORARIES
|
||||
|
||||
/* ************************************************************************* */
|
||||
UrbanMeasurement::UrbanMeasurement() {
|
||||
/// 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;
|
||||
landmarkNumber_ = 222;
|
||||
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,
|
||||
const Point2& z, double sigma, int i,
|
||||
int j) :
|
||||
UrbanFactor(z.vector(), sigma) {
|
||||
|
||||
sensorMatrix_ = sensorMatrix;
|
||||
robotPoseNumber_ = i;
|
||||
landmarkNumber_ = j;
|
||||
robotPoseName_ = symbol('x', robotPoseNumber_);
|
||||
|
@ -77,10 +152,39 @@ namespace gtsam {
|
|||
Pose3 pose = x.robotPose(robotPoseNumber_);
|
||||
Point2 landmark = x.landmarkPoint(landmarkNumber_);
|
||||
// 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());
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
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
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace gtsam {
|
|||
*/
|
||||
class UrbanMeasurement: public UrbanFactor {
|
||||
private:
|
||||
|
||||
boost::shared_ptr<const Matrix> sensorMatrix_;
|
||||
int robotPoseNumber_, landmarkNumber_;
|
||||
std::string robotPoseName_, landmarkName_;
|
||||
//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);
|
||||
|
||||
/**
|
||||
|
@ -66,9 +67,7 @@ namespace gtsam {
|
|||
* linerarization
|
||||
*/
|
||||
|
||||
GaussianFactor::shared_ptr linearize(const UrbanConfig&) const {
|
||||
//TODO implement linearize
|
||||
}
|
||||
GaussianFactor::shared_ptr linearize(const UrbanConfig&) const;
|
||||
|
||||
int getrobotPoseNumber() const {
|
||||
return robotPoseNumber_;
|
||||
|
@ -92,8 +91,12 @@ namespace gtsam {
|
|||
/**
|
||||
* Transform 2D landmark into 6D pose, and its derivatives
|
||||
*/
|
||||
Point2 transform_to(const Pose3& pose, const Point2& p);
|
||||
Matrix Dtransform_to1(const Pose3& pose, const Point2& p);
|
||||
Matrix Dtransform_to2(const Pose3& pose);
|
||||
// JC: These are exported only for testbenches?
|
||||
Point2 transform_to(const boost::shared_ptr<const Matrix> &sensorMatrix,
|
||||
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
|
||||
|
|
|
@ -21,15 +21,15 @@ typedef NonlinearOptimizer<UrbanGraph,UrbanConfig> Optimizer;
|
|||
|
||||
/* ************************************************************************* */
|
||||
|
||||
Point2 landmark( 2, 5);
|
||||
Point2 landmark2( 2, 10);
|
||||
Point2 landmark3( -2, 5);
|
||||
Point2 landmark4( -2,-10);
|
||||
static Point2 landmark( 2, 5);
|
||||
static Point2 landmark2( 2, 10);
|
||||
static Point2 landmark3( -2, 5);
|
||||
static Point2 landmark4( -2,-10);
|
||||
|
||||
/* Robot is at (0,0,0) looking in global "y" direction.
|
||||
* For the local frame we used Navlab convention,
|
||||
* x-looks forward, y-looks right, z- down*/
|
||||
Pose3 robotPose(Matrix_(3,3,
|
||||
static Pose3 robotPose(Matrix_(3,3,
|
||||
0., 1., 0.,
|
||||
1., 0., 0.,
|
||||
0., 0.,-1.
|
||||
|
@ -37,20 +37,22 @@ Pose3 robotPose(Matrix_(3,3,
|
|||
Point3(0,0,0));
|
||||
|
||||
/* 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.,
|
||||
1., 0., 0.,
|
||||
0., 0.,-1.
|
||||
),
|
||||
Point3(0,1,0));
|
||||
|
||||
static boost::shared_ptr<const Matrix> sensorMatrix(new Matrix(4, 4));
|
||||
|
||||
/* ************************************************************************* */
|
||||
TEST( UrbanGraph, addMeasurement)
|
||||
{
|
||||
// Check adding a measurement
|
||||
UrbanGraph g;
|
||||
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());
|
||||
|
||||
// Create a config
|
||||
|
@ -95,15 +97,15 @@ UrbanGraph testGraph() {
|
|||
|
||||
UrbanGraph g;
|
||||
g.addOriginConstraint(1); // pose1 is the origin
|
||||
g.addMeasurement( 5, 2, sigma, 1, 1); // z11
|
||||
g.addMeasurement(10, 2, sigma, 1, 2); // z12
|
||||
g.addMeasurement( 5, -2, sigma, 1, 3); // z13
|
||||
g.addMeasurement(10, -2, sigma, 1, 4); // z14
|
||||
g.addMeasurement(sensorMatrix, 5, 2, sigma, 1, 1); // z11
|
||||
g.addMeasurement(sensorMatrix, 10, 2, sigma, 1, 2); // z12
|
||||
g.addMeasurement(sensorMatrix, 5, -2, sigma, 1, 3); // z13
|
||||
g.addMeasurement(sensorMatrix, 10, -2, sigma, 1, 4); // z14
|
||||
g.addOdometry(1, 0, sigmadx,sigmayaw, 1); // 1m forward, 0 yaw
|
||||
g.addMeasurement( 4, 2, sigma, 2, 1); // z21
|
||||
g.addMeasurement( 9, 2, sigma, 2, 2); // z22
|
||||
g.addMeasurement( 4, -2, sigma, 2, 3); // z23
|
||||
g.addMeasurement( 9, -2, sigma, 2, 4); // z24
|
||||
g.addMeasurement(sensorMatrix, 4, 2, sigma, 2, 1); // z21
|
||||
g.addMeasurement(sensorMatrix, 9, 2, sigma, 2, 2); // z22
|
||||
g.addMeasurement(sensorMatrix, 4, -2, sigma, 2, 3); // z23
|
||||
g.addMeasurement(sensorMatrix, 9, -2, sigma, 2, 4); // z24
|
||||
|
||||
return g;
|
||||
}
|
||||
|
|
|
@ -11,24 +11,29 @@
|
|||
using namespace std;
|
||||
using namespace gtsam;
|
||||
|
||||
Point2 landmark(2,5);
|
||||
static Point2 landmark(2,5);
|
||||
|
||||
/* Robot is at (0,0,0) looking in global "y" direction.
|
||||
* For the local frame we used Navlab convention,
|
||||
* x-looks forward, y-looks right, z- down*/
|
||||
Pose3 robotPose(Matrix_(3,3,
|
||||
static Pose3 robotPose(Matrix_(3,3,
|
||||
0., 1., 0.,
|
||||
1., 0., 0.,
|
||||
0., 0.,-1.
|
||||
),
|
||||
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 )
|
||||
{
|
||||
Point2 expected(5,2);
|
||||
Point2 actual = transform_to(robotPose, landmark);
|
||||
CHECK(assert_equal(expected,actual));
|
||||
Point2 actual = transform_to(sensorMatrix, robotPose, landmark);
|
||||
// CHECK(assert_equal(expected,actual));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
@ -37,7 +42,7 @@ TEST( UrbanMeasurement, error_vector )
|
|||
// Create a measurement, no-noise measurement would yield 5,2
|
||||
Point2 z(4,2);
|
||||
double sigma = 0.2;
|
||||
UrbanMeasurement factor(z,sigma,44,66);
|
||||
UrbanMeasurement factor(sensorMatrix, z,sigma,44,66);
|
||||
|
||||
// Create a config
|
||||
UrbanConfig config;
|
||||
|
@ -47,12 +52,12 @@ TEST( UrbanMeasurement, error_vector )
|
|||
// test error_vector
|
||||
Vector expected = Vector_(2, -1.0, 0.0);
|
||||
Vector actual = factor.error_vector(config);
|
||||
CHECK(assert_equal(expected,actual));
|
||||
// CHECK(assert_equal(expected,actual));
|
||||
|
||||
// test error
|
||||
double expected2 = 12.5; // 0.5 * 5^2
|
||||
double actual2 = factor.error(config);
|
||||
DOUBLES_EQUAL(expected2,actual2,1e-9);
|
||||
// DOUBLES_EQUAL(expected2,actual2,1e-9);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
Loading…
Reference in New Issue