当前位置: 首页 > news >正文

CV学习笔记-MobileNet

MobileNet

文章目录

  • MobileNet
    • 1. MobileNet概述
    • 2. 深度可分离卷积(depthwise separable convolution)
      • 2.1 深度可分离卷积通俗理解
      • 2.2 深度可分离卷积对于参数的优化
    • 3. MobileNet网络结构
    • 4. 代码实现
      • 4.1 卷积块
      • 4.2 深度可分离卷积块
      • 4.3 MobileNet定义
      • 4.4 完整代码

1. MobileNet概述

  网络专注于移动端或者嵌入式设备中的轻量级CNN,相比于传统卷积神经网络,在准确率小幅度降低的前提下大大减少模型参数与运算量。

  传统卷积与DW卷积(Depthwise Conv)的差异,在传统卷积中,每个卷积核的channel与输入特征矩阵的channel相等(每个卷积核都会与输入特征矩阵的每一个维度进行卷积运算),输出特征矩阵channel等于卷积核的个数。而在DW卷积中,每个卷积核的channel都是等于1的(每个卷积核只负责输入特征矩阵的一个channel,卷积核的个数=输入特征矩阵的channel数=输出特征矩阵的channel数)

  MobileNet模型是Google针对手机等嵌入式设备提出的一种轻量级的深层神经网络,其使用的核心思想便是深度可分离卷积 depthwise separable convolution

2. 深度可分离卷积(depthwise separable convolution)

2.1 深度可分离卷积通俗理解

  传统的三通道卷积过程如下:

三通道卷积

  那么深度可分离卷积能用到轻量级的网络中,保证效率减少参数量,其具体是如何巧妙设计优化的,下图为深度可分离卷积的运作过程。

点卷积和深度卷积

  深度可分离卷积分为两个部分,Depthwise Convolution (深度卷积)、Pointwise Convolution(点卷积),这是两个不同的卷积过程,传统三通道卷积的过程是一个卷积核的厚度有三层,每一层与输入的feature map的每一层做加权求和,从而生成输出的一个通道,故输出通道数取决于卷积核的数量,卷积核的通道数等同于输入feature map的通道数(对齐)

传统卷积卷积核与输入矩阵的对应关系

  而Depthwise Convolution不同,其卷积核的厚度只有1,对于输入的feature map(特征图谱)的每一个通道,都有一个不同的厚度为1的卷积核相对应(卷积核数量与输入通道数对应),每个卷积核都只对对应通道的feature map做卷积操作,这样得到的每一个通道的卷积操作得到一个1通道的feature map,这样深度卷积之后的输出通道数与输入通道数一致。下图中绿色、红色、黄色表示的不同的通道数为1的卷积核。

深度卷积

  点卷积(Pointwise Convolution)是采用1×11\times11×1大小的卷积核(此时的卷积核是传统卷积核,其通道数与输入通道数相同)分别遍历输入的所有通道,得到的输出通道取决于卷积核的个数,点卷积与传统卷积方式相同,只是卷积核使用1×11\times11×1,起到一个改变通道数的作用,直观意义上讲就是加厚输出的feature map,其实际的意义在于由于深度卷积的方式对于特征提取不够明显,再通过点卷积进行一些信息补偿,在保证参数量减少的情况下,尽可能的让网络的效果保持一个可观的水平。

点卷积

2.2 深度可分离卷积对于参数的优化

  举例说明优化的效果,由于MobileNet是用于嵌入式设备等受限的设备上的网络,其参数肯定是大幅度降低的。

  有下列场景:
  使用3×33\times33×3大小的卷积层,输入通道数为16,输出通道数为32.

  使用传统卷积的方式,需要32个3×33\times33×3的卷积核对16个通道中的每一层数据进行卷积计算,最后得到32个输出通道的feature map,所需要的参数量为:
16×32×3×3=460816\times 32\times 3\times 3=4608 16×32×3×3=4608
  采用深度可分离卷积,由于输入的通道数为16,那么采用16个3×33\times33×3大小的卷积核对16个通道的feature map分别卷积,得到一个16通道的输出feature map,为了得到所需要的32通道的输出,采用点卷积的方式,使用32个1×11\times11×1大小的卷积核遍历16个通道的输入feature map,那么参数量为:
16×3×3+16×32×1×1=65616\times 3\times 3+16\times 32\times 1\times 1=656 16×3×3+16×32×1×1=656
  通过上述实验对比,参数量确实大大减少。

