动手学习VGG16

article/2025/8/30 5:10:08

VGG 论文

《Very Deep Convolutional Networks for Large-Scale Image Recognition》
论文地址:https://arxiv.org/abs/1409.1556

使用重复元素的网络(VGG)

以学习VGG的收获、VGG16的复现二大部分,简述VGG16网络。

一. 学习VGG的收获

  1. VGG网络明确指出并实践证明了,浅而大的卷积核不如深而小的卷积核
    假设卷积块a、b的输入输出维度相同(不妨设维度=C),其中卷积块a(1个7 * 7的卷积层),卷积块b(由3个3 * 3的卷积层组成)。

特征方面:不论h、w是否发生改变,对于卷积块a都只能得到浅层的特征(轮廓),而卷积块b能得到深层的特征(轮廓,波纹,花边等等)。当h、w发生改变的时候,卷积块a和卷积块b都具有相同的感受野。
参数方面:卷积块a的参数Pa = 7 * 7 * C^2 = 49 * C^2,卷积块b的参数Pb = 3 * (3 * 3 * C^2) = 3 * 9 * C^2 = 27 * C^2,明显 Pa > Pb。

  1. 图片的尺度N(h * w)决定了VGG网络的性能,多尺度N训练有助于提升VGG网络的性能。

    VGG网络结构图如下所示。
    在这里插入图片描述

VGG16网络由5个卷积块和1个全连接块组成,每个卷积块后接一个步幅为2的2 * 2的最大池化层,根据图片卷积后的尺寸计算公式:

h ′ = ( h − F + 2 P ) / S + 1 h' = (h-F+2P)/S+1 h=(hF+2P)/S+1
w ′ = ( w − F + 2 P ) / S + 1 w' = (w-F+2P)/S+1 w=(wF+2P)/S+1

其中输入图片的尺度W(h, w),Filter大小(卷积层或池化层的卷积核大小) F * F,步幅 S,填充 P,输出图片的尺度N(h’, w’)。
VGG16网的输入图片的尺度必须为32的整数倍。由于组成VGG16的卷积块都是由数个步幅为1,填充为1的3 * 3的卷积层组成,不会改变图片的尺度。对于卷积块后接步幅为2的2 * 2最大池化层,图片的尺度缩小为原尺度的1/2,有5个步幅为2的2 * 2最大池化层,因此图片的尺度缩小为原尺度的(1/2)^5=1/32。

不妨设输入图片的尺度为N,采用多尺度(N-32, N, N+32)可以显著提升VGG16网络的性能。

二. VGG16网络的复现

  1. 数据预处理
    VGG网络采用极为简单的处理方式,RGB三通道分别减去样本RGB三通道的均值。

样本数量较少时

"""
path 为文件路径的前缀
folders 为一个字典(类别kind,列表list),其中列表内存放着图片名
"""
# 样本数量较少
import cv2
import numpy as np
means    = [0., 0., 0.]
stdevs   = [0., 0., 0.]
img_list = []for string in folders:path_next = path + "\\" + stringfor file in folders[string]:file = path_next + "\\" + file#opencv 读入的矩阵是BGRimg = cv2.imread(file)img = img[:, :, :, np.newaxis]# print(img.shape)# img.shape = (h, w, 3, 1)img_list.append(img)imgs = np.concatenate(img_list, axis=3)
# print(imgs.shape)
# imgs.shape = (h, w, 3, n)
imgs = imgs.astype(np.float32) / (255.)for i in range(3):pixels    = imgs[:, :, i, :].ravel()  # 拉成一行# pixels.shape = (h*w*n, )means[i]  += np.mean(pixels)stdevs[i] += np.std(pixels)# BGR --> RGB , CV读取的需要转换,PIL读取的不用转换
# 也可以这么思考,opencv读取的是BGR,PIL读取的是RGB,如果交叉使用则需要使用。
means.reverse()
stdevs.reverse()

样本数量过多时

