39 #include <ceres/ceres.h> 40 #include <ceres/local_parameterization.h> 49 #if not(CERES_VERSION_MAJOR > 1 || (CERES_VERSION_MAJOR == 1 && CERES_VERSION_MINOR >= 12)) 68 const std::map<int, Transform> & poses,
69 const std::multimap<int, Link> & edgeConstraints,
70 cv::Mat & outputCovariance,
71 std::list<std::map<int, Transform> > * intermediateGraphes,
75 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
76 std::map<int, Transform> optimizedPoses;
78 UDEBUG(
"Optimizing graph (pose=%d constraints=%d)...", (
int)poses.size(), (int)edgeConstraints.size());
79 if(edgeConstraints.size()>=1 && poses.size()>=2 &&
iterations() > 0)
82 ceres::Problem problem;
83 std::map<int, ceres::examples::Pose2d> poses2d;
86 UDEBUG(
"fill poses to Ceres...");
89 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
93 UASSERT(!iter->second.isNull());
95 p.
x = iter->second.x();
96 p.y = iter->second.y();
98 poses2d.insert(std::make_pair(iter->first, p));
104 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
108 UASSERT(!iter->second.isNull());
110 p.
p.x() = iter->second.x();
111 p.p.y() = iter->second.y();
112 p.p.z() = iter->second.z();
113 p.q = iter->second.getQuaterniond();
114 poses3d.insert(std::make_pair(iter->first, p));
120 ceres::LossFunction* loss_function =
NULL;
121 ceres::LocalParameterization* angle_local_parameterization =
NULL;
122 ceres::LocalParameterization* quaternion_local_parameterization =
NULL;
124 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
126 int id1 = iter->second.from();
127 int id2 = iter->second.to();
129 if(id1 != id2 && id1 > 0 && id2 > 0)
131 UASSERT(poses.find(id1) != poses.end() && poses.find(id2) != poses.end());
135 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
138 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
139 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
140 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
141 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
142 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
143 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
144 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
145 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
146 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
150 const Eigen::Matrix3d sqrt_information = information.llt().matrixL();
154 iter->second.transform().x(),
155 iter->second.transform().y(),
159 std::map<int, ceres::examples::Pose2d>::iterator pose_begin_iter = poses2d.find(id1);
160 std::map<int, ceres::examples::Pose2d>::iterator pose_end_iter = poses2d.find(id2);
162 problem.AddResidualBlock(
163 cost_function, loss_function,
164 &pose_begin_iter->second.x, &pose_begin_iter->second.y, &pose_begin_iter->second.yaw_radians,
165 &pose_end_iter->second.x, &pose_end_iter->second.y, &pose_end_iter->second.yaw_radians);
167 if(angle_local_parameterization ==
NULL)
171 problem.SetParameterization(&pose_begin_iter->second.yaw_radians, angle_local_parameterization);
172 problem.SetParameterization(&pose_end_iter->second.yaw_radians, angle_local_parameterization);
176 ceres::examples::MapOfPoses::iterator pose_begin_iter = poses3d.find(id1);
177 ceres::examples::MapOfPoses::iterator pose_end_iter = poses3d.find(id2);
179 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
182 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
186 t.
p.x() = iter->second.transform().x();
187 t.
p.y() = iter->second.transform().y();
188 t.
p.z() = iter->second.transform().z();
189 t.
q = iter->second.transform().getQuaterniond();
191 const Eigen::Matrix<double, 6, 6> sqrt_information = information.llt().matrixL();
194 problem.AddResidualBlock(cost_function, loss_function,
195 pose_begin_iter->second.p.data(), pose_begin_iter->second.q.coeffs().data(),
196 pose_end_iter->second.p.data(), pose_end_iter->second.q.coeffs().data());
197 if(quaternion_local_parameterization ==
NULL)
201 problem.SetParameterization(pose_begin_iter->second.q.coeffs().data(), quaternion_local_parameterization);
202 problem.SetParameterization(pose_end_iter->second.q.coeffs().data(), quaternion_local_parameterization);
217 std::map<int, ceres::examples::Pose2d>::iterator pose_start_iter = rootId>0?poses2d.find(rootId):poses2d.begin();
218 UASSERT(pose_start_iter != poses2d.end());
219 problem.SetParameterBlockConstant(&pose_start_iter->second.x);
220 problem.SetParameterBlockConstant(&pose_start_iter->second.y);
221 problem.SetParameterBlockConstant(&pose_start_iter->second.yaw_radians);
232 ceres::examples::MapOfPoses::iterator pose_start_iter = rootId>0?poses3d.find(rootId):poses3d.begin();
233 UASSERT(pose_start_iter != poses3d.end());
234 problem.SetParameterBlockConstant(pose_start_iter->second.p.data());
235 problem.SetParameterBlockConstant(pose_start_iter->second.q.coeffs().data());
240 ceres::Solver::Options options;
242 options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
243 options.function_tolerance = this->
epsilon();
244 ceres::Solver::Summary summary;
246 ceres::Solve(options, &problem, &summary);
250 std::cout << summary.FullReport() <<
'\n';
252 if(!summary.IsSolutionUsable())
254 UWARN(
"ceres: Could not find a usable solution, aborting optimization!");
255 return optimizedPoses;
260 *finalError = summary.final_cost;
264 *iterationsDone = summary.iterations.size();
266 UINFO(
"Ceres optimizing end (%d iterations done, error=%f, time = %f s)", (
int)summary.iterations.size(), summary.final_cost, timer.
ticks());
269 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
275 const std::map<int, ceres::examples::Pose2d>::iterator & pter = poses2d.find(iter->first);
277 iter->second.getEulerAngles(roll, pitch, yaw);
279 Transform newPose(pter->second.x, pter->second.y, iter->second.z(),
roll,
pitch, pter->second.yaw_radians);
281 UASSERT_MSG(!newPose.isNull(),
uFormat(
"Optimized pose %d is null!?!?", iter->first).c_str());
282 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
286 const std::map<int, ceres::examples::Pose3d, std::less<int>,
287 Eigen::aligned_allocator<std::pair<const int, ceres::examples::Pose3d> > >::
288 iterator& pter = poses3d.find(iter->first);
290 Transform newPose(pter->second.p.x(), pter->second.p.y(), pter->second.p.z(), pter->second.q.x(), pter->second.q.y(), pter->second.q.z(), pter->second.q.w());
292 UASSERT_MSG(!newPose.isNull(),
uFormat(
"Optimized pose %d is null!?!?", iter->first).c_str());
293 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
301 else if(poses.size() == 1 ||
iterations() <= 0)
303 optimizedPoses = poses;
307 UWARN(
"This method should be called at least with 1 pose!");
309 UDEBUG(
"Optimizing graph...end!");
311 UERROR(
"Not built with Ceres support!");
313 return optimizedPoses;
318 const std::map<int, Transform> & posesIn,
319 const std::multimap<int, Link> & links,
320 const std::map<int, CameraModel> & models,
321 std::map<int, cv::Point3f> & points3DMap,
322 const std::map<
int, std::map<int, FeatureBA> > & wordReferences,
323 std::set<int> * outliers)
328 std::map<int, Transform> poses(posesIn.lower_bound(1), posesIn.end());
333 baProblem.num_points_ = points3DMap.size();
334 baProblem.num_observations_ = 0;
335 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator iter=wordReferences.begin();
336 iter!=wordReferences.end();
339 baProblem.num_observations_ += iter->second.size();
342 baProblem.point_index_ =
new int[baProblem.num_observations_];
343 baProblem.camera_index_ =
new int[baProblem.num_observations_];
344 baProblem.observations_ =
new double[4 * baProblem.num_observations_];
345 baProblem.cameras_ =
new double[6 * baProblem.num_cameras_];
346 baProblem.points_ =
new double[3 * baProblem.num_points_];
351 std::map<int, int> camIdToIndex;
352 for(std::map<int, Transform>::const_iterator iter=poses.begin();
357 std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
358 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
360 const Transform & t = (iter->second * iterModel->second.localTransform()).
inverse();
361 cv::Mat R = (cv::Mat_<double>(3,3) <<
362 (
double)t.r11(), (double)t.r12(), (double)t.r13(),
363 (double)t.r21(), (double)t.r22(), (double)t.r23(),
364 (double)t.r31(), (double)t.r32(), (double)t.r33());
366 cv::Mat rvec(1,3, CV_64FC1);
367 cv::Rodrigues(R, rvec);
369 UASSERT(oi+6 <= baProblem.num_cameras_*6);
371 baProblem.cameras_[oi++] = rvec.at<
double>(0,0);
372 baProblem.cameras_[oi++] = rvec.at<
double>(0,1);
373 baProblem.cameras_[oi++] = rvec.at<
double>(0,2);
374 baProblem.cameras_[oi++] = t.x();
375 baProblem.cameras_[oi++] = t.y();
376 baProblem.cameras_[oi++] = t.z();
378 camIdToIndex.insert(std::make_pair(iter->first, camIndex++));
380 UASSERT(oi == baProblem.num_cameras_*6);
384 std::map<int, int> pointIdToIndex;
385 for(std::map<int, cv::Point3f>::const_iterator kter = points3DMap.begin(); kter!=points3DMap.end(); ++kter)
387 UASSERT(oi+3 <= baProblem.num_points_*3);
389 baProblem.points_[oi++] = kter->second.x;
390 baProblem.points_[oi++] = kter->second.y;
391 baProblem.points_[oi++] = kter->second.z;
393 pointIdToIndex.insert(std::make_pair(kter->first, pointIndex++));
395 UASSERT(oi == baProblem.num_points_*3);
398 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator iter=wordReferences.begin();
399 iter!=wordReferences.end();
402 for(std::map<int, FeatureBA>::const_iterator jter=iter->second.begin();
403 jter!=iter->second.end();
406 std::map<int, CameraModel>::const_iterator iterModel = models.find(jter->first);
407 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
409 baProblem.camera_index_[oi] = camIdToIndex.at(jter->first);
410 baProblem.point_index_[oi] = pointIdToIndex.at(iter->first);
411 baProblem.observations_[4*oi] = jter->second.kpt.pt.x - iterModel->second.cx();
412 baProblem.observations_[4*oi+1] = jter->second.kpt.pt.y - iterModel->second.cy();
413 baProblem.observations_[4*oi+2] = iterModel->second.fx();
414 baProblem.observations_[4*oi+3] = iterModel->second.fy();
418 UASSERT(oi == baProblem.num_observations_);
421 const double* observations = baProblem.observations();
424 ceres::Problem problem;
426 for (
int i = 0; i < baProblem.num_observations(); ++i) {
430 ceres::CostFunction* cost_function =
433 observations[4 * i + 1],
434 observations[4 * i + 2],
435 observations[4 * i + 3]);
436 ceres::LossFunction* loss_function =
new ceres::HuberLoss(8.0);
437 problem.AddResidualBlock(cost_function,
439 baProblem.mutable_camera_for_observation(i),
440 baProblem.mutable_point_for_observation(i));
447 ceres::Solver::Options options;
448 options.linear_solver_type = ceres::DENSE_SCHUR;
451 options.function_tolerance = this->
epsilon();
452 ceres::Solver::Summary summary;
453 ceres::Solve(options, &problem, &summary);
457 std::cout << summary.FullReport() <<
"\n";
459 if(!summary.IsSolutionUsable())
461 UWARN(
"ceres: Could not find a usable solution, aborting optimization!");
466 std::map<int, Transform> newPoses = poses;
468 for(std::map<int, Transform>::iterator iter=newPoses.begin(); iter!=newPoses.end(); ++iter)
470 cv::Mat rvec = (cv::Mat_<double>(1,3) <<
471 baProblem.cameras_[oi], baProblem.cameras_[oi+1], baProblem.cameras_[oi+2]);
474 cv::Rodrigues(rvec, R);
475 Transform t(R.at<
double>(0,0), R.at<
double>(0,1), R.at<
double>(0,2), baProblem.cameras_[oi+3],
476 R.at<
double>(1,0), R.at<
double>(1,1), R.at<
double>(1,2), baProblem.cameras_[oi+4],
477 R.at<
double>(2,0), R.at<
double>(2,1), R.at<
double>(2,2), baProblem.cameras_[oi+5]);
483 t = (models.at(iter->first).localTransform() * t).
inverse();
484 t = iter->second.
inverse() * t;
485 iter->second *= t.
to3DoF();
489 iter->second = (models.at(iter->first).localTransform() * t).
inverse();
496 for(std::map<int, cv::Point3f>::iterator kter = points3DMap.begin(); kter!=points3DMap.end(); ++kter)
498 kter->second.x = baProblem.points_[oi++];
499 kter->second.y = baProblem.points_[oi++];
500 kter->second.z = baProblem.points_[oi++];
506 UERROR(
"RTAB-Map is not built with ceres!");
507 return std::map<int, Transform>();
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
static ceres::CostFunction * Create(const double observed_x, const double observed_y, const double fx, const double fy)
static ceres::CostFunction * Create(double x_ab, double y_ab, double yaw_ab_radians, const Eigen::Matrix3d &sqrt_information)
bool isCovarianceIgnored() const
Basic mathematics functions.
Some conversion functions.
T NormalizeAngle(const T &angle_radians)
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
#define UASSERT(condition)
Wrappers of STL for convenient functions.
static ceres::LocalParameterization * Create()
#define UASSERT_MSG(condition, msg_str)
virtual std::map< int, Transform > optimize(int rootId, const std::map< int, Transform > &poses, const std::multimap< int, Link > &edgeConstraints, cv::Mat &outputCovariance, std::list< std::map< int, Transform > > *intermediateGraphes=0, double *finalError=0, int *iterationsDone=0)
std::map< int, Pose3d, std::less< int >, Eigen::aligned_allocator< std::pair< const int, Pose3d > > > MapOfPoses
static ULogger::Level level()
ULogger class and convenient macros.
static ceres::CostFunction * Create(const Pose3d &t_ab_measured, const Eigen::Matrix< double, 6, 6 > &sqrt_information)
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
virtual std::map< int, Transform > optimizeBA(int rootId, const std::map< int, Transform > &poses, const std::multimap< int, Link > &links, const std::map< int, CameraModel > &models, std::map< int, cv::Point3f > &points3DMap, const std::map< int, std::map< int, FeatureBA > > &wordReferences, std::set< int > *outliers=0)
std::string UTILITE_EXP uFormat(const char *fmt,...)
GLM_FUNC_DECL matType< T, P > inverse(matType< T, P > const &m)