gtsam/cpp/UrbanMeasurement.cpp

189 lines
6.1 KiB
C++

/**
* @file UrbanMeasurement.cpp
* @brief A non-linear factor specialized to the Visual SLAM problem
* @author Frank Dellaert
* @author Viorela Ila
*/
#include "UrbanMeasurement.h"
#include "Pose3.h"
using namespace std;
#define _GET_TEMPORARIES \
namespace gtsam {
/* ************************************************************************* */
// JC: I'd like to break this out into component calculations, but there's too much
// 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.
UrbanMeasurement::Transform transform_to(const boost::shared_ptr<const Matrix> &sensorMatrix,
const Pose3& robotpose, const Point2& lmpos,
bool getJacobians) {
// Notation:
// ph = phi = roll
// th = theta = pitch
// ps = psi = yaw
Vector rpy = RQ(robotpose.rotation().matrix());
double dx = lmpos.x() - robotpose.translation().x();
double dy = lmpos.y() - robotpose.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;
UrbanMeasurement::Transform ret;
ret.get<0>().reset(new 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)));
if (getJacobians) {
ret.get<1>().reset(new Matrix(2, 6));
Matrix &D1 = *(ret.get<1>());
D1(0, 0) = (-cps*secth-sps*tph*tth);
D1(0, 1) = (-secth*sps+cps*tph*tth);
D1(0, 2) = 0;
D1(0, 3) = -((secph*secph*(cps*dy+s23*sph-dx*sps)*tth));
D1(0, 4) = (-((secth*(secth*(s23*secph+(cps*dy-dx*sps)*tph)
+(-cps*dx-dy*sps)*tth))));
D1(0, 5) = ((cth*(cps*dy-dx*sps)+(cps*(dy*sth+dx*tph)
+sps*(-dx*sth+dy*tph))*tth));
D1(1, 0) = secph*sps;
D1(1, 1) = -cps*secph;
D1(1, 2) = 0;
D1(1, 3) = secph*(s23*secph+(cps*dy-dx*sps)*tph);
D1(1, 4) = 0;
D1(1, 5) = secph*(-cps*dx-dy*sps);
ret.get<2>().reset(new Matrix(2, 2));
Matrix &D2 = *(ret.get<2>());
D2(0, 0) = cps*secth+sps*tph*tth;
D2(0, 1) = secth*sps-cps*tph*tth;
D2(1, 0) = -secph*sps;
D2(1, 1) = cps*secph;
}
return ret;
}
/* ************************************************************************* */
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_);
landmarkName_ = symbol('l', landmarkNumber_);
keys_.push_back(robotPoseName_);
keys_.push_back(landmarkName_);
}
/* ************************************************************************* */
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_);
landmarkName_ = symbol('l', landmarkNumber_);
keys_.push_back(robotPoseName_);
keys_.push_back(landmarkName_);
}
/* ************************************************************************* */
void UrbanMeasurement::print(const std::string& s) const {
printf("%s %s %s\n", s.c_str(), robotPoseName_.c_str(),
landmarkName_.c_str());
gtsam::print(this->z_, s + ".z");
}
/* ************************************************************************* */
bool UrbanMeasurement::equals(const UrbanMeasurement& p, double tol) const {
if (&p == NULL) return false;
if (robotPoseNumber_ != p.robotPoseNumber_ || landmarkNumber_
!= p.landmarkNumber_) return false;
if (!equal_with_abs_tol(this->z_, p.z_, tol)) return false;
return true;
}
/* ************************************************************************* */
Vector UrbanMeasurement::error_vector(const UrbanConfig& x) const {
Pose3 pose = x.robotPose(robotPoseNumber_);
Point2 landmark = x.landmarkPoint(landmarkNumber_);
// Right-hand-side b = (z - h(x))/sigma
// fprintf(stderr, "p: %.16g l: %.16g\n",
// pose.rotation().vector()[0],
// landmark.x());
boost::shared_ptr<Point2> h = transform_to(sensorMatrix_, pose,landmark, false).get<0>();
// fprintf(stderr, "H: %.16g %.16g\nz: %.16g %.16g\n",
// h->vector()[0], h->vector()[1],
// z_[0], z_[1]);
return (z_ - h->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 the time of
// GaussianFactor construction?
//
// Is GaussianFactor copying its constructor arguments? (If not, this is
// not safe, nor is what this code replaced).
//
// 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();
Transform tr = transform_to(sensorMatrix_, pose,landmark, true);
return GaussianFactor::shared_ptr(
new GaussianFactor(posekey,
*(tr.get<1>()),
lmkey,
*(tr.get<2>()),
z_ - tr.get<0>()->vector(),
sigma_));
}
} // namespace gtsam