NASA Astrobee Robot Software  0.19.1
Flight software for the Astrobee robots operating inside the International Space Station.
vive.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 LOCALIZATION_VIVE_LOCALIZATION_SRC_VIVE_H_
20 #define LOCALIZATION_VIVE_LOCALIZATION_SRC_VIVE_H_
21 
22 // FSW code
25 #include <ff_common/ff_names.h>
26 
27 // Messages
28 #include <geometry_msgs/TransformStamped.h>
29 #include <ff_hw_msgs/ViveLighthouses.h>
30 #include <ff_hw_msgs/ViveTrackers.h>
31 #include <ff_hw_msgs/ViveLight.h>
32 #include <ff_msgs/CameraRegistration.h>
33 #include <ff_msgs/VisualLandmarks.h>
34 
35 // We will use OpenCV to bootstrap solution
36 #include <opencv2/calib3d/calib3d.hpp>
37 
38 // Eigen
39 #include <Eigen/Core>
40 #include <Eigen/Geometry>
41 
42 // STL
43 #include <string>
44 #include <vector>
45 #include <map>
46 
50 namespace vive_localization {
51 
52 // Universal constants
53 static constexpr size_t NUM_SENSORS = 32;
54 
55 // ESSENTIAL STRUCTURES
56 
57 // Lighthouse parameters
58 enum Params {
65 };
66 
67 // Lighthouse parameters
68 enum Errors {
74 };
75 
76 enum Motors {
80 };
81 
82 // Lighthouse data structure
83 struct Lighthouse {
84  std::string serial;
85  double vTl[6];
87  bool ready;
88 };
89 typedef std::map<uint8_t, Lighthouse> LighthouseMap;
90 
91 // Tracker data structure
92 struct Tracker {
93  double bTh[6];
94  double tTh[6];
95  double tTi[6];
96  double sensors[NUM_SENSORS*6];
97  double errors[NUM_ERRORS][3];
98  bool ready;
99 };
100 typedef std::map<std::string, Tracker> TrackerMap;
101 
102 // Pulse measurements
103 struct Measurement {
104  double wTb[6];
105  ff_hw_msgs::ViveLight light;
106 };
107 typedef std::map<ros::Time, Measurement> MeasurementMap;
108 
109 // Correction data structure
110 typedef std::map<ros::Time, geometry_msgs::TransformStamped> CorrectionMap;
111 
112 // TRANSFORM ENGINE
113 
114 void SendStaticTransform(geometry_msgs::TransformStamped const& tfs);
115 
116 void SendDynamicTransform(geometry_msgs::TransformStamped const& tfs);
117 
118 void SendTransforms(
119  std::string const& frame_world, // World name
120  std::string const& frame_vive, // Vive frame name
121  std::string const& frame_body, // Body frame name
122  double registration[6],
123  LighthouseMap const& lighthouses, TrackerMap const& trackers);
124 
125 // Convert a ceres to an Eigen transform
126 Eigen::Affine3d CeresToEigen(double ceres[6], bool invert = false);
127 
128 // Convert a Euler angle to a Quaternion
129 Eigen::Quaterniond toQuaternion(double roll, double pitch, double yaw);
130 
131 // CONFIG RELATED CALLS
132 
133 // Read Vive trajectory modification parameter
135  Eigen::Vector3d & modification_vector,
136  Eigen::Quaterniond & modification_quaternion);
137 
138 // Read lighthouse data
140  std::map<std::string, Eigen::Affine3d> & lighthouses);
141 
142 // Read tracker data
144  std::map<std::string, Tracker> & trackers);
145 
146 // Read registration data
147 bool ReadRegistrationConfig(config_reader::ConfigReader *config, double T[6]);
148 
149 // REUSABLE CALLS
150 
151 void LighthouseCallback(ff_hw_msgs::ViveLighthouses::ConstPtr const& msg,
152  LighthouseMap & lighthouses);
153 
154 void TrackerCallback(ff_hw_msgs::ViveTrackers::ConstPtr const& msg,
155  TrackerMap & trackers);
156 
157 // Estimate a pose from (image) <-> (world) correspondences
158 bool SolvePnP(std::vector<cv::Point3f> const& obj,
159  std::vector<cv::Point2f> const& img,
160  Eigen::Affine3d & transform);
161 
162 // TRACKING ROUTINES
163 
164 // This algorithm solves the Procrustes problem in that it finds an affine transform
165 // (rotation, translation, scale) that maps the "in" matrix to the "out" matrix
166 // Code from: https://github.com/oleg-alexandrov/projects/blob/master/eigen/Kabsch.cpp
167 // License is that this code is release in the public domain... Thanks, Oleg :)
168 template <typename T>
169 static bool Kabsch(
170  Eigen::Matrix<T, 3, Eigen::Dynamic> in,
171  Eigen::Matrix<T, 3, Eigen::Dynamic> out,
172  Eigen::Transform<T, 3, Eigen::Affine> &A, bool allowScale) {
173  // Default output
174  A.linear() = Eigen::Matrix<T, 3, 3>::Identity(3, 3);
175  A.translation() = Eigen::Matrix<T, 3, 1>::Zero();
176  // A simple check to see that we have a sufficient number of correspondences
177  if (in.cols() < 4) {
178  // ROS_WARN("Visualeyez needs to see at least four LEDs to track");
179  return false;
180  }
181  // A simple check to see that we have a sufficient number of correspondences
182  if (in.cols() != out.cols()) {
183  // ROS_ERROR("Same number of points required in input matrices");
184  return false;
185  }
186  // First find the scale, by finding the ratio of sums of some distances,
187  // then bring the datasets to the same scale.
188  T dist_in = T(0.0), dist_out = T(0.0);
189  for (int col = 0; col < in.cols()-1; col++) {
190  dist_in += (in.col(col+1) - in.col(col)).norm();
191  dist_out += (out.col(col+1) - out.col(col)).norm();
192  }
193  if (dist_in <= T(0.0) || dist_out <= T(0.0))
194  return true;
195  T scale = T(1.0);
196  if (allowScale) {
197  scale = dist_out/dist_in;
198  out /= scale;
199  }
200  // Find the centroids then shift to the origin
201  Eigen::Matrix<T, 3, 1> in_ctr = Eigen::Matrix<T, 3, 1>::Zero();
202  Eigen::Matrix<T, 3, 1> out_ctr = Eigen::Matrix<T, 3, 1>::Zero();
203  for (int col = 0; col < in.cols(); col++) {
204  in_ctr += in.col(col);
205  out_ctr += out.col(col);
206  }
207  in_ctr /= T(in.cols());
208  out_ctr /= T(out.cols());
209  for (int col = 0; col < in.cols(); col++) {
210  in.col(col) -= in_ctr;
211  out.col(col) -= out_ctr;
212  }
213  // SVD
214  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Cov = in * out.transpose();
215  Eigen::JacobiSVD < Eigen::Matrix < T, Eigen::Dynamic, Eigen::Dynamic > > svd(Cov,
216  Eigen::ComputeThinU | Eigen::ComputeThinV);
217  // Find the rotation
218  T d = (svd.matrixV() * svd.matrixU().transpose()).determinant();
219  if (d > T(0.0))
220  d = T(1.0);
221  else
222  d = T(-1.0);
223  Eigen::Matrix<T, 3, 3> I = Eigen::Matrix<T, 3, 3>::Identity(3, 3);
224  I(2, 2) = d;
225  Eigen::Matrix<T, 3, 3> R = svd.matrixV() * I * svd.matrixU().transpose();
226  // The final transform
227  A.linear() = scale * R;
228  A.translation() = scale*(out_ctr - R*in_ctr);
229  // Success
230  return true;
231 }
232 
233 // Given a point in space, predict the lighthouse angle
234 template <typename T>
235 static void Predict(T const* params, T const* xyz, T* ang, bool correct) {
236  for (size_t i = 0; i < 2; i++) {
237  T a = xyz[2];
238  T b = -xyz[0];
239  T c = xyz[1];
240  if (i == 1) {
241  a = xyz[2];
242  b = -xyz[1];
243  c = -xyz[0];
244  }
245  // Ideal angle in radians [0, 2pi] relative to the start of the sweep
246  ang[i] = atan2(a, b);
247  // Perturb this angle based on the base station parameters
248  if (correct) {
249  T const* p = &params[i*NUM_PARAMS];
250  // Motor correction
251  ang[i] -= p[PARAM_PHASE];
252  ang[i] -= asin((c * tan(p[PARAM_TILT])) / sqrt(a*a + b*b));
253  // Lens correction
254  ang[i] -= p[PARAM_GIB_MAG] * cos(ang[i] + p[PARAM_GIB_PHASE]);
255  ang[i] -= p[PARAM_CURVE] * atan2(c, a) * atan2(c, a);
256  }
257  }
258 }
259 
260 // Given the lighthouse angle, predict the point in space
261 template <typename T>
262 static void Correct(T const* params, T * angle, bool correct) {
263  if (correct) {
264  T ideal[2], pred[2], xyz[3];
265  ideal[0] = angle[0];
266  ideal[1] = angle[1];
267  for (size_t i = 0; i < 10; i++) {
268  xyz[0] = T(-1.0) / tan(ideal[0]);
269  xyz[1] = T(-1.0) / tan(ideal[1]);
270  xyz[2] = T(1.0);
271  Predict(params, xyz, pred, correct);
272  ideal[0] += (angle[0] - pred[0]);
273  ideal[1] += (angle[1] - pred[1]);
274  }
275  angle[0] = ideal[0];
276  angle[1] = ideal[1];
277  }
278 }
279 
280 } // namespace vive_localization
281 
282 #endif // LOCALIZATION_VIVE_LOCALIZATION_SRC_VIVE_H_
vive_localization::NUM_PARAMS
@ NUM_PARAMS
Definition: vive.h:64
vive_localization::Tracker::sensors
double sensors[NUM_SENSORS *6]
Definition: vive.h:96
vive_localization::Tracker
Definition: vive.h:92
vive_localization::Tracker::errors
double errors[NUM_ERRORS][3]
Definition: vive.h:97
vive_localization::Lighthouse::vTl
double vTl[6]
Definition: vive.h:85
vive_localization::SendStaticTransform
void SendStaticTransform(geometry_msgs::TransformStamped const &tfs)
Definition: vive.cc:38
vive_localization::LighthouseCallback
void LighthouseCallback(ff_hw_msgs::ViveLighthouses::ConstPtr const &msg, LighthouseMap &lighthouses)
Definition: vive.cc:263
vive_localization::PARAM_CURVE
@ PARAM_CURVE
Definition: vive.h:63
vive_localization::ERROR_ACC_BIAS
@ ERROR_ACC_BIAS
Definition: vive.h:71
vive_localization::ReadRegistrationConfig
bool ReadRegistrationConfig(config_reader::ConfigReader *config, double T[6])
Definition: vive.cc:239
vive_localization::TrackerMap
std::map< std::string, Tracker > TrackerMap
Definition: vive.h:100
vive_localization::ERROR_GYR_BIAS
@ ERROR_GYR_BIAS
Definition: vive.h:69
vive_localization::Errors
Errors
Definition: vive.h:68
vive_localization::Tracker::ready
bool ready
Definition: vive.h:98
vive_localization::Params
Params
Definition: vive.h:58
vive_localization::Measurement::light
ff_hw_msgs::ViveLight light
Definition: vive.h:105
vive_localization::NUM_SENSORS
static constexpr size_t NUM_SENSORS
Definition: vive.h:53
vive_localization::PARAM_PHASE
@ PARAM_PHASE
Definition: vive.h:59
vive_localization::ReadModificationVector
bool ReadModificationVector(config_reader::ConfigReader *config, Eigen::Vector3d &modification_vector, Eigen::Quaterniond &modification_quaternion)
Definition: vive.cc:162
vive_localization::ERROR_ACC_SCALE
@ ERROR_ACC_SCALE
Definition: vive.h:72
vive_localization::SendDynamicTransform
void SendDynamicTransform(geometry_msgs::TransformStamped const &tfs)
Definition: vive.cc:43
vive_localization::SendTransforms
void SendTransforms(std::string const &frame_world, std::string const &frame_vive, std::string const &frame_body, double registration[6], LighthouseMap const &lighthouses, TrackerMap const &trackers)
Definition: vive.cc:48
point_cloud_common::Vector3d
Eigen::Vector3d Vector3d(const PointType &point)
Definition: utilities.h:328
vive_localization::Correct
static void Correct(T const *params, T *angle, bool correct)
Definition: vive.h:262
vive_localization::Tracker::tTi
double tTi[6]
Definition: vive.h:95
ff_names.h
vive_localization::Lighthouse::params
double params[NUM_MOTORS *NUM_PARAMS]
Definition: vive.h:86
vive_localization::CorrectionMap
std::map< ros::Time, geometry_msgs::TransformStamped > CorrectionMap
Definition: vive.h:110
vive_localization::Lighthouse
Definition: vive.h:83
vive_localization::LighthouseMap
std::map< uint8_t, Lighthouse > LighthouseMap
Definition: vive.h:89
vive_localization::Lighthouse::ready
bool ready
Definition: vive.h:87
vive_localization::CeresToEigen
Eigen::Affine3d CeresToEigen(double ceres[6], bool invert)
Definition: vive.cc:125
config_reader::ConfigReader
Definition: config_reader.h:48
vive_localization::Tracker::bTh
double bTh[6]
Definition: vive.h:93
vive_localization::TrackerCallback
void TrackerCallback(ff_hw_msgs::ViveTrackers::ConstPtr const &msg, TrackerMap &trackers)
Definition: vive.cc:282
vive_localization::MOTOR_HORIZONTAL
@ MOTOR_HORIZONTAL
Definition: vive.h:78
vive_localization
Definition: vive.cc:34
vive_localization::PARAM_GIB_MAG
@ PARAM_GIB_MAG
Definition: vive.h:62
tfs
geometry_msgs::TransformStamped tfs
Definition: teleop_tool.cc:80
vive_localization::MOTOR_VERTICAL
@ MOTOR_VERTICAL
Definition: vive.h:77
config_reader.h
vive_localization::ERROR_GYR_SCALE
@ ERROR_GYR_SCALE
Definition: vive.h:70
vive_localization::Motors
Motors
Definition: vive.h:76
optimization_common::Affine3d
Eigen::Affine3d Affine3d(const Eigen::Matrix< double, 7, 1 > &affine_vector)
Definition: utilities.cc:43
vive_localization::Measurement::wTb
double wTb[6]
Definition: vive.h:104
vive_localization::PARAM_TILT
@ PARAM_TILT
Definition: vive.h:60
vive_localization::PARAM_GIB_PHASE
@ PARAM_GIB_PHASE
Definition: vive.h:61
vive_localization::toQuaternion
Eigen::Quaterniond toQuaternion(double roll, double pitch, double yaw)
Definition: vive.cc:143
msg_conversions.h
vive_localization::NUM_MOTORS
@ NUM_MOTORS
Definition: vive.h:79
vive_localization::ReadLighthouseConfig
bool ReadLighthouseConfig(config_reader::ConfigReader *config, std::map< std::string, Eigen::Affine3d > &lighthouses)
Definition: vive.cc:178
vive_localization::Predict
static void Predict(T const *params, T const *xyz, T *ang, bool correct)
Definition: vive.h:235
vive_localization::Measurement
Definition: vive.h:103
vive_localization::Lighthouse::serial
std::string serial
Definition: vive.h:84
vive_localization::NUM_ERRORS
@ NUM_ERRORS
Definition: vive.h:73
vive_localization::SolvePnP
bool SolvePnP(std::vector< cv::Point3f > const &obj, std::vector< cv::Point2f > const &img, Eigen::Affine3d &transform)
Definition: vive.cc:375
vive_localization::Kabsch
static bool Kabsch(Eigen::Matrix< T, 3, Eigen::Dynamic > in, Eigen::Matrix< T, 3, Eigen::Dynamic > out, Eigen::Transform< T, 3, Eigen::Affine > &A, bool allowScale)
Definition: vive.h:169
vive_localization::MeasurementMap
std::map< ros::Time, Measurement > MeasurementMap
Definition: vive.h:107
vive_localization::ReadTrackerConfig
bool ReadTrackerConfig(config_reader::ConfigReader *config, std::map< std::string, Tracker > &trackers)
Definition: vive.cc:206
vive_localization::Tracker::tTh
double tTh[6]
Definition: vive.h:94