- 2025-01-10
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
⑴遇到过什么技术问题,是否解决了?怎么解决的。没解决需要帮助也可以说说,论坛伙伴们帮忙看看
解决了项目中can通讯丢包的问题
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
⑷最想要什么支持?比如更多的某方面教程、资料、活动?:最想要STM32N6的开发板。还有AI部署到嵌入式平台的一些资料
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
⑶最想关注什么技术?:AI吧
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
⑸立一个新年Flag:吃好睡好然后多读书,多学习
- 2025-01-09
-
回复了主题帖:
【RainbowLink USB 协议转换器】2、环境搭建及TTL测试
xinmeng_wit 发表于 2025-1-5 20:26
又做了个实验,直接将TTL1和TTL2的TX和RX交叉连接,使用2M的波特率通讯,完全正常。说明RaninbowLink支持2M ...
换了一台电脑又测试了,发现TTL1和TTL2直连以4M的速度通讯又是可以的的,5M不行。
-
发表了主题帖:
【RainbowLink USB 协议转换器】3、RS485测试
一、说明
对于RS485的测试,因为手上还有另外一个RS485模块,也是平时一直在用的模块,所以本次我还是将这两个RS485模块对接进行测试。实际上我手在用的这个模块用的串口芯片是CH340,和RainbowLink用的CH344Q是同一家的。
本次测试的PC端软件我这边使用Modbus Poll和Modbus Slave。前者用于模拟Modbus主机,后者用于模拟Modbus从机。因为提到RS485,工业上通常会跑Modbus协议,所以选择了这种测试方法。
二、RS485测试
Modbus配置,除了波特率以外,其它配置都按照下面的配置来做测试。
先使用RainbowLink做主机来进行测试。
波特率115200bps测试
这个显然是没有任何压力的
波特率1000000bps测试
没有任何压力
波特率2000000bps测试
无错误。
波特率3000000bps测试
可以,无错误
波特率4000000bps测试
这时候就不行了,一直是timeout的状态了。
可能极限就是3Mbps了。
然后我们交换以下Modbus的主从设备再做测试,这次就不从头开始测了,直接摸极限波特率。
直接上4M,发现是一样的timeout。
再降到3M也是可以的,说明与主从没什么关系。
三、总结
经过测试,RainbowLink可能最高的RS485的速度是3Mbps多达不到4Mbps,通常情况下也不会用到这么高的速度,工业现场还是以稳定性为主,根据项目经验,工业现场波特率不会超过38400bps,至少我司的产品是这样的。
- 2025-01-06
-
回复了主题帖:
【RainbowLink USB 协议转换器】2、环境搭建及TTL测试
秦天qintian0303 发表于 2025-1-5 20:40
循环发送,周期10ms,个人感觉要是测试掉包,应该连续发送比较好,没有间隔更好
没错,其实我是想测丢包率的,但是串口工具不支持设置发送次数,只能再想其他办法测丢包率了
- 2025-01-05
-
回复了主题帖:
【RainbowLink USB 协议转换器】2、环境搭建及TTL测试
又做了个实验,直接将TTL1和TTL2的TX和RX交叉连接,使用2M的波特率通讯,完全正常。说明RaninbowLink支持2M是完全没有问题的。但如果波特率再高就不行了。
- 2025-01-04
-
发表了主题帖:
【RainbowLink USB 协议转换器】2、环境搭建及TTL测试
一、环境搭建
首先直接将RainbowLink通过USB接入电脑,此时的设备管理器会显示四个COM口,如下:
但是,现在有一个问题,不知道COM口怎么跟ABCD功能对应,比如,不知道哪个COM是对应RS485功能的。
为了确认对应,其实有两种方法。
第一种:随便选一个COM口通讯试试,根据RainbowLink上的通讯灯来确认是哪个功能。
第二种:安装官方提供的一个驱动,安装好以后会直接在设备管理器中显示对应的ABCD功能。
下载链接:https://img.dfrobot.com.cn/wikicn/5d57611a3416442fa39bffca/9aa91ec7eea25fd161bd414604a591b4.zip
我们下载安装试试,安装完成后,设备管理器显示如下:
现在就能直接看到对应关系了,very good。
二、TTL测试
使用另外一个USB转TTL模块与RainbowLink对接进行测试,使用RainbowLink的TTL1进行测试。
参数配置:9600,8N1,循环发送,周期10ms。(其实10ms太小了,不太合理)
两边都设置为相同的参数,这样Rainbowink收发就是同步进行,周期10ms,测试了大概10分钟,没有问题
直接将波特率调高吧,调到115200bps,计算一下,大概算一下传20个字节的时间:1000/115200*9*20 = 1.56ms。
那就将周期设置为3ms试试。
若干分钟后,也没发现什么问题。
再将波特率调到256000,周期保持3ms,实际上也没发现什么问题,还是没有看到发送和接收错误的情况。
我将波特率直接调到2000000bps,这时候很快就会发现另外一个TTL出错了,丢帧了,但是RainbowLink这端却未发现有错误,暂时还不好确定是RainbowLInk的问题还是测试的另外一个TTL的问题。
后面测了1500000bps,发现也是有问题的,但是1000000bps是没有问题的。
三、总结
经过测试,目前看来,波特率在1Mbps以下是能保证稳定通讯的,在往上就不太稳定了,会出现漏帧的问题。
当然,这不能完全证明是RainbowLink的问题,有可能是与之配合测试的那个TTL模块的问题,如果非要确认是谁的问题,可能需要找一个更加强大的TTL
工具。其实这个1Mbps已经是在官方宣称的128000范围以外了,其实我觉得达到这个水平已经足够日常使用了。
另外,这个测试不是最严苛的测试,最严苛的测试应该是4路串口一起以最快的速度通讯,如果后面有条件我也会做这个测试。
- 2024-12-30
-
发表了主题帖:
【RainbowLink USB 协议转换器】1、开箱
## 感谢
板子收到了,感谢EEWORLD和DFROBOT提供这么好的平台和这么好的板子。
接下来开始开箱。
## 开箱
1、外包装,貌似DFROBOT的包装盒都是这个样子的。
2、拆开外包装并拆开里面的塑料袋拿出板子,板子的上下各有一块亚克力板
3、撕掉上面的纸,可以看到亚克力板子上的丝印,确实很醒目很美观,很优雅。
4、拆下亚克力板,卡一看到板子的真容
上面的4个主要芯片我都已经标注在如下的图片中了
CH344Q是沁恒的USB转4路串口芯片
3232E是3Peak的TTL转232芯片
TP7576E是3Peak的TTL转485芯片
TPS61175是TI的升压芯片(3A 40V)
根据手册描述,电压输出能力如下:
RainbowLink 提供三种电压输出:12V/800mA、5V/2A、3.3V/200mA
还是相当不错的。
但是电源和485接口采用了压接端子,总感觉会很脆弱,容易坏,有点担心。
板子做过和颜值还是挺高的。
今天就到这里了,下次进行上电测试。
谢谢大家。
- 2024-12-29
-
加入了学习《Arduino? Nano RP2040 Connect 任务视频》,观看 串口工具初尝试
- 2024-12-24
-
回复了主题帖:
【测评入围名单】RainbowLink USB 协议转换器(RS485 / RS232 / TTL)
个人信息无误,确认可以完成测评分享计划
- 2024-12-06
-
回复了主题帖:
AI编程工具来了~
感觉跟cursor差不多呀
- 2024-11-26
-
回复了主题帖:
【颁奖】10月月度奖励发放!原创、提问讨论、回复答疑都有奖!
@okhxyyo, 问一下,这个奖励怎么领取呢?
- 2024-11-20
-
回复了主题帖:
STM32全球线上峰会,STM32N6重磅发布啦!
已报名
- 2024-11-11
-
回复了主题帖:
《动手学深度学习(PyTorch版)》7、循环神经网络
hellokitty_bean 发表于 2024-11-11 09:33
Wit兄加油哦。。。。。。。。。。。。。。。。。。。。。每天跟着你的步伐进步也蛮好
谦虚了兄弟,您才是大神
-
回复了主题帖:
《动手学深度学习(PyTorch版)》7、循环神经网络
hellokitty_bean 发表于 2024-11-11 09:32
个人感觉,至少要介绍RNN面临的问题,然后针对这些面临的问题又采取了哪些方法来解决
你是想说CNN遇到的问题吧?
-
回复了主题帖:
《动手学深度学习(PyTorch版)》7、循环神经网络
hellokitty_bean 发表于 2024-11-11 09:31
LSTM(Long Short Term Memory)要介绍不?。。。。。。。。
请hellokitty_bean兄给我们科普以下?
-
回复了主题帖:
《动手学深度学习(PyTorch版)》7、循环神经网络
Jacktang 发表于 2024-11-11 07:31
最后的这个循环神经网络还是比较麻烦的
挺麻烦的,没有那么好懂,理论公式太多了,难以理解。
- 2024-11-10
-
发表了主题帖:
《动手学深度学习(PyTorch版)》7、循环神经网络
一、循环神经网络产生的背景
到目前为止,我们遇到过两种类型的数据:表格数据和图像数据。对于图像数据,我们设计了专门的卷积神经网络架构来为这类特殊的数据结构建模。到目前为止我们默认数据都来自于某种分布, 并且所有样本都是独立同分布的 (independently and identically distributed,i.i.d.)。 然而,大多数的数据并非如此。 例如,文章中的单词是按顺序写的,如果顺序被随机地重排,就很难理解文章原始的意思。 同样,视频中的图像帧、对话中的音频信号以及网站上的浏览行为都是有顺序的。 因此,针对此类数据而设计特定模型,可能效果会更好。
另一个问题来自这样一个事实: 我们不仅仅可以接收一个序列作为输入,而是还可能期望继续猜测这个序列的后续。 例如,一个任务可以是继续预测2,4,6,8,10,…。
为了处理这种序列信息,循环神经网络(recurrent neural network,RNN)就从此诞生了。循环神经网络通过引入状态变量存储过去的信息和当前的输入,从而可以确定当前的输出。
二、序列模型的统计工具
处理序列数据需要统计工具和新的深度神经网络架构。 为了简单起见,我们以 如下所示的股票价格(富时100指数)为例。
其中,用xt表示价格,即在时间步(time step) t∈Z+时,观察到的价格xt。 请注意,t对于本文中的序列通常是离散的,并在整数或其子集上变化。 假设一个交易员想在t日的股市中表现良好,于是通过以下途径预测xt:
1、自回归模型
为了实现这个预测,可以使用回归模型,但是有一个主要的问题要解决:输入数据的数量, 输入xt−1,…,x1本身因t而异。也就是说,输入数据的数量这个数字将会随着我们遇到的数据量的增加而增加, 因此需要一个近似方法来使这个计算变得容易处理。
解决这个问题,由两种策略:
第一种策略,假设在现实情况下相当长的序列 xt−1,…,x1可能是不必要的, 因此我们只需要满足某个长度为τ的时间跨度, 即使用观测序列xt−1,…,xt−τ。 当下获得的最直接的好处就是参数的数量总是不变的, 至少在t>τ时如此,这就使我们能够训练一个上面提及的深度网络。 这种模型被称为自回归模型(autoregressive models), 因为它们是对自己执行回归。
第二种策略,如 下图所示, 是保留一些对过去观测的总结ht, 并且同时更新预测x^t和总结ht。 这就产生了基于x^t=P(xt∣ht)估计xt, 以及公式ht=g(ht−1,xt−1)更新的模型。 由于ht从未被观测到,这类模型也被称为 隐变量自回归模型(latent autoregressive models)。
序列的估计值公式:
2、马尔可夫模型
在自回归模型的近似法中, 我们使用xt−1,…,xt−τ 而不是xt−1,…,x1来估计xt。 只要这种是近似精确的,我们就说序列满足马尔可夫条件(Markov condition)。 特别是,如果τ=1,得到一个 一阶马尔可夫模型(first-order Markov model), P(x)由下式给出:
3、因果关系
原则上,将P(x1,…,xT)倒序展开也没什么问题。 毕竟,基于条件概率公式,我们总是可以写出:
事实上,如果基于一个马尔可夫模型, 我们还可以得到一个反向的条件概率分布。 然而,在许多情况下,数据存在一个自然的方向,即在时间上是前进的。 很明显,未来的事件不能影响过去。 因此,如果我们改变xt,可能会影响未来发生的事情xt+1,但不能反过来。 也就是说,如果我们改变xt,基于过去事件得到的分布不会改变。 因此,解释P(xt+1∣xt)应该比解释P(xt∣xt+1)更容易。
三、训练
在了解了上述统计工具后,让我们在实践中尝试一下! 首先,我们生成一些数据:使用正弦函数和一些可加性噪声来生成序列数据, 时间步为1,2,…,1000。
import torch
from torch import nn
from d2l import torch as d2l
T = 1000 # 总共产生1000个点
time = torch.arange(1, T + 1, dtype=torch.float32)
x = torch.sin(0.01 * time) + torch.normal(0, 0.2, (T,))
d2l.plot(time, [x], 'time', 'x', xlim=[1, 1000], figsize=(6, 3))
d2l.plt.show()
接下来,我们将这个序列转换为模型的特征-标签(feature-label)对。 基于嵌入维度τ,我们将数据映射为数据对yt=xt 和xt=[xt−τ,…,xt−1]。 这比我们提供的数据样本少了τ个, 因为我们没有足够的历史记录来描述前τ个数据样本。 一个简单的解决办法是:如果拥有足够长的序列就丢弃这几项; 另一个方法是用零填充序列。 在这里,我们仅使用前600个“特征-标签”对进行训练。
tau = 4
features = torch.zeros((T - tau, tau))
for i in range(tau):
features[:, i] = x[i: T - tau + i]
labels = x[tau:].reshape((-1, 1))
batch_size, n_train = 16, 600
# 只有前n_train个样本用于训练
train_iter = d2l.load_array((features[:n_train], labels[:n_train]),
batch_size, is_train=True)
使用一个相当简单的架构训练模型: 一个拥有两个全连接层的多层感知机,ReLU激活函数和平方损失。
# 初始化网络权重的函数
def init_weights(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
# 一个简单的多层感知机
def get_net():
net = nn.Sequential(nn.Linear(4, 10),
nn.ReLU(),
nn.Linear(10, 1))
net.apply(init_weights)
return net
# 平方损失。注意:MSELoss计算平方误差时不带系数1/2
loss = nn.MSELoss(reduction='none')
模型训练:
def train(net, train_iter, loss, epochs, lr):
trainer = torch.optim.Adam(net.parameters(), lr)
for epoch in range(epochs):
for X, y in train_iter:
trainer.zero_grad()
l = loss(net(X), y)
l.sum().backward()
trainer.step()
print(f'epoch {epoch + 1}, '
f'loss: {d2l.evaluate_loss(net, train_iter, loss):f}')
net = get_net()
train(net, train_iter, loss, 5, 0.01)
训练结果:
四、预测
由于训练损失很小,因此我们期望模型能有很好的工作效果。 让我们看看这在实践中意味着什么。 首先是检查模型预测下一个时间步的能力, 也就是单步预测(one-step-ahead prediction)。
onestep_preds = net(features)
d2l.plot([time, time[tau:]],
[x.detach().numpy(), onestep_preds.detach().numpy()], 'time',
'x', legend=['data', '1-step preds'], xlim=[1, 1000],
figsize=(6, 3))
五、文本预处理
对于序列数据处理问题,上面评估了所需的统计工具和预测时面临的挑战。 这样的数据存在许多种形式,文本是最常见例子之一。
我们将解析文本的常见预处理步骤。 这些步骤通常包括:
将文本作为字符串加载到内存中。
将字符串拆分为词元(如单词和字符)。
建立一个词表,将拆分的词元映射到数字索引。
将文本转换为数字索引序列,方便模型操作。
1、读取数据集
首先,我们从H.G.Well的时光机器中加载文本。 这是一个相当小的语料库,只有30000多个单词,但足够我们小试牛刀, 而现实中的文档集合可能会包含数十亿个单词。 下面的函数将数据集读取到由多条文本行组成的列表中,其中每条文本行都是一个字符串。 为简单起见,我们在这里忽略了标点符号和字母大写。
2、词元化
下面的tokenize函数将文本行列表(lines)作为输入, 列表中的每个元素是一个文本序列(如一条文本行)。 每个文本序列又被拆分成一个词元列表,词元(token)是文本的基本单位。 最后,返回一个由词元列表组成的列表,其中的每个词元都是一个字符串(string)。
def tokenize(lines, token='word'): #[url=home.php?mod=space&uid=472666]@save[/url] """将文本行拆分为单词或字符词元"""
if token == 'word':
return [line.split() for line in lines]
elif token == 'char':
return [list(line) for line in lines]
else:
print('错误:未知词元类型:' + token)
tokens = tokenize(lines)
for i in range(11):
print(tokens[i])
3、词表
词元的类型是字符串,而模型需要的输入是数字,因此这种类型不方便模型使用。 现在,让我们构建一个字典,通常也叫做词表(vocabulary), 用来将字符串类型的词元映射到从0开始的数字索引中。 我们先将训练集中的所有文档合并在一起,对它们的唯一词元进行统计, 得到的统计结果称之为语料(corpus)。 然后根据每个唯一词元的出现频率,为其分配一个数字索引。 很少出现的词元通常被移除,这可以降低复杂性。 另外,语料库中不存在或已删除的任何词元都将映射到一个特定的未知词元“<unk>”。 我们可以选择增加一个列表,用于保存那些被保留的词元, 例如:填充词元(“<pad>”); 序列开始词元(“<bos>”); 序列结束词元(“<eos>”)。
class Vocab: #@save
"""文本词表"""
def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):
if tokens is None:
tokens = []
if reserved_tokens is None:
reserved_tokens = []
# 按出现频率排序
counter = count_corpus(tokens)
self._token_freqs = sorted(counter.items(), key=lambda x: x[1],
reverse=True)
# 未知词元的索引为0
self.idx_to_token = ['<unk>'] + reserved_tokens
self.token_to_idx = {token: idx
for idx, token in enumerate(self.idx_to_token)}
for token, freq in self._token_freqs:
if freq < min_freq:
break
if token not in self.token_to_idx:
self.idx_to_token.append(token)
self.token_to_idx[token] = len(self.idx_to_token) - 1
def __len__(self):
return len(self.idx_to_token)
def __getitem__(self, tokens):
if not isinstance(tokens, (list, tuple)):
return self.token_to_idx.get(tokens, self.unk)
return [self.__getitem__(token) for token in tokens]
def to_tokens(self, indices):
if not isinstance(indices, (list, tuple)):
return self.idx_to_token[indices]
return [self.idx_to_token[index] for index in indices]
@property
def unk(self): # 未知词元的索引为0
return 0
@property
def token_freqs(self):
return self._token_freqs
def count_corpus(tokens): #@save
"""统计词元的频率"""
# 这里的tokens是1D列表或2D列表
if len(tokens) == 0 or isinstance(tokens[0], list):
# 将词元列表展平成一个列表
tokens = [token for line in tokens for token in line]
return collections.Counter(tokens)
我们首先使用时光机器数据集作为语料库来构建词表,然后打印前几个高频词元及其索引。
vocab = Vocab(tokens)
print(list(vocab.token_to_idx.items())[:10])
如下:
[('<unk>', 0), ('the', 1), ('i', 2), ('and', 3), ('of', 4), ('a', 5), ('to', 6), ('was', 7), ('in', 8), ('that', 9)]
现在,我们可以将每一条文本行转换成一个数字索引列表。
for i in [0, 10]:
print('文本:', tokens[i])
print('索引:', vocab[tokens[i]])
六、循环神经网络
循环神经网络(recurrent neural networks,RNNs) 是具有隐状态的神经网络。
假设我们在时间步t有小批量输入Xt∈Rn×d。 换言之,对于n个序列样本的小批量, Xt的每一行对应于来自该序列的时间步t处的一个样本。 接下来,用Ht∈Rn×h 表示时间步t的隐藏变量。 与多层感知机不同的是, 我们在这里保存了前一个时间步的隐藏变量Ht−1, 并引入了一个新的权重参数Whh∈Rh×h, 来描述如何在当前时间步中使用前一个时间步的隐藏变量。 具体地说,当前时间步隐藏变量由当前时间步的输入 与前一个时间步的隐藏变量一起计算得出:
从相邻时间步的隐藏变量Ht和 Ht−1之间的关系可知, 这些变量捕获并保留了序列直到其当前时间步的历史信息, 就如当前时间步下神经网络的状态或记忆, 因此这样的隐藏变量被称为隐状态(hidden state)。 由于在当前时间步中, 隐状态使用的定义与前一个时间步中使用的定义相同, 因此 上面公式的计算是循环的(recurrent)。 于是基于循环计算的隐状态神经网络被命名为 循环神经网络(recurrent neural network)。 在循环神经网络中执行 上面计算的层 称为循环层(recurrent layer)。
有许多不同的方法可以构建循环神经网络, 由 上式定义的隐状态的循环神经网络是非常常见的一种。 对于时间步t,输出层的输出类似于多层感知机中的计算:
循环神经网络的参数包括隐藏层的权重 Wxh∈Rd×h,Whh∈Rh×h和偏置bh∈R1×h, 以及输出层的权重Whq∈Rh×q 和偏置bq∈R1×q。 值得一提的是,即使在不同的时间步,循环神经网络也总是使用这些模型参数。 因此,循环神经网络的参数开销不会随着时间步的增加而增加。
后面还有循环神经网络的反向传播,涉及到非常多的理论计算,还暂时无法全部理解,感觉后面越来越难了,就先不进行介绍了,由兴趣的同学可以自行去看看。