注意:深度可分离卷积指的是深度卷积(Depthwise Conv)+点卷积(Pointwise Conv),而并不是一个dw(Depthwise的缩写)就是一个深度可分离卷积,这个概念混淆度很高

3. MobileNet网络结构

一个深度可分离卷积的结构如下所示:

深度可分离卷积

  首先对输入做深度卷积(Depthwise Conv),深度卷积的特点是是卷积核的个数与处理的输入通道数一致同时每个卷积核通道数都是1,每个1通道卷积核去卷积输入feature map里的一个通道的数据,导致深度卷积处理之后输入与输出的通道数总是不变的。

  深度卷积之后就是BN层,BN层为Batch Normalization,对数据做归一化处理,再通过激活函数,接下来是一个1×11\times11×1的Conv层,其就是点卷积的层,使用1×11\times11×1的卷积核进行卷积操作,改变通道数,再进行归一化和激活输出。

BN的作用以及应用过程已经在笔者的博文《CV学习笔记-Inception》中详细介绍,不再赘述。

MobileNet的网络结构图如下所示

MobileNet

  在此不进行逐层分析,仅对初始几层进行分析,后面的读者可以自行推导,计算过程一致。

  首先输入为224×224×3224\times 224\times 3224×224×3,卷积核的shape为3×3×3×323\times 3\times 3\times 323×3×3×32,通过卷积核的shape,能够看出本次卷积操作采用的是一个传统的卷积计算,卷积核的通道数为3,个数为32,那么得到的输出为112×112×32112\times 112\times 32112×112×32(stride步长为2),此处的通道数32与卷积采用的卷积核个数32对应,之后经过Conv dw层,这个是深度卷积层,dw即Depthwise,输入feature map为112×112×32112\times 112\times 32112×112×32,所用卷积核shape为3×3×323\times 3\times 323×3×32注意与上面一层的卷积层相区别,少了一个维度,因为每一个卷积核的通道数为1,每个1通道卷积核只需要卷积feature map中的一个通道的数据,故经过深度卷积之后的输出通道数不变,输出的feature map为112×112×32112\times 112\times 32112×112×32,然后通过一个点卷积层,卷积核shape为1×1×32×641\times 1\times 32\times 641×1×32×64,即使用64个1×1×1×321\times 1\times 1 \times 321×1×1×32的卷积核进行卷积操作,点卷积卷积的方式与传统的卷积相同,只是在尺寸上采用的1×11\times 11×1的卷积核,用点卷积的方式改变通道数,输出的feature map shape为112×112×64112\times 112\times 64112×112×64,接下来就可以留给读者推算了,深度可分离卷积部分已经作出了分析。

4. 代码实现

4.1 卷积块

  代码按照传统的Conv-BN-Relu组合拳,进行卷积块的定义padding采用same方式

def _conv_block(inputs, filters, kernel=(3, 3), strides=(1, 1)):x = Conv2D(filters, kernel,padding='same',use_bias=False,strides=strides,name='conv1')(inputs)x = BatchNormalization(name='conv1_bn')(x)return Activation(relu6, name='conv1_relu')(x)

4.2 深度可分离卷积块

  深度可分离卷积分为深度卷积和点卷积,x = DepthwiseConv2D([params])即为深度卷积块的定义,使用的是DepthwiseConv2D(),在深度卷积完成BN+Relu后,紧接着接上了一个点卷积模块,x = Conv2D(pointwise_conv_filters,(1, 1)),使用1×11\times 11×1卷积核来改变通道数。

def _depthwise_conv_block(inputs, pointwise_conv_filters,depth_multiplier=1, strides=(1, 1), block_id=1):x = DepthwiseConv2D((3, 3),padding='same',depth_multiplier=depth_multiplier,strides=strides,use_bias=False,name='conv_dw_%d' % block_id)(inputs)x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x)x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)x = Conv2D(pointwise_conv_filters, (1, 1),padding='same',use_bias=False,strides=(1, 1),name='conv_pw_%d' % block_id)(x)x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x)return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)

4.3 MobileNet定义

  按照MobileNet的网络结构,将其复现成python程序,整合的就是上面定义的卷积模块和深度可分离卷积模块。(预训练权重文件可以在网络上下载)