样本数量过多时,采用《概率论与数理统计》中的参数估计的方法。
对于样本x1,x2,x3,…,xn,期望u1,u2,u3,…,un,方差 v1^2, v2^2, v3^2,…, vn^2,都来源于同一个样本X采用随机取样得到。
不妨设 u1,u2,u3,…,un的均值为u,v1^2, v2^2, v3^2,…, vn^2的均值为v。
NX = x1+x2+x3+…+xn
E(NX) = u1+u2+u3+…+un = nu
D(NX) = v1^2 + v2^2 + v3^2 +…+ vn^2 = n
v^2
X = NX/n = (x1+x2+x3+…+xn)/n
E(X) = E(NX)/n = u
D(X) = D(NX)/(n*n) = v^2/n
样本X的期望为u,标准差为v/sqrt(n)。

# 样本数量过多
import cv2
import random
import math
import numpy as npmeans    = [0., 0., 0.]
stdevs   = [0., 0., 0.]
for epoch in range(1000):img_list = []for string in folders:path_next = path + "\\" + stringfor file in folders[string]:random_num = np.random.uniform() # np.random.uniform(0,1) 0-1之间按照均匀分布采样if random_num < 0.001:file = path_next + "\\" + fileimg = cv2.imread(file)#opencv 读入的矩阵是BGRimg = img[:, :, :, np.newaxis]# print(img.shape)# img.shape = (h, w, 3, 1)img_list.append(img)imgs = np.concatenate(img_list, axis=3)#print(imgs.shape)# imgs.shape = (h, w, 3, n)imgs = imgs.astype(np.float32) / (255.)for i in range(3):pixels    = imgs[:, :, i, :].ravel()  # 拉成一行# print(pixels.shape)# pixels.shape = (h*w*n, )means[i]  += np.mean(pixels)stdevs[i] += np.std(pixels)#if (epoch+1)%100 == 0:#print("normMean = {}".format(means))#print("normStd = {}".format(stdevs))# BGR --> RGB , CV读取的需要转换,PIL读取的不用转换
# 也可以这么思考,opencv读取的是BGR,PIL读取的是RGB,如果交叉使用则需要使用。
means.reverse()
stdevs.reverse()use_means  = [0., 0., 0.]
use_stdevs = [0., 0., 0.]
for i in range(3):use_means[i]  = means[i] / 1000 use_stdevs[i] = stdevs[i] / math.sqrt(1000)
print(use_means)
print(use_stdevs)
  1. 参数控制
batch_size = 8            # 每次喂入的数据量,batch_size可以根据电脑的配置调整
lr         = 0.01         # 学习率
step_size  = 1            # 每n个epoch更新一次学习率,数据集过大因此调小
epoch_num  = 50           # 总迭代次数
num_print  = 1120         # 每n次batch打印一次,数据集过大因此调大
num_check  = 1            # 每n个epoch验证一次模型,若效果更优则保存模型,数据集过大因此调小
  1. 数据集构建
"""
train_path、verification_path、test_path 同为字典(类别kind,列表list),其中列表内存放着图片的绝对路径。
labels 也是一个字典(类别kind,序号number),序号为1~n的数字
"""import torch
from torch.autograd import Variable
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
import cv2# 独立设置尺寸,有利于VGG网络的多尺度训练
size = 224# 均值和标准差均是按照VGG论文设置的,减去样本均值,标准差设置为1。
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5142455680072308, 0.4990353952050209, 0.5186490820050239), (1.0, 1.0, 1.0))])# -----------------ready the dataset--------------------------
def default_loader(path, img_size):img = cv2.imread(path)if img_size is not None:img = cv2.resize(img,(img_size,img_size),interpolation=cv2.INTER_NEAREST)return imgclass MyDataset(Dataset):# 构造函数def __init__(self, path, transform=None, target_transform=None, loader=default_loader, img_size = None):imgs = []for classification in path:for i in range(len(path[classification])):img_path  = path[classification][i]img_label = labels[classification]imgs.append((img_path,int(img_label)))#imgs中包含有图像路径和标签self.path             = pathself.imgs             = imgsself.transform        = transformself.target_transform = target_transformself.loader           = loaderself.img_size         = img_size# hash_map建立def __getitem__(self, index):img_path, img_label = self.imgs[index]# 调用 opencv 打开图片img = self.loader(img_path,self.img_size)if self.transform is not None:img = self.transform(img)img_label -= 1return img, img_labeldef __len__(self):return len(self.imgs)train_data        = MyDataset(train_path, transform=transform, img_size=size)
verification_data = MyDataset(verification_path, transform=transform, img_size=size)
test_data         = MyDataset(test_path, transform=transform, img_size=size)#train_data 、verification_data和test_data包含多有的训练、验证与测试数据,调用DataLoader批量加载
train_loader        = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
verification_loader = DataLoader(dataset=verification_data, batch_size=batch_size, shuffle=False)
test_loader         = DataLoader(dataset=test_data, batch_size=batch_size, shuffle=False)
  1. VGG16网络构建
