【chatgpt代码系列】激光与地图匹配置信度算法-信息熵
迪丽瓦拉
2025-06-01 07:38:58
0

Q1:激光定位算法中,激光雷达数据和栅格地图的匹配置信度如何评价,有什么数学公式可以推导

激光定位算法中,激光雷达数据和栅格地图的匹配可以使用配准分数进行评价。

一种常见的配准分数是信息熵分数,其计算公式为:

H=−∑iNpilog⁡2piH=-\sum_{i}^{N}p_i\log_2p_iH=−∑iN​pi​log2​pi​

其中,NNN为地图栅格数量,pip_ipi​为对于某一栅格iii而言,激光雷达数据和地图的差异程度,可表示为激光雷达数据在该栅格中所覆盖的面积与该栅格面积之比。HHH越接近0表示两者匹配度越高,反之则表示匹配度越低。

此外,还可以使用其他的配准分数进行评价,如互信息、相关系数等。同时,不同类型的匹配任务需要选择不同的评价指标。

Q2:举个c++程序例子吧,用信息熵分数的方案

以下是一个使用信息熵分数评估激光雷达数据与栅格地图匹配度的 C++ 代码示例:

#include 
#include //定义栅格地图、激光雷达的数据结构
struct GridMap{int rows; //栅格行数int cols; //栅格列数double res; //栅格分辨率int *data; //栅格数组,0表示空闲,1表示障碍
};struct LaserScan{int num_points; //激光点云数量double *x; //激光点的X坐标double *y; //激光点的Y坐标double *z; //激光点的Z坐标
};//计算信息熵分数
double calcEntropyScore(GridMap map, LaserScan scan){int num_cells = map.rows * map.cols; //栅格总数double eps = 1e-8; //防止出现log(0)的情况,加入极小值epsdouble score = 0;for(int i=0; idouble cell_area = map.res * map.res; //每个栅格的面积double p_map = (double)map.data[i] / cell_area; //栅格中障碍物占用面积double p_scan = scan.num_points > 0 ? scanInCell(map, scan, i) / cell_area : 0; //激光雷达点云中覆盖此栅格的面积double p_joint = p_map * p_scan + eps; //联合概率score -= p_joint * log2(p_joint); //信息熵计算}return score;
}//计算激光雷达点云中覆盖某个栅格的面积
double scanInCell(GridMap map, LaserScan scan, int cell_index){double cell_x = (cell_index % map.cols + 0.5) * map.res; //栅格中心点的X坐标double cell_y = (cell_index / map.cols + 0.5) * map.res; //栅格中心点的Y坐标double r2_scan_cell = map.res * map.res * 0.25; //激光点到栅格中心点距离平方的阈值double count = 0;for(int i=0; idouble dx = scan.x[i] - cell_x;double dy = scan.y[i] - cell_y;if(dx*dx + dy*dy <= r2_scan_cell){ // 激光点覆盖该栅格count += 1;}}return count;
}

这段代码中,calcEntropyScore(GridMap map, LaserScan scan) 函数计算信息熵分数,scanInCell(GridMap map, LaserScan scan, int cell_index) 函数用于计算激光雷达点云中覆盖某个栅格的面积。其中,GridMap 结构体存储了栅格地图的信息,LaserScan 结构体存储了激光雷达的点云数据。

Q3:代码似乎不完整,需要有激光雷达话题订阅回调和地图订阅回调,以及主函数

是的,这段代码只是展示了如何计算配准分数,缺少与 ROS 系统交互的部分。下面是一个完整的 C++ 程序示例,该程序完成以下任务:

  • 订阅 /scan 话题,获取激光雷达扫描数据;
  • 订阅 /map 话题,获取栅格地图信息;
  • 实时计算并输出激光雷达数据和栅格地图的匹配度。

因为这个例子需要接入ROS,所以您需要先在ROS环境下运行此代码。

