从零开始详解OpenCV条形码区域分割

前言

在识别二维码之前,首先要划分出二维码的区域,在本篇文章中将从零开始实现二维码分割的功能,并详细介绍用到的方法。

我们需要处理的图像如下:
在这里插入图片描述

完整代码

首先我们先放出完整代码,然后根据整个分割流程介绍用到的函数和方法,完整代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

class barCodes:
    def __init__(self, img_path, col_scale=800):
        self.image = cv2.imread(img_path)  # BGR图像
        self.gray_image = cv2.cvtColor(self.image.copy(), cv2.COLOR_RGB2GRAY)  # 灰度图
        self.col_scale = col_scale  # 根据列等比缩放图片
        self.scale = col_scale / self.image.shape[1]  # 缩放后图像宽与当前实际图像的宽的比值
        self.unscale = 1.0 / self.scale  # self.scale的倒值,用于还原缩放
        self.boxs = []  # 用于存储分割出来的框

    def scaleImage(self, image):
        # 根据比值来缩放图像
        im = image.copy()
        scale=self.scale
        im = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))
        return im

    def blackHat(self, image, kernel = np.ones((1, 3), np.uint8)):
        # 黑帽操作
        im = image.copy()
        im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel=kernel, anchor=(1, 0))
        return im

    def threshold(self, image, low=10, heigh=255):
        # 根据阈值二值化
        im = image.copy()
        thresh, im = cv2.threshold(im, low, heigh, cv2.THRESH_BINARY)
        return im

    def morph_close(self, image, kernel=np.ones((1, 5), np.uint8), iterations=2):
        # 闭操作
        im = image.copy()
        im = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel, iterations=iterations)
        return im

    def dilate(self, image, kernel = np.ones((1,3),np.uint8)):
        # 腐蚀操作
        im = image.copy()
        im = cv2.dilate(im, kernel)
        return im

    def getContours(self, image):
        # 获取边框
        im = image.copy()
        im_out = self.image.copy()
        unscale = self.unscale
        contours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
        if contours != None:
            for contour in contours:
                if cv2.contourArea(contour) < 2000:
                    continue
                rect = cv2.minAreaRect(contour)
                rect = ((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \
                       (int(rect[1][0] * unscale), int(rect[1][1] * unscale)),
                        rect[2]
                       )
        
                box = np.int0(cv2.boxPoints(rect))
                self.boxs.append(box)
                cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness=2)
        return im_out

    def forward(self, image, process_lst):
        im = image.copy()
        for i, process in enumerate(process_lst):
            im = process(im)
            self.plot_show(im, cmap=plt.cm.gray)

        return im

    def plot_show(self, image, cmap=None):
        # 显示图像
        if cmap is None:
            plt.imshow(image)
        else:
            plt.imshow(image, cmap)
        plt.show()
        
    def getBarRect(self):
        self.plot_show(self.image[:,:,::-1])
        process_lst = [self.scaleImage, self.blackHat, self.threshold, self.morph_close, self.dilate, self.getContours]
        image = self.forward(self.gray_image, process_lst)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        self.plot_show(image)

barcode = barCodes('test.png')
barcode.getBarRect()

函数详解

初始化

def __init__(self, img_path, col_scale=800):
	self.image = cv2.imread(img_path)  # BGR图像
	self.gray_image = cv2.cvtColor(self.image.copy(), cv2.COLOR_RGB2GRAY)  # 灰度图
	self.col_scale = col_scale  # 根据列等比缩放图片
	self.scale = col_scale / self.image.shape[1]  # 缩放后图像宽与当前实际图像的宽的比值
	self.unscale = 1.0 / self.scale  # self.scale的倒值,用于还原缩放
	self.boxs = []  # 用于存储分割出来的框

在OpenCV中使用cv2.imread从文件中读入图像

语法:
	img = cv2.imread(filename[, flags])
