NASA Astrobee Robot Software  0.19.1
Flight software for the Astrobee robots operating inside the International Space Station.
inverse_depth_measurement.h
Go to the documentation of this file.
1 /* Copyright (c) 2017, United States Government, as represented by the
2  * Administrator of the National Aeronautics and Space Administration.
3  *
4  * All rights reserved.
5  *
6  * The Astrobee platform is licensed under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with the
8  * License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15  * License for the specific language governing permissions and limitations
16  * under the License.
17  */
18 
19 #ifndef VISION_COMMON_INVERSE_DEPTH_MEASUREMENT_H_
20 #define VISION_COMMON_INVERSE_DEPTH_MEASUREMENT_H_
21 
23 
24 #include <gtsam/base/Lie.h>
25 #include <gtsam/base/Manifold.h>
26 #include <gtsam/base/Matrix.h>
27 #include <gtsam/geometry/Pose3.h>
28 
29 #include <string>
30 
31 namespace vision_common {
32 
41  public:
42  // Required for gtsam optimization
43  enum { dimension = 1 };
44 
45  InverseDepthMeasurement(const double inverse_depth, const Eigen::Vector2d& image_coordinates,
46  const Eigen::Matrix3d& intrinsics, const gtsam::Pose3& body_T_sensor)
47  : inverse_depth_(inverse_depth),
48  image_coordinates_(image_coordinates),
49  intrinsics_(intrinsics),
50  body_T_sensor_(body_T_sensor) {}
51 
52  // Returns sensor_t_point
53  Eigen::Vector3d Backproject(boost::optional<gtsam::Matrix&> d_backprojected_point_d_depth = boost::none) const {
54  return vision_common::Backproject(image_coordinates_, intrinsics_, depth(), d_backprojected_point_d_depth);
55  }
56 
57  // Computes measurement projection in sensor frame of the provided target pose.
58  boost::optional<Eigen::Vector2d> Project(
59  const gtsam::Pose3& world_T_source_body, const gtsam::Pose3& world_T_target_body,
60  boost::optional<gtsam::Matrix&> d_projected_point_d_world_T_source_body = boost::none,
61  boost::optional<gtsam::Matrix&> d_projected_point_d_world_T_target_body = boost::none,
62  boost::optional<gtsam::Matrix&> d_projected_point_d_inverse_depth = boost::none) const {
63  if (d_projected_point_d_world_T_source_body || d_projected_point_d_world_T_target_body ||
64  d_projected_point_d_inverse_depth) {
65  // Jacobian Calculations:
66  // projected_point = (project(target_sensor_T_source_sensor*backproject(inverse_depth)))
67  // call backproject(inverse_depth) = source_sensor_t_point
68  // call target_sensor_T_source_sensor* source_sensor_t_point = target_sensor_t_point
69  // Pose Jacobians:
70  // d_projected_point_d_world_T_source_body = d_projected_point_d_target_sensor_t_point *
71  // d_target_sensor_t_point_d_world_T_source_sensor * d_world_T_source_sensor_d_world_T_source_body
72  // d_projected_point_d_world_T_target_body = d_projected_point_d_target_sensor_t_point *
73  // d_target_sensor_t_point_d_world_T_target_sensor * d_world_T_target_sensor_d_world_T_target_body
74  // where:
75  // d_target_sensor_t_point_d_world_T_source_sensor = d_target_sensor_t_point_d_target_sensor_T_source_sensor *
76  // d_target_sensor_T_source_sensor_d_world_T_source_sensor d_target_sensor_t_point_d_world_T_target_sensor =
77  // d_target_sensor_t_point_d_target_sensor_T_source_sensor *
78  // d_target_sensor_T_source_sensor_d_world_T_target_sensor d_target_sensor_T_source_sensor_d_world_T_target_sensor
79  // = d_target_sensor_T_source_sensor_d_target_sensor_T_world * d_target_sensor_T_world_d_world_T_target_sensor
80  // Inverse Depth Jacobian:
81  // d_projected_point_d_inverse_depth = d_projected_point_d_target_sensor_t_point *
82  // d_target_sensor_t_point_d_depth * d_depth_d_inverse_depth
83  // where:
84  // d_target_sensor_t_point_d_depth =
85  // d_target_sensor_t_point_d_source_sensor_t_point * d_source_sensor_t_point_d_depth
86  // d_depth_d_inverse_depth = -1/(inverse_depth^2)
87 
88  // Intermediate Jacobians
89  gtsam::Matrix d_source_sensor_t_point_d_depth;
90  gtsam::Matrix d_target_sensor_T_world_d_world_T_target_sensor;
91  gtsam::Matrix d_target_sensor_T_source_sensor_d_world_T_source_sensor;
92  gtsam::Matrix d_target_sensor_T_source_sensor_d_target_sensor_T_world;
93  gtsam::Matrix d_target_sensor_t_point_d_target_sensor_T_source_sensor;
94  gtsam::Matrix d_target_sensor_t_point_d_source_sensor_t_point;
95  gtsam::Matrix d_projected_point_d_target_sensor_t_point;
96  gtsam::Matrix d_world_T_source_sensor_d_world_T_source_body;
97  gtsam::Matrix d_world_T_target_sensor_d_world_T_target_body;
98  const auto projeced_point =
99  ProjectHelper(world_T_source_body, world_T_target_body, d_world_T_source_sensor_d_world_T_source_body,
100  d_world_T_target_sensor_d_world_T_target_body, d_target_sensor_T_world_d_world_T_target_sensor,
101  d_source_sensor_t_point_d_depth, d_target_sensor_T_source_sensor_d_target_sensor_T_world,
102  d_target_sensor_T_source_sensor_d_world_T_source_sensor,
103  d_target_sensor_t_point_d_target_sensor_T_source_sensor,
104  d_target_sensor_t_point_d_source_sensor_t_point, d_projected_point_d_target_sensor_t_point);
105  // Final pose Jacobians
106  const gtsam::Matrix d_target_sensor_T_source_sensor_d_world_T_target_sensor =
107  d_target_sensor_T_source_sensor_d_target_sensor_T_world * d_target_sensor_T_world_d_world_T_target_sensor;
108  const gtsam::Matrix d_target_sensor_t_point_d_world_T_source_sensor =
109  d_target_sensor_t_point_d_target_sensor_T_source_sensor *
110  d_target_sensor_T_source_sensor_d_world_T_source_sensor;
111  const gtsam::Matrix d_target_sensor_t_point_d_world_T_target_sensor =
112  d_target_sensor_t_point_d_target_sensor_T_source_sensor *
113  d_target_sensor_T_source_sensor_d_world_T_target_sensor;
114  if (d_projected_point_d_world_T_source_body)
115  *d_projected_point_d_world_T_source_body = d_projected_point_d_target_sensor_t_point *
116  d_target_sensor_t_point_d_world_T_source_sensor *
117  d_world_T_source_sensor_d_world_T_source_body;
118  if (d_projected_point_d_world_T_target_body)
119  *d_projected_point_d_world_T_target_body = d_projected_point_d_target_sensor_t_point *
120  d_target_sensor_t_point_d_world_T_target_sensor *
121  d_world_T_target_sensor_d_world_T_target_body;
122  // Final inverse depth Jacobian
123  const double d_depth_d_inverse_depth = -1.0 / (inverse_depth_ * inverse_depth_);
124  const gtsam::Matrix d_target_sensor_t_point_d_depth =
125  d_target_sensor_t_point_d_source_sensor_t_point * d_source_sensor_t_point_d_depth;
126  if (d_projected_point_d_inverse_depth)
127  *d_projected_point_d_inverse_depth =
128  d_projected_point_d_target_sensor_t_point * d_target_sensor_t_point_d_depth * d_depth_d_inverse_depth;
129  return projeced_point;
130  }
131 
132  // Jacobians not required
133  return ProjectHelper(world_T_source_body, world_T_target_body);
134  }
135 
136  double depth() const { return 1.0 / inverse_depth_; }
137 
138  double inverse_depth() const { return inverse_depth_; }
139 
140  // Required operations for using as state parameter with gtsam
141  inline size_t dim() const { return dimension; }
142 
143  static size_t Dim() { return dimension; }
144 
145  // Boxplus
146  inline InverseDepthMeasurement retract(const gtsam::Vector& d) const {
147  return InverseDepthMeasurement(inverse_depth_ + d(0), image_coordinates_, intrinsics_, body_T_sensor_);
148  }
149 
150  // Boxminus
151  gtsam::Vector1 localCoordinates(const InverseDepthMeasurement& T2) const {
152  return gtsam::Vector1(T2.inverse_depth() - inverse_depth());
153  }
154 
155  void print(const std::string& s = std::string()) const {
156  std::cout << "inverse depth: " << inverse_depth_ << std::endl;
157  }
158 
159  bool equals(const InverseDepthMeasurement& s, double tol = 1e-9) const {
160  return intrinsics_.isApprox(s.intrinsics_, tol) && body_T_sensor_.equals(s.body_T_sensor_, 1e-9) &&
161  std::abs(inverse_depth_ - s.inverse_depth_) < 1e-9 &&
162  image_coordinates_.isApprox(s.image_coordinates_, 1e-9);
163  }
164 
165  private:
166  // Intermediate call to optionally fill required jacobians. Allows for code reuse whether the jacobians are need or
167  // not.
168  boost::optional<Eigen::Vector2d> ProjectHelper(
169  const gtsam::Pose3& world_T_source_body, const gtsam::Pose3& world_T_target_body,
170  boost::optional<gtsam::Matrix&> d_world_T_source_sensor_d_world_T_source_body = boost::none,
171  boost::optional<gtsam::Matrix&> d_world_T_target_sensor_d_world_T_target_body = boost::none,
172  boost::optional<gtsam::Matrix&> d_target_sensor_T_world_d_world_T_target_sensor = boost::none,
173  boost::optional<gtsam::Matrix&> d_source_sensor_t_point_d_depth = boost::none,
174  boost::optional<gtsam::Matrix&> d_target_sensor_T_source_sensor_d_target_sensor_T_world = boost::none,
175  boost::optional<gtsam::Matrix&> d_target_sensor_T_source_sensor_d_world_T_source_sensor = boost::none,
176  boost::optional<gtsam::Matrix&> d_target_sensor_t_point_d_target_sensor_T_source_sensor = boost::none,
177  boost::optional<gtsam::Matrix&> d_target_sensor_t_point_d_source_sensor_t_point = boost::none,
178  boost::optional<gtsam::Matrix&> d_projected_point_d_target_sensor_t_point = boost::none) const {
179  const gtsam::Pose3 world_T_source_sensor =
180  world_T_source_body.compose(body_T_sensor_, d_world_T_source_sensor_d_world_T_source_body);
181  const gtsam::Pose3 world_T_target_sensor =
182  world_T_target_body.compose(body_T_sensor_, d_world_T_target_sensor_d_world_T_target_body);
183  const Eigen::Vector3d source_sensor_t_point = Backproject(d_source_sensor_t_point_d_depth);
184  const gtsam::Pose3 target_sensor_T_world =
185  world_T_target_sensor.inverse(d_target_sensor_T_world_d_world_T_target_sensor);
186  const gtsam::Pose3 target_sensor_T_source_sensor =
187  target_sensor_T_world.compose(world_T_source_sensor, d_target_sensor_T_source_sensor_d_target_sensor_T_world,
188  d_target_sensor_T_source_sensor_d_world_T_source_sensor);
189  const Eigen::Vector3d target_sensor_t_point = target_sensor_T_source_sensor.transformFrom(
190  source_sensor_t_point, d_target_sensor_t_point_d_target_sensor_T_source_sensor,
191  d_target_sensor_t_point_d_source_sensor_t_point);
192  if (target_sensor_t_point.z() < 0) return boost::none;
193  return vision_common::Project(target_sensor_t_point, intrinsics_, d_projected_point_d_target_sensor_t_point);
194  }
195 
196  Eigen::Vector2d image_coordinates_;
197  Eigen::Matrix3d intrinsics_;
198  gtsam::Pose3 body_T_sensor_;
199  double inverse_depth_;
200 };
201 } // namespace vision_common
202 
203 namespace gtsam {
204 template <>
205 struct traits<vision_common::InverseDepthMeasurement>
206  : public internal::Manifold<vision_common::InverseDepthMeasurement> {};
207 
208 template <>
209 struct traits<const vision_common::InverseDepthMeasurement>
210  : public internal::Manifold<vision_common::InverseDepthMeasurement> {};
211 } // namespace gtsam
212 
213 #endif // VISION_COMMON_INVERSE_DEPTH_MEASUREMENT_H_
vision_common::Project
Eigen::Vector2d Project(const Eigen::Vector3d &cam_t_point, const Eigen::Matrix3d &intrinsics, gtsam::OptionalJacobian< 2, 3 > d_projected_point_d_cam_t_point=boost::none)
Definition: utilities.cc:44
vision_common::InverseDepthMeasurement::Backproject
Eigen::Vector3d Backproject(boost::optional< gtsam::Matrix & > d_backprojected_point_d_depth=boost::none) const
Definition: inverse_depth_measurement.h:53
vision_common
Definition: brisk_feature_detector_and_matcher.h:25
vision_common::InverseDepthMeasurement::Project
boost::optional< Eigen::Vector2d > Project(const gtsam::Pose3 &world_T_source_body, const gtsam::Pose3 &world_T_target_body, boost::optional< gtsam::Matrix & > d_projected_point_d_world_T_source_body=boost::none, boost::optional< gtsam::Matrix & > d_projected_point_d_world_T_target_body=boost::none, boost::optional< gtsam::Matrix & > d_projected_point_d_inverse_depth=boost::none) const
Definition: inverse_depth_measurement.h:58
vision_common::InverseDepthMeasurement::Dim
static size_t Dim()
Definition: inverse_depth_measurement.h:143
vision_common::InverseDepthMeasurement::depth
double depth() const
Definition: inverse_depth_measurement.h:136
vision_common::InverseDepthMeasurement::dim
size_t dim() const
Definition: inverse_depth_measurement.h:141
vision_common::InverseDepthMeasurement::dimension
@ dimension
Definition: inverse_depth_measurement.h:43
vision_common::InverseDepthMeasurement::print
void print(const std::string &s=std::string()) const
Definition: inverse_depth_measurement.h:155
point_cloud_common::Vector3d
Eigen::Vector3d Vector3d(const PointType &point)
Definition: utilities.h:328
vision_common::InverseDepthMeasurement::inverse_depth
double inverse_depth() const
Definition: inverse_depth_measurement.h:138
gtsam
Definition: cumulative_factor.h:26
vision_common::InverseDepthMeasurement::retract
InverseDepthMeasurement retract(const gtsam::Vector &d) const
Definition: inverse_depth_measurement.h:146
vision_common::Backproject
Eigen::Vector3d Backproject(const Eigen::Vector2d &measurement, const Eigen::Matrix3d &intrinsics, const double depth, gtsam::OptionalJacobian< 3, 1 > d_backprojected_point_d_depth=boost::none)
Definition: utilities.cc:25
utilities.h
vision_common::InverseDepthMeasurement::InverseDepthMeasurement
InverseDepthMeasurement(const double inverse_depth, const Eigen::Vector2d &image_coordinates, const Eigen::Matrix3d &intrinsics, const gtsam::Pose3 &body_T_sensor)
Definition: inverse_depth_measurement.h:45
vision_common::InverseDepthMeasurement::equals
bool equals(const InverseDepthMeasurement &s, double tol=1e-9) const
Definition: inverse_depth_measurement.h:159
vision_common::InverseDepthMeasurement::localCoordinates
gtsam::Vector1 localCoordinates(const InverseDepthMeasurement &T2) const
Definition: inverse_depth_measurement.h:151
vision_common::InverseDepthMeasurement
Definition: inverse_depth_measurement.h:40