#include 
#include 
#include 
#include //定义栅格地图、激光雷达的数据结构
struct GridMap {int rows; //栅格行数int cols; //栅格列数double res; //栅格分辨率int *data; //栅格数组,0表示空闲,1表示障碍
};struct LaserScan {int num_points; //激光点云数量double *x; //激光点的X坐标double *y; //激光点的Y坐标double *z; //激光点的Z坐标
};//计算信息熵分数
double calcEntropyScore(GridMap map, LaserScan scan) {int num_cells = map.rows * map.cols; //栅格总数double eps = 1e-8; //防止出现log(0)的情况,加入极小值epsdouble score = 0;for(int i = 0; i < num_cells; ++i) {double cell_area = map.res * map.res; //每个栅格的面积double p_map = (double)map.data[i] / cell_area; //栅格中障碍物占用面积double p_scan = scan.num_points > 0 ? scanInCell(map, scan, i) / cell_area : 0; //激光雷达点云中覆盖此栅格的面积double p_joint = p_map * p_scan + eps; //联合概率score -= p_joint * log2(p_joint); //信息熵计算}return score;
}//计算激光雷达点云中覆盖某个栅格的面积
double scanInCell(GridMap map, LaserScan scan, int cell_index) {double cell_x = (cell_index % map.cols + 0.5) * map.res; //栅格中心点的X坐标double cell_y = (cell_index / map.cols + 0.5) * map.res; //栅格中心点的Y坐标double r2_scan_cell = map.res * map.res * 0.25; //激光点到栅格中心点距离平方的阈值double count = 0;for(int i = 0; i < scan.num_points; ++i) {double dx = scan.x[i] - cell_x;double dy = scan.y[i] - cell_y;if(dx * dx + dy * dy <= r2_scan_cell) { // 激光点覆盖该栅格count += 1;}}return count;
}//激光雷达数据回调函数
void laserScanCallback(const sensor_msgs::LaserScan::ConstPtr& msg, LaserScan& scan) {int num_points = (msg->angle_max - msg->angle_min) / msg->angle_increment; //激光点云数量scan.num_points = num_points;scan.x = new double[num_points];scan.y = new double[num_points];scan.z = new double[num_points];double angle = msg->angle_min;for(int i = 0; i < num_points; ++i) {double dist = msg->ranges[i];if(std::isnan(dist)) { //无效点scan.x[i] = NAN;scan.y[i] = NAN;scan.z[i] = NAN;} else {scan.x[i] = dist * cos(angle);scan.y[i] = dist * sin(angle);scan.z[i] = 0;}angle += msg->angle_increment;}
}//地图数据回调函数
void mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg, GridMap& map) {map.rows = msg->info.height;map.cols = msg->info.width;map.res = msg->info.resolution;map.data = new int[map.rows * map.cols];for(int i = 0; i < map.rows * map.cols; ++i) {if(msg->data[i] > 50) { //障碍物map.data[i] = 1;} else if(msg->data[i] >= 0) { //空闲map.data[i] = 0;} else { //未知map.data[i] = -1;}}
}int main(int argc, char** argv) {ros::init(argc, argv, "calc_entropy_score");ros::NodeHandle nh;GridMap map;LaserScan scan;//激光雷达数据订阅ros::Subscriber laser_sub = nh.subscribe("/scan", 1, boost::bind(&laserScanCallback, _1, boost::ref(scan)));//栅格地图订阅ros::Subscriber map_sub = nh.subscribe("/map", 1, boost::bind(&mapCallback, _1, boost::ref(map)));//循环计算配准分数并输出while(ros::ok()) {ros::spinOnce();double score = calcEntropyScore(map, scan);ROS_INFO("Matching score = %.3f", score);}delete [] map.data;delete [] scan.x;delete [] scan.y;delete [] scan.z;return 0;
}

这里我们采用了 ROS 的 Callback 机制,对激光雷达数据和栅格地图进行订阅。当数据到达时,分别调用 laserScanCallbackmapCallback 函数,将数据存储到 LaserScanGridMap 结构体中,然后在主循环中计算配准分数并输出。当程序退出时,释放动态分配的内存。

Q4:请给我个cmake文件,用于编译你刚刚给我的c++程序,去掉多余的注释,使用install方法

好的,这里给您提供一个去掉多余注释的 CMakeLists.txt 文件,用于编译刚刚提供的 C++ 程序,并使用 install 方法安装程序:

cmake_minimum_required(VERSION 3.5)
project(your_project_name)# 设置编译标准为11
set(CMAKE_CXX_STANDARD 11)# 添加依赖库
find_package(Boost REQUIRED COMPONENTS system thread)
find_package(PCL REQUIRED)# 头文件路径
include_directories(include ${Boost_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS})# 源文件列表
set(SOURCES src/main.cpp )# 生成可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})# 链接依赖库
target_link_libraries(${PROJECT_NAME}${Boost_LIBRARIES}${PCL_COMMON_LIBRARIES}${PCL_IO_LIBRARIES}${PCL_LIBRARIES}
)# 将可执行文件安装到 /usr/bin 目录下
install(TARGETS ${PROJECT_NAME}RUNTIME DESTINATION bin
)# 安装头文件和库文件
install(DIRECTORY include/ DESTINATION include)
install(TARGETS ${Boost_LIBRARIES} ${PCL_LIBRARIES} DESTINATION lib)

需要注意的是,这个 CMakeLists.txt 使用了 find_package 命令找到并链接了 Boost 和 PCL 两个依赖库。其中,使用了 COMPONENTS 参数指定了 Boost 依赖库需要的组件,而 PCL 依赖库使用了其内置的 FIND_COMPONENTS 功能。

同时,我们添加了头文件路径和源文件列表,并在 target_link_libraries 中链接了所有的依赖库。最后,使用 install 方法将可执行文件、头文件和库文件都安装到了合适的位置,以方便其他人使用。请根据您自己的项目情况进行修改。

相关内容