参数:
	filename:要读取的图像的文件名或文件路径。
	flags(可选):读取图像时的标志。常用的标志有:	
		cv2.IMREAD_COLOR:以彩色模式加载图像。这是默认值。
		cv2.IMREAD_GRAYSCALE:以灰度模式加载图像。
		cv2.IMREAD_UNCHANGED:包括图像的 alpha 通道(如果存在)

为了方便显示,我们首先选择cv2.IMREAD_COLOR读取彩色BGR图片,因为随后的处理过程需要用到灰度图,所以我们需要将彩色图片转换成灰度图。

cv2.cvtColor 用于颜色空间转换。将图像从一个颜色空间转换到另一个颜色空间。

语法:
	dst = cv2.cvtColor(src, code[, dst[, dstCn]])
参数:
	src:输入图像,即要进行颜色空间转换的原始图像。
	code:转换类型。OpenCV 提供了多种预定义的颜色空间转换类型
		cv2.COLOR_BGR2GRAY:BGR 到灰度图。
		cv2.COLOR_BGR2HSV:BGR 到 HSV(色相、饱和度、亮度)。
		cv2.COLOR_BGR2HLS:BGR 到 HLS(色相、亮度、饱和度,与 HSV 类似,但计算方式不同)。
		cv2.COLOR_HSV2BGR:HSV 到 BGR。
		cv2.COLOR_GRAY2BGR:灰度图到 BGR(结果图像的每个通道都将具有相同的灰度值)
	dst(可选):输出图像,即颜色空间转换后的图像。
	dstCn(可选):目标图像的通道。

有时我们需要处理的图像过大,会影响到处理的速度,所以我们一般需要对图像进行缩放。但是如果直接对图像reshape,会影响到图像的比例导致图像失真,所以我们最好根据比例进行缩放。

我们设置了一个名为scale的变量,来保存我们需要的宽度与图像实际宽度的比,后续可以通过scale与图像的实际宽高相乘获取到等比例缩小后的长和宽。

显示图像

def plot_show(self, image, cmap=None):
	# 显示图像
    if cmap is None:
        plt.imshow(image)
    else:
        plt.imshow(image, cmap)
    plt.show()

在本篇中使用matplotlib显示图像。

按比例缩放

def scaleImage(self, image):
	# 根据比值来缩放图像
	im = image.copy()
	scale=self.scale
	im = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))
	return im

这个功能还是很简单的,获取到缩小后的长和宽之后调用cv2.resize对图像比例缩放。

语法:
	dst = cv2.resize(src, dsize[, fx[, fy[, interpolation]]])
参数:
	src:输入图像。
	dsize:目标图像的大小(宽度,高度)。
	fx(可选):水平轴(宽度)的缩放因子。当 dsize 为(0, 0)时,它才有效。
	fy(可选):垂直轴(高度)的缩放因子。当 dsize 为(0, 0) 时,它才有效。
		通常设置为与 fx 相同,以保持图像的纵横比。
	interpolation(可选):插值方法。常用的插值方法有:
		cv2.INTER_LINEAR:线性插值(默认)。
		cv2.INTER_NEAREST:最近邻插值。
		cv2.INTER_AREA:区域插值(当缩小图像时使用)。
		cv2.INTER_CUBIC:三次样条插值(当放大图像时使用)。
		cv2.INTER_LANCZOS4:Lanczos 插值。

黑帽操作

def blackHat(self, image, kernel = np.ones((1, 3), np.uint8)):
	# 黑帽操作
	im = image.copy()
	im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel=kernel, anchor=(1, 0))
	return im
黑帽操作(Black Hat)是形态学操作中的一种常见操作。
原理:	
	闭运算结果与原始图像之间的差别。
	闭运算:先膨胀,再腐蚀。
	膨胀:白色像素占据黑色像素
	腐蚀:黑色像素占据白色像素
作用:
	黑帽操作可以得到图像中较亮的小区域,而不考虑更暗的大区域。
	通常用于提取图像中的大型结构、背景或者光照不均匀造成的影响。

