目录
一、尝试通过处理无人机图像来提取地形图
二、专利-DWG文件转成PGM文件
三、实际场景与机器人(ROS)地图的对应关系
2. 地图坐标系与Python canvas鼠标点击位置之间的对应关系:
3. Python Canvas与机器人内ROS之间的数据传输(http)
4. Python上显示机器人位置以及正确发送点击坐标
四、ROS 机器人如何使用GPS定位导航
一、尝试通过处理无人机图像来提取地形图
1、TIF文件的波段? 直接问相机的商家镜头是什么的
2、不知道发来的tif文件是不是3个波段 ? 不是,可见光的不分波段
后来知道了,买的大疆精灵4 rtk ,无人机的相机可见光的,是不分波段的,so
3、刚开始的思路是:识别农田的轮廓,用的方法是论文:基于无人机倾斜摄影数据的地类边界划分
的基于gpb与SLIC相结合的地籍边界提取方法
4、代码源:https://github.com/willingup/gPb-Contour-Detection
5、期间用到的代码
### Import script in QGIS Python console ###
"""
# add directory with script to Python search path
import sys
sys.path.append(r"E:\map\gPb-Contour-Detection-master\gPb-Contour-Detection-master\2_Scripts")
# import own module
import A1_resizing
# rerun module after changing the source code
reload(A1_resizing)
"""
### Predefine variables ###
# Data directory of input UVA imagery (RGB)
data_dir = r"E:/map"
# Initial width/height of output raster in pixels
pix_min = 1000
# Final width/height of output raster in pixels
pix_max = 1000
# Increment steps in width/height of output raster in pixels
pix_step = 1000
# Resampling method, available methods in gdalogr:translate {nearest(default),bilinear,cubic,cubicspline,lanczos,
# average,mode}
res_method = "nearest"
# (Re)initialize counter for number of tiles, number needs to be multiplied by itself for final number of tiles (e.g.
# tiles = 2 results in 4 final tiles, tiles = 3 results in 9 final tiles)
tiles = 2
### Import required QGIS modules ###
# noinspection PyUnresolvedReferences
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.utils import *
from qgis.analysis import *
import processing
import os
### Main processing part ###
# Change into data directory
os.chdir(data_dir)
files = os.listdir(os.curdir)
# Loop over all .tif files in input directory
for f in files:
# Check if file extension is .tif
if os.path.splitext(f)[1] == '.tif':
# Load UAV image as raster layer
fileInfo = QFileInfo(f)
baseName = fileInfo.baseName()
filePath = str(os.path.abspath(f))
rlayer = QgsRasterLayer(filePath, baseName)
# Check validity of raster layer and print user message
print "%s is loaded as a valid input raster.\n" % f
if not rlayer.isValid():
print "%s failed to load!\n" % f
# Read, save and print raster layer extent
extent = rlayer.extent()
xmin = extent.xMinimum()
xmax = extent.xMaximum()
ymin = extent.yMinimum()
ymax = extent.yMaximum()
x_range = xmax - xmin
y_range = ymax - ymin
print "The extent (xmin, xmax, ymin, ymax) of %s is: %f, %f, %f, %f\n" % (f, xmin, xmax, ymin, ymax)
print "The range of %s is %i in x-direction and %i in y-direction.\n" % (f, x_range, y_range)
# Check if directory to store output files exists and create if not
directory = "Resizing_" + str(os.path.splitext(f)[0])
if os.path.exists(directory):
print "The directory %s has already been created.\n" % directory
else:
print "The directory %s has been created.\n" % directory
os.mkdir(directory)
# Change into (newly) created directory
os.chdir(directory)
# (Re)initialize minimum width/height of output raster in pixels
j = pix_min
### Resample rasters of width/height from pix_min to pix_max at increment steps of pix_step ###
while j <= pix_max:
print "### Resampling input of %i pixels ###\n" % j
# Define name of resampled raster
resampled = str(os.path.splitext(f)[0] + "_" + str(j) + "pixels_" + res_method + ".tif")
# Run resampling of raster
processing.runalg('gdalogr:translate', {"INPUT": rlayer, "OUTSIZE": j, "OUTSIZE_PERC": False, "EXPAND": 0,
"EXTRA": "-r {%s}" % res_method,
"PROJWIN": "%f,%f,%f,%f" % (xmin, xmax, ymin, ymax),
"OUTPUT": resampled})
print "--> %s has been resampled to a raster of %i pixels in width/height and saved as %s in %s\n" % (
f, j, resampled, directory)
### Create tiles of 1000 pixels in width/height from downsampled rasters ###
print "### Tiling input of %i pixels ###\n" % j
# Description:
# The image is tiled as follows:
# First, the bottom left tile is created (first loop)
# Then, all tiles in that bottom row (all in x direction) are created (second loop)
# Then, the tiling restarts at the left side but moves up one tiling window (+1 in y direction) (first loop)
# Then, all tiles in that row (all in x direction are created (second loop)
# This iteration continues, until all tiles are created
# Load resampled raster layer
fileInfo = QFileInfo(resampled)
baseName = fileInfo.baseName()
filePath = str(os.path.abspath(resampled))
reslayer = QgsRasterLayer(filePath, baseName)
# Check validity of raster layer and print user message
print "%s is loaded as a valid resampled raster.\n" % resampled
if not reslayer.isValid():
print "%s failed to load!\n" % resampled
print "%s will now be tiled in %i raster(s) of 1000 pixels in width/height...\n" % (
resampled, tiles * tiles)
# Create tiles
for k in range(0, tiles):
# Set extent for tiling
t_xmin = xmin
t_xmax = xmin + (x_range / tiles)
t_ymin = ymin + k * (y_range / tiles)
t_ymax = ymin + (k + 1) * (y_range / tiles)
# Define name of tiled raster
tiled = str(
os.path.splitext(f)[0] + "_" + str(j) + "pixels_" + res_method + "_tiling_x0y" + str(k) + ".tif")
# Uncomment the next line if the raster should only be tiled without being resampled
# reslayer = rlayer
# Run tiling of raster and save TIF and TFW File
processing.runalg('gdalogr:cliprasterbyextent',
{"INPUT": reslayer, "PROJWIN": "%f,%f,%f,%f" % (t_xmin, t_xmax, t_ymin, t_ymax),
"TFW": True, "OUTPUT": tiled})
print "--> %s has been tiled and saved as %s in %s\n" % (resampled, tiled, directory)
# Create all tiles of specific row in x direction
for l in range(0, tiles - 1):
t_xmin = t_xmax
t_xmax = t_xmin + (x_range / tiles)
# Define name of tiled raster
tiled = str(os.path.splitext(f)[0] + "_" + str(j) + "pixels_" + res_method + "_tiling_x" + str(
l + 1) + "y" + str(k) + ".tif")
# Run tiling of raster save TIF and TFW File
processing.runalg('gdalogr:cliprasterbyextent',
{"INPUT": reslayer, "PROJWIN": "%f,%f,%f,%f" % (t_xmin, t_xmax, t_ymin, t_ymax),
"TFW": True, "OUTPUT": tiled})
print "--> %s has been tiled and saved as %s in %s\n" % (resampled, tiled, directory)
# Increment width/height of output raster in pixels
j += pix_step
# Print final tiling message
print "Tiling of %s finished.\n" % resampled
# Change into initial working directory
os.chdir("..")
# Print final overall message
print "All processing has been finished."
### Import script in QGIS Python console ###
# add directory with script to Python search path
import sys
sys.path.append(r"E:\map\gPb-Contour-Detection-master\gPb-Contour-Detection-master\2_Scripts\1_PyQGIS")
# import own module
import A1_resizing
# rerun module after changing the source code
reload(A1_resizing)
5、安装QGIS文件
https://www.jianshu.com/p/5356a7f83c80
6、裁剪图像 软件ENVI
7、最后发现购买无人机不是大疆精灵4 rtk 相机是不分波段的,所以不能用上面的方法来做,上面的必须要输入RGB3个波段的 TIF文件。
8、灵魂考问 就算把这个田块的轮廓识别出来了,有什么用,可以把里边的变为空白的,将其当做是地图吗?
二、专利-DWG文件转成PGM文件
有一个其它人的建图方法:
随着机器人技术的发展,机器人的应用越来越多,而移动机器人因其能够更灵活地参与到人类生活和工作的空间,受到广泛关注。
机器人在空间中自由地移动,也就是机器人自主导航,其实现需要机器人拥有其能够识别所在空间的地图,以此为参照,再通过准确的定位和可靠的运动系统来保障其能够顺利地由起始位置驶向目标位置。因此拥有一张机器人能够识别的地图,就成了实现机器人自主导航的首要任务。目前这张地图的获取主要通过同时定位与地图构建(SLAM)的方法来实现,地图文件的格式为PGM,配套的还有一个YAML格式的地图描述文件。PGM格式的地图文件是一种点阵式的图像文件,YAML格式的地图描述文件是一种文本文件。
目前同时定位与地图构建的实现,通常需要人工辅助机器人在空间中进行探索。这个过程通常进度较慢,尤其是在大空间中,耗时较多,而且容易受到环境和自身条件的限制和影响,因此应用场景也会受到很多限制。通常使用的传感器有激光测距仪、超声测距仪和摄像机。如果使用激光测距仪来进行同时定位与地图构建,会遇到有些透光材质如玻璃,所得到的地图会与实际环境有很大差别,会忽略掉玻璃等透光材质的障碍物。如果使用超声测距仪来进行同时定位与地图构建,会遇到分辨率的问题,因为超声发出后,以某个波束回传的数据来确定这个波束角范围内障碍物,所以波束角的大小决定了对障碍物的分辨程度,常用的超声测距仪的波束角较大,容易引入较多干扰数据,而且如果遇到吸音材质,也会影响构建地图的效果。如果使用摄像机,会受到环境光照情况的限制,这在实际应用中受环境约束较大。当然,也可以多种传感器同时使用,彼此弥补优缺点,但这对数据融合算法要求较高,也会增加硬件的成本。
实际应用中,即便是硬件条件都满足的情况下,还是会遇到一些问题。有时会因为轮子打滑或者机械故障等,造成建了一半的地图出现错乱,之前的工作全都归零,要重新开始,这就大大加重了工作量,尤其是在大空间并且环境条件复杂的情况下,可能需要多次尝试。还有的情况是地图出现断裂,在某个位置出现局部整块的倾斜,使得整个地图像是被撕裂了。有时工作空间非常大,构建地图的工作无法当天一次性完成,或者因为电量原因无法继续建图,只能获得局部的地图,很难得到一张完整的空间地图。
在硬件和软件条件都满足并且没有故障的情况下,实际应用时,会有人员或车辆等动态的障碍物出现在空间中,这就有可能在同时定位与地图构建所得到的地图中留下障碍物的标记,但这个障碍物是动态的,只是在构建地图的瞬间在那个位置,而地图更多的是要标记已存在的固定的障碍物所在的位置,所以这样的地图的参考价值就降低了,机器人自主导航时可能会为自己设置一个莫须有的只存在于地图上的障碍物。
更新于2020.8.9
下面是一张实际环境中的二维地图,在图片的左下角是地图的原点,(map所在的位置),在原点上已经标明了这个地图的三维坐标轴,其中红色的代表x轴方向,绿色代表y轴方向。
在ROS的地图中,地图同样是以像素标记的,每一个像素代表0.05m,也就是一个像素是5厘米。比如下图上的星星,他的位置在像素层面上的坐标为:x:400像素,y:150像素;那么这个机器人实际的横坐标就应该是:400*0.05=20米,纵坐标是150*0.05=7.5m。再换句话说,如果一个地图的场合宽都是800像素,那么它能够代表的实际长和宽度为800*0.05=40米。
在ROS中,地图的类型是nav_msgs/OccupancyGrid
每个像素代表的长度以及地图的原点都被定义在了消息结构体之中:
2. 地图坐标系与Python canvas鼠标点击位置之间的对应关系:
我们使用python编写客户端的时候(Java于此类似),地图是要被绘制到canvas,也就是虚拟画布上面。虚拟画布是以其左上角为坐标原点(0,0)(如下图)。我们举一个例子,现在有一个800x800的地图,我们需要使用鼠标或触摸选择了APP中地图的左上角,那么显然的,在APP中,我们得到的点击位置是(0,0)。
但是如果我们把这个数据输入ROS的地图中,可以看到,在ROS中,(0,0)点位置对应的是左下角。所以造成的问题就会如下图:
这个问题解决的方法很简单,就是在用户端软件(如APP)中,将点击的点做一些处理(坐标系变换),再将其数据传输到机器人上即可。
3. Python Canvas与机器人内ROS之间的数据传输(http)
一般的机器人与客户端的通讯方式采用http方式传输信息,http协议的详细内容请参见新安浅滩的博文:
https://blog.csdn.net/hu694028833/article/details/80862695
整个机器人与Python之间地图与坐标系的传递过程中,Python要向机器人获取三个信息(已知地图每个像素为0.05米):
1)机器人中当前地图的图片
2)地图图片的右下角在机器人坐标系中所处的位置(或者说地图的原点在地图图片中所处的像素位置)
3)机器人相对于地图坐标系原点的位置
由于传输数据量很大,所以Python客户端向机器人请求地图的频率不能太高(基本为数秒一次,或只在加载时请求)。而因为机器人在时刻移动,机器人相对地图坐标原点的位置我们必须要频繁的请求。所以在数据通讯上,我们将以上三个信息分为两类:
1)当前机器人内部地图图片以及地图原点信息(加载地图,以及绘制地图时请求)
2)机器人相对地图原点的位置(数百毫秒,轮询请求)
例如,Python在机器人加载地图后,向机器人发送一个名为: GET /robot_map HTTP/1.1的消息
机器人向Python反馈jpeg压缩格式的地图图片
Python程序向机器人发送一个名为:GET /robot_map_origin HTTP/1.1的消息
机器人反馈地图的原点坐标:
{
"x": 1.0
"y":1.0
}
这样就实现了地图与地图坐标原点的传输。
同时,Python每间隔1000ms向机器人发送:GET /robot_state HTTP/1.1的消息
机器人反馈机器人实时位置:
{
“navigation_state”:
{
“x”:1.23 //注意:这里指的是机器人相对地图原点的实际位置,带小数点的。
“y”:2.34
}
}
这样就获取到了机器人的位置。
4. Python上显示机器人位置以及正确发送点击坐标
http://www.yzrobot.cn/newsshow-16-121-1.html
现在我们得到了地图,地图原点,机器人在实际环境中相对于地图原点的位置。那么我们就要在地图中正确显示出机器人所在位置,具体的方法如下:
1)采用比例变换,将机器人相对于原点的实际位置坐标转换为像素坐标
2)在已知地图原点在图片中像素的前提下,将机器人相对于地图原点的像素坐标,映射到相对地图图片的像素中坐标。
3)在地图图片上,按照机器人的像素坐标叠加机器人图片。
4)显示
如果我们想向机器人输出一个鼠标或触摸点击的坐标,我们要执行以下步骤:
1)获得鼠标点击的位置像素坐标(x,y)
2)对Python Canvas的坐标进行Y轴反转,获得临时像素坐标(例如地图高度为800,点击了(100,700)位置,那么发送的Y坐标为800-700=100)
3)在获取地图原点的基础上,利用坐标系变换,将临时像素坐标转换为相对于原点的像素坐标
4)根据像素-实际尺寸的比例变换,将像素坐标变为实际坐标
5)通过http的POST结合json数据格式将点击数据下发至机器人。
四、ROS 机器人如何使用GPS定位导航
四、YANML文件
image: testmap.png
resolution: 0.1
origin: [0.0, 0.0, 0.0]
occupied_thresh: 0.65
free_thresh: 0.196
negate: 0
必填字段:
image:包含占用数据的图像文件的路径; 可以是绝对的,也可以是相对于YAML文件的位置
resolution:地图的分辨率,米/像素
origin:地图中左下角像素的二维姿态,为(x,y,yaw),偏航为逆时针旋转(偏航= 0表示无旋转)。 系统的许多部分目前忽略了偏航。
occupied_thresh:大于此阈值的像素被视为完全占用。
free_thresh:小于此阈值的像素被认为是完全空闲的。
negate:是否应该反转 白/黑 空闲/占用(阈值的解释不受影响)
可选参数:
mode:可以有三个值之一:trinary,scale或raw。 Trinary是默认值。
参数解释:
如果像素的COLOR值x在[0,255]范围内,我们应该如何解释这个值?首先,我们将整数x转换为一个浮点数p,转换公式具体取决于yaml中negate标志的定义。
如果negate为0,则p =(255 - x)/ 255.0。这意味着黑色(0)现在具有最高值(1.0)而白色(255)具有最低值(0.0)。
如果negate为1,则p = x / 255.0。这是图像的非标准解释。
trinary 标准解释是三元解释,即解释所有值,使输出最终成为三个值之一。
如果p> occupied_thresh,则输出值100以指示单元格已被占用。
如果p <free_thresh,则输出值0以指示单元格是空闲的。
否则,输出-1 a.k.a. 255(作为无符号字符),以指示该单元格未知。
scale 这将调整上述解释,以允许更多的输出值。
如前所述,如果p> occupied_thresh,则输出值100,如果p <free_thresh,则输出值0。
否则,输出99 *(p - free_thresh)/(occupied_thresh - free_thresh)
这将允许您输出范围为[0,100]的完整渐变值。要输出-1,只需使用png的alpha通道,其中任何透明度都将被解释为未知。
raw 此模式将为每个像素输出x,因此输出值为[0,255]。
四、自己生成简略的地图文件
很感谢这个博主,下面的内容来自这篇文章:https://www.cnblogs.com/long5683/p/13217783.html
创建自定义尺寸的空白地图
在ROS中所使用的地图文件是.pgm文件、yaml文件
环境:ubuntu
安装
sudo apt-get install gimp
GPS相关的知识点:
1、经度纬度,一直都是相互垂直的
2、华山运动场的坐标
网站查坐标
网站测距离
左上:
左下:
右上:
右下:
3、根据两个经度纬度计算距离
现在是有一个程序计算,下边这个应该是会更好,更加的精确一点,所以找时间进行优化修改
程序参考链接
4、输出小数点后7位
5、python 把GPS坐标显示在地图上
from folium import plugins
import folium
import os
m = folium.Map([39.1289, 117.3539], zoom_start=10)
location =[[39.1289, 117.3539], [39.1277262, 117.3542938], [39.1277275, 117.3543001], [39.1277262, 117.3542938],
[39.1277275, 117.3543001], [39.1277262, 117.3542938], [39.1277262, 117.3542938],
[39.1271896, 117.3541359], [39.127121, 117.354126], [39.127121, 117.354126],
[39.1269348, 117.3541107], [39.1268692, 117.3541061], [39.1263994, 117.3540649],
[39.1257591, 117.3540165], [39.125608, 117.3540192], [39.1251984, 117.3539717],
[39.1250038, 117.3539568], [39.1246886, 117.3539276], [39.1246033, 117.3539269],
[39.1244316, 117.353912], [39.1242828, 117.353912], [39.1241112, 117.3538971],
[39.1238623, 117.3538666], [39.1233153, 117.3538361], [39.1232643, 117.3538374],
[39.1230354, 117.3537478], [39.1229895, 117.353714], [39.1228638, 117.3535239], [39.122818, 117.3534493],
[39.1227334, 117.353241], [39.1226985, 117.3531494], [39.122652, 117.3530273], [39.122652, 117.3529968],
[39.1225821, 117.352829], [39.1225239, 117.3526764], [39.1224861, 117.3525835], [39.1224774, 117.3525391],
[39.1224657, 117.3525238], [39.1224174, 117.3523745], [39.1221886, 117.3517625], [39.1221771, 117.3517327],
[39.1221399, 117.3516388], [39.1221199, 117.3515834], [39.1220169, 117.3512998], [39.1219769, 117.3512115],
[39.1219482, 117.3511057], [39.1219188, 117.3510437], [39.121814, 117.3507996], [39.1217791, 117.3507385],
[39.1217558, 117.350708], [39.1215935, 117.3505982], [39.121558, 117.3505859], [39.1213417, 117.3506131],
[39.1211014, 117.3507475], [39.121011, 117.3507996], [39.1209528, 117.3508301], [39.120883, 117.3508759],
[39.1208481, 117.3509064], [39.1207352, 117.3509714], [39.1204834, 117.3511356], [39.120369, 117.3511953],
[39.120369, 117.3511953], [39.1202774, 117.351255], [39.1186409, 117.3522551], [39.1185321, 117.3523254],
[39.1182976, 117.3524641], [39.1181374, 117.3525686], [39.1173566, 117.3530426], [39.1171188, 117.3531955],
[39.1168213, 117.3533746], [39.1163325, 117.3536682], [39.115867, 117.3539581], [39.1156691, 117.3540802],
[39.1156342, 117.3540955], [39.1156342, 117.3540955], [39.1144981, 117.3547927], [39.113551, 117.3553772],
[39.113551, 117.3553772], [39.1125069, 117.3559869], [39.1120846, 117.3557129], [39.1120846, 117.3557129],
[39.1119118, 117.3553002], [39.1118546, 117.355136], [39.111782, 117.3549652], [39.1115456, 117.3542404],
[39.1115958, 117.3540649], [39.1115958, 117.3540649], [39.1128411, 117.3532562], [39.1131786, 117.3530426],
[39.113807, 117.3526459], [39.113807, 117.3526459], [39.1191902, 117.3493593], [39.1197308, 117.3490295],
[39.1206386, 117.3484802], [39.1208713, 117.3483276], [39.1214676, 117.347971], [39.1214676, 117.347971],
[39.1214676, 117.347971], [39.1214676, 117.347971]]
route = folium.PolyLine(
location,
weight=3,
color='orange',
opacity=0.8
).add_to(m)
m.save(os.path.join(r'/home/robot/hei', 'Heatmap1.html'))
经过修改原来的代码,以及配置环境成功运行
源码来源
1、安装folium
python3 -m pip install folium
2、就是有一点不能用python2来运行代码了,需要用python3,运行的命令是
python3 show_gps_point.py
GPS坐标转换
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)