hector_gps_calibration.cpp
Go to the documentation of this file.
4 
5 #include <ceres/ceres.h>
6 #include <geodesy/utm.h>
7 #include <sensor_msgs/NavSatFix.h>
8 
9 #include <iostream>
10 #include <fstream>
11 
13  : tf_listener(tf_buffer),
14  translation_{{0,0}},
15  rotation_(0.0)
16 {
17  nh.param<double>("translation_x", translation_[0], 0.0);
18  nh.param<double>("translation_y", translation_[1], 0.0);
19 
20  nh.param<double>("orientation", rotation_, 0.0);
21 
22  nh.param<bool>("write_debug_file", write_debug_file_, false);
23  nh.param<double>("max_covariance", max_covariance_, 10.0);
24  nh.param<double>("min_pose_distance", min_pose_distance_, 0.2);
25 
26  ROS_INFO("Initial GPS transformation: \n t: %f %f \n r: %f", translation_[0], translation_[1], rotation_);
27 
28  nav_sat_sub_ = nh.subscribe("/odom_gps", 1, &GPSCalibration::navSatCallback, this);
29  optimize_sub_ = nh.subscribe("gps/run_optimization", 1, &GPSCalibration::navSatCallback, this);
30 
31  nav_sat_fix_pub_ = nh.advertise<sensor_msgs::NavSatFix>("/gps_calibration/gps/fix", 5);
32 
33  /*TEST
34  Eigen::Matrix<double, 3, 1> pos_gps(0, 0, 10);
35  gps_poses_.emplace_back(pos_gps);
36  pos_gps = Eigen::Matrix<double, 3, 1>(0, 1, 10);
37  gps_poses_.emplace_back(pos_gps);
38  pos_gps = Eigen::Matrix<double, 3, 1>(0, 2, 10);
39  gps_poses_.emplace_back(pos_gps);
40  pos_gps = Eigen::Matrix<double, 3, 1>(0, 3, 10);
41  gps_poses_.emplace_back(pos_gps);
42 
43  Eigen::Matrix<double, 3, 1> pos_world(0.1, 0, 0);
44  world_poses_.emplace_back(pos_world);
45  pos_world = Eigen::Matrix<double, 3, 1>(0, 1, 0.1);
46  world_poses_.emplace_back(pos_world);
47  pos_world = Eigen::Matrix<double, 3, 1>(0, 2, 0);
48  world_poses_.emplace_back(pos_world);
49  pos_world = Eigen::Matrix<double, 3, 1>(0, 3, 0);
50  world_poses_.emplace_back(pos_world);
51  optimize();*/
52 
53  wall_timers_.push_back(nh.createWallTimer(ros::WallDuration(0.1), &GPSCalibration::publishTF, this));
54 }
55 
56 void GPSCalibration::navSatCallback(nav_msgs::Odometry msg)
57 {
58  msg.header.stamp = ros::Time::now(); //Ugly hack to work around timestamp issues specific to our robot
59 
60  if(msg.pose.covariance[0] > max_covariance_ ) {
61  //ROS_WARN("Dropping GPS data. Covariance limit exceeded. Covariance: %f > %f", msg.pose.covariance[0], max_covariance_);
62  return;
63  }
64 
65  Eigen::Matrix<double, 2, 1> pos_gps(msg.pose.pose.position.x,
66  msg.pose.pose.position.y);
67 
68  geometry_msgs::TransformStamped transformStamped;
69  try{
70  transformStamped = tf_buffer.lookupTransform("world", "navsat_link",
71  msg.header.stamp, ros::Duration(1.0));
72  }
73  catch (tf2::TransformException &ex) {
74  ROS_WARN("%s",ex.what());
75 
76  return;
77  }
78 
79  Eigen::Matrix<double, 2, 1> pos_world(transformStamped.transform.translation.x,
80  transformStamped.transform.translation.y);
81  bool redundant_data = false;
82  if(gps_poses_.size() > 0)
83  {
84  Eigen::Matrix<double, 2, 1> delta_pose = world_poses_[gps_poses_.size() - 1] - pos_world;
85  double pose_distance = std::sqrt(delta_pose.transpose() * delta_pose);
86  if(pose_distance < min_pose_distance_)
87  redundant_data = true;
88  }
89  if(!redundant_data)
90  {
91  gps_poses_.emplace_back(pos_gps);
92  world_poses_.emplace_back(pos_world);
93  covariances_.emplace_back(msg.pose.covariance[0]);
94  }
95 
96  if((world_poses_.size() % 10 == 0) && world_poses_.size() > 0)
97  optimize();
98 
99 }
100 
101 void GPSCalibration::optimizeCallback(std_msgs::Empty msg)
102 {
103  optimize();
104 }
105 
106 
108 {
109  int i = 0;
110 
111  ceres::Problem problem;
112 
113  for(i = 0; i < world_poses_.size(); ++i)
114  {
115  problem.AddResidualBlock(
116  new ceres::AutoDiffCostFunction<TransformDeltaCostFunctor,
117  2, 2, 1>(
119  gps_poses_[i],
120  covariances_[i])),
121  nullptr, translation_.data(), &rotation_);
122  }
123 
124  ceres::LocalParameterization* angle_local_parameterization =
126 
127  problem.SetParameterization(&rotation_, angle_local_parameterization);
128 
129 
130  ceres::Solver::Options options;
131  options.linear_solver_type = ceres::DENSE_QR;
132  // options.minimizer_progress_to_stdout = true;
133  ceres::Solver::Summary summary;
134  ceres::Solve(options, &problem, &summary);
135  if(summary.termination_type != ceres::TerminationType::CONVERGENCE)
136  ROS_WARN("%s", summary.FullReport().c_str());
137  ROS_INFO("Translation %f %f", translation_[0], translation_[1]);
138  ROS_INFO("Rotation %f", rotation_);
139 
141  {
142  std::ofstream myfile;
143  myfile.open ("gps_alignment_solution.csv");
144  const Rigid3<double> transform = Rigid3<double>(
145  Eigen::Matrix<double, 3, 1>(translation_[0], translation_[1], 0.0),
146  Eigen::Quaternion<double>(std::cos(rotation_/2.0), 0.0, 0.0,
147  std::sin(rotation_/2.0)));
148 
149  myfile <<"gps_x"<<","<<"gps_y"<<","
150  <<"world_x"<<","<<"world_y"<<","<<"covariance"<<"\n";
151  for(size_t i = 0; i<gps_poses_.size(); ++i)
152  {
153  const Eigen::Matrix<double, 3, 1> pos_world_gps = transform * Eigen::Matrix<double, 3, 1>(world_poses_[i][0], world_poses_[i][1], 0.0);
154  myfile << std::setprecision (15)<< gps_poses_[i][0]<<","<<gps_poses_[i][1]<<","
155  <<pos_world_gps[0]<<","<<pos_world_gps[1]<<","<<covariances_[i]<<"\n";
156  }
157  myfile.close();
158  }
159 }
160 
161 void GPSCalibration::publishTF(const ::ros::WallTimerEvent& unused_timer_event)
162 {
163  ros::Time publish_time = ros::Time::now();
164 
165  geometry_msgs::TransformStamped utm_world_transform;
166  utm_world_transform.header.stamp = publish_time;
167  utm_world_transform.header.frame_id = "utm";
168  utm_world_transform.child_frame_id = "world";
169  utm_world_transform.transform.translation.x = translation_[0];
170  utm_world_transform.transform.translation.y = translation_[1];
171  utm_world_transform.transform.translation.z = 0;
172  utm_world_transform.transform.rotation.w = std::cos(rotation_/2.0);
173  utm_world_transform.transform.rotation.x = 0.0;
174  utm_world_transform.transform.rotation.y = 0.0;
175  utm_world_transform.transform.rotation.z = std::sin(rotation_/2.0);
176  //tf_broadcaster.sendTransform(utm_world_transform);
177 
178 
179  geometry_msgs::TransformStamped worldenu_world_transform;
180  worldenu_world_transform.header.stamp = publish_time;
181  worldenu_world_transform.header.frame_id = "world_enu";
182  worldenu_world_transform.child_frame_id = "world";
183  worldenu_world_transform.transform.translation.x = 0;
184  worldenu_world_transform.transform.translation.y = 0;
185  worldenu_world_transform.transform.translation.z = 0;
186  worldenu_world_transform.transform.rotation.w = std::cos(rotation_/2.0);
187  worldenu_world_transform.transform.rotation.x = 0.0;
188  worldenu_world_transform.transform.rotation.y = 0.0;
189  worldenu_world_transform.transform.rotation.z = std::sin(rotation_/2.0);
190  tf_broadcaster.sendTransform(worldenu_world_transform);
191 
192  geometry_msgs::TransformStamped world_navsat_transform;
193  try{
194  world_navsat_transform = tf_buffer.lookupTransform("world", "base_link",
195  publish_time, ros::Duration(1.0));
196  }
197  catch (tf2::TransformException &ex) {
198  ROS_WARN("%s",ex.what());
199  return;
200  }
201 
202 
203  geometry_msgs::TransformStamped utm_navsat_transform;
204  tf2::doTransform(world_navsat_transform, utm_navsat_transform, utm_world_transform);
205 
206  geodesy::UTMPoint utm_point;
207  utm_point.band = 'U';
208  utm_point.zone = 32;
209  utm_point.easting = utm_navsat_transform.transform.translation.x;
210  utm_point.northing = utm_navsat_transform.transform.translation.y;
211  utm_point.altitude = 0.0;
212  geographic_msgs::GeoPoint geo_point = geodesy::toMsg(utm_point);
213 
214  sensor_msgs::NavSatFix nav_sat_fix;
215  nav_sat_fix.header.stamp = publish_time;
216  nav_sat_fix.header.frame_id = "base_link";
217  nav_sat_fix.latitude = geo_point.latitude;
218  nav_sat_fix.longitude = geo_point.longitude;
219  nav_sat_fix.altitude = 0.0;
220  nav_sat_fix_pub_.publish(nav_sat_fix);
221 
222 }
ros::Publisher nav_sat_fix_pub_
std::vector< double > covariances_
void publish(const boost::shared_ptr< M > &message) const
tf2_ros::TransformBroadcaster tf_broadcaster
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
std::vector< ros::WallTimer > wall_timers_
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
#define ROS_WARN(...)
void navSatCallback(nav_msgs::Odometry msg)
static geographic_msgs::GeoPoint toMsg(double lat, double lon)
static ceres::LocalParameterization * Create()
std::vector< Eigen::Matrix< double, 2, 1 > > world_poses_
GPSCalibration(ros::NodeHandle &nh)
#define ROS_INFO(...)
options
ros::Subscriber optimize_sub_
void sendTransform(const geometry_msgs::TransformStamped &transform)
std::vector< Eigen::Matrix< double, 2, 1 > > gps_poses_
static Time now()
ros::Subscriber nav_sat_sub_
void publishTF(const ros::WallTimerEvent &unused_timer_event)
std::array< double, 2 > translation_
void optimizeCallback(std_msgs::Empty msg)
tf2_ros::Buffer tf_buffer


hector_gps_calibration
Author(s):
autogenerated on Mon Jun 10 2019 13:34:46