import torch
from torch import optim
import torchvision
import matplotlib.pyplot as plt
import numpy as np
from torchvision.utils import make_grid
import time

关于多尺度训练的问题,有两个思路。
思路1:采用模型加载函数model.load_state_dict(torch.load(PATH), strict=False)。在加载部分模型参数进行预训练的时候,很可能会碰到键不匹配的情况(模型权重都是按键值对的形式保存并加载回来的)。因此,无论是缺少键还是多出键的情况,都可以通过在load_state_dict()函数中设定strict参数为False来忽略不匹配的键。(不推荐)
思路2:参照《动手学深度学习》的卷积神经网络NiN、GoogLeNet、ResNet、DenseNet等网络,对VGG16网络的全连接块进行魔改,采用卷积块temp代替全连接块的思路。卷积块temp由2个1 * 1的卷积层及1个全局平均池化层组成,最后接2(其中1层为塑造类别的全连接层)个全连接层。

思路1代码实现

from torch import nn
from torchsummary import summaryclass VGG16Net(nn.Module):def __init__(self):super(VGG16Net,self).__init__()# 第一层,2个卷积层和1个最大池化层self.layer1 = nn.Sequential(# 输入3通道,卷积核3*3,输出64通道(如224*224*3的样本图片,(224+2*1-3)/1+1=224,输出224*224*64)nn.Conv2d(3,64,3,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# 输入64通道,卷积核3*3,输出64通道(输入224*224*64,卷积3*3*64*64,输出224*224*64)nn.Conv2d(64,64,3,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# 输入224*224*64,输出112*112*64nn.MaxPool2d(kernel_size=2,stride=2))# 第二层,2个卷积层和1个最大池化层self.layer2 = nn.Sequential(# 输入64通道,卷积核3*3,输出128通道nn.Conv2d(64,128,3,padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# 输入128通道,卷积核3*3,输出128通道nn.Conv2d(128,128,3,padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# 输入112*112*128,输出56*56*128nn.MaxPool2d(kernel_size=2,stride=2))# 第三层,3个卷积层和1个最大池化层self.layer3 = nn.Sequential(# 输入128通道,卷积核3*3,输出256通道nn.Conv2d(128,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# 输入256通道,卷积核3*3,输出256通道nn.Conv2d(256,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# 输入256通道,卷积核3*3,输出256通道nn.Conv2d(256,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),#输入56*56*256,输出28*28*256nn.MaxPool2d(kernel_size=2,stride=2))# 第四层,3个卷积层和1个最大池化层self.layer4 = nn.Sequential(# 输入256通道,卷积核3*3,输出512通道nn.Conv2d(256,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),#输入28*28*512,输出14*14*256nn.MaxPool2d(kernel_size=2,stride=2))# 第五层,3个卷积层和1个最大池化层self.layer5 = nn.Sequential(# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),#输入14*14*512,输出7*7*256nn.MaxPool2d(kernel_size=2,stride=2))# VGG16--13个卷积层self.conv_layer = nn.Sequential(self.layer1,self.layer2,self.layer3,self.layer4,self.layer5)# VGG16--3个全连接层self.fc = nn.Sequential("""多尺度训练,代表训练3次分别把第一个全连接层,设置成如下A、nn.Linear(512 * 6 * 6, 4096)B、nn.Linear(512 * 8 * 8, 4096)C、nn.Linear(512 * 7 * 7, 4096)保证C为最后一次训练即可。"""nn.Linear(512 * 7 * 7, 4096),nn.ReLU(inplace=True),nn.Dropout(0.5),# 随机丢弃50%的神经元nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(0.5),# 随机丢弃50%的神经元"""第一种方法nn.Linear(4096, n)shape = (-1, n),n表示类别的数量第二种方法如下,在VGG16后接1个全连接层"""nn.Linear(4096, 1000),# 后接1个全连接层,shape = (-1, n),n表示类别的数量nn.Linear(1000, 29))def forward(self,x):x = self.conv_layer(x)x = x.view(x.size(0), -1)x = self.fc(x)return x
if __name__ == "__main__": device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')vgg_model=VGG16Net().to(device)summary(vgg_model, (3,224,224)) #打印网络结构