在黑帽操作中,第一步是先膨胀再腐蚀然后与原始图像相减,这个步骤使得处理后的图像白色区域比黑色区域更多,即黑色区域被白色区域侵蚀;第二步是与原本图像相减,经过这个步骤,处理的结果中就只剩下了边缘轮廓的线条,而条形码区域的边缘轮廓线条较为密集,经过这一步骤可以除去图像中的大部分背景。

在OpenCV中cv2.morphologyEx用于执行形态学操作,我们可以通过cv2.morphologyEx实现黑帽操作

语法:
	dst = cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

参数
	src:源图像,必须是单通道的灰度图像。
	op:形态学操作的类型,可以是以下几种之一:
		cv2.MORPH_ERODE:腐蚀操作。
		cv2.MORPH_DILATE:膨胀操作。
		cv2.MORPH_OPEN:开运算(先腐蚀后膨胀)。
		cv2.MORPH_CLOSE:闭运算(先膨胀后腐蚀)。
		cv2.MORPH_GRADIENT:形态学梯度(膨胀后减去腐蚀)。
		cv2.MORPH_TOPHAT:顶帽运算(原图像减去开运算结果)。
		cv2.MORPH_BLACKHAT:黑帽运算(闭运算结果减去原图像)。
		kernel:结构元素或卷积核,用于定义形态学操作的结构和大小。
			它通常是一个奇数大小的二维数组(矩阵)。
		dst(可选):输出图像,如果未指定,则函数会创建一个新的输出图像。
		anchor(可选):锚点位置,指定了卷积核的基准点,
			默认为(-1, -1),表示位于卷积核的中心。
		iterations(可选):操作迭代的次数,默认为1。
			对于腐蚀和膨胀操作,增加迭代次数可以加大效果。
		borderType(可选):像素外推法选择标志,默认为cv2.BORDER_CONSTANT。
		borderValue(可选):当使用cv2.BORDER_CONSTANT时,此值指定了边界像素的值。

经过这一过程后图像变为
在这里插入图片描述

阈值二值化

def threshold(self, image, low=10, heigh=255):
	# 根据阈值二值化
	im = image.copy()
	thresh, im = cv2.threshold(im, low, heigh, cv2.THRESH_BINARY)
	return im

经过黑帽操作后的图像显然还不够清晰,我们可以通过阈值二值化操作将图像的像素点变为纯黑和纯白两种颜色。

在opencv中可以使用cv2.threshold完成这一操作

语法:
	retval, dst = cv2.threshold(src, thresh, maxval, type[, dst])
参数:
	src:源图像(必须是灰度图像)。
	thresh:阈值。
	maxval:用于替换超过阈值的像素的最大值。
	type:阈值处理的类型。OpenCV 提供了几种不同的类型,
		其中最常见的有:
		cv2.THRESH_BINARY:二值化阈值处理(0~maxval)
		cv2.THRESH_BINARY_INV:反二值化阈值处理(0~maxval)
		cv2.THRESH_TRUNC:截断阈值处理,如果像素值大于阈值,则像素值设置为阈值。
		cv2.THRESH_TOZERO:低阈值归零处理,如果像素值小于或等于阈值,则像素值设置为0。
		cv2.THRESH_TOZERO_INV:高阈值归零处理,如果像素值大于阈值,则像素值设置为0。
		cv2.THRESH_OTSU(结合此值,函数会计算并选择一个最佳的全局阈值)
		cv2.THRESH_TRIANGLE(结合此值,函数会计算并选择一个基于三角形方法的最佳全局阈值)
	dst(可选):输出图像。如果省略,则使用一个新的图像。
返回值:
	retval:分割的阈值
	dst:阈值处理后的图像。

经过这一操作,图像变为
在这里插入图片描述

闭操作

def morph_close(self, image, kernel=np.ones((1, 5), np.uint8), iterations=2):
	# 闭操作
	im = image.copy()
	im = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel, iterations=iterations)
	return im

通过闭操作,先膨胀(白色像素吞噬黑色像素)后腐蚀(黑色像素吞噬白色像素),可以使得条形码分散的各部分合并在一起。

经过操作后图像效果如下
在这里插入图片描述

