Skip to content

Commit a2f917a

Browse files
authored
Merge pull request #1934 from truher/team100_frc_factors
projection and SFM for 2d poses
2 parents 09b0d48 + 993ac90 commit a2f917a

File tree

5 files changed

+784
-0
lines changed

5 files changed

+784
-0
lines changed

gtsam/geometry/Pose3.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ Pose3 Pose3::Create(const Rot3& R, const Point3& t, OptionalJacobian<6, 3> HR,
4343
return Pose3(R, t);
4444
}
4545

46+
// Pose2 constructor Jacobian is always the same.
47+
static const Matrix63 Hpose2 = (Matrix63() << //
48+
0., 0., 0., //
49+
0., 0., 0.,//
50+
0., 0., 1.,//
51+
1., 0., 0.,//
52+
0., 1., 0.,//
53+
0., 0., 0.).finished();
54+
55+
Pose3 Pose3::FromPose2(const Pose2& p, OptionalJacobian<6, 3> H) {
56+
if (H) *H << Hpose2;
57+
return Pose3(p);
58+
}
59+
4660
/* ************************************************************************* */
4761
Pose3 Pose3::inverse() const {
4862
Rot3 Rt = R_.inverse();

gtsam/geometry/Pose3.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class GTSAM_EXPORT Pose3: public LieGroup<Pose3, 6> {
7878
OptionalJacobian<6, 3> HR = {},
7979
OptionalJacobian<6, 3> Ht = {});
8080

81+
/** Construct from Pose2 in the xy plane, with derivative. */
82+
static Pose3 FromPose2(const Pose2& p, OptionalJacobian<6,3> H = {});
83+
8184
/**
8285
* Create Pose3 by aligning two point pairs
8386
* A pose aTb is estimated between pairs (a_point, b_point) such that a_point = aTb * b_point

gtsam/slam/PlanarProjectionFactor.h

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/**
2+
* ProjectionFactor, but with pose2 (robot on the floor)
3+
*
4+
* This factor is useful for high-school robotics competitions,
5+
* which run robots on the floor, with use fixed maps and fiducial
6+
* markers.
7+
*
8+
* @see https://www.firstinspires.org/
9+
*
10+
* @file PlanarProjectionFactor.h
11+
* @brief for planar smoothing
12+
* @date Dec 2, 2024
13+
* @author joel@truher.org
14+
*/
15+
#pragma once
16+
17+
#include <gtsam/base/Testable.h>
18+
#include <gtsam/base/Lie.h>
19+
20+
#include <gtsam/geometry/Cal3DS2.h>
21+
#include <gtsam/geometry/PinholeCamera.h>
22+
#include <gtsam/geometry/Point3.h>
23+
#include <gtsam/geometry/Pose2.h>
24+
#include <gtsam/geometry/Pose3.h>
25+
#include <gtsam/geometry/Rot3.h>
26+
#include <gtsam/nonlinear/NonlinearFactor.h>
27+
#include <gtsam/base/numericalDerivative.h>
28+
29+
30+
namespace gtsam {
31+
32+
/**
33+
* @class PlanarProjectionFactorBase
34+
* @brief Camera projection for robot on the floor. Measurement is camera pixels.
35+
*/
36+
class PlanarProjectionFactorBase {
37+
protected:
38+
PlanarProjectionFactorBase() {}
39+
40+
/**
41+
* @param measured pixels in the camera frame
42+
*/
43+
PlanarProjectionFactorBase(const Point2& measured) : measured_(measured) {}
44+
45+
/**
46+
* Predict the projection of the landmark in camera pixels.
47+
*
48+
* @param landmark point3 of the target
49+
* @param wTb "world to body": planar pose2 of vehicle body frame in world frame
50+
* @param bTc "body to camera": camera pose in body frame, oriented parallel to pose2 zero i.e. +x
51+
* @param calib camera calibration with distortion
52+
* @param Hlandmark jacobian
53+
* @param HwTb jacobian
54+
* @param HbTc jacobian
55+
* @param Hcalib jacobian
56+
*/
57+
Point2 predict(
58+
const Point3& landmark,
59+
const Pose2& wTb,
60+
const Pose3& bTc,
61+
const Cal3DS2& calib,
62+
OptionalJacobian<2, 3> Hlandmark = {}, // (x, y, z)
63+
OptionalJacobian<2, 3> HwTb = {}, // (x, y, theta)
64+
OptionalJacobian<2, 6> HbTc = {}, // (rx, ry, rz, x, y, theta)
65+
OptionalJacobian<2, 9> Hcalib = {}
66+
) const {
67+
#ifndef GTSAM_THROW_CHEIRALITY_EXCEPTION
68+
try {
69+
#endif
70+
Matrix63 Hp; // 6x3
71+
Matrix66 H0; // 6x6
72+
Pose3 wTc = Pose3::FromPose2(wTb, HwTb ? &Hp : nullptr).compose(bTc, HwTb ? &H0 : nullptr);
73+
PinholeCamera<Cal3DS2> camera = PinholeCamera<Cal3DS2>(wTc, calib);
74+
if (HwTb || HbTc) {
75+
// Dpose is for pose3 (R,t)
76+
Matrix26 Dpose;
77+
Point2 result = camera.project(landmark, Dpose, Hlandmark, Hcalib);
78+
if (HbTc)
79+
*HbTc = Dpose;
80+
if (HwTb)
81+
*HwTb = Dpose * H0 * Hp;
82+
return result;
83+
} else {
84+
return camera.project(landmark, {}, {}, {});
85+
}
86+
#ifndef GTSAM_THROW_CHEIRALITY_EXCEPTION
87+
} catch (CheiralityException& e) {
88+
std::cout << "****** CHIRALITY EXCEPTION ******\n";
89+
if (Hlandmark) Hlandmark->setZero();
90+
if (HwTb) HwTb->setZero();
91+
if (HbTc) HbTc->setZero();
92+
if (Hcalib) Hcalib->setZero();
93+
// return a large error
94+
return Matrix::Constant(2, 1, 2.0 * calib.fx());
95+
}
96+
#endif
97+
}
98+
99+
Point2 measured_; // pixel measurement
100+
};
101+
102+
103+
/**
104+
* @class PlanarProjectionFactor1
105+
* @brief One variable: the pose.
106+
* Landmark, camera offset, camera calibration are constant.
107+
* This is intended for localization within a known map.
108+
*/
109+
class PlanarProjectionFactor1
110+
: public PlanarProjectionFactorBase, public NoiseModelFactorN<Pose2> {
111+
public:
112+
typedef NoiseModelFactorN<Pose2> Base;
113+
using Base::evaluateError;
114+
PlanarProjectionFactor1() {}
115+
116+
~PlanarProjectionFactor1() override {}
117+
118+
/// @return a deep copy of this factor
119+
NonlinearFactor::shared_ptr clone() const override {
120+
return std::static_pointer_cast<NonlinearFactor>(
121+
NonlinearFactor::shared_ptr(new PlanarProjectionFactor1(*this)));
122+
}
123+
124+
125+
/**
126+
* @brief constructor for known landmark, offset, and calibration
127+
* @param poseKey index of the robot pose2 in the z=0 plane
128+
* @param landmark point3 in the world
129+
* @param measured corresponding point2 in the camera frame
130+
* @param bTc "body to camera": constant camera offset from pose
131+
* @param calib constant camera calibration
132+
* @param model stddev of the measurements, ~one pixel?
133+
*/
134+
PlanarProjectionFactor1(
135+
Key poseKey,
136+
const Point3& landmark,
137+
const Point2& measured,
138+
const Pose3& bTc,
139+
const Cal3DS2& calib,
140+
const SharedNoiseModel& model = {})
141+
: PlanarProjectionFactorBase(measured),
142+
NoiseModelFactorN(model, poseKey),
143+
landmark_(landmark),
144+
bTc_(bTc),
145+
calib_(calib) {}
146+
147+
/**
148+
* @param wTb "world to body": estimated pose2
149+
* @param HwTb jacobian
150+
*/
151+
Vector evaluateError(const Pose2& wTb, OptionalMatrixType HwTb) const override {
152+
return predict(landmark_, wTb, bTc_, calib_, {}, HwTb, {}, {}) - measured_;
153+
}
154+
155+
private:
156+
Point3 landmark_; // landmark
157+
Pose3 bTc_; // "body to camera": camera offset to robot pose
158+
Cal3DS2 calib_; // camera calibration
159+
};
160+
161+
template<>
162+
struct traits<PlanarProjectionFactor1> :
163+
public Testable<PlanarProjectionFactor1> {};
164+
165+
/**
166+
* @class PlanarProjectionFactor2
167+
* @brief Two unknowns: the pose and the landmark.
168+
* Camera offset and calibration are constant.
169+
* This is similar to GeneralSFMFactor, used for SLAM.
170+
*/
171+
class PlanarProjectionFactor2
172+
: public PlanarProjectionFactorBase, public NoiseModelFactorN<Pose2, Point3> {
173+
public:
174+
typedef NoiseModelFactorN<Pose2, Point3> Base;
175+
using Base::evaluateError;
176+
177+
PlanarProjectionFactor2() {}
178+
179+
~PlanarProjectionFactor2() override {}
180+
181+
/// @return a deep copy of this factor
182+
NonlinearFactor::shared_ptr clone() const override {
183+
return std::static_pointer_cast<NonlinearFactor>(
184+
NonlinearFactor::shared_ptr(new PlanarProjectionFactor2(*this)));
185+
}
186+
187+
/**
188+
* @brief constructor for variable landmark, known offset and calibration
189+
* @param poseKey index of the robot pose2 in the z=0 plane
190+
* @param landmarkKey index of the landmark point3
191+
* @param measured corresponding point in the camera frame
192+
* @param bTc "body to camera": constant camera offset from pose
193+
* @param calib constant camera calibration
194+
* @param model stddev of the measurements, ~one pixel?
195+
*/
196+
PlanarProjectionFactor2(
197+
Key poseKey,
198+
Key landmarkKey,
199+
const Point2& measured,
200+
const Pose3& bTc,
201+
const Cal3DS2& calib,
202+
const SharedNoiseModel& model = {})
203+
: PlanarProjectionFactorBase(measured),
204+
NoiseModelFactorN(model, landmarkKey, poseKey),
205+
bTc_(bTc),
206+
calib_(calib) {}
207+
208+
/**
209+
* @param wTb "world to body": estimated pose2
210+
* @param landmark estimated landmark
211+
* @param HwTb jacobian
212+
* @param Hlandmark jacobian
213+
*/
214+
Vector evaluateError(
215+
const Pose2& wTb,
216+
const Point3& landmark,
217+
OptionalMatrixType HwTb,
218+
OptionalMatrixType Hlandmark) const override {
219+
return predict(landmark, wTb, bTc_, calib_, Hlandmark, HwTb, {}, {}) - measured_;
220+
}
221+
222+
private:
223+
Pose3 bTc_; // "body to camera": camera offset to robot pose
224+
Cal3DS2 calib_; // camera calibration
225+
};
226+
227+
template<>
228+
struct traits<PlanarProjectionFactor2> :
229+
public Testable<PlanarProjectionFactor2> {};
230+
231+
/**
232+
* @class PlanarProjectionFactor3
233+
* @brief Three unknowns: the pose, the camera offset, and the camera calibration.
234+
* Landmark is constant.
235+
* This is intended to be used for camera calibration.
236+
*/
237+
class PlanarProjectionFactor3 : public PlanarProjectionFactorBase, public NoiseModelFactorN<Pose2, Pose3, Cal3DS2> {
238+
public:
239+
typedef NoiseModelFactorN<Pose2, Pose3, Cal3DS2> Base;
240+
using Base::evaluateError;
241+
242+
PlanarProjectionFactor3() {}
243+
244+
~PlanarProjectionFactor3() override {}
245+
246+
/// @return a deep copy of this factor
247+
NonlinearFactor::shared_ptr clone() const override {
248+
return std::static_pointer_cast<NonlinearFactor>(
249+
NonlinearFactor::shared_ptr(new PlanarProjectionFactor3(*this)));
250+
}
251+
252+
/**
253+
* @brief constructor for variable pose, offset, and calibration, known landmark.
254+
* @param poseKey index of the robot pose2 in the z=0 plane
255+
* @param offsetKey index of camera offset from pose
256+
* @param calibKey index of camera calibration
257+
* @param landmark point3 in the world
258+
* @param measured corresponding point2 in the camera frame
259+
* @param model stddev of the measurements, ~one pixel?
260+
*/
261+
PlanarProjectionFactor3(
262+
Key poseKey,
263+
Key offsetKey,
264+
Key calibKey,
265+
const Point3& landmark,
266+
const Point2& measured,
267+
const SharedNoiseModel& model = {})
268+
: PlanarProjectionFactorBase(measured),
269+
NoiseModelFactorN(model, poseKey, offsetKey, calibKey),
270+
landmark_(landmark) {}
271+
272+
/**
273+
* @param wTb "world to body": estimated pose2
274+
* @param bTc "body to camera": pose3 offset from pose2 +x
275+
* @param calib calibration
276+
* @param HwTb pose jacobian
277+
* @param HbTc offset jacobian
278+
* @param Hcalib calibration jacobian
279+
*/
280+
Vector evaluateError(
281+
const Pose2& wTb,
282+
const Pose3& bTc,
283+
const Cal3DS2& calib,
284+
OptionalMatrixType HwTb,
285+
OptionalMatrixType HbTc,
286+
OptionalMatrixType Hcalib) const override {
287+
return predict(landmark_, wTb, bTc, calib, {}, HwTb, HbTc, Hcalib) - measured_;
288+
}
289+
290+
private:
291+
Point3 landmark_; // landmark
292+
};
293+
294+
template<>
295+
struct traits<PlanarProjectionFactor3> :
296+
public Testable<PlanarProjectionFactor3> {};
297+
298+
} // namespace gtsam

gtsam/slam/slam.i

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,38 @@ virtual class BetweenFactor : gtsam::NoiseModelFactor {
2424
void serialize() const;
2525
};
2626

27+
#include <gtsam/slam/PlanarProjectionFactor.h>
28+
virtual class PlanarProjectionFactor1 : gtsam::NoiseModelFactor {
29+
PlanarProjectionFactor1(
30+
size_t poseKey,
31+
const gtsam::Point3& landmark,
32+
const gtsam::Point2& measured,
33+
const gtsam::Pose3& bTc,
34+
const gtsam::Cal3DS2& calib,
35+
const gtsam::noiseModel::Base* model);
36+
void serialize() const;
37+
};
38+
virtual class PlanarProjectionFactor2 : gtsam::NoiseModelFactor {
39+
PlanarProjectionFactor2(
40+
size_t poseKey,
41+
size_t landmarkKey,
42+
const gtsam::Point2& measured,
43+
const gtsam::Pose3& bTc,
44+
const gtsam::Cal3DS2& calib,
45+
const gtsam::noiseModel::Base* model);
46+
void serialize() const;
47+
};
48+
virtual class PlanarProjectionFactor3 : gtsam::NoiseModelFactor {
49+
PlanarProjectionFactor3(
50+
size_t poseKey,
51+
size_t offsetKey,
52+
size_t calibKey,
53+
const gtsam::Point3& landmark,
54+
const gtsam::Point2& measured,
55+
const gtsam::noiseModel::Base* model);
56+
void serialize() const;
57+
};
58+
2759
#include <gtsam/slam/ProjectionFactor.h>
2860
template <POSE, LANDMARK, CALIBRATION>
2961
virtual class GenericProjectionFactor : gtsam::NoiseModelFactor {

0 commit comments

Comments
 (0)