def MobileNet(input_shape=[224,224,3],depth_multiplier=1,dropout=1e-3,classes=1000):img_input = Input(shape=input_shape)# 224,224,3 -> 112,112,32x = _conv_block(img_input, 32, strides=(2, 2))# 112,112,32 -> 112,112,64x = _depthwise_conv_block(x, 64, depth_multiplier, block_id=1)# 112,112,64 -> 56,56,128x = _depthwise_conv_block(x, 128, depth_multiplier,strides=(2, 2), block_id=2)# 56,56,128 -> 56,56,128x = _depthwise_conv_block(x, 128, depth_multiplier, block_id=3)# 56,56,128 -> 28,28,256x = _depthwise_conv_block(x, 256, depth_multiplier,strides=(2, 2), block_id=4)# 28,28,256 -> 28,28,256x = _depthwise_conv_block(x, 256, depth_multiplier, block_id=5)# 28,28,256 -> 14,14,512x = _depthwise_conv_block(x, 512, depth_multiplier,strides=(2, 2), block_id=6)# 14,14,512 -> 14,14,512x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=7)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=8)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=9)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=10)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=11)# 14,14,512 -> 7,7,1024x = _depthwise_conv_block(x, 1024, depth_multiplier,strides=(2, 2), block_id=12)x = _depthwise_conv_block(x, 1024, depth_multiplier, block_id=13)# 7,7,1024 -> 1,1,1024x = GlobalAveragePooling2D()(x)x = Reshape((1, 1, 1024), name='reshape_1')(x)x = Dropout(dropout, name='dropout')(x)x = Conv2D(classes, (1, 1),padding='same', name='conv_preds')(x)x = Activation('softmax', name='act_softmax')(x)x = Reshape((classes,), name='reshape_2')(x)inputs = img_inputmodel = Model(inputs, x, name='mobilenet_1_0_224_tf')model_name = 'mobilenet_1_0_224_tf.h5'model.load_weights(model_name)return model

4.4 完整代码

  本代码完成了一个图片的识别,图片如下,可以重命名为elephant.jpg输入网络运行。

美洲象

#-------------------------------------------------------------#
#   MobileNet的网络部分
#-------------------------------------------------------------#
import warnings
import numpy as np# from keras.preprocessing import imagefrom keras.models import Model
from keras.layers import DepthwiseConv2D,Input,Activation,Dropout,Reshape,BatchNormalization,GlobalAveragePooling2D,GlobalMaxPooling2D,Conv2D
from keras.applications.imagenet_utils import decode_predictions
from keras.utils import image_utils as image
from keras import backend as Kdef MobileNet(input_shape=[224,224,3],depth_multiplier=1,dropout=1e-3,classes=1000):img_input = Input(shape=input_shape)# 224,224,3 -> 112,112,32x = _conv_block(img_input, 32, strides=(2, 2))# 112,112,32 -> 112,112,64x = _depthwise_conv_block(x, 64, depth_multiplier, block_id=1)# 112,112,64 -> 56,56,128x = _depthwise_conv_block(x, 128, depth_multiplier,strides=(2, 2), block_id=2)# 56,56,128 -> 56,56,128x = _depthwise_conv_block(x, 128, depth_multiplier, block_id=3)# 56,56,128 -> 28,28,256x = _depthwise_conv_block(x, 256, depth_multiplier,strides=(2, 2), block_id=4)# 28,28,256 -> 28,28,256x = _depthwise_conv_block(x, 256, depth_multiplier, block_id=5)# 28,28,256 -> 14,14,512x = _depthwise_conv_block(x, 512, depth_multiplier,strides=(2, 2), block_id=6)# 14,14,512 -> 14,14,512x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=7)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=8)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=9)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=10)x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=11)# 14,14,512 -> 7,7,1024x = _depthwise_conv_block(x, 1024, depth_multiplier,strides=(2, 2), block_id=12)x = _depthwise_conv_block(x, 1024, depth_multiplier, block_id=13)# 7,7,1024 -> 1,1,1024x = GlobalAveragePooling2D()(x)x = Reshape((1, 1, 1024), name='reshape_1')(x)x = Dropout(dropout, name='dropout')(x)x = Conv2D(classes, (1, 1),padding='same', name='conv_preds')(x)x = Activation('softmax', name='act_softmax')(x)x = Reshape((classes,), name='reshape_2')(x)inputs = img_inputmodel = Model(inputs, x, name='mobilenet_1_0_224_tf')model_name = 'mobilenet_1_0_224_tf.h5'model.load_weights(model_name)return modeldef _conv_block(inputs, filters, kernel=(3, 3), strides=(1, 1)):x = Conv2D(filters, kernel,padding='same',use_bias=False,strides=strides,name='conv1')(inputs)x = BatchNormalization(name='conv1_bn')(x)return Activation(relu6, name='conv1_relu')(x)def _depthwise_conv_block(inputs, pointwise_conv_filters,depth_multiplier=1, strides=(1, 1), block_id=1):x = DepthwiseConv2D((3, 3),padding='same',depth_multiplier=depth_multiplier,strides=strides,use_bias=False,name='conv_dw_%d' % block_id)(inputs)x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x)x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)x = Conv2D(pointwise_conv_filters, (1, 1),padding='same',use_bias=False,strides=(1, 1),name='conv_pw_%d' % block_id)(x)x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x)return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)def relu6(x):return K.relu(x, max_value=6)def preprocess_input(x):x /= 255.x -= 0.5x *= 2.return xif __name__ == '__main__':model = MobileNet(input_shape=(224, 224, 3))img_path = 'elephant.jpg'img = image.load_img(img_path, target_size=(224, 224))x = image.img_to_array(img)x = np.expand_dims(x, axis=0)x = preprocess_input(x)print('Input image shape:', x.shape)preds = model.predict(x)print(np.argmax(preds))print('Predicted:', decode_predictions(preds,1))  # 只显示top1

