理想电压源 + 串联内阻
内阻和电阻有什么区别,表述上是否可以替换?
理想电压源 + 串联内阻
内阻和电阻有什么区别,表述上是否可以替换?
无论外接负载如何变化,它都能输出恒定大小的电流。 关键特性: 输出电流固定 输出电压会随负载变化而自动调整 理想情况下:内阻无限大 用一句话概括: 电流源“关心的是电流,不关心电压是多少”。
什么是负载?
电磁波 = 电场和磁场在空间里传播的波。
什么是波?电磁波是真实存在的物质吗?
WiFi 使用电磁波作为传输媒介,常见的工作频段有 2.4GHz 和 5GHz。
电磁波是什么?
GHz和MHz怎么换算,之前学习stm32的时候,涉及到主频,就讲到了MHz,讲到了脉冲,1Hz就表示一次脉冲是吧?
P——比例 就好比你看见小车歪了多少,就立刻用多大的力去扶正。 偏差越大,用力就越大;偏差小,就轻轻调一下。 问题:如果只用“比例”,可能会出现小车来回晃个不停。 I——积分 想象一下:小车老是微微往一边歪,但幅度不大,靠比例调节总是纠不过来。 这时候“积分”就会把这些小小的偏差累积起来,慢慢加大纠正的力度,直到完全扶正。 问题:积分太大可能会“过度补偿”,导致小车突然反向又歪过去。 D——微分 这个就像“提前预判”。它看的是小车“歪得有多快”。 如果小车倾斜速度很快,微分项会立刻发力,像刹车一样减缓它的趋势,避免冲过头。
所以这三者的作用是层层递进的吗
或固件本身支持运行时调参(串口/蓝牙/EEPROM)。
支持串口/蓝牙/EEPROM就不需要重新改原代码,烧录了吗?
控制器持续读取 IMU(俯仰角),计算误差,并通过驱动轮子做小幅前后移动来抵消倾斜,使车身总体保持垂直。
这里说的控制器指的是MPU6050模块还是TB6612双路电机驱动模块?
国际上约定:电流方向是正电荷移动的方向。
我们说电荷只是性质,这里说正电荷的移动方向,也就是说质子带着正电荷在移动?,在流动的不是电子吗?
. 以闪电为例 云层和地面之间会因为摩擦等原因,产生电荷分离: 云底可能积累大量负电荷(电子多)。 地面因此感应出正电荷。 当电场强到一定程度,空气被击穿,电子会成群结队地从云层快速移动到地面(或者反过来)。 这种电子的移动,就是“电荷转移”的物理过程。
这种情况,云层和地面这个整体是不是可以看作一个电容,空气就相当于是电介质.
放电(比如闪电、静电火花)就是电荷大量转移的过程。
这里例子说闪电是电荷大量转移的过程,那么转移的是正电核还是负电荷,到底是电子转移了,还是电荷转移了?刚刚不是说电子被拉走带上正电荷,电子多余出来带上负电荷,这里怎么又说是电荷的转移呢?
当我们给电容两端接上电压(比如电池),正极板会被“拉走”电子 → 带正电荷
为什么接上电压正极板会被拉走电子,我对电压,电子,电荷的概念已经完全忘却了,给我回忆一下并且保证我对这些概念已经理解
什么是电荷 电荷是物质的一种基本属性,就像质量一样。它分为正电荷和负电荷
电荷只是一种性质吗?还是说它像电子那样是真实存在的?
在原子中,电子分布在原子核外,绕着原子核“运动”(更准确地说是分布在不同的能级或轨道上)。
什么是分子,什么是原子?
单片机上电/复位启动 STM32 复位后,会从 Flash 中取出程序,开始执行 main() 函数。
什么是上电,复位是通过按下reset复位吗?
STM32 把硬件外设(GPIO、USART、I2C、ADC…)的“控制开关”映射到一段特殊的地址空间。
这里说的硬件外设指的是那些引脚,地址空间指的就是寄存器?为什么要将控制快关放在这个寄存器里面?
作用:通过读写寄存器,你可以直接控制芯片硬件的行为。
单片机控制硬件的逻辑到底是怎样的啊?我之前一直以为是CPU从ROM中读取程序,然后计算或控制硬件,怎么这里又说可以通过读写寄存器来控制硬件了?
每组 GPIO 有自己的一套寄存器(GPIOx_MODER、GPIOx_IDR、GPIOx_ODR…),方便软件统一操作。
这里说的寄存器是什么?和ROM和RAM有关系吗?
常用 GPIO(带复用功能) 每个 IO 都有多种功能,下面是一些常用的: PA9 (USART1_TX), PA10 (USART1_RX):串口1(最常用的下载/通信接口)。 PB6 (I2C1_SCL), PB7 (I2C1_SDA):I2C 接口。 PA5 (SPI1_SCK), PA6 (SPI1_MISO), PA7 (SPI1_MOSI):SPI 接口。 PA0-PA7, PB0-PB15, PC13-PC15:普通 GPIO,可输入/输出/ADC。 PC13:连接到板载 LED(默认点灯实验用)。 VBAT:RTC 电源(可接纽扣电池,掉电保持时间)。 PA0:支持外部中断(可做按键输入)。
GPIO是什么意思?最小系统板上都写的是A3,A4...你这里在A,C,B前面都加了一个P,这个P是什么意思?
G / GND:地。
GND是怎么控制接地的?为什么需要接地?GND是地的缩写吗?我们平时的一些大功率家用电器都需要接地,这个我都不知道是为什么,给我详细讲讲.
SWDIO, SWCLK:用于 SWD 调试(下载程序、单步调试)。
SWD调试是什么?怎么调试?SWDIO是用来将程序从电脑传输到STM32单片机中的Flash中的吗?
单片机内部有 PLL(锁相环倍频电路) 和 分频器。
单片机芯片内部不是只有两个存储器,I/O接口,和CPU吗?怎么又来了个PLL和分屏器?
晶体振荡器(Crystal Oscillator):接一个石英晶体(外部小方块元件),它会稳定地产生一个固定频率(比如 12 MHz、8 MHz)。 RC 振荡器(电阻+电容):成本低,但精度差。 PLL(锁相环电路):可以把外部晶体频率放大(比如 8 MHz 输入,经 PLL 变成 72 MHz 主频)。
在51单片机和stm32上有一个电器元件叫晶振,是不是就是这个元件在记录时钟频率
每秒有多少个这样的周期,就是 频率(Hz)。
就是说1HZ等于每秒有一个脉冲吗?
CPU 需要一个“节拍器”来协调它的动作,不能乱跑。 这个节拍器发出的均匀脉冲,就叫 时钟脉冲(Clock Pulse)。 每一个时钟脉冲,CPU 就完成一个最小的工作步骤(例如移动数据、做一次逻辑判断等)。
这个节拍器是真实存在于CPU中的吗?如何记录这个时钟脉冲?
高电平 → 低电平 → 高电平 …
电平从高到底再到高的这个过程就是一个脉冲是吗?
主频和指令数的关系 主频 = CPU 每秒产生的时钟脉冲数。 指令数 = CPU 每秒能执行多少条指令。
什么是脉冲,什么是时钟脉冲?指令数和时钟脉冲和主频有什么关系?
51 单片机:有大约 111 条指令,覆盖数据传送、算术、逻辑、跳转、位操作等基本功能。
这里说有111条指令也就是说cpu可以执行111个基本动作是吗?
👉 这就是 CPU 每次“敲锤子”时的最基本循环。
以上这些基本动作进行一个循环才算是进行了一个基本动作吗?
它相当于“CPU 的节拍器”,决定了 CPU 一秒钟能执行多少个最基本的动作(指令节拍)。
CPU执行哪些基本动作,有多少种基本动作?
51:主频通常在几 MHz ~几十 MHz,8 位数据宽度。 STM32:主频一般在 72 MHz、168 MHz,甚至几百 MHz,32 位数据宽度,能处理更复杂的数据和任务。
主频是什么意思?MHZ单位表示什么?
你是做 推理 (inference),不是训练。
是不是就是说下面只要用到了predict预测,就需要用到推理了,就得开启eval模式?
jsonify:把 Python 字典/数据结构转换为 JSON 格式响应,返回给前端。
json的格式是字典格式,然后键必须是字符串是吗?响应的概念有点淡忘了,给我回忆一下.json是json文件,ify是什么意思?
request:用来获取客户端发送过来的请求数据(如表单、文件、参数等)。
这里的客户端指的是访问网站的人吗?一共有几种端,这个和端口是否有关系?
nohup后台运行python
nohup是指一个在Linux系统中用于运行命令或脚本的工具,确保这些命令或脚本在用户注销或终端会话结束时仍能继续运行。
time.sleep(2)
👉 让程序暂停 2 秒钟,什么都不做。
print('training remain time (hour:minute:second) %02d:%02d:%02d' % (h,m,s))
输出格式为训练剩余时间 (时:分:秒)。
remain_batch = epochs * times - (epoch*times + index + 1)
使用总的批次数减去已经完成的batch数计算剩余的批次数.
def need_time(epoch,epochs,index,times,start,end):
epoch → 当前训练的 第几个 epoch(从 0 开始计数)。
epochs → 总的训练轮数。
index → 当前 batch 在本轮中的索引(从 0 开始)。
times → 每个 epoch 中的 batch 数量(即一个 epoch 有多少 step)。
start → 当前 batch 开始的时间戳。
end → 当前 batch 结束的时间戳。
def data_split_batch(paths,labels,batch): size = len(paths) index_point = list(range(size))[0:size:batch] split_batch = [] for index,point in enumerate(index_point): if index != len(index_point)-1: img_paths = paths[point:point+batch] img_labels = labels[point:point+batch] split_batch.append([img_paths,img_labels]) else: img_paths = paths[point:] img_labels = labels[point:] split_batch.append([img_paths, img_labels]) return split_batch,range(len(index_point))
用 SceneData().imgs_ids() 读取训练集和测试集的图片路径及对应标签。
def img_preprocess(path): img = cv2.imread(path) img = cv2.resize(img, (224, 224)) img = (img - mean) / std img = img.astype('float32').transpose(2, 0, 1) img = np.expand_dims(img, axis=0) return img def batch_imgs(paths): imgs = [] for path in paths: img = img_preprocess(path) imgs.append(img) return np.concatenate(imgs,axis=0)
定义img_preprocess 和 batch_imgs,完成图像的读取、缩放、归一化、维度变换和打包成 batch。
from torch.optim import lr_scheduler
学习率调度器用于优化学习率
classify-小批量图像
img1、img2、img3 单独是 样本。
拼接后的 img_1_2_3 是一个 小批量 (mini-batch)。
这就是小批量梯度下降的核心:一次参数更新不是看单个样本,而是看一小批样本的“平均情况”。
小批量 (mini-batch) 的本质就是: 把多个样本一起打包成一个批次输入网络,而不是单个样本一张张地输入。
img_1_2_3 = np.concatenate((img1,img2,img3), axis=0)
把 NumPy 数组转换成 PyTorch 张量 (Tensor)
return np.concatenate(imgs,axis=0)
np.concatenate 是 NumPy 用来把多个数组在指定轴上拼接的函数。
mean = np.array([[[123.675, 116.28, 103.53]]]) std = np.array([[[58.395, 57.12, 57.375]]])
mean → 每个通道的均值
std → 每个通道的标准差
使用这俩个指标是为了对图像做 标准化(standardization): img_norm=(img-mean)/std
print('\n--------img1---------') img1 = cv2.imread('./0001.jpg')
这里./0001.jpg图片虽然我们没有显示其图像,但是其实就是表示我们自己通过列表转换生成的图片,形状一般为(H,W,C)
imgs_path.extend(glob.glob(test_dir+folder+'/*.jpg'))
glob.glob(pred_dir+'*.jpg') 👉 查找 pred_dir 下所有扩展名为 .jpg 的文件
import glob
glob 是 Python 的 文件路径匹配模块,类似于 Linux 里的通配符。
它能找到所有符合某个模式的文件路径,并返回一个列表。
x = torch.from_numpy(x_numpy) label = torch.tensor(label_list)
torch.from_numpy()是numpy转torch
torch.tensor(),列表转torch
data_iters = iter(data_batch) for _ in index_point: paths,label_list = next(data_iters)
iter(obj) → 将一个 可迭代对象 转换成一个 迭代器 (iterator)。
迭代器 (iterator):除了能被遍历,还能记住当前位置,可以用 next() 逐步取值
img_remove_1dim = np.squeeze(img_add_dim)
在不使用axis指定位置的情况下np.squeeze(img_add_dim)表示删除目标维度中,所以长度为1的维度.
例如:原数组 (1, 3, 1, 5) → squeeze 后 (3, 5)
添加aixs参数后,只删除指定位置的长度为1的维度,如果指定的位置维度不为1,则会报错.
例如:arr_squeezed = np.squeeze(arr, axis=0) # 只去掉第 0 维
img_add_dim = np.expand_dims(img, axis=0)
np.expand_dims(a, axis) 用来 在指定位置插入一个新的维度,返回一个新的数组。 它和np.newaxis对比,一个是切片形式的添加维度,一个是函数形式的添加维度.
img_add_dim = img[np.newaxis, :]
这里再np.newaxis后面加了:其实和不加没有什么区别
假定img为一个四维数组: 我们写img_add_dim = img[np.newaxis]表示再第零维之前加一个维度.剩下的维度按顺序继续切出
我们在np后面几个:或加...其实和不加都是一样的,除非我们加在前面.
img_add_dim = img[:,np.newaxis]表示在第一维之前加一个维度
img_add_dim = img[:,:,np.newaxis]表示在第二维之前加一个维度...
img_add_dim = img[...,np.newaxis]表示在最后一维之前加一个维度, ... 表示多个:,具体是几个根据img的维度推导
img的维度为4,则img_add_dim = img[:,...,np.newaxis]依旧表示在最后一维添加一个维度,但是这里 ... 表示三个:,即三个维度.
img_add_dim = img[np.newaxis]
np.newaxis 是 NumPy 提供的一个特殊对象(本质上就是 None 的别名),专门用来在切片时 增加一个新维度。
例子: 假设原图像数组是: img.shape # (H, W, C) = (512, 148, 3)
img_add_dim = img[np.newaxis] print(img_add_dim.shape) # (1, 512, 148, 3)
这里 np.newaxis 在最前面加了一维,结果变成 四维数组。
维度变换
维度转换 (transpose) 并不会让图像在视觉上旋转或翻转,它只是改变了数组在内存里“怎么排列维度”。
RGB ↔ BGR:在同一维度内调换通道顺序,颜色会变
要区别于RGB转BGR不要混淆
img_transpose = img.transpose(2,0,1)
transpose 会按照给定的轴顺序重新排列数组的维度。
原来是 (0, 1, 2) → (height, width, channels)
你写 (2, 0, 1),就变成 (channels, height, width)
transpose 只是调换数组的维度顺序,不会改变里面的数据内
容或关键点:维度转置不改变通道内部的顺序
img_3_reverse = img[:,:,::-1]
img[:,:,::-1],这个格式的切片,前两个冒号表示左右行所有列,后面的两个冒号的格式是[start:stop:step],就是取所有维度然后倒着取.
img = img.resize((200,50))
这里使用PIL的Image对象调用resize,其中的参数也是先宽后高,和OpenCV一样.
from PIL import Image
PIL 是 Python Imaging Library,已多年不再维护;现代项目都用 Pillow(兼容 PIL API)。
安装:pip install pillow
引用方式仍是:from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageDraw, ImageFont
这里实际上我们是用的是 Pillow —— 它是对 PIL (Python Imaging Library) 的替代和升级版
img = cv2.resize(img,(50,200))
cv2.resize中第二个参数,表示将图像的形状转别为宽50,高200.将图像纵向拉长了
content = f.readline()
optimizer.step()
w<---update ----- w-阿尔法*L对权重的偏导
epoch: 0, iter: 0-
epoch针对你的整个数据集,表示对数据集整体训练的次数 iter表示一轮训练中有几个批次,批次等于总的数据大小/批次大小。
for epoch in range(4): for iter in range(3): x = torch.rand(2, 3, 224, 224)
这里x的生成写在训练批次的循环里面,循环三次,每次随机生成3张随机图片
print(model.sex.weight.grad.shape)
new_w=old_w-lr*梯度
model.train()
这里求损失,用于后续反向传播更新梯度,所以用训练模式。
class ResNet(nn.Module):
网络越深(层数越多),理论上应该能学到更复杂的特征;
但是实际中,深层网络在训练时会出现 梯度消失/梯度爆炸 或 退化问题:
训练误差和测试误差反而比浅层网络更高。
model.eval()
这个代码的作用是让模型切换到推理模式,有逻辑的改变。
在训练模式下,模型会进行Dropout,和Batch Normalize ,Dropout可以防止过拟合,Batch Normalize是为了防止梯度爆炸
在测试模式下,我们不需要考虑过拟合和梯度爆炸的问题,所以我们切换为推理模式,去掉上面的操作
如果你在测试/预测时忘记调用 model.eval(),这些层会继续用训练时的逻辑,导致预测结果不稳定甚至错误。
x = torch.rand(1,3,224,224)
torch.rand 会生成在 [0,1) 区间的随机浮点数。
x = torch.rand(1, 3, 224, 224) # 随机生成一张假图片
L = F.cross_entropy(x,label)
cross_entropy:这个函数内部已经包含了 softmax 的计算, 内部会先做 log_softmax 再算交叉熵
softmax+cross-entropy
softmax作用: 把网络输出的 logits(任意实数)转化为一个 概率分布。 一般用在分类模型的最后一层。
Cross-Entropy作用: 作为损失函数,衡量预测的概率分布和真实标签分布之间的差异。
二者的关系 Softmax 把输出变成概率分布。 Cross-Entropy 根据概率分布和真实标签,算出“预测对不对、差多少”。
混合池化(Mixed Pooling) 随机在最大池化和平均池化之间切换。 特点:结合二者优势,提高泛化能力。
什么是泛化能力,什么是鲁棒性? 泛化能力就是模型在 未见过的新数据 上依然能保持良好表现的能力。
鲁棒性就是模型在 噪声、干扰或异常情况下 依然能稳定输出的能力。
🔑 区别总结 泛化能力:针对 新样本,能不能学以致用。
鲁棒性:针对 扰动或噪声,能不能保持稳定。
👉 可以这样理解: 泛化能力是“能不能举一反三”;
鲁棒性是“遇到小问题会不会慌”。
torch池化(pooling)
随机池化:引入随机性,减少过拟合
混合池化:随机在最大池化和平均池化之间切换,特点:结合二者优势,提高泛化能力
torch卷积(convolution)
卷积操作: 卷积核上的每一个元素都是可学习的权重参数,网络训练的过程,就是不断更新这些权重,使它们能够提取出有用的特征。
卷积求特征值: 使用卷积核上的权重参数与原始图上的像素值对应位置作计算求和得到特征值
卷积核元素更新: 卷积核中的权重值也是通过计算损失,反向传播进行更新的.
input_trans = input_tensor.permute(2,0,1) input_unsqueeze = torch.unsqueeze(input_trans,0)
permute -- 排列,置换,交换 permute(2, 0, 1) 的意思是 重新排列维度顺序: 原来:(H, W, C) 变成:(C, H, W),通道数放前面符合pytorch习惯.
unsqueeze(在PyTorch等深度学习框架中,该词常被译为“升维” ) unsqueeze(0) 会在 第 0 维 加一个维度。 原来:(C, H, W) 变成:(1, C, H, W)
转置的区别: .T/transpose() 转置一般只交换两个维度
print(input_tensor.shape)
这里的形状为[7,7,3],表示7行像素,每行7列像素,每个像素有三个通道.即[H,W,C] 卷积层nn.Conv2d函数需要的输入格式为NCH*W: 1. N表示batch size 也就是一个批次的样本数
value = np.multiply(input_array[row:row+3,col:col+3,:],w_array)
np.multiply 是 逐元素相乘,不会做矩阵乘法(dot product),只是对应位置相乘.得到的结果类似: np.multiply(input_patch, w_array): [[ 0, 0, 0], [ 0, 2, 3], [-1, 0, 2]],所以后面需要value.sum()+b_array
矩阵乘法是@或np.dot 矩阵乘法会执行“行 × 列”的规则,自动把相乘结果加和 numpy中dot既可以点积又可以矩阵相乘,但是在torch中,dot只能作点积,矩阵相乘用matmul,然后无论是numpy还是torch都推荐用@作矩阵相乘.
input_trans = input_tensor.permute(2,0,1) input_unsqueeze = torch.unsqueeze(input_trans,0)
permute -- 排列,置换,交换 permute(2, 0, 1) 的意思是 重新排列维度顺序: 原来:(H, W, C) 变成:(C, H, W),通道数放前面符合pytorch习惯.
unsqueeze(在PyTorch等深度学习框架中,该词常被译为“升维” ) unsqueeze(0) 会在 第 0 维 加一个维度。 原来:(C, H, W) 变成:(1, C, H, W)
转置的区别: .T/transpose() 转置一般只交换两个维度
print(input_tensor.shape)
这里的形状为[7,7,3],表示7行像素,每行7列像素,每个像素有三个通道.即[H,W,C] 卷积层nn.Conv2d函数需要的输入格式为NCH*W: 1. N表示batch size 也就是一个批次的样本数
rows,cols,_ = input_array.shape
rows:几行 cols:几列 _:表示通道数,通道数与卷积核个数对应.
row_index = list(range(rows))[0:rows-1:2] col_index = list(range(cols))[0:cols-1:2]
range(rows)生成一个 从 0 到 rows-1 的整数序列。 例:range(6) -> 0,1,2,3,4,5,6 list(range(rows))把 range 对象转成真正的 列表。 list(range(6)) → [0, 1, 2, 3, 4, 5]
permute(2, 0, 1) 的意思是 重新排列维度顺序: 原来:(H, W, C) 变成:(C, H, W) 👉 把通道数(C)放到前面,符合 PyTorch 习惯。
1. 卷积核的本质 卷积核里的每一个元素,确实就是一个 可学习的权重参数。 网络训练的过程,就是不断更新这些权重,使它们能够提取出有用的特征。
低层卷积核学到的特征:边缘、线条、角点、颜色变化等(类似人眼感受到的基本图形元素)。
中层卷积核学到的特征:局部纹理、形状(比如眼睛的弧度、轮廓)。
高层卷积核学到的特征:复杂的语义模式(比如“这是只猫的脸”、“这是车轮”)。
MobileNetV1 还引入了两个超参数,用来在精度和速度之间折中:
啊发生的阿萨
深度可分离卷积(Depthwise Separable Convolution) 来替代标准卷积,大幅减少参数量和计算量
什么是深度可分离卷积,它和普通卷积的区别在哪? 1.