腐蚀操作

def dilate(self, image, kernel = np.ones((1,3),np.uint8)):
	# 腐蚀操作
	im = image.copy()
	im = cv2.dilate(im, kernel)
    return im

通过腐蚀操作(黑色像素吞噬白色像素)可以进一步去除非条形码的线条干扰。

经过操作后图像效果如下,在这里效果并不明显,可以再多尝试一些参数组合。
在这里插入图片描述

获取边框

 def getContours(self, image):
 	# 获取边框
 	im = image.copy()
 	im_out = self.image.copy()
 	unscale = self.unscale
 	contours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
 	if contours != None:
 		for contour in contours:
 			if cv2.contourArea(contour) < 2000:
 				continue
 			rect = cv2.minAreaRect(contour)
 			rect = ((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \
                       (int(rect[1][0] * unscale), int(rect[1][1] * unscale)),
                        rect[2]
                       )
        	box = np.int0(cv2.boxPoints(rect))
        	self.boxs.append(box)
        	cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness=2)
	return im_out

根据之前的处理结果已经可以看到一些雏形了,接下来我们将直接使用 cv2.findContours直接获取条形码轮廓区域。
cv2.findContours的用法:

语法:
	contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
参数:
	image: 源图像,必须是8位单通道二值图像。
	mode: 轮廓检索模式。可选参数:
		cv2.RETR_EXTERNAL: 只检索最外层的轮廓。
		cv2.RETR_LIST: 检索所有的轮廓,但不建立父子关系。
		cv2.RETR_CCOMP: 检索所有的轮廓,并建立两个级别的关系
			(即,谁是谁的外部轮廓,谁是谁的孔)。
		cv2.RETR_TREE: 检索所有的轮廓,并重新建立嵌套的轮廓关系。
	method: 轮廓近似方法。可以是以下之一:
		cv2.CHAIN_APPROX_NONE: 存储轮廓的每个点。
		cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只留下它们的端点。
		cv2.CHAIN_APPROX_TC89_L1, cv2.CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chinl 的近似算法。
	contours: 检测到的轮廓。每个轮廓都是图像边界点的一个点集。
	hierarchy: 可选的输出向量,其中包含了有关图像拓扑的信息。
返回值:
	contours和hierarchy。

上述代码中首先获取了条形码所有的轮廓区域,随后遍历所有轮廓区域,通过cv2.contourArea检测轮廓面积大小,当轮廓面积过小时,很显然并不是我们所需的条形码区域,所以我们直接跳过处理。如果大于设定的轮廓面积,使用cv2.minAreaRect获取符合轮廓形状的最小矩形,获取的矩形就是条形码的区域。随后就可以画图了。

cv2.drawContours用法

语法:
	cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
参数:
	image:源图像,可以是二值图像或彩色图像。
		注意,cv2.drawContours 会在图像上直接绘制轮廓。
	contours:要绘制的轮廓。
	contourIdx:要绘制的轮廓的索引。如果为 -1,则绘制所有轮廓。
	color:轮廓的颜色,是一个三元组,表示 BGR颜色。
	thickness:线条的粗细。如果为负数,则轮廓将被填充。
	lineType:线条类型。默认cv2.LINE_8(8连通线)。
	hierarchy:轮廓的层次关系,是一个 NumPy 数组。通常这个参数可以省略。
	maxLevel:最多绘制的轮廓层次级别。如果未指定,则绘制所有级别。
	offset:轮廓点相对于图像原点的偏移量。通常这个参数可以省略。

这一步骤后图像效果是蓝色的,因为opencv是BGR格式而matplotlib是RGB格式的
在这里插入图片描述
如果BGR转RGB,结果如下
在这里插入图片描述

流程化

根据之前的步骤我们可以看出,执行的过程是线性的,所以我们可以将代码流程化

def forward(self, image, process_lst):
	im = image.copy()
    for i, process in enumerate(process_lst):
        im = process(im)
        self.plot_show(im, cmap=plt.cm.gray)
    return im

