机器人控制算法—如何使用C++读取pgm格式的栅格地图并转化为ROS地图格式的data?

1.Introduction

近期正在做全局规划+局部动态规划的项目,目前遇到的问题是,我们如何利用C++处理pgm地图文件。即将地图信息要与像素点结合起来。所以我们需要知道地图读取和处理的底层原理,这样更好地在非ROS平台下移植。

2.Main

如下几条信息需要了解:
(1)data[]是按照那张地图图片的自底向上,自左至右逐个像素点存储的.
(2) 在使用二维地图定位导航时,建好的地图文件中包括 m a p . p g m map.pgm map.pgm m a p . y a m l map.yaml map.yaml.其中.yaml文件如下:

image: map.pgm  #文件名
resolution: 0.050000  #地图分辨率 单位:米/像素
origin: [-49.0286, -107.401, 0.0]   #图像左下角在地图坐标下的坐标
negate: 0    #是否应该颠倒 白:自由/黑:的语义(阈值的解释不受影响)
occupied_thresh: 0.65   #占用概率大于此阈值的像素被认为已完全占用
free_thresh: 0.196   #用率小于此阈值的像素被认为是完全空闲的

需要注意的是origin: [-49.0286, -107.401, 0.0] #图像左下角在地图坐标下的坐标,我们后续利用这条信息,建立像素与世界坐标系之间的关系。
(3)实际上,我们在路径规划实施过程中,是接收到地图像素信息data[],(一维数组),然后将其复原为原来的像素坐标,再进行路径规划处理。
data[]复原成地图图片像素坐标关系为:

 for(int i = 0; i<map_info_width*map_info_height; i++){x = i%map_info_width;  //还原为像素坐标y = i/map_info_width;  //还原为像素坐标if(data[i] != 0){   cout<<"obstacle:"<<endl;//PG.map_generator_.addCollision({x, y}, 3);PG.map_generator_.addCollision({x, y}, 3);}cout<<endl;}   

(4) 由地图坐标->图像像素坐标
基于地图的坐标转换到图像坐标系上
w x w y w_x w_y wxwy表示地图坐标系下的坐标,resolution为分辨率,则:

image_x = (wx - origin_x) / resolution
image_y = (wy - origin_y) / resolution

(5)由图像像素坐标->地图坐标
image_x,image_y表示在图像像素坐标系中的坐标
w x w y w_x w_y wxwy表示地图坐标系下的坐标,resolution为分辨率,则:

wx=image_x*resolution+origin_x
wy=image_y*resolution+origin_y
3.Examples

我们举了一个从地图pgm读取到处理成目标地图数据格式data[] 的例子。

int main(int argc, char **argv)
{PathGenerator PG;//Read pgmcv::Mat m4 = cv::imread("/home/juchunyu/20231013/globalPlanner/AStar-ROS/map/map.pgm",cv::IMREAD_GRAYSCALE);cout << "图像宽为:" << m4.cols << "\t高度为:" << m4.rows << "\t通道数为:" << m4.channels() << endl;/*for (int r = 0; r < m4.rows; ++r) {for (int c = 0; c < m4.cols; ++c) {int data = m4.at<unsigned char>(r, c);}}cout<<"0"<<endl;*/// Round goal coordinatefloat goal_x            = 10;//round(goal_msg->pose.position.x*10)/10;float goal_y            = 10;//round(goal_msg->pose.position.y*10)/10;double origin[3]        = {-9.500000, -10.000000, 0.0};double  occupied_thresh =  0.65;double  free_thresh     =  0.196;int Occupy              = 1;int NoOccupy            = 0;double map_resolution   = 0.05;/*vector<vector<int>> maze = {{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1 },{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1 },{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 },{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 },{ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }};*/vector<vector<int>> maze = {{ 0, 0, 0, 0, 0, 0, 0 },{ 0, 0, 0, 1, 0, 0, 0 },{ 0, 0, 0, 0, 0, 0, 0 },{ 0, 0, 0, 0, 0, 0, 0 },{ 0, 0, 0, 0, 0, 0, 0 },{ 0, 0, 0, 0, 0, 0, 0 }};vector<int> data;for(int i = m4.rows-1;i >= 0;i--){for(int j = 0;j < m4.cols;j++){if(m4.at<unsigned char>(i,j)/255 > free_thresh){data.push_back(Occupy);} else {data.push_back(NoOccupy);}}}/*int num = 0;for(int i =maze.size()-1;i >= 0;i--){for(int j = 0;j < maze[0].size();j++){num++;if(maze[i][j] > free_thresh){data.push_back(Occupy);} else {data.push_back(NoOccupy);}}}cout<<"cishu"<<num<<endl;*/for(int i = 0;i<data.size();i++){cout<<data[i]<<" ";}cout<<endl;//cout<<"maze.size()="<<maze.size()<<endl;//cout<<"maze[0].size()"<<maze[0].size()<<endl;//cv::imshow("res_mat", m4);//cv::waitKey(0);// map_exsit_ = false;//map_info_ = map_msg->info;//int  map_info_width  = maze[0].size();//m4.cols;//int  map_info_height = maze.size();//m4.rows;int  map_info_width  = m4.cols;int  map_info_height = m4.rows;// Generate Map, OptionsPG.map_generator_.setWorldSize({map_info_width, map_info_height}); //{x, y}PG.map_generator_.setHeuristic(AStar::Heuristic::manhattan);PG.map_generator_.setDiagonalMovement(true);cout<<"-3"<<endl;// Add Wallint x, y;for(int i = 0; i<map_info_width*map_info_height; i++){x = i%map_info_width;y = i/map_info_width;cout<<"i"<<i<<endl;cout<<"sum:"<<map_info_width*map_info_height<<endl;double v = double(i/(map_info_width*map_info_height));cout<<v<<"%"<<endl;if(data[i] != 0){   cout<<"obstacle:"<<endl;//PG.map_generator_.addCollision({x, y}, 3);PG.map_generator_.addCollision({x, y}, 3);cout<<"("<<x<<","<<y<<")"<<" ";}cout<<endl;}   cout<<"-2"<<endl; // Remmaping coordinateAStar::Vec2i target;target.x = 162;//6;//2;//161;//(goal_x - origin[0]) / map_resolution;target.y = 105;//3;//9;//112; //(goal_y - origin[1]) / map_resolution;AStar::Vec2i source;source.x = 94;//0;//94;//(0 - origin[0]) /  map_resolution;source.y = 99;//99;//(0 - origin[1]) / map_resolution;cout<<"1"<<endl;// Find Pathauto path =  PG.map_generator_.findPath(source, target);cout<<"2"<<endl;//cout<<path->x<<' '<<path->y<<endl;//nav_msgs::Path path_msg;if(path.empty()){cout<<"\033[1;31mFail generate path!\033[0m"<<endl;//ROS_INFO("\033[1;31mFail generate path!\033[0m");}for(auto coordinate=path.end()-1; coordinate>=path.begin(); --coordinate){// geometry_msgs::PoseStamped point_pose;// Remmaping coordinate//point_pose.pose.position.x = (coordinate->x + map_info_.origin.position.x / map_info_.resolution) * map_info_.resolution;//point_pose.pose.position.y = (coordinate->y + map_info_.origin.position.y / map_info_.resolution) * map_info_.resolution;//path_msg.poses.push_back(point_pose);cout<<coordinate->x<<" "<<coordinate->y<<endl;}//path_msg.header.frame_id = "map";// pub_robot_path_.publish(path_msg);//ROS_INFO("\033[1;36mSuccess generate path!\033[0m");// ros::spin();return 0;
}

完整工程参见https://github.com/JackJu-HIT/A-star/tree/master.

4.Reference
  1. ROS-根据map.yaml进行像素坐标和map坐标的转换
  2. ROS中map、costmap数据格式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/129626.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud Alibaba 【四】Openfeign

Openfeign配置与使用 前言介绍openfeign使用openfeign导入依赖启动类正式使用测试结果 前言 在springcloud中消费者项目需要调用提供者项目的接口&#xff0c;一开始用的是RestTemplate中的方法。但是RestTemplate进行远程调用时&#xff0c;直接调用controller层的接口&#…

牛客网 链表中倒数第k个结点

目录 1,解题思路2.代码实现 1,解题思路 遍历一遍链表求出链表的元素个数&#xff0c;再将链表元素个数减去k就得出正数的数&#xff0c;在遍历到正数的地方即可. 2.代码实现 struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) { if(pListHeadNULL)ret…

Ubuntu Linux 23.10安装manimgl

1. 简介&#xff1a;manimgl是使用Python语言开发数学动画的一个库。用来创建数学动画。版本有很多&#xff0c;今天介绍manimgl&#xff0c;他要依赖OpenGL库。 2. 打开Shell命令行&#xff0c;连接上互联网。先安装opengl。 Shell>>> sudo apt install l…

GoLong的学习之路(十八)基础工具之GORM(操作数据库)(删除delete)

上回书说到&#xff0c;更新也叫修改&#xff0c;update&#xff0c;此章说明删除操作。 文章目录 删除删除一条记录的时候根据条件删除钩子函数批量删除阻止全局删除返回删除行的数据 软删除查找被软删除的记录永久删除删除标志注意 删除 删除一条记录的时候 删除一条记录时…

What exactly are the practices involved in DevOps?

目录 1. Continuous Integration (CI) 2. Continuous Deployment (CD) 3. Infrastructure as Code (IAC) 4. Configuration Management 5. Monitoring and Logging 6. Automated Testing 7. Collaboration and Communication 8. Microservices Architecture 9. Conta…

图论08-图的建模-状态的表达与理解 - 倒水问题为例

文章目录 状态的表达例题1题解1 终止条件&#xff1a;有一个数位为42 状态的改变&#xff1a;a表示十位数&#xff0c;b表示个位数3 其他设置 例题2 力扣773 滑动谜题JavaC 状态的表达 例题1 从初始的(x&#xff0c;y)状态&#xff0c;到最后变成&#xff08;4&#xff0c;&am…

服务器有哪些用途呢

服务器可以用于许多行业&#xff0c;包括但不限于以下几个领域&#xff1a; 企业&#xff1a;服务器可以用于企业的数据存储、应用程序和网站托管、电子邮件和协作工具、数据库管理等。 金融&#xff1a;服务器可用于高频交易、投资组合优化、风险管理和分析。 零售和电子商务…

JAVA提取嵌套夹带文件之Apache Tika

目录结构 前言tika简介Tika支持的文件格式MAVEN依赖JAVA程序JAVA测试程序测试文件测试结果部分文件提取失败参考连接 前言 Apache Tika提取文件整理如下&#xff0c;如有特定的文件需要提取可以先参照【部分文件提取失败】章节对照&#xff0c;以免浪费您的宝贵时间&#xff0c…

3.Docker的客户端指令学习与实战

1.Docker的命令 1.1 启动Docker&#xff08;systemctl start docker&#xff09; systemctl start docker1.2 查看docker的版本信息&#xff08;docker version&#xff09; docker version1.3 显示docker系统范围的信息&#xff08;docker info&#xff09; docker info1.4…

夯实c语言基础(2)

夯实c语言基础&#xff08;2&#xff09; 题干执行以下程序段执行后的输出结果分别为&#xff08;  b &#xff09;、&#xff08; a  &#xff09;、&#xff08;  a &#xff09;、&#xff08;  d &#xff09;。 程序段一&#xff1a; int i1,a0; while(i<…

亚马逊云科技:让生成式AI真正走向普惠

伴随着ChatGPT的横空出世&#xff0c;生成式AI&#xff08;Artificial Intelligence Generated Content&#xff0c;也称AIGC&#xff09;大潮也以锐不可当之势席卷全球。从各行各业的商业领袖&#xff0c;到千千万万的程序员和开发者&#xff0c;都在思考如何借助生成式AI技术…

(论文阅读15/100)You Only Look Once: Unified, Real-Time Object Detection

文献阅读笔记 简介 题目 You Only Look Once: Unified, Real-Time Object Detection 作者 Joseph Redmon, Santosh Divvala, Ross Girshick, Ali Farhadi 原文链接 https://arxiv.org/pdf/1506.02640.pdf 《You Only Look Once: Unified, Real-Time Object Detection》…

多级菜单 树结构 排序 前端 后端 java

目录 省流&#xff1a; 正文&#xff1a; v1.0版 前端传的值&#xff1a; 后端代码&#xff1a; v2.0版 v3.0版 省流&#xff1a; 前端提交过来整个树即可。 给整个树进行sort。代码如下&#xff1a; public static void sort(List<Node> tree){int i 0;for…

Git https方式拉的代码IDEA推送代码报错

报错信息 fatal: could not read Username for ‘https://codehub-cn-south-1.devcloud.huaweicloud.com’: No such file or directory 18:18:39.885: [recovery_pattern] git -c credential.helper -c core.quotepathfalse -c log.showSignaturefalse push --progress --porc…

1000 合并石头的最低成本(区间DP)(前缀和)(灵神笔记)

题目 合并石头的最低成本 有 n 堆石头排成一排&#xff0c;第 i 堆中有 stones[i] 块石头。 每次 移动 需要将 连续的 k 堆石头合并为一堆&#xff0c;而这次移动的成本为这 k 堆中石头的总数。 返回把所有石头合并成一堆的最低成本。如果无法合并成一堆&#xff0c;返回 -1…

Portraiture4.0介绍与插件安装包下载

相信有很多需要经常进行图像处理的小伙伴的电脑上都有一款PS软件吧&#xff0c;PS的功能非常强大&#xff0c;各种细节处理都非常细致&#xff0c;但还是需要一些插件来帮我们快速处理图片&#xff0c;能够省去很多时间和精力。今天给大家介绍一款PS磨皮插件&#xff0c;能够快…

路由器基础(十一):ACL 配置

访问控制列表 (Access Control List,ACL) 是目前使用最多的访问控制实现技术。访问控制列表是路由器接口的指令列表&#xff0c;用来控制端口进出的数据包。ACL适用于所有的被路由协议&#xff0c;如IP、IPX、AppleTalk 等。访问控制列表可以分为基本访问控制列表和高级访问控制…

【IDEA】在工具栏设置快速创建包和类的图表

页面效果&#xff1a; 操作步骤&#xff1a; 设置 --> 外观与行为 --> 菜单与工具栏 --> 点击 主工具栏 --> 点击 ---- --> 点击 号 --> 添加操作 主菜单 --> 文件 --> 文件打开操作 --> 打开项目操作 --> 新建 --> 往下找 找到 clas…

【漏洞库】XXL-JOB 默认accessToken权限绕过导致RCE

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- EXP 编写 漏洞挖掘修复建议 漏洞描述 XXL-JOB 是一款开源的分布式任务调度平台&#xff0c;用于实现大规模任务的调度和执行。 XXL-JOB 默认配置下&#xff0c;用于调度通讯的 accessToken 不是随机生成的&#xff0c;而…