|
@@ -59,11 +59,21 @@ void Laser_Odom::init() {
|
|
|
voxelgrid->setLeafSize(downsample_resolution, downsample_resolution, downsample_resolution);
|
|
|
downsample_filter.reset(voxelgrid);
|
|
|
|
|
|
- registration->setNumThreads(5); // 把 registration = hdl_graph_slam::select_registration_method(nh); 核心部分弄出来
|
|
|
- registration->setTransformationEpsilon(1e-3);
|
|
|
- registration->setMaximumIterations(6400);
|
|
|
- registration->setMaxCorrespondenceDistance(0.2);
|
|
|
- registration->setCorrespondenceRandomness(5);
|
|
|
+ // 二维数据里程计求解参数
|
|
|
+ // 把 registration = hdl_graph_slam::select_registration_method(nh); 核心部分弄出来
|
|
|
+ registration->setNumThreads(5); // 求解的核心数
|
|
|
+ registration->setTransformationEpsilon(1e-5); // 配准算法的收敛容差,即当连续迭代的位姿变换小于该值时,算法将认为已经收敛并停止迭代
|
|
|
+ registration->setMaximumIterations(6400); // 设置配准算法的最大迭代次数
|
|
|
+ registration->setMaxCorrespondenceDistance(0.2); // 设置配准过程中两点之间的最大对应距离。超出此距离的对应关系将被忽略
|
|
|
+ registration->setCorrespondenceRandomness(5); // 指定计算对应点时使用的随机性,通常用于减少计算量或增强配准的鲁棒性。较小的值会限制对应点的选择范围,较大的值可以增加算法对较少数据的适应性
|
|
|
+
|
|
|
+ // 三维数据里程计求解参数
|
|
|
+ // registration->setNumThreads(5); // 把 registration = hdl_graph_slam::select_registration_method(nh); 核心部分弄出来
|
|
|
+ // registration->setTransformationEpsilon(0.01);
|
|
|
+ // registration->setMaximumIterations(64);
|
|
|
+ // registration->setMaxCorrespondenceDistance(2.5);
|
|
|
+ // registration->setCorrespondenceRandomness(20);
|
|
|
+
|
|
|
// registration->setRANSACOutlierRejectionThreshold();
|
|
|
trans_cloud.reset(new pcl::PointCloud<PointT>());
|
|
|
|
|
@@ -74,15 +84,20 @@ void Laser_Odom::init() {
|
|
|
keyframe_points_pub = nh.advertise<sensor_msgs::PointCloud2>("/keyframe_points", 32); // 对齐后的点云
|
|
|
map_points_pub = nh.advertise<sensor_msgs::PointCloud2>("/map_points", 1, true);
|
|
|
|
|
|
- // ros::Subscriber sub = nh.subscribe<sensor_msgs::LaserScan>("/scan", 1, &Laser_Odom::cloud_callback, this);
|
|
|
- ros::Subscriber sub = nh.subscribe<sensor_msgs::PointCloud2>("/filtered_points", 1, &Laser_Odom::cloud_callback, this);
|
|
|
-
|
|
|
+ ros::Subscriber sub = nh.subscribe<sensor_msgs::LaserScan>("/scan", 1, &Laser_Odom::cloud_callback, this);
|
|
|
+ ros::Subscriber sub2 = nh.subscribe<sensor_msgs::PointCloud2>("/velodyne_points", 1, &Laser_Odom::cloud_callback2, this);
|
|
|
+ // ros::Subscriber opt_odom_sub = nh.subscribe<nav_msgs::Odometry>("/opt_odom", 1, &Laser_Odom::opt_odom_callback, this);
|
|
|
+
|
|
|
+ ros::Subscriber opt_odom_sub = nh.subscribe<geometry_msgs::PoseStamped>("opt_odom", 10, &Laser_Odom::opt_odom_callback, this);
|
|
|
+
|
|
|
std::thread proc_thread(&Laser_Odom::procThread, this);
|
|
|
proc_thread.detach();
|
|
|
|
|
|
ros::spin();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
void Laser_Odom::procThread()
|
|
|
{
|
|
|
while(ros::ok())
|
|
@@ -103,35 +118,6 @@ void Laser_Odom::procThread()
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- void cloud_callback(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) { // cloud_msg:滤波后的点云数据
|
|
|
- if(!ros::ok()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>()); // 创建一个 pcl::PointCloud<PointT> 类型的指针
|
|
|
- pcl::fromROSMsg(*cloud_msg, *cloud); // 将ROS消息格式的点云(*cloud_msg)转换为PCL(Point Cloud Library)格式的点云(*cloud)
|
|
|
-
|
|
|
- Eigen::Matrix4f pose = matching(cloud_msg->header.stamp, cloud); // 点云匹配函数,返回
|
|
|
- publish_odometry(cloud_msg->header.stamp, cloud_msg->header.frame_id, pose); // 发布里程计数据
|
|
|
-
|
|
|
- // In offline estimation, point clouds until the published time will be supplied
|
|
|
- std_msgs::HeaderPtr read_until(new std_msgs::Header());
|
|
|
- read_until->frame_id = points_topic;
|
|
|
- read_until->stamp = cloud_msg->header.stamp + ros::Duration(1, 0);
|
|
|
- read_until_pub.publish(read_until);
|
|
|
-
|
|
|
- read_until->frame_id = "/filtered_points";
|
|
|
- read_until_pub.publish(read_until);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
Laser_Odom* Laser_Odom::laser_odom = nullptr; // 在 .cpp 文件中定义静态成员变量
|
|
|
// 这种实现方法不算单例实现,如果有多个laser_odom的实例,this指针都会被覆盖,指向最后创建的那个实例
|
|
|
// 这里就不改了吧,后端不能这么写,因为后端要多线程
|
|
@@ -151,24 +137,51 @@ void Laser_Odom::cloud_callback(const sensor_msgs::LaserScan::ConstPtr& laser_sc
|
|
|
MatchPCL mpc;
|
|
|
mpc.cloud = cloud;
|
|
|
mpc.time = laser_scan_msg->header.stamp;
|
|
|
-
|
|
|
-
|
|
|
clouds_.push(mpc);
|
|
|
-
|
|
|
return;
|
|
|
- // // 点云匹配
|
|
|
- // Eigen::Matrix4f pose = laser_odom->matching(laser_scan_msg->header.stamp, laser_odom->cloud); // 点云匹配函数,返回
|
|
|
-
|
|
|
- // // 发布激光里程计数据
|
|
|
- // laser_odom->publish_odometry(laser_scan_msg->header.stamp, laser_scan_msg->header.frame_id, pose);
|
|
|
+}
|
|
|
+void Laser_Odom::cloud_callback2(const sensor_msgs::PointCloud2::ConstPtr& laser_scan_msg) {
|
|
|
+ if(!ros::ok()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 消息类型转换
|
|
|
+ // LOG(INFO) << "cloud callback 2 ";
|
|
|
+ pcl::PointCloud<PointT> cloud;
|
|
|
+ // laser_odom->projector.projectLaser(*laser_scan_msg, laser_odom->cloud2);
|
|
|
+ pcl::fromROSMsg(*laser_scan_msg, cloud);
|
|
|
|
|
|
- // // 发布对齐后的点云
|
|
|
- // pcl::PointCloud<PointT>::Ptr aligned(new pcl::PointCloud<PointT>());
|
|
|
- // pcl::transformPointCloud(*laser_odom->cloud, *aligned, laser_odom->odom);
|
|
|
- // aligned->header.frame_id = "odom";
|
|
|
- // laser_odom->aligned_points_pub.publish(*aligned);
|
|
|
+ MatchPCL mpc;
|
|
|
+ mpc.cloud = cloud;
|
|
|
+ mpc.time = laser_scan_msg->header.stamp;
|
|
|
+ clouds_.push(mpc);
|
|
|
+ return;
|
|
|
+}
|
|
|
|
|
|
- // // 发布点云地图
|
|
|
+Eigen::Matrix4f matrix;
|
|
|
+void Laser_Odom::opt_odom_callback(const geometry_msgs::PoseStamped::ConstPtr& msg) {
|
|
|
+ // 创建一个 4x4 的 Eigen::Matrix4f 矩阵
|
|
|
+ matrix = Eigen::Matrix4f::Identity();
|
|
|
+
|
|
|
+ // 提取平移部分
|
|
|
+ matrix(0, 3) = msg->pose.position.x;
|
|
|
+ matrix(1, 3) = msg->pose.position.y;
|
|
|
+ matrix(2, 3) = msg->pose.position.z;
|
|
|
+
|
|
|
+ // 提取旋转部分(四元数转换为旋转矩阵)
|
|
|
+ Eigen::Quaternionf quaternion(
|
|
|
+ msg->pose.orientation.w,
|
|
|
+ msg->pose.orientation.x,
|
|
|
+ msg->pose.orientation.y,
|
|
|
+ msg->pose.orientation.z
|
|
|
+ );
|
|
|
+ Eigen::Matrix3f rotation_matrix = quaternion.toRotationMatrix();
|
|
|
+
|
|
|
+ // 将旋转部分赋值给 4x4 矩阵的左上 3x3 子矩阵
|
|
|
+ matrix.block<3, 3>(0, 0) = rotation_matrix;
|
|
|
+
|
|
|
+ // 输出或使用这个转换后的矩阵
|
|
|
+ ROS_INFO_STREAM("Received 4x4 Transformation Matrix:\n" << matrix);
|
|
|
+ // odom = matrix;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -200,6 +213,10 @@ Eigen::Matrix4f Laser_Odom::matching(const ros::Time& stamp, const pcl::PointClo
|
|
|
pcl::PointCloud<PointT>::Ptr aligned(new pcl::PointCloud<PointT>()); // 创建一个新的点云对象aligned,用于存储对齐后的点云数据
|
|
|
registration->align(*aligned, prev_trans * msf_delta.matrix()); // 用 registration 来对齐点云, 如果没有imu或者odom信息, msf_delta 是单位矩阵, 即将观测到的点云数据对齐到关键帧?
|
|
|
|
|
|
+ // LOG(INFO) << "matrix = \n" << matrix;
|
|
|
+ // LOG(INFO) << "prev_trans * msf_delta.matrix() = \n" << prev_trans * msf_delta.matrix();
|
|
|
+ // LOG(INFO) << "keyframe_pose = \n" << keyframe_pose;
|
|
|
+
|
|
|
// publish_scan_matching_status(stamp, cloud->header.frame_id, aligned, msf_source, msf_delta); // 发布扫描匹配的状态信息,包括时间戳、坐标帧ID、对齐后的点云、位姿来源和位姿更新
|
|
|
|
|
|
if(!registration->hasConverged()) { // 检查扫描匹配是否收敛(是否匹配成功?)。如果没有收敛,输出信息并返回上一次的位姿
|
|
@@ -210,6 +227,14 @@ Eigen::Matrix4f Laser_Odom::matching(const ros::Time& stamp, const pcl::PointClo
|
|
|
Eigen::Matrix4f trans = registration->getFinalTransformation(); // 获得当前点云和上一帧点云 关键帧 的仿射变换
|
|
|
odom = keyframe_pose * trans; // 算出来 odom
|
|
|
|
|
|
+ // if(std::abs(matrix(0, 3) - odom(0, 3) > 0.5) || std::abs(matrix(1, 3) - odom(1, 3) > 0.5)){
|
|
|
+ // if(matrix(0, 3) != 0 || matrix(1, 3) !=0){
|
|
|
+ // odom = matrix;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // }
|
|
|
+
|
|
|
+
|
|
|
prev_time = stamp; // 当前帧的时间戳
|
|
|
prev_trans = trans; // 当前帧的仿射变换
|
|
|
|
|
@@ -227,8 +252,10 @@ Eigen::Matrix4f Laser_Odom::matching(const ros::Time& stamp, const pcl::PointClo
|
|
|
// 如果有一个超过阈值,更新关键帧
|
|
|
keyframe = filtered;
|
|
|
registration->setInputTarget(keyframe);
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
keyframe_pose = odom;
|
|
|
+
|
|
|
keyframe_stamp = stamp;
|
|
|
prev_time = stamp;
|
|
|
prev_trans.setIdentity();
|
|
@@ -334,54 +361,5 @@ void Laser_Odom::publish_odometry(const ros::Time& stamp, const std::string& bas
|
|
|
odom.twist.twist.linear.y = 0.0;
|
|
|
odom.twist.twist.angular.z = 0.0;
|
|
|
|
|
|
- // 上面算出来的odom朝向有问题,需要顺时针转90度修正,代码如下:
|
|
|
- // 提取原始朝向
|
|
|
- // tf2::Quaternion original_orientation(odom.pose.pose.orientation.x, odom.pose.pose.orientation.y, odom.pose.pose.orientation.z, odom.pose.pose.orientation.w);
|
|
|
-
|
|
|
- // // 定义顺时针旋转90度的四元数
|
|
|
- // tf2::Quaternion rotation_90(0, 0, -0.7071, 0.7071);
|
|
|
-
|
|
|
- // // 计算旋转后的朝向
|
|
|
- // tf2::Quaternion new_orientation = rotation_90 * original_orientation;
|
|
|
- // new_orientation.normalize(); // 确保结果为单位四元数
|
|
|
-
|
|
|
- // // 将旋转后的朝向赋值回原变量
|
|
|
- // odom.pose.pose.orientation.x = new_orientation.x();
|
|
|
- // odom.pose.pose.orientation.y = new_orientation.y();
|
|
|
- // odom.pose.pose.orientation.z = new_orientation.z();
|
|
|
- // odom.pose.pose.orientation.w = new_orientation.w();
|
|
|
-
|
|
|
odom_pub.publish(odom);
|
|
|
}
|
|
|
-
|
|
|
-// /**
|
|
|
-// * @brief publish odometry
|
|
|
-// * @param stamp timestamp
|
|
|
-// * @param pose odometry pose to be published
|
|
|
-// */
|
|
|
-// void Laser_Odom::publish_keyfrrame(const ros::Time& stamp, const std::string& base_frame_id, const Eigen::Matrix4f& pose) { // 发布里程计数据
|
|
|
-// // publish transform stamped for IMU integration
|
|
|
-// // geometry_msgs::TransformStamped odom_trans = matrix2transform(stamp, pose, odom_frame_id, base_frame_id);
|
|
|
-// geometry_msgs::TransformStamped odom_trans = matrix2transform(stamp, pose, "odom", base_frame_id);
|
|
|
-// // trans_pub.publish(odom_trans);
|
|
|
-
|
|
|
-// // // broadcast the transform over tf
|
|
|
-// // odom_broadcaster.sendTransform(odom_trans);
|
|
|
-
|
|
|
-// // publish the transform
|
|
|
-// nav_msgs::Odometry odom;
|
|
|
-// odom.header.stamp = stamp;
|
|
|
-// odom.header.frame_id = "odom";
|
|
|
-
|
|
|
-// odom.pose.pose.position.x = pose(0, 3);
|
|
|
-// odom.pose.pose.position.y = pose(1, 3);
|
|
|
-// odom.pose.pose.position.z = pose(2, 3);
|
|
|
-// odom.pose.pose.orientation = odom_trans.transform.rotation;
|
|
|
-
|
|
|
-// odom.child_frame_id = base_frame_id;
|
|
|
-// odom.twist.twist.linear.x = 0.0;
|
|
|
-// odom.twist.twist.linear.y = 0.0;
|
|
|
-// odom.twist.twist.angular.z = 0.0;
|
|
|
-
|
|
|
-// odom_pub.publish(odom);
|
|
|
-// }
|