其中process_lst是执行过程组成的列表,image是图像,每步执行后调用plot_show显示结果。

def getBarRect(self):
	self.plot_show(self.image[:,:,::-1])
	process_lst = [self.scaleImage, self.blackHat, self.threshold, self.morph_close, self.dilate, self.getContours]
	image = self.forward(self.gray_image, process_lst)
	image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
	self.plot_show(image)

在使用时传入图像路径后调用getBarRect即可完成条码区域区分功能

barcode = barCodes('test.png')
barcode.getBarRect()

视频讲解

从零开始详解OpenCV条形码区域分割


从零开始详解OpenCV条形码区域分割

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows 使用 workerman

简单示例 workerman从3.5.3版本开始已经能够同时支持linux系统和windows系统。 1、需要PHP>5.4&#xff0c;并配置好PHP的环境变量。 2、Windows版本的Workerman不依赖任何扩展。 3、安装使用以及使用限制这里。 4、由于Workerman在Windows下有诸多使用限制&#xff0c…

【万字面试题】Redis

文章目录 常见面试题布隆过滤器原理和数据结构&#xff1a;特点和应用场景&#xff1a;缺点和注意事项&#xff1a;在python中使用布隆过滤器 三种数据删除策略LRU (Least Recently Used)工作原理&#xff1a;应用场景&#xff1a; LFU (Least Frequently Used)工作原理&#x…

vs2019 c++中模板 enable_if_t 的使用

&#xff08;1&#xff09; 该模板的定义如下&#xff1a; template <bool _Test, class _Ty void> struct enable_if {}; // no member "type" when !_Testtemplate <class _Ty> struct enable_if<true, _Ty> { // type is _Ty for _Testusing …

OSEK应用模式

1 前言 应用模式&#xff08;Application modes)用于区分不同的场景&#xff0c;以便在系统运行时&#xff0c;组织各自相互独立的OS相关的资源集合&#xff0c;是一种分而治之的思想体现。不同的应用模式是互斥的&#xff0c;即系统当前必须在一种应用模式&#xff08;且只能在…

OV SSL证书的特点

OV SSL证书&#xff0c;全称为Organization Validation SSL Certificate&#xff08;组织验证型SSL证书&#xff09;&#xff0c;是一种中级的SSL证书类型。与仅验证域名所有权的DV&#xff08;Domain Validation&#xff09;证书不同&#xff0c;OV证书在颁发前会执行更加严格…

01.认识HTML及常用标签

目录 URL&#xff08;统一资源定位系统&#xff09; HTML&#xff08;超文本标记语言&#xff09; 1&#xff09;html标签 2&#xff09;head标签 3&#xff09;title标签 4&#xff09;body标签 标签的分类 DTD文档声明 基础标签 1&#xff09;H系列标签 2&#xff09…

C++基础与深度解析 | 语句 | 分支语句 | 循环语句 | 达夫设备

文章目录 一、语句基础二、分支语句1.分支语句--if2.分支语句--switch 三、循环语句1.循环语句--while2.循环语句--do-while3.循环语句--for4.循环语句--基于范围的for循环5.break / continue语句四、语句的综合应用--达夫设备 一、语句基础 语句的常见类别&#xff1a; 表达…

如何在 Ubuntu 12.10 上使用 Python 创建 Nagios 插件

介绍 Python 是一种在 Linux 上默认可用的流行命令处理器。 我们之前已经介绍过如何在 Ubuntu 12.10 x64 上安装 Nagios 监控服务器。 这一次&#xff0c;我们将扩展这个想法&#xff0c;使用 Python 创建 Nagios 插件。 这些插件将在客户 VPS 上运行&#xff0c;并通过 NR…

树莓派|角速度和加速度传感器

角速度传感器和加速度传感器是常见的惯性传感器&#xff0c;常用于测量物体的旋转和线性运动。 角速度传感器&#xff08;Gyroscope&#xff09;用于测量物体绕三个轴&#xff08;X、Y、Z&#xff09;的旋转速度或角速度。它可以提供关于物体在空间中的旋转方向和角度变化的信…