实验输出:

在这里插入图片描述

相关文章:

CV学习笔记-MobileNet

MobileNet 文章目录MobileNet1. MobileNet概述2. 深度可分离卷积(depthwise separable convolution)2.1 深度可分离卷积通俗理解2.2 深度可分离卷积对于参数的优化3. MobileNet网络结构4. 代码实现4.1 卷积块4.2 深度可分离卷积块4.3 MobileNet定义4.4 完…...

C++进阶——继承

C进阶——继承 1.继承的概念及定义 面向对象三大特性:封装、继承、多态。 概念: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这…...

数据结构---单链表

专栏:数据结构 个人主页:HaiFan. 专栏简介:从零开始,数据结构!! 单链表前言顺序表的缺陷链表的概念以及结构链表接口实现打印链表中的元素SLTPrintphead->next!NULL和phead!NULL的区别开辟空间SLTNewNod…...

redis数据结构的底层实现

文章目录一.引言二.redis的特点三.Redis的数据结构a.字符串b.hashc.listd.sete.zset(有序集合)一.引言 redis是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value的NoSQL数据库。 通常使用redis作为缓存中间件来降低数据库的压力,除此…...

【JavaSE】复习(进阶)

文章目录1.final关键字2.常量3.抽象类3.1概括3.2 抽象方法4. 接口4.1 接口在开发中的作用4.2类型和类型之间的关系4.3抽象类和接口的区别5.包机制和import5.1 包机制5.2 import6.访问控制权限7.Object7.1 toString()7.2 equals()7.3 String类重写了toString和equals8.内部类8.1…...

Java 主流日志工具库

日志系统 java.util.logging (JUL) JDK1.4 开始,通过 java.util.logging 提供日志功能。虽然是官方自带的log lib,JUL的使用确不广泛。 JUL从JDK1.4 才开始加入(2002年),当时各种第三方log lib已经被广泛使用了JUL早期存在性能问题&#x…...

产品经理有必要考个 PMP吗?(含PMP资料)

现在基本上做产品的都有一个PMP证件,从结果导向来说,不对口不会有这么大范围的人来考,但是需要因地制宜,在公司内部里,标准程序并不流畅,产品和项目并不规范,关系错综复杂。 而产品经理的职能又…...

什么是原型、原型链?原型和原型链的作用

1、ES6之前&#xff0c;继承都用构造函数来实现&#xff1b;对象的继承,先申明一个对象&#xff0c;里面添加实例成员<!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><script…...

条件期望4

条件期望例题----快排算法的分析 快速排序算法的递归定义如下: 有n个数(n≥2n\geq 2n≥2), 一开始随机选取一个数xix_ixi​, 并将xix_ixi​和其他n-1个数进行比较, 记SiS_iSi​为比xix_ixi​小的元素构成的集合, Siˉ\bar{S_i}Si​ˉ​为比xix_ixi​大的元素构成的集合, 然后分…...

网络协议分析(2)判断两个ip数据包是不是同一个数据包分片

一个节点收到两个IP包的首部如下&#xff1a;&#xff08;1&#xff09;45 00 05 dc 18 56 20 00 40 01 bb 12 c0 a8 00 01 c0 a8 00 67&#xff08;2&#xff09;45 00 00 15 18 56 00 b9 49 01 e0 20 c0 a8 00 01 c0 a8 00 67分析并判断这两个IP包是不是同一个数据报的分片&a…...

