图像语义分割网络FCN(32s、16s、8s)原理及MindSpore实现

news2025/2/2 22:49:09

一、FCN网络结构

         全卷积网络(Fully Convolutional Networks),是较早用于图像语义分割的神经网络。根据名称可知,FCN主要网络结构全部由卷积层组成,在图像领域,卷积是一种非常好的特征提取方式。本质上,图像分割是一个分类任务,需要做的就是对图像上每一个像素按照人工标注进行分类。

FCN大致网络结构如下:

上图模型结构为针对VOC数据集的21个语义分割,即数据集包含21种不同分割类型。当图像进入神经网络,第一个卷积层将图像由三通道转换为96通道featuremap,第二个卷积层转换为256个通道,第三个卷积层384个通道,直到最后一个卷积层变为21个通道,每个通道对应不同分割类型。实际上,卷积层整个网络结构中卷积层的通道数可以根据不同任务进行调整,前面每经过一层会对图像进行一次宽高减半的下采样,经过5个卷积层以后,featuremap为输入的1/32,最后通过反卷积层将featuremap宽高恢复到输入图像大小。

二、FCN模型结构实现

         FCN模型结构可以根据分割细粒度使用FCN32s、FCN16s、FCN8s等结构,32s即从32倍下采样的特征图恢复至输入大小,16s和8s则是从16倍和8倍下采样恢复至输入大小,当然还可以使用4s、2s结构,数字越小使用的反卷积层进行上采样越多,对应模型结构更加复杂,理论上分割的效果更精细。这里采用深度学习框架MindSpore来搭建模型结构。

FCN32s模型结构示意图:

 模型构建脚本:

