odometry vis to be done

main
feixyz10 2020-11-02 17:16:42 +08:00 committed by feixyz
parent 469b6a9804
commit 2a052c462e
11 changed files with 171 additions and 51 deletions

View File

@ -6,24 +6,41 @@
#include <g3log/logworker.hpp> #include <g3log/logworker.hpp>
#include <iostream> #include <iostream>
// heere we define G3LOG instead of use LOG directly, since LOG macro in g3log
// is conflict with LOG macro in glog. Same for G3LOG_IF and G3CHECK
#define G3LOG(level) \
if (!g3::logLevel(level)) { \
} else \
INTERNAL_LOG_MESSAGE(level).stream()
#define G3LOG_IF(level, boolean_expression) \
if (false == (boolean_expression) || !g3::logLevel(level)) { \
} else \
INTERNAL_LOG_MESSAGE(level).stream()
#define G3CHECK(boolean_expression) \
if (true == (boolean_expression)) { \
} else \
INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
const LEVELS ERROR{WARNING.value + 100, "ERROR"}; const LEVELS ERROR{WARNING.value + 100, "ERROR"};
const LEVELS USER(ERROR.value + 100, "USER"); const LEVELS USER(ERROR.value + 100, "USER");
#define ADEBUG LOG(DEBUG) #define ADEBUG G3LOG(DEBUG)
#define AINFO LOG(INFO) #define AINFO G3LOG(INFO)
#define AWARN LOG(WARNING) #define AWARN G3LOG(WARNING)
#define AERROR LOG(ERROR) #define AERROR G3LOG(ERROR)
#define AUSER LOG(USER) #define AUSER G3LOG(USER)
#define AFATAL LOG(FATAL) #define AFATAL G3LOG(FATAL)
// LOG_IF // LOG_IF
#define ADEBUG_IF(cond) LOG_IF(DEBUG, cond) #define ADEBUG_IF(cond) G3LOG_IF(DEBUG, cond)
#define AINFO_IF(cond) LOG_IF(INFO, cond) #define AINFO_IF(cond) G3LOG_IF(INFO, cond)
#define AWARN_IF(cond) LOG_IF(WARNING, cond) #define AWARN_IF(cond) G3LOG_IF(WARNING, cond)
#define AERROR_IF(cond) LOG_IF(ERROR, cond) #define AERROR_IF(cond) G3LOG_IF(ERROR, cond)
#define AUSER_IF(cond) LOG_IF(USER, cond) #define AUSER_IF(cond) G3LOG_IF(USER, cond)
#define AFATAL_IF(cond) LOG_IF(FATAL, cond) #define AFATAL_IF(cond) G3LOG_IF(FATAL, cond)
#define ACHECK(cond) CHECK(cond) #define ACHECK(cond) G3CHECK(cond)
namespace g3 { namespace g3 {
class CustomSink { class CustomSink {

View File

@ -3,7 +3,7 @@
namespace oh_my_loam { namespace oh_my_loam {
Pose3D Interpolate(const Pose3D& pose_from, const Pose3D& pose_to, double t) { Pose3D Interpolate(const Pose3D& pose_from, const Pose3D& pose_to, double t) {
return pose_from.InterPolate(pose_to, t); return pose_from.Interpolate(pose_to, t);
} }
Pose3D operator*(const Pose3D& lhs, const Pose3D& rhs) { Pose3D operator*(const Pose3D& lhs, const Pose3D& rhs) {

View File

@ -20,9 +20,9 @@ class Pose3D {
Pose3D(const double* const q, const double* const p) : q_(q), p_(p) {} Pose3D(const double* const q, const double* const p) : q_(q), p_(p) {}
Pose3D Inv() const { Pose3D Inv() const {
auto q_inv = q_.inverse(); Eigen::Quaterniond q_inv = q_.inverse();
auto p_inv = q_inv * p_; Eigen::Vector3d p_inv = q_inv * p_;
return {q_inv, p_inv}; return {q_inv, -p_inv};
} }
Eigen::Vector3d Transform(const Eigen::Vector3d& vec) const { Eigen::Vector3d Transform(const Eigen::Vector3d& vec) const {
@ -37,9 +37,9 @@ class Pose3D {
PointT Transform(const PointT& pt) const { PointT Transform(const PointT& pt) const {
PointT pt_n = pt; PointT pt_n = pt;
Eigen::Vector3d vec = Transform(Eigen::Vector3d(pt.x, pt.y, pt.z)); Eigen::Vector3d vec = Transform(Eigen::Vector3d(pt.x, pt.y, pt.z));
pt_n.x = vec.x; pt_n.x = static_cast<float>(vec.x());
pt_n.y = vec.y; pt_n.y = static_cast<float>(vec.y());
pt_n.z = vec.z; pt_n.z = static_cast<float>(vec.z());
return pt_n; return pt_n;
} }
@ -55,9 +55,9 @@ class Pose3D {
Eigen::Vector3d Rotate(const Eigen::Vector3d& vec) const { return q_ * vec; } Eigen::Vector3d Rotate(const Eigen::Vector3d& vec) const { return q_ * vec; }
// Spherical linear interpolation to `pose_to`, `t` belongs [0, 1] // Spherical linear interpolation to `pose_to`, `t` belongs [0, 1]
Pose3D InterPolate(const Pose3D& pose_to, double t) const { Pose3D Interpolate(const Pose3D& pose_to, double t) const {
auto q_interp = q_.slerp(t, pose_to.q_); Eigen::Quaterniond q_interp = q_.slerp(t, pose_to.q_);
auto p_interp = (pose_to.p_ - p_) * t; Eigen::Vector3d p_interp = (pose_to.p_ - p_) * t + p_;
return {q_interp, p_interp}; return {q_interp, p_interp};
} }

View File

@ -46,7 +46,12 @@ using TCTPointCloudConstPtr = TCTPointCloud::ConstPtr;
struct PointXYZT { struct PointXYZT {
PCL_ADD_POINT4D; PCL_ADD_POINT4D;
float time; union EIGEN_ALIGN16 {
float time;
// make sure VoxelGrid can work with this custom point type:
// https://github.com/PointCloudLibrary/pcl/issues/2331
float intensity;
};
PointXYZT() { PointXYZT() {
x = y = z = 0.0f; x = y = z = 0.0f;

View File

@ -10,12 +10,12 @@ extractor_config:
vis: true vis: true
sharp_corner_point_num: 2 sharp_corner_point_num: 2
corner_point_num: 20 corner_point_num: 20
flat_surf_point_num: 3 flat_surf_point_num: 4
surf_point_num: 3 ## useless currently surf_point_num: 3 ## useless currently
corner_point_curvature_thres: 0.5 corner_point_curvature_thres: 0.5
surf_point_curvature_thres: 0.5 surf_point_curvature_thres: 0.5
neighbor_point_dist_thres: 0.05 neighbor_point_dist_thres: 0.05
downsample_voxel_size: 0.5 downsample_voxel_size: 0.3
odometry_config: odometry_config:
icp_iter_num : 2 icp_iter_num : 2

View File

@ -47,8 +47,8 @@ void Extractor::Process(const PointCloud& cloud, FeaturePoints* const feature) {
AINFO << "Time elapsed (ms): scan_split = " << std::setprecision(3) AINFO << "Time elapsed (ms): scan_split = " << std::setprecision(3)
<< time_split << ", curvature_compute = " << time_curv - time_split << time_split << ", curvature_compute = " << time_curv - time_split
<< ", type_assign = " << time_assign - time_curv << ", type_assign = " << time_assign - time_curv
<< ", store = " << time_store - time_assign << ", store = " << time_store - time_assign;
<< ", TOTAL = " << time_store; AUSER << "Time elapsed (ms): whole extraction = " << time_store;
if (is_vis_) Visualize(cloud, *feature); if (is_vis_) Visualize(cloud, *feature);
} }
@ -181,15 +181,14 @@ void Extractor::SetNeighborsPicked(const TCTPointCloud& scan, size_t ix,
void Extractor::GenerateFeaturePoints(const TCTPointCloud& scan, void Extractor::GenerateFeaturePoints(const TCTPointCloud& scan,
FeaturePoints* const feature) const { FeaturePoints* const feature) const {
// TPointCloudPtr less_flat_surf_pts(new TPointCloud); TPointCloudPtr less_flat_surf_pts(new TPointCloud);
for (const auto& pt : scan.points) { for (const auto& pt : scan.points) {
switch (pt.type) { switch (pt.type) {
case PointType::FLAT: case PointType::FLAT:
feature->flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, pt.time); feature->flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, pt.time);
// no break: FLAT points are also LESS_FLAT // no break: FLAT points are also LESS_FLAT
case PointType::LESS_FLAT: case PointType::LESS_FLAT:
feature->less_flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, less_flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, pt.time);
pt.time);
break; break;
case PointType::SHARP: case PointType::SHARP:
feature->sharp_corner_pts->points.emplace_back(pt.x, pt.y, pt.z, feature->sharp_corner_pts->points.emplace_back(pt.x, pt.y, pt.z,
@ -201,16 +200,14 @@ void Extractor::GenerateFeaturePoints(const TCTPointCloud& scan,
break; break;
default: default:
// all the rest are also LESS_FLAT // all the rest are also LESS_FLAT
feature->less_flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, less_flat_surf_pts->points.emplace_back(pt.x, pt.y, pt.z, pt.time);
pt.time);
break; break;
} }
} }
// TPointCloudPtr filtered_less_flat_surf_pts(new TPointCloud); TPointCloudPtr filtered_less_flat_surf_pts(new TPointCloud);
// VoxelDownSample<TPoint>(less_flat_surf_pts, VoxelDownSample<TPoint>(less_flat_surf_pts, filtered_less_flat_surf_pts.get(),
// filtered_less_flat_surf_pts.get(), config_["downsample_voxel_size"].as<double>());
// config_["downsample_voxel_size"].as<double>()); *feature->less_flat_surf_pts += *filtered_less_flat_surf_pts;
// *feature->less_flat_surf_pts += *filtered_less_flat_surf_pts;
} }
void Extractor::Visualize(const PointCloud& cloud, void Extractor::Visualize(const PointCloud& cloud,

View File

@ -10,6 +10,30 @@ inline float GetTime(const TPoint& pt) {
inline int GetScanId(const TPoint& pt) { return static_cast<int>(pt.time); } inline int GetScanId(const TPoint& pt) { return static_cast<int>(pt.time); }
/**
* @brief Transform a lidar point to the start of the scan
*
* @param pose Relative pose, end scan time w.r.t. start scan time
* @param time Point time relative to the start time of the scan, \in [0, 1]
*/
void TransformToStart(const Pose3D& pose, const TPoint& pt_in,
TPoint* const pt_out) {
Pose3D pose_interp = Pose3D().Interpolate(pose, GetTime(pt_in));
*pt_out = pose_interp * pt_in;
}
/**
* @brief Transform a lidar point to the end of the scan
*
* @param pose Relative pose, end scan time w.r.t. start scan time
* @param time Point time relative to the start time of the scan, \in [0, 1]
*/
void TransformToEnd(const Pose3D& pose, const TPoint& pt_in,
TPoint* const pt_out) {
TransformToStart(pose, pt_in, pt_out);
*pt_out = pose.Inv() * (*pt_out);
}
struct PointLinePair { struct PointLinePair {
TPoint pt; TPoint pt;
struct Line { struct Line {

View File

@ -72,13 +72,15 @@ void Odometry::Process(const FeaturePoints& feature, Pose3D* const pose) {
AWARN << "Pose increase: " << pose_curr2last_.ToString(); AWARN << "Pose increase: " << pose_curr2last_.ToString();
AWARN << "Pose after: " << pose_curr2world_.ToString(); AWARN << "Pose after: " << pose_curr2world_.ToString();
UpdatePre(feature); UpdatePre(feature);
AINFO << "Time elapsed (ms): whole odometry = " << timer_whole.toc(); AUSER << "Time elapsed (ms): whole odometry = " << timer_whole.toc();
} }
void Odometry::MatchCornPoints(const TPointCloud& src, const TPointCloud& tgt, void Odometry::MatchCornPoints(const TPointCloud& src, const TPointCloud& tgt,
std::vector<PointLinePair>* const pairs, std::vector<PointLinePair>* const pairs,
double dist_sq_thresh) const { double dist_sq_thresh) const {
for (const auto& query_pt : src) { for (const auto& q_pt : src) {
TPoint query_pt;
TransformToStart(pose_curr2last_, q_pt, &query_pt);
std::vector<int> indices; std::vector<int> indices;
std::vector<float> dists; std::vector<float> dists;
kdtree_corn_pts_->nearestKSearch(query_pt, 1, indices, dists); kdtree_corn_pts_->nearestKSearch(query_pt, 1, indices, dists);
@ -111,7 +113,7 @@ void Odometry::MatchCornPoints(const TPointCloud& src, const TPointCloud& tgt,
} }
if (pt2_idx >= 0) { if (pt2_idx >= 0) {
TPoint pt2 = tgt.points[pt2_idx]; TPoint pt2 = tgt.points[pt2_idx];
pairs->emplace_back(query_pt, pt1, pt2); pairs->emplace_back(q_pt, pt1, pt2);
} }
} }
} }
@ -119,7 +121,9 @@ void Odometry::MatchCornPoints(const TPointCloud& src, const TPointCloud& tgt,
void Odometry::MatchSurfPoints(const TPointCloud& src, const TPointCloud& tgt, void Odometry::MatchSurfPoints(const TPointCloud& src, const TPointCloud& tgt,
std::vector<PointPlanePair>* const pairs, std::vector<PointPlanePair>* const pairs,
double dist_sq_thresh) const { double dist_sq_thresh) const {
for (const auto& query_pt : src) { for (const auto& q_pt : src) {
TPoint query_pt;
TransformToStart(pose_curr2last_, q_pt, &query_pt);
std::vector<int> indices; std::vector<int> indices;
std::vector<float> dists; std::vector<float> dists;
kdtree_surf_pts_->nearestKSearch(query_pt, 1, indices, dists); kdtree_surf_pts_->nearestKSearch(query_pt, 1, indices, dists);
@ -134,7 +138,6 @@ void Odometry::MatchSurfPoints(const TPointCloud& src, const TPointCloud& tgt,
int scan_id = GetScanId(pt); int scan_id = GetScanId(pt);
if (scan_id > query_pt_scan_id + kNearbyScanNum) break; if (scan_id > query_pt_scan_id + kNearbyScanNum) break;
double dist_squre = DistanceSqure(query_pt, pt); double dist_squre = DistanceSqure(query_pt, pt);
// AWARN_IF(scan_id != 0) << "SA" << query_pt_scan_id << ", " << scan_id;
if (scan_id == query_pt_scan_id && dist_squre < min_dist_pt2_squre) { if (scan_id == query_pt_scan_id && dist_squre < min_dist_pt2_squre) {
pt2_idx = i; pt2_idx = i;
min_dist_pt2_squre = dist_squre; min_dist_pt2_squre = dist_squre;
@ -150,7 +153,6 @@ void Odometry::MatchSurfPoints(const TPointCloud& src, const TPointCloud& tgt,
int scan_id = GetScanId(pt); int scan_id = GetScanId(pt);
if (scan_id < query_pt_scan_id - kNearbyScanNum) break; if (scan_id < query_pt_scan_id - kNearbyScanNum) break;
double dist_squre = DistanceSqure(query_pt, pt); double dist_squre = DistanceSqure(query_pt, pt);
// AWARN_IF(scan_id != 0) << "SD" << query_pt_scan_id << ", " << scan_id;
if (scan_id == query_pt_scan_id && dist_squre < min_dist_pt2_squre) { if (scan_id == query_pt_scan_id && dist_squre < min_dist_pt2_squre) {
pt2_idx = i; pt2_idx = i;
min_dist_pt2_squre = dist_squre; min_dist_pt2_squre = dist_squre;
@ -163,14 +165,29 @@ void Odometry::MatchSurfPoints(const TPointCloud& src, const TPointCloud& tgt,
} }
if (pt2_idx >= 0 && pt3_idx >= 0) { if (pt2_idx >= 0 && pt3_idx >= 0) {
TPoint pt2 = tgt.points[pt2_idx], pt3 = tgt.points[pt3_idx]; TPoint pt2 = tgt.points[pt2_idx], pt3 = tgt.points[pt3_idx];
pairs->emplace_back(query_pt, pt1, pt2, pt3); pairs->emplace_back(q_pt, pt1, pt2, pt3);
} }
} }
} }
void Odometry::UpdatePre(const FeaturePoints& feature) { void Odometry::UpdatePre(const FeaturePoints& feature) {
surf_pts_pre_ = feature.less_flat_surf_pts; if (surf_pts_pre_ == nullptr) {
corn_pts_pre_ = feature.less_sharp_corner_pts; surf_pts_pre_.reset(new TPointCloud);
}
if (corn_pts_pre_ == nullptr) {
corn_pts_pre_.reset(new TPointCloud);
}
surf_pts_pre_->resize(feature.less_flat_surf_pts->size());
for (size_t i = 0; i < feature.less_flat_surf_pts->size(); ++i) {
TransformToEnd(pose_curr2last_, feature.less_flat_surf_pts->points[i],
&surf_pts_pre_->points[i]);
}
corn_pts_pre_->resize(feature.less_sharp_corner_pts->size());
for (size_t i = 0; i < feature.less_sharp_corner_pts->size(); ++i) {
TransformToEnd(pose_curr2last_, feature.less_sharp_corner_pts->points[i],
&corn_pts_pre_->points[i]);
}
kdtree_surf_pts_->setInputCloud(surf_pts_pre_); kdtree_surf_pts_->setInputCloud(surf_pts_pre_);
kdtree_corn_pts_->setInputCloud(corn_pts_pre_); kdtree_corn_pts_->setInputCloud(corn_pts_pre_);
} }

View File

@ -29,11 +29,11 @@ class Odometry {
Pose3D pose_curr2world_; Pose3D pose_curr2world_;
Pose3D pose_curr2last_; Pose3D pose_curr2last_;
TPointCloudPtr surf_pts_pre_; TPointCloudPtr surf_pts_pre_{nullptr};
TPointCloudPtr corn_pts_pre_; TPointCloudPtr corn_pts_pre_{nullptr};
pcl::KdTreeFLANN<TPoint>::Ptr kdtree_surf_pts_; pcl::KdTreeFLANN<TPoint>::Ptr kdtree_surf_pts_{nullptr};
pcl::KdTreeFLANN<TPoint>::Ptr kdtree_corn_pts_; pcl::KdTreeFLANN<TPoint>::Ptr kdtree_corn_pts_{nullptr};
bool is_initialized = false; bool is_initialized = false;
YAML::Node config_; YAML::Node config_;

View File

@ -0,0 +1,38 @@
#include "Odometry_visualizer.h"
namespace oh_my_loam {
void OdometryVisualizer::Draw() {
auto frame = GetCurrentFrame<OdometryVisFrame>();
{ // add raw point cloud
std::string id = "raw point cloud";
DrawPointCloud(*frame.cloud, WHITE, id, viewer_.get());
rendered_cloud_ids_.push_back(id);
}
{ // add less_flat_surf_pts
std::string id = "less_flat_surf_pts";
DrawPointCloud(*frame.feature_pts.less_flat_surf_pts, CYAN, id,
viewer_.get(), 5);
rendered_cloud_ids_.push_back(id);
}
{ // add flat_surf_pts
std::string id = "flat_surf_pts";
DrawPointCloud(*frame.feature_pts.flat_surf_pts, BLUE, id, viewer_.get(),
7);
rendered_cloud_ids_.push_back(id);
}
{ // add less_sharp_corner_pts
std::string id = "less_sharp_corner_pts";
DrawPointCloud(*frame.feature_pts.less_sharp_corner_pts, PURPLE, id,
viewer_.get(), 5);
rendered_cloud_ids_.push_back(id);
}
{ // add sharp_corner_pts
std::string id = "sharp_corner_pts";
DrawPointCloud(*frame.feature_pts.sharp_corner_pts, RED, id, viewer_.get(),
7);
rendered_cloud_ids_.push_back(id);
}
};
} // namespace oh_my_loam

View File

@ -0,0 +1,22 @@
#pragma once
#include "base_visualizer.h"
#include "extractor/feature_points.h"
namespace oh_my_loam {
struct OdometryVisFrame : public VisFrame {
FeaturePoints feature_pts;
};
class OdometryVisualizer : public Visualizer {
public:
explicit OdometryVisualizer(const std::string &name = "OdometryVisualizer",
size_t max_history_size = 10)
: Visualizer(name, max_history_size) {}
private:
void Draw() override;
};
} // namespace oh_my_loam