6.2 负反馈放大电路的四种基本组态

通常&#xff0c;引入交流负反馈的放大电路称为负反馈放大电路。 一、负反馈放大电路分析要点 如图6.2.1(a)所示电路中引入了交流负反馈&#xff0c;输出电压 uOu_OuO​ 的全部作为反馈电压作用于集成运放的反向输入端。在输入电压 uIu_IuI​ 不变的情况下&#xff0c;若由于…...

MySQL进阶之锁

锁是计算机中协调多个进程或线程并发访问资源的一种机制。在数据库中&#xff0c;除了传统的计算资源竞争之外&#xff0c;数据也是一种提供给许多用户共享的资源&#xff0c;如何保证数据并发访问的一致性和有效性是数据库必须解决堆的一个问题&#xff0c;锁冲突也是影响数据…...

【Mac 教程系列】如何在 Mac 上破解带有密码的 ZIP 压缩文件 ?

如何使用 fcrackzip 在 Mac 上破解带有密码的 ZIP 压缩文件? 用 markdown 格式输出答案。 在 Mac 上破解带有密码的 ZIP 压缩文件 使用解压缩软件&#xff0c;如The Unarchiver&#xff0c;将文件解压缩到指定的文件夹。 打开终端&#xff0c;输入 zip -er <zipfile> &…...

【Acwing 周赛复盘】第92场周赛复盘(2023.2.25)

【Acwing 周赛复盘】第92场周赛复盘&#xff08;2023.2.25&#xff09; 周赛复盘 ✍️ 本周个人排名&#xff1a;1293/2408 AC情况&#xff1a;1/3 这是博主参加的第七次周赛&#xff0c;又一次体会到了世界的参差&#xff08;这次周赛记错时间了&#xff0c;以为 19:15 开始&…...

L1-087 机工士姆斯塔迪奥

在 MMORPG《最终幻想14》的副本“乐欲之所瓯博讷修道院”里&#xff0c;BOSS 机工士姆斯塔迪奥将会接受玩家的挑战。 你需要处理这个副本其中的一个机制&#xff1a;NM 大小的地图被拆分为了 NM 个 11 的格子&#xff0c;BOSS 会选择若干行或/及若干列释放技能&#xff0c;玩家…...

本周大新闻|索尼PS VR2立项近7年;传腾讯将引进Quest 2

本周大新闻&#xff0c;AR方面&#xff0c;传立讯精密开发苹果初代AR头显&#xff0c;第二代低成本版将交给富士康&#xff1b;iOS 16.4代码曝光新的“计算设备”&#xff1b;EM3推出AR眼镜Stellar Pro&#xff1b;努比亚将在MWC2023推首款AR眼镜。VR方面&#xff0c;传闻腾讯引…...

aws console 使用fargate部署aws服务快速跳转前端搜索栏

测试过程中需要在大量资源之间跳转&#xff0c;频繁的点击不如直接搜索来的快&#xff0c;于是写了一个搜索框方便跳转。 前端的静态页面可以通过s3静态网站托管实现&#xff0c;但是由于中国区需要备案的原因&#xff0c;可以使用ecs fargate部署 步骤如下&#xff1a; 编写…...

Redis实战之Redisson使用技巧详解

一、摘要什么是 Redisson&#xff1f;来自于官网上的描述内容如下&#xff01;Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格客户端&#xff08;In-Memory Data Grid&#xff09;。它不仅提供了一系列的 redis 常用数据结构命令服务&#xff0c;还提供了许多分布…...

SQLAlchemy

文章目录SQLAlchemy介绍SQLAlchemy入门使用原生sql使用orm外键关系一对多关系多对多关系基于scoped_session实现线程安全简单表操作实现方案CRUDFlask 集成 sqlalchemySQLAlchemy 介绍 SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上&#xff0c;使用关系…...

【Linux学习笔记】8.Linux yum 命令和apt 命令

前言 本章介绍Linux的yum命令和apt命令。 Linux yum 命令 yum&#xff08; Yellow dog Updater, Modified&#xff09;是一个在 Fedora 和 RedHat 以及 SUSE 中的 Shell 前端软件包管理器。 基于 RPM 包管理&#xff0c;能够从指定的服务器自动下载 RPM 包并且安装&#xf…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…...