思路2代码实现

from torch import nn
from torchsummary import summaryclass VGG16Net(nn.Module):def __init__(self):super(VGG16Net,self).__init__()# 第一层,2个卷积层和1个最大池化层self.layer1 = nn.Sequential(# 输入3通道,卷积核3*3,输出64通道(如224*224*3的样本图片,(224+2*1-3)/1+1=224,输出224*224*64)nn.Conv2d(3,64,3,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# 输入64通道,卷积核3*3,输出64通道(输入224*224*64,卷积3*3*64*64,输出224*224*64)nn.Conv2d(64,64,3,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# 输入224*224*64,输出112*112*64nn.MaxPool2d(kernel_size=2,stride=2))# 第二层,2个卷积层和1个最大池化层self.layer2 = nn.Sequential(# 输入64通道,卷积核3*3,输出128通道nn.Conv2d(64,128,3,padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# 输入128通道,卷积核3*3,输出128通道nn.Conv2d(128,128,3,padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# 输入112*112*128,输出56*56*128nn.MaxPool2d(kernel_size=2,stride=2))# 第三层,3个卷积层和1个最大池化层self.layer3 = nn.Sequential(# 输入128通道,卷积核3*3,输出256通道nn.Conv2d(128,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# 输入256通道,卷积核3*3,输出256通道nn.Conv2d(256,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# 输入256通道,卷积核3*3,输出256通道nn.Conv2d(256,256,3,padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),#输入56*56*256,输出28*28*256nn.MaxPool2d(kernel_size=2,stride=2))# 第四层,3个卷积层和1个最大池化层self.layer4 = nn.Sequential(# 输入256通道,卷积核3*3,输出512通道nn.Conv2d(256,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),#输入28*28*512,输出14*14*256nn.MaxPool2d(kernel_size=2,stride=2))# 第五层,3个卷积层和1个最大池化层self.layer5 = nn.Sequential(# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# 输入512通道,卷积核3*3,输出512通道nn.Conv2d(512,512,3,padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),#输入14*14*512,输出7*7*256nn.MaxPool2d(kernel_size=2,stride=2))# 魔改VGG16--第六层self.layer6 = nn.Sequential(nn.Conv2d(512, 4096,1),nn.BatchNorm2d(4096),nn.ReLU(inplace=True),nn.Conv2d(4096, 4096,1),nn.BatchNorm2d(4096),nn.ReLU(inplace=True))# VGG16--15个卷积层self.conv_layer = nn.Sequential(self.layer1,self.layer2,self.layer3,self.layer4,self.layer5,self.layer6)# VGG16--1个全连接层self.fc = nn.Sequential(nn.Linear(4096, 1000),nn.ReLU(inplace=True),nn.Dropout(0.5),# 随机丢弃50%的神经元nn.Linear(1000, 29))def forward(self,x):x = self.conv_layer(x)# 全局平均池化层x = nn.functional.adaptive_avg_pool2d(x, (1, 1))x = x.view(x.size(0), -1)x = self.fc(x)return x
if __name__ == "__main__": device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')vgg_model=VGG16Net().to(device)summary(vgg_model, (3,224,224)) #打印网络结构
  1. 模型训练