数据结构学习/复习14--归并排序的递归与循环实现/计数排序

一、归并排序 1.递归实现 注意事项&#xff1a;即使排序的数字个数不为2的倍数也可正常分解&#xff0c;其思想没有规定一定要左右数目对称才可合并 注意事项&#xff1a;归并的思想还适用于外排序 2.递归改循环 注意事项&#xff1a;边界处理与非2的n次方倍的处理 版本1&…

win10下,svn上传.so文件失败

问题&#xff1a;win10下使用TortoiseSVN&#xff0c;svn上传.so文件失败 解决&#xff1a;右键&#xff0c;选择Settings&#xff0c;Global ignore pattern中删除*.so&#xff0c;保存即可。

网络3--网络通信的深度理解(端口号)

网络通信的进一步理解 两个主机间进行通信&#xff0c;其实是两个主机间的软件进行通信&#xff0c;软件也就是可执行程序&#xff0c;运行时就是进程&#xff0c;所以也为进程间通信。 进程间通信需要共享资源&#xff0c;这里两个主机间的共享资源是网络&#xff0c;利用的是…

指针(4)

1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* ; 一般使用: int main() {char i a;char* p &i;*p q;printf("%c", i);return 0; } 然后我们看这个例子,这是把⼀个字符串放到pstr指针变量里了吗&#xff1f; 事实上不是,他只是将…

如何管理多个版本的Node.js

我们如何在本地管理多个版本的Node.js&#xff0c;有没有那种不需要重新安装软件再修改配置文件和环境变量的方法&#xff1f;经过我的查找&#xff0c;还真有这种方式&#xff0c;那就是nvm&#xff08;Node Version Manager&#xff09;。 下面我就给大家介绍下NVM的使用 1…

笔记本黑屏,重新开机主板没有正常运作的解决办法

拆开笔记本后壳&#xff0c;打开看到主板&#xff0c;将主板上的这颗纽扣电池拆下来&#xff0c;如果是带连接线的&#xff08;如下图&#xff09;&#xff0c;可以将接口处线头拔出&#xff0c;等1分钟再把线接上。 ------------- 以下是科普 首先&#xff0c;电脑主板上的这…

Llama-Factory + Ollama 打造属于自己的中文版 Llama3

Meta 推出 Llama3 也有一小段时间了。Llama3 包含 8B 和 70B 两种参数规模&#xff0c;涵盖预训练和指令调优的变体。Llama 3 支持多种商业和研究用途&#xff0c;并已在多个行业标准测试中展示了其卓越的性能&#xff08;关于Llama3的具体介绍可以参考本站另外一篇博文&#x…

详解xlsxwriter 操作Excel的常用API

我们知道可以通过pandas 对excel 中的数据进行处理分析&#xff0c;但是pandas本身对格式化数据方面提供了很少的支持&#xff0c;如果我们想对pandas进行数据分析后的数据进行格式化相关操作&#xff0c;我们可以使用xlsxwriter&#xff0c;本文就对xlsxwriter的常见excel格式…

Java聚合项目打包运行笔记

聚合项目创建 略 聚合项目打包配置 父工程 pom文件添加 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>…

【18-Ⅰ】Head First Java 学习笔记

HeadFirst Java 本人有C语言基础&#xff0c;通过阅读Java廖雪峰网站&#xff0c;简单速成了java&#xff0c;但对其中一些入门概念有所疏漏&#xff0c;阅读本书以弥补。 第一章 Java入门 第二章 面向对象 第三章 变量 第四章 方法操作实例变量 第五章 程序实战 第六章 Java…

ROS2+TurtleBot3+Cartographer+Nav2实现slam建图和导航

0 引言 入门机器人最常见的应用就是slam建图和导航&#xff0c;本文将详细介绍这一流程&#xff0c; 便于初学这快速上手。 首先对需要用到的软件包就行简单介绍。 turtlebot3: 是一个小型的&#xff0c;基于ros的移动机器人。 学习机器人的很多示例程序都是基于turtlebot3。 …