class FCN32s(nn.Cell):
    def __init__(self, n_class=21):
        super(FCN32s, self).__init__()
        self.block1 = nn.SequentialCell(
            nn.Conv2d(3, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(64, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block2 = nn.SequentialCell(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(128, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block3 = nn.SequentialCell(
            nn.Conv2d(128, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block4 = nn.SequentialCell(
            nn.Conv2d(256, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block5 = nn.SequentialCell(
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block6 = nn.SequentialCell(
            nn.Conv2d(512, 4096, 7),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.block7 = nn.SequentialCell(
            nn.Conv2d(4096, 4096, 1),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.upscore = nn.SequentialCell(
            nn.Conv2d(4096, n_class, 1),
            nn.Conv2dTranspose(n_class, n_class, 4, 2, has_bias=False),
            nn.Conv2dTranspose(n_class, n_class, 32, 16, has_bias=False)
        )

    def construct(self, x):
        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)
        x = self.block6(x)
        x = self.block7(x)
        x = self.upscore(x)
        return x

FCN16s模型结构示意图:

FCN16s模型脚本:

class FCN16s(nn.Cell):
    def __init__(self, n_class=21):
        super(FCN16s, self).__init__()
        self.block1 = nn.SequentialCell(
            nn.Conv2d(3, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(64, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block2 = nn.SequentialCell(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(128, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block3 = nn.SequentialCell(
            nn.Conv2d(128, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block4 = nn.SequentialCell(
            nn.Conv2d(256, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block5 = nn.SequentialCell(
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block6 = nn.SequentialCell(
            nn.Conv2d(512, 4096, 7),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.block7 = nn.SequentialCell(
            nn.Conv2d(4096, 4096, 1),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.upscore_pool5 = nn.SequentialCell(
            nn.Conv2d(4096, n_class, 1),
            nn.Conv2dTranspose(n_class, n_class, 4, 2)
        )
        self.score_pool4 = nn.Conv2dTranspose(512, n_class, 1, has_bias=False)
        self.add = op.Add()
        self.upscore_pool = nn.Conv2dTranspose(n_class, n_class, 32, 16, has_bias=False)

    def construct(self, x):
        x1 = self.block1(x)
        x2 = self.block2(x1)
        x3 = self.block3(x2)
        x4 = self.block4(x3)
        x5 = self.block5(x4)
        x6 = self.block6(x5)
        x7 = self.block7(x6)
        pool5 = self.upscore_pool5(x7)
        pool4 = self.score_pool4(x4)
        pool = self.add(pool4, pool5)
        pool = self.upscore_pool(pool)
        return pool

 FCN8s模型结构示意图:

 FCN8s模型脚本:

class FCN8s(nn.Cell):
    def __init__(self, n_class=21):
        super(FCN8s, self).__init__()
        self.block1 = nn.SequentialCell(
            nn.Conv2d(3, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(64, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block2 = nn.SequentialCell(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(128, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block3 = nn.SequentialCell(
            nn.Conv2d(128, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block4 = nn.SequentialCell(
            nn.Conv2d(256, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block5 = nn.SequentialCell(
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        )
        self.block6 = nn.SequentialCell(
            nn.Conv2d(512, 4096, 7),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.block7 = nn.SequentialCell(
            nn.Conv2d(4096, 4096, 1),
            nn.BatchNorm2d(4096),
            nn.ReLU()
        )
        self.upscore_pool5 = nn.SequentialCell(
            nn.Conv2d(4096, n_class, 1),
            nn.Conv2dTranspose(n_class, n_class, 4, 2, has_bias=False)
        )
        self.score_pool4 = nn.Conv2dTranspose(512, n_class, 1, has_bias=False)
        self.score_pool3 = nn.Conv2dTranspose(256, n_class, 1, has_bias=False)
        self.add = op.Add()
        self.upscore_pool4 = nn.Conv2dTranspose(n_class, n_class, 4, 2, has_bias=False)
        self.upscore_pool = nn.Conv2dTranspose(n_class, n_class, 16, 8, has_bias=False)

    def construct(self, x):
        x1 = self.block1(x)
        x2 = self.block2(x1)
        x3 = self.block3(x2)
        x4 = self.block4(x3)
        x5 = self.block5(x4)
        x6 = self.block6(x5)
        x7 = self.block7(x6)
        pool5 = self.upscore_pool5(x7)
        pool4 = self.score_pool4(x4)
        pool3 = self.score_pool3(x3)
        pool4 = self.add(pool4, pool5)
        pool4 = self.upscore_pool4(pool4)
        pool = self.add(pool3, pool4)
        pool = self.upscore_pool(pool)
        return pool

三、数据集

         模型结构定义好后,我们需要通过对数据集的训练来检验模型性能。这里使用开源的细胞分割数据集:https://www.kaggle.com/code/kerneler/starter-isbi-challenge-dataset-21087002-9/data。数据集包含30张果蝇一龄幼虫腹神经索(VNC)的连续透射电子显微镜图像数据。

首先通过数值替换对分割标签图像进行转换,将白色背景替换为1。

标签图像预处理:

def convert(path, outpath):
    files = os.listdir(path)
    for i in range(len(files)):
        file = files[i]
        img_path = os.path.join(path, file)
        img = cv2.imread(img_path)
        img[img==255] = 1
        out = os.path.join(outpath, file)
        cv2.imwrite(out, img)

定义数据集:

class Cell_seg_dataset:
    def __init__(self, root_path):
        img_path = os.path.join(root_path, 'images')
        label_path = os.path.join(root_path, 'labels')
        self.img_list = []
        self.label_list = []
        img_names = os.listdir(img_path)
        label_names = os.listdir(label_path)
        self.img_index = np.array(range(len(img_names)))
        self.label_index = np.array(range(len(label_names)))
        for i in range(len(img_names)):
            self.img_list.append(os.path.join(img_path, img_names[i]))
            self.label_list.append(os.path.join(label_path, label_names[i]))
            self.img_index[i] = i
            self.label_index[i] = i
        if len(img_names) != len(label_names):
            raise 'images is not equal to labels !'

    def __getitem__(self, index):
        return self.img_index[index], self.label_index[index]

    def __len__(self):
        return len(self.img_list)

数据预处理:

def _preprocess(dataset, images, labels, classes, batch_size, img_channel, img_shape, label_shape):
    img_path = []
    label_path = []
    for i in range(batch_size):
        img_path.append(dataset.img_list[images[i]])
        label_path.append(dataset.label_list[labels[i]])
    one_hot = ops.OneHot()
    transpose = ops.Transpose()
    img_out = np.zeros((batch_size, img_channel, img_shape, img_shape))
    label_out = np.zeros((batch_size, label_shape, label_shape, classes))
    for i in range(len(images)):
        img = cv2.imread(img_path[i])
        img = img / 255.0
        img = Tensor(img, dtype=mindspore.float32)
        img = transpose(img, (2, 0, 1))
        label = cv2.imread(label_path[i])
        label = cv2.cvtColor(label, cv2.COLOR_RGB2GRAY)
        label = one_hot(Tensor(label, dtype=mindspore.int32), classes,
                        Tensor(1, dtype=mindspore.float32),
                        Tensor(0, dtype=mindspore.float32))
        img_out[i] = img.asnumpy()
        label_out[i] = label.asnumpy()
    img_out = Tensor(img_out, dtype=mindspore.float32)
    label_out = Tensor(label_out, dtype=mindspore.float32)
    return img_out, label_out

四、模型训练

    首先需要根据模型输出结果结合标签数据进行损失计算,这里使用的数据集为二分类图像分割数据,通过onehot将标签图像转换为2通道的featuremap,将网络输出结果与标签featuremap进行逐像素计算loss,通过反向传播更新模型。

    优化器:Adam

    损失函数:交叉熵损失

计算loss:

class MyWithLossCell(nn.Cell):
    def __init__(self, backbone, loss_func, batch_size, classes, label_shape):
        super(MyWithLossCell, self).__init__()
        self._backbone = backbone
        self._loss_func = loss_func
        self.transpose = ops.Transpose()
        self.shape = (batch_size * label_shape * label_shape, classes)
        self.reshape = ops.Reshape()
        self.sum = ops.ReduceSum(False)

    def construct(self, inputs, labels):
        logits = self._backbone(inputs)
        logits = self.transpose(logits, (0, 2, 3, 1))
        logits = self.reshape(logits, self.shape)
        labels = self.reshape(labels, self.shape)
        loss = self._loss_func(logits, labels)
        loss = self.sum(loss)
        return loss

定义训练脚本:

def train():
    train_data_path = config.train_data
    dataset = Cell_seg_dataset(train_data_path)
    train_data = ds.GeneratorDataset(dataset, ["data", "label"], shuffle=True)
    train_data = train_data.batch(config.batch_size)

    if config.backbone == 'FCN8s':
        net = FCN8s(config.num_classes)
    elif config.backbone == 'FCN16s':
        net = FCN16s(config.num_classes)
    else:
        net = FCN32s(config.num_classes)

    if config.use_pretrain_ckpt:
        ckpt_file = config.pretrain_ckpt_path
        param_dict = load_checkpoint(ckpt_file)
        load_param_into_net(net, param_dict)

    opt = nn.Adam(params=net.trainable_params(), learning_rate=config.lr, weight_decay=0.9)
    loss_func = nn.SoftmaxCrossEntropyWithLogits()
    loss_net = MyWithLossCell(net, loss_func, config.batch_size, config.num_classes, config.label_shape)
    train_net = nn.TrainOneStepCell(loss_net, opt)
    train_net.set_train()
    for epoch in range(config.epochs):
        train_loss = 0
        step = 0
        for data in train_data.create_dict_iterator():
            images, labels = _preprocess(dataset, data['data'], data['label'], config.num_classes, config.batch_size,
                                         config.input_channel, config.input_shape, config.label_shape)
            loss = train_net(images, labels)
            step += 1
            print(f'step:{step},loss:{loss}')
            train_loss += loss
        iter = epoch + 1
        print(f'epoch:{iter}, train loss:{train_loss}')
        if iter % 10 == 0:
            save_checkpoint(net, f'{iter}.ckpt')

训练过程loss输出:

 

五、推理验证

     训练完成后,通过加载保存的ckpt文件,在测试数据上进行推理验证。

推理脚本:

import mindspore
from mindspore import load_checkpoint, load_param_into_net, Tensor, ops
from src.model import FCN8s
import numpy as np
import cv2
import matplotlib.pyplot as plt


def main(ckptPath, imagePath, classes):
    img = cv2.imread(imagePath)
    img = img / 255.0
    img = Tensor(img, dtype=mindspore.float32)
    transpose = ops.Transpose()
    img = transpose(img, (2, 0, 1))
    expand_dim = ops.ExpandDims()
    img = expand_dim(img, 0)
    net = FCN8s(classes)
    param_dict = load_checkpoint(ckptPath)
    load_param_into_net(net, param_dict)
    net.set_train(False)
    result = net(img)
    result = np.squeeze(result.asnumpy())
    return result


if __name__ == '__main__':
    img_path = '0.jpg'
    ckpt_path = '800.ckpt'
    num_classes = 2
    result = main(ckpt_path, img_path, num_classes)
    print(result.shape) 
    img_rgb = [[0, 0, 0], [255, 255, 255]]
    img = np.ones((512, 512, 3))
    for i in range(512):
        for j in range(512):
            max_value = 0
            max_index = 0
            for k in range(num_classes):
                value = result[k, i, j]
                if value > max_value:
                    max_value = value
                    max_index = k
            img[i][j] = img_rgb[max_index]
    plt.figure('image')
    plt.imshow(img)
    plt.show()

 

 

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

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

相关文章

DBCO-PEG-NH2/Amine二苯基环辛炔-聚乙二醇-氨基 简介。用于以高特异性和反应性标记叠氮化物修饰的生物分子。

中文名称: 二苯基环辛炔-聚乙二醇-氨基 氨基-聚乙二醇-二苯基环辛炔 英文简称: DBCO-PEG-NH2/Amine Amine/H2N-PEG-DBCO 外观: 灰白色固体或半固体,取决于PEG的分子量 溶剂: 部分常规有机溶剂 存储…

day20-django

文件上传 批量上传数据 案例:混合数据(Form) 提交页面:用户输入数据文件(输入不能为空,报错) django开发过程中两个特殊的文件夹 static:存放静态文件的路径,包括css、js、项目图片 media&…

【问题记录】Git问题记录

文章目录问题1Failed to connect to github.com port 443 after 21085 ms: Timed outOpenSSL SSL_read: Connection问题2unable to access https://github.com//: OpenSSL SSL_read: Connection was reset, errno 10054网速慢问题clone太慢,pull太慢怎么办问题1 Fa…

记录C,C++关键字的位置,直接跳过注释和字符串文本。

依据第二版本&#xff0c;可以写一个跳过注释的查找函数 C_IndexOfWord Java_IndexOfWord CSharp_IndexOfWord 还有一种方法&#xff0c;可以先把所有注释用空格代替&#xff0c;查出的字符位置也不变。 以前版本&#xff1a; DList<TextColor> Syntax::GetTextColor…

m基于matlab的连续相位调制(CPM)解调系统仿真,包括解调,同步等模块

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 随着数字通信技术的飞速发展&#xff0c;数字通信的调制技术也得到了快速发展&#xff0c;其中连续相位调制(Continuous Phase Modulation&#xff0c;CPM)作为一种全新的通信调制方法得到了越来…

GNN 极简入门

文章目录图基本知识GNN简介GCNPYG极简入门Data Handling of GraphsCommon Benchmark DatasetsMini-batchesData TransformsLearning Methods on Graphs图基本知识 &#x1f638;图是由节点的有穷非空集合和节点之间边的集合组成&#xff0c;通常表示为 G(V,E)G(V, E)G(V,E)&am…

涨知识系列:爆款短视频拍摄技巧之一,构图

拍摄技巧主要分为两个部分&#xff0c;一个是构图&#xff0c;一个是拍摄手法。这部分内容其实也是比较简单的&#xff0c;因为短视频毕竟不需要做到像拍电影那么专业。所以在短视频当中我们只要学会一些基本的技巧就完全够用了。更重要的是我们需要对构图和拍摄手法有一个概念…

基于KT6368A的双模蓝牙模块打印机的方案

目录 一、打印机蓝牙模块简介 目前主流的打印机&#xff0c;很多都还是不带蓝牙。大部分的受限于成本等等原因&#xff0c;都还是通过USB的方式和电脑进行通讯&#xff0c;从而完成打印的数据交互 因为早期蓝牙技术发展的比较缓慢&#xff0c;而打印机类型的产品&#xff0c;…

_12LeetCode代码随想录算法训练营第十二天-C++二叉树

_12LeetCode代码随想录算法训练营第十二天-C二叉树 二叉树基础知识 二叉树的种类 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 完全二叉树 完全二叉树的定义如下…

线段树能解决多少问题?

背景 给一个两个数组&#xff0c;其中一个数组是 A [1,2,3,4]&#xff0c;另外一个数组是 B [5,6,7,8]。让你求两个数组合并后的大数组的&#xff1a; 最大值最小值总和 这题是不是很简单&#xff1f;我们直接可以很轻松地在 O(mn) 的时间解决&#xff0c;其中 m 和 n 分别为…

maven的java工程获取mysql数据库数据【问题及解决过程记录】

创建数据库maven&#xff0c;指定字符集和排序规则 UTF8MB4常用的排序规则&#xff1a;utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_bin&#xff0c;选用哪种方式呢&#xff1f;先来分析一下&#xff1a; 1、准确性&#xff1a; &#xff08;1&#xff09;utf8mb4_unico…

ffmpeg-时间基tbn、tbc、tbr

时间基的作用 源码来自ffmpeg5.1。 时间基在ffmpeg中是通过数据结构有理数AVRational描述的。时间基为时间戳的单位&#xff0c;比如时间基tbn(AVStream.time_base)0.001秒&#xff0c;AVPacket的pts40&#xff0c;则表明该AVPacket要在tbn*pts0.04秒开始显示。 /** 代码路径…

JavaScript 网页特效

一、Offset 1.1 概述 offset > 偏移量 &#xff0c;可以动态的获取的元素的位置、大小等属性。 获得元素距离带有定位父元素的位置获得元素自身的大小(宽度高度) 返回的数值都不带单位 offset常用属性&#xff1a; 属性作用element.offsetParent返回作为该元素带有定位…

全球汽车后行业发展现状:欧洲市场保持稳健 中国产业规模增速较快

根据观研报告网发布的《2022年中国汽车后市场分析报告-市场发展格局与投资潜力研究》显示&#xff0c;汽车后市场&#xff08;AM市场&#xff09;是指汽车在销售之后维修和保养服务及其所包含的汽车零部件、汽车用品和材料的交易市场&#xff0c;它涵盖了消费者买车后所需要的一…

LeetCode 321 周赛

2485. 找出中枢整数 给你一个正整数 n &#xff0c;找出满足下述条件的 中枢整数 x &#xff1a; 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数&#xff0c;则返回 -1 。题目保证对于给定的输入&#xff0c;至多存在一个中…

STM32单片机直流电机PID速度控制正反转控制(霍尔磁铁测速)LCD1602

实践制作DIY- GC0116-直流电机PID速度控制 一、功能说明&#xff1a; 基于STM32单片机设计-直流电机PID速度控制 功能介绍&#xff1a; STM32F103C系列最小系统LCD1602直流电机磁铁霍尔传感器MX15系列驱动模块4个按键&#xff08;速度减、速度加、开/关、正转/反转&#xff0…

【复习笔记】【嵌入式】嵌入式系统及其原理复习重点——篇二

嵌入式系统及其原理复习重点笔记 2 ARM处理器和指令集 ARM处理器简介 ARM架构与ARM处理器对应关系 V1版架构 该版架构只在原型机ARM1出现过,处理能力有限&#xff0c;其基本性能&#xff1a; 寻址空间&#xff1a;64M字节(26位)基本的数据处理指令(无乘法)字节、半字和字的…

4个封神的电脑软件,颠覆你对白嫖的认知,干货奉上

闲话少说&#xff0c;直上干货。 1、TinyWow TinyWow虽说是国外网站工具&#xff0c;但不得不承认真的无敌好用&#xff0c;收纳工具超200个&#xff0c;完全免费&#xff0c;无任何弹屏广告&#xff0c;更为良心的是&#xff0c;不需要注册登录&#xff0c;随用随走&#xff0…

如何优化大场景实时渲染?HMS Core 3D Engine这么做

在先前举办的华为开发者大会2022&#xff08;HDC&#xff09;上&#xff0c;华为通过3D数字溪村展示了自有3D引擎“HMS Core 3D Engine”&#xff08;以下简称3D Engine&#xff09;的强大能力。作为一款高性能、高画质、高扩展性的3D引擎&#xff0c;3D Engine不仅能通过实时光…

C++文件流

1、【转】string和stringstream用法总结 - 小金乌会发光&#xff0d;Z&M - 博客园 2、C&#xff1a;std::stringstream【数据类型转换、多个字符串拼接、分割字符串】_u013250861的博客-CSDN博客_c stringstream转string 3、C使用stringstream进行数据类型转换_puppylpg的…