# VGG16
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model  = VGG16Net().to(device)
# 调参
# 交叉熵
criterion = nn.CrossEntropyLoss() 
# 迭代器
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.8, weight_decay=0.001)
# 更新学习率
schedule  = optim.lr_scheduler.StepLR(optimizer, step_size=step_size, gamma=0.5, last_epoch=-1)
# 训练# 损失图
loss_list         = []
start             = time.time()
correct_optimal   = 0.0for epoch in range(epoch_num):model.train()running_loss = 0.0for i, (inputs, labels) in enumerate(train_loader, 0):# 从train_loader中取出64个数据inputs, labels = inputs.to(device), labels.to(device)# 梯度清零optimizer.zero_grad()# 模型训练outputs = model(inputs)#print(outputs.shape)# 反向传播loss = criterion(outputs,labels).to(device)loss.backward()optimizer.step()running_loss += loss.item()if (i+1) % num_print == 0:print('[%d epoch, %d]  loss:%.6f' %(epoch+1, i+1, running_loss/num_print))loss_list.append(running_loss/num_print)running_loss = 0.0# 打印学习率及确认学习率是否进行更新lr_1 = optimizer.param_groups[0]['lr']print("learn_rate: %.15f"%lr_1)schedule.step()# 验证模式if (epoch+1) % num_check == 0:# 不需要梯度更新model.eval()correct = 0.0total   = 0with torch.no_grad():print("=======================check=======================")for inputs, labels in verification_loader:# 从train_loader中取出batch_size个数据inputs, labels = inputs.to(device), labels.to(device)# 模型验证outputs = model(inputs)pred    = outputs.argmax(dim=1) #返回每一行中最大值的索引total   = total + inputs.size(0)correct = correct + torch.eq(pred, labels).sum().item()correct = 100 * correct/totalprint("Accuracy of the network on the 19850 verification images:%.2f %%" %correct )print("===================================================")# 模型保存if correct > correct_optimal:PATH = "VVG\\VGG16 model_" + str(epoch) + "_" + str(correct) + ".pth"torch.save(model.state_dict(), 'VGG/VGG16_%03d-correct%.3f.pth' % (epoch + 1, correct))correct_optimal = correctend=time.time()
print("time:{}".format(end-start))
  1. 绘制损失图
import matplotlib.pyplot as pltx = [ i+1 for i in range(len(loss_list)) ]# plot函数作图
plt.plot(x, loss_list)  # show函数展示出这个图,如果没有这行代码,则程序完成绘图,但看不到
plt.show()  

损失图如下,其中5个单位代表1次epoch。
在这里插入图片描述
7. 模型检验

# 检验模式,不需要梯度更新
model.eval()
correct = 0.0
total   = 0
with torch.no_grad():print("=======================check=======================")for inputs, labels in test_loader:# 从train_loader中取出batch_size个数据inputs, labels = inputs.to(device), labels.to(device)# 模型检验outputs = model(inputs)pred    = outputs.argmax(dim=1) #返回每一行中最大值的索引total   = total + inputs.size(0)correct = correct + torch.eq(pred, labels).sum().item()correct = 100 * correct/totalprint("Accuracy of the network on the 25907 test images:%.2f %%" %correct )print("===================================================")

最后祝各位永远怀着一颗怀疑的心,时刻仰望理论,不止于纸上谈兵,把想法付诸于实践,并不断思考。


http://chatgpt.dhexx.cn/article/V2kvuQsj.shtml

相关文章

VGG16

VGG16模型的学习以及源码分析 part one 主要学习参考 pytorch 英文文档VGG16学习笔记VGG16网络原理分析与pytorch实现【深度学习】全面理解VGG16模型VGG模型的pytorch代码实现VGG16源代码详解【论文】 VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION p…

VGG16 - 用于分类和检测的卷积网络

VGG16是由牛津大学的K. Simonyan和A. Zisserman在“用于大规模图像识别的非常深卷积网络”的论文中提出的卷积神经网络模型。 该模型在ImageNet中实现了92.7&#xff05;的前5个测试精度&#xff0c;这是属于1000个类的超过1400万张图像的数据集。它是ILSVRC-2014提交的着名模型…

VGG-16网络结构详解

VGG&#xff0c;又叫VGG-16&#xff0c;顾名思义就是有16层&#xff0c;包括13个卷积层和3个全连接层&#xff0c;是由Visual Geometry Group组的Simonyan和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型&…

经典卷积神经网络——VGG16

VGG16 前言一、VGG发展历程二、VGG网络模型三、VGG16代码详解1.VGG网络架构2.VGG16网络验证2.读取数据&#xff0c;进行数据增强3.训练模型&#xff0c;测试准确率 四、VGG缺点 前言 我们都知道Alexnet是卷积神经网络的开山之作&#xff0c;但是由于卷积核太大&#xff0c;移动…

VGG16网络模型的原理与实现

VGG 最大的特点就是通过比较彻底地采用 3x3 尺寸的卷积核来堆叠神经网络&#xff0c;这样也加深整个神经网络的深度。这两个重要的改变对于人们重新定义卷积神经网络模型架构也有不小的帮助&#xff0c;至少证明使用更小的卷积核并且增加卷积神经网络的深度&#xff0c;可以更有…

深度学习——VGG16模型详解

1、网络结构 VGG16模型很好的适用于分类和定位任务&#xff0c;其名称来自牛津大学几何组&#xff08;Visual Geometry Group&#xff09;的缩写。 根据卷积核的大小核卷积层数&#xff0c;VGG共有6种配置&#xff0c;分别为A、A-LRN、B、C、D、E&#xff0c;其中D和E两种是最…

RK3399平台开发系列讲解(PCI/PCI-E)5.54、PCIE INTx中断机制

文章目录 一、PCIe中断过程二、PCIE 控制器支持的中断三、PCIE 控制器注册中断四、PCIe设备中断号分配沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章将介绍RK3399平台PCIE总线中断INTx相关内容。 一、PCIe中断过程 层级结构为:PCIe设备 => PCIe控制器 =&g…

RK3399—中断

中断是操作系统最常见的事件之一&#xff0c;无论是系统层的“软中断”还是CPU底层的“硬中断”都是编程时常用的。中断的作用之一是充分利用CPU资源&#xff0c;正常情况下&#xff0c;CPU执行用户任务&#xff0c;当外设触发中断产生时&#xff0c;CPU停止当前任务&#xff0…

基于RK3399分析Linux系统下的CPU时钟管理 - 第3篇

1. 时钟系统结构 rockchip的时钟系统代码位于drivers/clk/rockchip&#xff0c;目录整体结构如下&#xff1a; ├── rockchip │ ├── clk.c---------------时钟系统注册 │ ├── clk-cpu.c-----------CPU调频 │ ├── clk-ddr.c-----------DDR调频 │ ├──…

基于RK3399+PID的手持稳定云台的设计与实现

手持稳定云台的主要作用是将外界环境因数引起的相机姿态变化进行隔离。如因操作者运动造成的机体震动、风阻力矩等&#xff0c;为了确保工作中相机的视轴始终保持期望的姿态不动。云台相机要拍摄出高质量的影像最重要的就是保证相机的视轴相对目标保持稳定。因此在相机拍摄的过…

RK3399学习

RK3399学习 韦东山rk3399&#xff1a;http://dev.t-firefly.com/forum-460-1.html firefly官网教程&#xff1a;http://wiki.t-firefly.com/zh_CN/Firefly-RK3399/started.html firefly官网3399资料&#xff1a;http://dev.t-firefly.com/forum-263-1.html 100ask 3399-pc教…

RK3399平台开发系列讲解(内核入门篇)1.53、platform平台设备

🚀返回专栏总目录 文章目录 一、设备配置-非设备树1.1、资源1.2、平台数据1.3、声明平台设备二、设备配置 - DTS沉淀、分享、成长,让自己和他人都能有所收获!😄 📢平台设备在内核中表示为struct platform_device的实例。 有两种方法可以把有关设备所需的资源(IRQ、DMA…

RK3399 Android7.1 编译

RK3399 Android7.1 编译 文章目录 RK3399 Android7.1 编译前言设置 Linux 编译环境安装 JDK可选- 更新默认的 Java 版本 安装所需的程序包(Ubuntu 14.04) 下载 Android SDK 前言 RK官网编译 Android搭建编译环境 设置 Linux 编译环境 使用的环境Linux 16.0.4 安装 JDK 如…

基于RK3399+5G的医用视频终端设计

当前在各种先进的信息通信技术的驱动下&#xff0c;医疗行业已呈现出信息化、移动化、智能化的发展趋势。特别是 5G 通信技术的落地应用推动了智慧医疗行业的 蓬勃发展&#xff0c;涌现出大量基于 5G 技术的医疗健康应用与服务&#xff0c;进一步融合了 5G 、 物联网与大数据…

RK3399平台开发系列讲解(PCI/PCI-E)PCIE相关配置说明

🚀返回专栏总目录 文章目录 一、DTS 配置二、menuconfig 配置三、cmdline 配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍在使用 RK3399 平台 PCIE 时候的配置。 一、DTS 配置 ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>; 此项是设置 PCIe…

RK3399快速上手 | 02-rockchip rk3399 linux sdk的使用(编译内核、编译uboot)

更新时间更新内容2022-09-15增加内核编译方法2022-10-21增加uboot编译方法和sdk开发版配置链路分析一、sdk区别 瑞芯微提供了两套sdk,一套是通过官方git仓库释放,适合于项目使用,另一套是通过github释放,适合于爱好者。 本文中使用从瑞芯微官方释放的正式linux sdk 2.7版…

RK3399平台开发系列讲解(中断篇)中断控制器驱动初始化

🚀返回专栏总目录 文章目录 一、设备树源文件1.1、gic控制器节点1.2、timer节点二、中断控制器匹配表三、中断控制器初始化3.1、函数of_irq_init3.2、函数gicv3_of_init3.3、函数gic_init_bases沉淀、分享、成长,让自己和他人都能有所收获!😄 一、设备树源文件 ARM64架构…

RK3399平台开发系列讲解(内存篇)15.34、 Linux 进程内存布局

🚀返回专栏总目录 文章目录 一、抽象内存布局二、32位机器 Linux 进程内存布局三、64位机器 Linux 进程内存布局沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 我们一起来看下进程内部的虚拟内存布局,或者说单一进程是如何安排自己的各种数据的。 一、抽象内存布…

RK3399平台开发系列讲解(内核调试篇)2.50、嵌入式产品启动速度优化

平台内核版本安卓版本RK3399Linux4.4Android7.1🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢启动速度是嵌入式产品一个重要的性能指标,更快的启动速度会让客户有更好的使用体验,在某些方面还会节省能耗,因为可以直接关机而不需要休眠。 启动速…

钉钉F1 RK3399 咸鱼80元板子使用记录

1.简单介绍 12V电源&#xff0c;建议2A&#xff0c; 默认插电不开机&#xff0c;有大佬找到金属罩下的焊盘&#xff0c;短接可上电开机。 在usb旁边的旁边有个端子接口&#xff0c;短接就可以开机&#xff0c;建议找个一样大的接口接个开关&#xff0c;到目前为止还未测试需要…