lijinlei

个性签名:

MCU 开发者和爱好者

  • 2025-01-01
  • 回复了主题帖: 祝福2025!回帖即有奖!选取最有心的送5块国产开发板!

    回顾2024,测评了许多国产开发板,有灵动、芯源、雅特力、极海、敏矽微、富芮坤等等,还有高性能开发板如龙芯中科、赛昉·星光2等,切实感受到了国产芯片的崛起和壮大,硬件进步的同时,很欣慰地看到大家越来越重视软件和生态的建设,各大论坛也在不断助力推广国产芯片崛起……新的一年,我将继续活跃在论坛,为大家带来更多创新推文,为国产芯片的发展壮大添砖加瓦!

  • 2024-12-30
  • 发表了主题帖: #AI挑战营(进阶)# 模型训练

    # #AI挑战营(进阶)# 模型训练 在完成前面环境搭建的基础上,这里介绍手写数字识别的模型训练流程。 ## 1.准备工作 确认库函数安装完成,命令行终端执行如下代码 ```powershell pip install torchvision pip install tqdm pip install matplotlib ``` > - 这里需要 **tqdm** 库实现训练的可视化,它是一个动态显示库。 > - 需要 **matplotlib** 库用来实现科学绘图,呈现训练结果、保真度等结果,为后续的模型部署、可靠性等指标提供重要参考依据。 ## 2.流程图 ## 3.训练过程 ### 数据预处理 1.导入相关库函数 2.定义训练硬件基础,CPU 或 GPU,这里采用 CPU 训练 3.使用图片处理库 `torchvision.transforms` 将图片转换为网络张量 4.构建数据集,调用已下载的 MNIST 数据集、训练集和测试集,调用 `DataLoader` 函数进行训练; ### 模型训练 1.使用卷积神经网络基础结构 2.构建模型实例,将图形张量置于指定 CPU 或 GPU 设备, 3.构建迭代器与损失函数,使用 Adam 迭代器,使用交叉熵损失函数; 4.构建训练循环,即 `训练、验证、训练、验证 ...` 流程,构建训练的循环框架 5.代码测试 ```python #构造临时变量 #关闭模型的训练状态 #对测试集的DataLoader进行迭代 #存储测试结果 #计算总测试的平均准确率 #计算总测试的平均Loss #将本step结果进行可视化处理 ``` 6.训练的循环代码 ### 数据后处理 包括训练结果提取、**可视化**展示、模型**保存**等。 1.使用 `matplotlib` 实现可视化训练结果,展示损失函数、保真度反馈等信息; ```python #损失函数可视化 matplotlib.pyplot.plot(history['Test Loss'],label = 'Test Loss') matplotlib.pyplot.legend(loc='best') matplotlib.pyplot.grid(True) matplotlib.pyplot.xlabel('Epoch') matplotlib.pyplot.ylabel('Loss') matplotlib.pyplot.show() #保真度可视化 matplotlib.pyplot.plot(history['Test Accuracy'],color = 'red',label = 'Test Accuracy') matplotlib.pyplot.legend(loc='best') matplotlib.pyplot.grid(True) matplotlib.pyplot.xlabel('Epoch') matplotlib.pyplot.ylabel('Accuracy') matplotlib.pyplot.show() ``` 2.模型保存,可选择部分保存或整体模型保存; ```c++ torch.save(net,'./model.pth') ``` ## 4.代码 ```python # 初始化 import torch import torchvision from tqdm import tqdm import matplotlib # 构建模型 class Net(torch.nn.Module):     def __init__(self):         super(Net,self).__init__()         self.model = torch.nn.Sequential(             #The size of the picture is 28x28             torch.nn.Conv2d(in_channels = 1,out_channels = 16,kernel_size = 3,stride = 1,padding = 1),             torch.nn.ReLU(),             torch.nn.MaxPool2d(kernel_size = 2,stride = 2),                          #The size of the picture is 14x14             torch.nn.Conv2d(in_channels = 16,out_channels = 32,kernel_size = 3,stride = 1,padding = 1),             torch.nn.ReLU(),             torch.nn.MaxPool2d(kernel_size = 2,stride = 2),                          #The size of the picture is 7x7             torch.nn.Conv2d(in_channels = 32,out_channels = 64,kernel_size = 3,stride = 1,padding = 1),             torch.nn.ReLU(),                          torch.nn.Flatten(),             torch.nn.Linear(in_features = 7 * 7 * 64,out_features = 128),             torch.nn.ReLU(),             torch.nn.Linear(in_features = 128,out_features = 10),             torch.nn.Softmax(dim=1)         )              def forward(self,input):         output = self.model(input)         return output          # 图片张量转换 device = "cuda:0" if torch.cuda.is_available() else "cpu" transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),                                 torchvision.transforms.Normalize(mean = [0.5],std = [0.5])]) # 迭代器生成 BATCH_SIZE = 256 EPOCHS = 10               # 循环次数 trainData = torchvision.datasets.MNIST('./data/',train = True,transform = transform,download = True) testData = torchvision.datasets.MNIST('./data/',train = False,transform = transform) trainDataLoader = torch.utils.data.DataLoader(dataset = trainData,batch_size = BATCH_SIZE,shuffle = True) testDataLoader = torch.utils.data.DataLoader(dataset = testData,batch_size = BATCH_SIZE) # 模型实例 net = Net() print(net.to(device)) # 损失函数、迭代器 lossF = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(net.parameters()) # 构建循环框架 history = {'Test Loss':[],'Test Accuracy':[]} for epoch in range(1,EPOCHS + 1):     processBar = tqdm(trainDataLoader,unit = 'step')     net.train(True)     for step,(trainImgs,labels) in enumerate(processBar):         trainImgs = trainImgs.to(device)         labels = labels.to(device)         net.zero_grad()         outputs = net(trainImgs)         loss = lossF(outputs,labels)         predictions = torch.argmax(outputs, dim = 1)         accuracy = torch.sum(predictions == labels)/labels.shape[0]         loss.backward()         optimizer.step()         processBar.set_description("[%d/%d] Loss: %.4f, Acc: %.4f" %                                    (epoch,EPOCHS,loss.item(),accuracy.item()))                  if step == len(processBar)-1:             correct,totalLoss = 0,0             net.train(False)             with torch.no_grad():                 for testImgs,labels in testDataLoader:                     testImgs = testImgs.to(device)                     labels = labels.to(device)                     outputs = net(testImgs)                     loss = lossF(outputs,labels)                     predictions = torch.argmax(outputs,dim = 1)                                          totalLoss += loss                     correct += torch.sum(predictions == labels)                                          testAccuracy = correct/(BATCH_SIZE * len(testDataLoader))                     testLoss = totalLoss/len(testDataLoader)                     history['Test Loss'].append(testLoss.item())                     history['Test Accuracy'].append(testAccuracy.item())             # 单次循环结果的记录和可视化处理             processBar.set_description("[%d/%d] Loss: %.4f, Acc: %.4f, Test Loss: %.4f, Test Acc: %.4f" %                                    (epoch,EPOCHS,loss.item(),accuracy.item(),testLoss.item(),testAccuracy.item()))     processBar.close()     # 结果可视化 matplotlib.pyplot.plot(history['Test Loss'],label = 'Test Loss') matplotlib.pyplot.legend(loc='best') matplotlib.pyplot.grid(True) matplotlib.pyplot.xlabel('Epoch') matplotlib.pyplot.ylabel('Loss') matplotlib.pyplot.show() matplotlib.pyplot.plot(history['Test Accuracy'],color = 'red',label = 'Test Accuracy') matplotlib.pyplot.legend(loc='best') matplotlib.pyplot.grid(True) matplotlib.pyplot.xlabel('Epoch') matplotlib.pyplot.ylabel('Accuracy') matplotlib.pyplot.show() # 模型保存 torch.save(net,'./model.pth') ``` ## 5.效果 由于使用 CPU 进行训练,所需时间较长,迭代 10 次所需时间为 216 小时 > 这里使用的 CPU 型号为 Intel(R) Core(TM) **i5-10400** CPU @ **2.90GHz** ## 方案二 可使用如下代码测试,加快速度 基于 PyTorch 框架,采用 CNN 卷积神经网络实现 MNIST 手写数字识别 ### 代码 ```python import torch import numpy as np from matplotlib import pyplot as plt from torch.utils.data import DataLoader from torchvision import transforms from torchvision import datasets import torch.nn.functional as F """ 卷积运算 使用mnist数据集,和10-4,11类似的,只是这里:1.输出训练轮的acc 2.模型上使用torch.nn.Sequential """ # Super parameter ------------------------------------------------------------------------------------ batch_size = 64 learning_rate = 0.01 momentum = 0.5 EPOCH = 10 # Prepare dataset ------------------------------------------------------------------------------------ transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) # softmax归一化指数函数(https://blog.csdn.net/lz_peter/article/details/84574716),其中0.1307是mean均值和0.3081是std标准差 train_dataset = datasets.MNIST(root='./data/mnist', train=True, transform=transform, download=True)  # 本地没有就加上download=True test_dataset = datasets.MNIST(root='./data/mnist', train=False, transform=transform)  # train=True训练集,=False测试集 train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) fig = plt.figure() for i in range(12):     plt.subplot(3, 4, i+1)     plt.tight_layout()     plt.imshow(train_dataset.train_data, cmap='gray', interpolation='none')     plt.title("Labels: {}".format(train_dataset.train_labels))     plt.xticks([])     plt.yticks([]) plt.show() # 训练集乱序,测试集有序 # Design model using class ------------------------------------------------------------------------------ class Net(torch.nn.Module):     def __init__(self):         super(Net, self).__init__()         self.conv1 = torch.nn.Sequential(             torch.nn.Conv2d(1, 10, kernel_size=5),             torch.nn.ReLU(),             torch.nn.MaxPool2d(kernel_size=2),         )         self.conv2 = torch.nn.Sequential(             torch.nn.Conv2d(10, 20, kernel_size=5),             torch.nn.ReLU(),             torch.nn.MaxPool2d(kernel_size=2),         )         self.fc = torch.nn.Sequential(             torch.nn.Linear(320, 50),             torch.nn.Linear(50, 10),         )     def forward(self, x):         batch_size = x.size(0)         x = self.conv1(x)  # 一层卷积层,一层池化层,一层激活层(图是先卷积后激活再池化,差别不大)         x = self.conv2(x)  # 再来一次         x = x.view(batch_size, -1)  # flatten 变成全连接网络需要的输入 (batch, 20,4,4) ==> (batch,320), -1 此处自动算出的是320         x = self.fc(x)         return x  # 最后输出的是维度为10的,也就是(对应数学符号的0~9) model = Net() # Construct loss and optimizer ------------------------------------------------------------------------------ criterion = torch.nn.CrossEntropyLoss()  # 交叉熵损失 optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)  # lr学习率,momentum冲量 # Train and Test CLASS -------------------------------------------------------------------------------------- # 把单独的一轮一环封装在函数类里 def train(epoch):     running_loss = 0.0  # 这整个epoch的loss清零     running_total = 0     running_correct = 0     for batch_idx, data in enumerate(train_loader, 0):         inputs, target = data         optimizer.zero_grad()         # forward + backward + update         outputs = model(inputs)         loss = criterion(outputs, target)         loss.backward()         optimizer.step()         # 把运行中的loss累加起来,为了下面300次一除         running_loss += loss.item()         # 把运行中的准确率acc算出来         _, predicted = torch.max(outputs.data, dim=1)         running_total += inputs.shape[0]         running_correct += (predicted == target).sum().item()         if batch_idx % 300 == 299:  # 不想要每一次都出loss,浪费时间,选择每300次出一个平均损失,和准确率             print('[%d, %5d]: loss: %.3f , acc: %.2f %%'                   % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / running_total))             running_loss = 0.0  # 这小批300的loss清零             running_total = 0             running_correct = 0  # 这小批300的acc清零         # torch.save(model.state_dict(), './model_Mnist.pth')         # torch.save(optimizer.state_dict(), './optimizer_Mnist.pth') def test():     correct = 0     total = 0     with torch.no_grad():  # 测试集不用算梯度         for data in test_loader:             images, labels = data             outputs = model(images)             _, predicted = torch.max(outputs.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度,沿着行(第1个维度)去找1.最大值和2.最大值的下标             total += labels.size(0)  # 张量之间的比较运算             correct += (predicted == labels).sum().item()     acc = correct / total     print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch+1, EPOCH, 100 * acc))  # 求测试的准确率,正确数/总数     return acc # Start train and Test -------------------------------------------------------------------------------------- if __name__ == '__main__':     acc_list_test = []     for epoch in range(EPOCH):         train(epoch)         # if epoch % 10 == 9:  #每训练10轮 测试1次         acc_test = test()         acc_list_test.append(acc_test)     plt.plot(acc_list_test)     plt.xlabel('Epoch')     plt.ylabel('Accuracy On TestSet')     plt.show() ``` 代码文件见附件。 **训练结果** 训练识别精度演化曲线 训练过程 ## 6.总结 本文阐述了数字识别模型训练的整体逻辑框架,并使用 PyTorch 平台对 MNIST 数据集进行 10 次轮询模拟训练,取得了较好的实验结果,为后续模型部署做好准备。

  • 2024-12-26
  • 加入了学习《【Follow me第二季第4期】任务汇报》,观看 【Follow me第二季第4期】任务汇报

  • 2024-12-11
  • 回复了主题帖: 共读颁奖:《人工智能实践教程——从Python入门到机器学习》

    前段时间有事情耽搁了,这本书的完整测评如下: 【《人工智能实践教程》测评】卷积神经网络 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】神经网络 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】机器学习 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】Python 高级编程 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】Python 面向对象 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】Python 函数 - 嵌入式系统 - 电子工程世界-论坛 【《人工智能实践教程》测评】分支与循环 - 嵌入式系统 - 电子工程世界-论坛 《人工智能实践教程——从Python入门到机器学习》之运算符与循环 - 嵌入式系统 - 电子工程世界-论坛 《人工智能实践教程——从Python入门到机器学习》变量章节的学习和应用 - 嵌入式系统 - 电子工程世界-论坛 《人工智能实践教程 从Python入门到机器学习》阅读分享 - 编程基础 - 电子工程世界-论坛

  • 2024-12-10
  • 发表了主题帖: OLED 显示雷达数据

    # OLED 显示雷达数据 本文结合之前关于串口打印雷达监测数据的研究,进一步扩展至 OLED 屏幕显示。 该项目整体分为两部分: - 一、框架显示; - 二、数据采集与填充显示。 为了减小 MCU 负担,采用 **局部刷新** 的方案。 ## 1. 显示框架 所需库函数 `Wire.h` 、`Adafruit_GFX.h` 、`Adafruit_SSD1306.h` . ### 代码 ```c++ #include #include #include #include "logo_128x64.h" #include "logo_95x32.h" #define OLED_RESET 4 Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); void setup() {   Serial.begin(115200);   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)   display.clearDisplay(); // 清屏   display.drawBitmap(0, 0, logo, 128, 64, 1); //画出字符对应点阵数据   display.display();   delay(1000);   display.clearDisplay();   /*-------------------- Display picture and text ---------------------------*/   display.drawBitmap(16, 0, logo_small, 95, 32, 1);   display.setTextColor(WHITE);  //设置字体颜色   display.setTextSize(2);  //设置字体大小 1 is default 6x8, 2 is 12x16, 3 is 18x24   display.setCursor(0,33); //设置起始光标   display.print("v=");   display.setCursor(72,33); //设置起始光标   display.print("km/h");   display.setCursor(0,49); //设置起始光标   display.print("str=");   display.display(); } void loop() { } ``` ### 效果 ## 2. 显示数据 目标:实现雷达监测数据的对应填充显示,包括速度 `v` 和信号强度 `str` ### 代码 思路:将之前帖子中实现的串口打印数据与 OLED 显示框架结合,将 `v` 和 `str` 两数据分别填充至 OLED 屏预留位置处即可。 ```c++ #include #include #include #include "logo_128x64.h" #include "logo_95x32.h" #define OLED_RESET 4 Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); String comdata = ""; void setup() {   Serial.begin(115200);   while (Serial.read() >= 0){}//clear serialbuffer   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)   display.clearDisplay(); // 清屏   display.drawBitmap(0, 0, logo, 128, 64, 1); //画出字符对应点阵数据   display.display();   delay(1000);   display.clearDisplay();   /*-------------------- Display picture and text ---------------------------*/   display.drawBitmap(16, 0, logo_small, 95, 32, 1);   display.setTextColor(WHITE);  //设置字体颜色   display.setTextSize(2);  //设置字体大小 1 is default 6x8, 2 is 12x16, 3 is 18x24   display.setCursor(0,33); //设置起始光标   display.print("v=");   display.setCursor(80,33); //设置起始光标   display.print("km/h");   display.setCursor(0,49); //设置起始光标   display.print("str=");   display.display(); } void loop() {   if (Serial.available() > 0)   {     char data = Serial.read();     comdata += data;     if (data == '\n')     {// type of comdata: v=1.0 km/h, str=10151       int separatorIndex = comdata.indexOf(','); // 假设分隔符为逗号       if (separatorIndex != -1)       {         String part1 = comdata.substring(0, separatorIndex); // 第一个部分         String part2 = comdata.substring(separatorIndex + 1); // 第二个部分         // 打印分割后的数据         //Serial.println(part1); // type of part1: v=1.0 km/h         //Serial.println(part2); // type of part2:  str=10151         /*------------ part1 : v=1.0 km/h ----------*/         int part1separatorIndex = part1.indexOf('='); //index of '='         if (part1separatorIndex != -1)         {           String vlc = part1.substring(part1separatorIndex + 1); // index of velocity, type of vlc is 1.0 km/h           // vlc: 1.0 km/h           int VLCseparatorIndex = vlc.indexOf(' '); // index of ' '           String v = vlc.substring(0, VLCseparatorIndex);// v only include number           float Vn = v.toFloat();           Serial.print(Vn); // print velocity number           Serial.print(',');           //display.setCursor(25,33); //设置起始光标           display.fillRect(25, 33, 60, 16, BLACK);           display.display();           display.setCursor(25,33); //设置起始光标           display.print(Vn);           display.display();         }         /*------------- part2 :  str=10151 ------------------*/         int part2separatorIndex = part2.indexOf('='); //index of '='         if (part2separatorIndex != -1)         {           String strng = part2.substring(part2separatorIndex + 1); // strng only include number           int Sn = strng.toInt();           Serial.print(Sn); // print strength number           Serial.println();           //display.setCursor(49,49); //设置起始光标           display.fillRect(49, 49, 79, 16, BLACK);           //display.setPixelColor();           display.display();           display.setCursor(49,49); //设置起始光标           display.print(Sn);           display.display();         }       }       comdata = "";     }   } } ``` ### 效果 这里由于字体设置为 2 号,无法满足 km/h 单位的完整填充,因此被数据覆盖住一部分,可根据实际需求调整字体大小。

  • 2024-12-09
  • 发表了主题帖: Arduino 采集雷达模块数据与串口绘图

    # Arduino 采集雷达模块数据与串口绘图 当采用串口输出模式时,雷达检测到运动时,则输出 `v=0.0km/h, str=1234` 字样; `v` 表示目标速度大小,`str` 表示信号强度; 当雷达检测不到目标时,串口停止输出。 ## 项目实现 Arduino 串口采集雷达模块数据,并通过串口打印至接收窗口,绘制 **速度演化曲线** 和 **信号强度演化曲线** 。 ### 原始代码 ```c++ void setup() {   Serial.begin (115200);   while (Serial.read() >= 0){}//clear serialbuffer } String comdata = ""; void loop() {   if (Serial.available() > 0)   {     char data = Serial.read();     comdata += data;     if (data == '\n')     {        Serial.println (comdata);        comdata = "";     }   } } ``` ### 串口打印 注意到串口打印出的字符串既包含文字也包含数字,若要实现串口绘图,则需将其中的数字部分提取出来。 ### 代码升级 ```c++ String comdata = ""; void setup() {   Serial.begin (115200);   while (Serial.read() >= 0){}//clear serialbuffer } void loop() {   if (Serial.available() > 0)   {     char data = Serial.read();     comdata += data;     if (data == '\n')     {       // 分割字符串       int separatorIndex = comdata.indexOf(','); // 假设分隔符为逗号       if (separatorIndex != -1)       {         String part1 = comdata.substring(0, separatorIndex); // 第一个部分         String part2 = comdata.substring(separatorIndex + 1); // 第二个部分         // 打印分割后的数据         Serial.println(part1);         Serial.println(part2);       }       comdata = "";     }   } } ``` #### 效果 ### 数字提取 同理,对 `part1` 和 `part2` 进行分离提取索引数字 ```c++ String comdata = ""; void setup() {   Serial.begin (115200);   while (Serial.read() >= 0){}//clear serialbuffer } void loop() {   if (Serial.available() > 0)   {     char data = Serial.read();     comdata += data;     if (data == '\n')     {// type of comdata: v=1.0 km/h, str=10151       int separatorIndex = comdata.indexOf(','); // 假设分隔符为逗号       if (separatorIndex != -1)       {         String part1 = comdata.substring(0, separatorIndex); // 第一个部分         String part2 = comdata.substring(separatorIndex + 1); // 第二个部分         // 打印分割后的数据         //Serial.println(part1); // type of part1: v=1.0 km/h         //Serial.println(part2); // type of part2:  str=10151         /*------------ part1 : v=1.0 km/h ----------*/         int part1separatorIndex = part1.indexOf('='); //index of '='         if (part1separatorIndex != -1)         {           String vlc = part1.substring(part1separatorIndex + 1); // index of velocity, type of vlc is 1.0 km/h           // vlc: 1.0 km/h           int VLCseparatorIndex = vlc.indexOf(' '); // index of ' '           String v = vlc.substring(0, VLCseparatorIndex);// v only include number           float Vn = v.toFloat();           Serial.print(Vn); // print velocity number           Serial.print(',');         }         /*------------- part2 :  str=10151 ------------------*/         int part2separatorIndex = part2.indexOf('='); //index of '='         if (part2separatorIndex != -1)         {           String strng = part2.substring(part2separatorIndex + 1); // strng only include number           int Sn = strng.toInt();           Serial.print(Sn); // print strength number         }         Serial.println();       }       comdata = "";     }   } } ``` #### 流程图 串口打印数字 ### 串口绘图

  • 2024-12-06
  • 发表了主题帖: #AI挑战营(进阶)# 开发板硬件测试、开发环境部署

    # #AI挑战营(进阶)# 开发板硬件测试、开发环境部署 介绍了本项目的硬件和软件平台部分,包括 LuckFox Pico Max-RV1106 开发板、PyTorch 平台、MNIST 数据集, 以及 InsightFace,为后续数字识别和人脸识别的开发作铺垫。 ## LuckFox Pico Max-RV1106 开发板 RV1106 是一款专门用于**人工智能**相关应用的高度集成 **IPC 视觉处理器 SoC**。 它基于**单核 ARM Cortex-A7 32 位内核**,集成了 NEON 和 FPU,并内置 NPU 支持 INT4 / INT8 / INT16 混合运算,RV1106G2 计算能力高达 0.5TOPS,RV1106G3计算能力高达 1TOPS。 此外,它采用了全新的基于硬件的 ISP,支持多种算法加速器,如 HDR、3A、LSC、3DNR、2DNR、锐化、去雾、伽马校正等。同时,它还具有内置的16位DRAM DDR3L,可维持要求苛刻的内存带宽,以及内置的 POR,RTC,音频编解码器和 MAC PHY。 开发板支持多个接口,包括 GPIO、UART、SPI、I2C、USB 等,方便开发者进行快速开发和调试。值得注意的是,Luckfox Pico 全系列开发板支持 **buildroot** 和 **Ubuntu22.04** 系统,让开发者根据项目需求选择合适的系统,更灵活满足特定应用要求。 详见:[LUCKFOX WIKI](https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-quick-start/) . ### 开箱 ### RV1106 开发板简介 **Luckfox Pico Pro/Max** 是一款基于**瑞芯微 RV1106** 芯片具有高性价比的微型 **Linux** 开发板,旨在为开发者提供一个简单且高效的开发平台; 支持多种接口,包括 MIPI CSI、GPIO、UART、SPI、I2C、USB 等,便于快速开发和调试 #### 主要参数 | 项目                 | 参数                                                         | | -------------------- | ------------------------------------------------------------ | | 处理器               | Cortex A7@1.2GHz                                             | | 神经网络处理器 (NPU) | Pro 版本: 0.5TOPS,支持 int4、int8、int16 Max 版本: 1TOPS,支持 int4、int8、int16 | | 图像处理器 (ISP)     | 输入 5M @30fps                                               | | 内存                 | Pro 版本: 128MB DDR3L Max 版本: 256MB DDR3L                  | | USB                  | USB 2.0 Host/Device                                          | | 摄像头接口           | MIPI CSI 2-lane                                              | | GPIO                 | 26 个 GPIO 引脚                                              | | 网口                 | 10/100M Ethernet controller and embedded PHY                 | | 默认存储介质         | SPI NAND FLASH (256MB)                                       | ##### 高度集成 强劲性能 集成 CPU、NPU、ISP 等处理器 - 单核 ARM Cortex-A7 32 位内核,集成了 NEON 和 FPU - 内置瑞芯微自研第 4 代 NPU,运算精度高,支持 int4、int8、int16 混合量化,其中 RV1106G2 int8 算力为 0.5TOPS,RV1106G3 int8 算力为 1TOPS - 内置自研第 3 代 ISP3.2,支持 500 万像素,支持 HDR、WDR、多级降噪等多种图像增强和矫正算法 - 具有强大的编码性能,支持智能编码,根据场景自适应节省码流,比常规 CBR 模式码率节省 50% 以上,让拍摄画面既高清,体积更小,存储空间提升一倍 - 内置的 16 位 DRAM DDR3L,可维持要求苛刻的内存带宽 - 集成了内置的 POR,音频编解码器和 MAC PHY ##### 开发更简单 集成 Micro SD 卡座、摄像头接口与网口 ##### SLC NAND FLASH 为你提供更好使用体验 #### 资源简介 1. USB Type-C 接口:可用于供电与程序烧录 2. ACT-LED 3. BOOT 按键:上电时按下,进入下载模式 4. RESET 按键 5. Micro SD 卡座 6. RV1106:视觉处理器 SoC,内置 ARM Cortex-A7 内核 (注:Pro 版为 RV1106G2,Max 版为 RV1106G3) 7. RTC 电池接口:MX1.25 连接器,可用于接入 RTC 电池 8. CSI 摄像头接口:MIPI CSI 2-lane 9. RJ45 网口 10. USB 测试点:TP1:USB_N;TP2:USB_P 11. USB 检测测试点 12. W25N02KV:256MB SLC NAND FLASH 13. GPIO 接口 官方资料:https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-RV1106/Luckfox-Pico-Pro-Max/Luckfox-Pico-quick-start/ ### SPI NAND FLASH 镜像烧录 1.[下载](https://files.luckfox.com/wiki/Luckfox-Pico/Software/SocToolKit_v1.98_20240705_01_win.zip) 瑞芯微烧录工具,打开可执行文件,运行程序; 2.开发板选择 RV1106 ; 3.烧录固件步骤 (1) 选择 USB 设备,按住 BOOT 键连接电脑,松开 BOOT 键,软件会显示 MaskRom 设备; > 若提示无法识别设备,则更换数据线,重新按住 BOOT 键的同时,将USB端插入电脑接口即可正常识别。 (2) 加载固件的存放文件夹,重载 env 文件,并勾选所有项。 (3) 勾选 `重启设备` 选项,点击 `下载` 即可。 烧录完成后,开发板上的指示灯开始闪烁。 ### 串口调试 通过串口工具连接开发板并和其进行交互,为了确保通信效果,需要连接网线。 #### 硬件连接 开发板连接网线、TypeC数据线(保证供电)、USB转TTL线 其中 USB 转 TTL 可根据开发板引脚定义连接, 注意 RX 与 TX 交叉连接,确保数据线供电充足、网线连接无误。 [下载](https://files.luckfox.com/wiki/Core3566/MobaXterm_Portable_v22.0.zip) MobaXterm 远程登录软件, 解压并打开该软件,选择 Session - Serial,设置串口的波特率波特率为115200 进入命令主界面,此时按下开发板的 Reset 键重启,串口输出相应的启动信息,如下 ```c # [    4.722869] rk_gmac-dwmac ffa80000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx udhcpc: sending discover udhcpc: sending select for 172.27.35.5 udhcpc: lease of 172.27.35.5 obtained, lease time 36000 deleting routers adding dns 172.27.35.1 adding dns 202.120.80.2 # uname -a Linux Rockchip 5.10.110 #1 Wed Sep 27 18:02:28 CST 2023 armv7l GNU/Linux # df -h Filesystem                Size      Used Available Use% Mounted on ubi0:rootfs              21.9M      5.4M     16.5M  25% / devtmpfs                 90.9M         0     90.9M   0% /dev tmpfs                    91.0M         0     91.0M   0% /dev/shm tmpfs                    91.0M      4.0K     91.0M   0% /tmp tmpfs                    91.0M    120.0K     90.9M   0% /run /dev/ubi5_0              36.3M     17.1M     19.2M  47% /oem /dev/ubi6_0             136.5M     24.0K    136.5M   0% /userdata # ls media          linuxrc        usr            opt            dev lib64          root           var            oem            bin lib32          sbin           sys            mnt rockchip_test  proc           tmp            lib userdata       data           run            etc # ``` 输入相应的指令进行测试,获取内存分配等信息。 分区信息通过 `df -Th` 查询,挂载点包含的信息如下 - `ubi0:rootfs` 是根文件系统(`/`),挂载在 UBIFS(UBI Flash 文件系统)上 - `devtmpfs` 和 `tmpfs` 是虚拟文件系统,分别用于设备节点和临时文件的存储,它们是在内存中创建的,并不会占用实际的闪存空间 - `/dev/ubi4_0` 是 `oem` 分区,主要存放`RK`相关驱动 - `/dev/ubi5_0` 是 `userdata` 分区,主要存放的是`rkipc`配置文件和`MAC`地址等信息 `ls` 命令用于显示指定工作目录下之内容(列出目前工作目录所含之文件及子目录 )。 `cd` 命令用于改变当前工作目录的命令。 `df` 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计。 `touch` 命令用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。 `mkdir` 命令用于创建目录。 `cp` 命令主要用于复制文件或目录。 `mv` 用来为文件或目录改名、或将文件或目录移入其它位置。 `rm` 命令用于删除一个文件或者目录。 `chmod` 命令是控制用户对文件的权限的命令。 ### SSH 登录 将开发板通过网线连接至WiFi路由器,并且和Windows操作系统在同一个局域网,即电脑也需要和该路由器相连。 打开 MobaXterm 远程登录软件,选择 Session - SSH 在 Remote host 中输入开发板的静态 IP : 172.32.0.93 ## 开发环境搭建和平台部署 基于 PyTorch 框架,采用 CNN 卷积神经网络实现 MNIST 手写数字识别,由于手边没有 NVDIA 显卡,仅考虑使用 CPU 训练的情况。 ### PyTorch #### 简介 Pytorch 是 torch 的 Python 版本,是由 Facebook 开源的神经网络框架,专门针对 GPU 加速的深度神经网络(DNN)编程。 Torch 是一个经典的对多维矩阵数据进行操作的张量库,在机器学习和其他数学密集型应用有广泛应用。 与 Tensorflow 的静态计算图不同,PyTorch 的计算图是动态的,可以根据计算需要实时改变计算图。 #### 部署 **1**. 下载和安装 [Python 3.12.0](https://www.python.org/downloads/release/python-3120/) > 注意这里的 Python 版本需要和模型支持的版本对应,最新版本的 Python 可能暂不支持。 在 CMD 命令行或 PowerShell 输入 `python` 验证是否安装成功。 **2**. 部署 [PyTorch](https://pytorch.org/) . 选择对应的配置,复制命令 ```python pip3 install torch torchvision torchaudio ``` 在命令行终端或 PowerShell 中运行指令,即可完成部署。 **3**. 安装所需软件包 在命令行终端或 PowerShell 中运行如下指令 ```python pip3 install numpy pip3 install matplotlib pip3 install tqdm pip3 install torchvision ``` 安装和导入如下开发包 ```python import torch import numpy as np from matplotlib import pyplot as plt from torch.utils.data import DataLoader from torchvision import transforms from torchvision import datasets import torch.nn.functional as F ``` ### MNIST [MNIST](https://yann.lecun.com/exdb/mnist/)(modified national institute of standard and technology)数据集是由 Yann LeCun 等人 1994 年创建,包含 0~9 的手写数字数据库。 MNIST 数据集的原始数据来源于美国国家标准和技术研究院(national institute of standard and technology, NIST)的两个数据集:special database 1 和 special database 3,分别由 NIST 的员工和美国高中生手写的 0-9数字组成。 原始数据集是 128*128 像素的黑白图像,LeCun 等人将其调整后得到 28×28 的灰度图像,于是就有了现在的 MNIST . MNIST 包含如下四个数据集: - 训练数据集:train-images-idx3-ubyte.gz (9.45 MB,包含 60,000 个样本) - 训练数据集标签:train-labels-idx1-ubyte.gz(28.2 KB,包含 60,000 个标签) - 测试数据集:t10k-images-idx3-ubyte.gz(1.57 MB ,包含 10,000 个样本) - 测试数据集标签:t10k-labels-idx1-ubyte.gz(4.43 KB,包含 10,000 个样本的标签) 数据集可在官网下载,或附件下载。 ### InsightFace [InsightFace](https://insightface.ai/) 是先进的深度人脸分析数据库,包括用于高质量人脸识别的解决方案和自定义程序 SDK 开发,并提供先进工具推动各个行业的创造力和创新发展。 [**InsightFace**](https://insightface.ai/) 官网 #### 人脸识别训练 人脸识别作为计算机视觉领域的一个重要应用,已广泛应用于安全监控、智能支付、人机交互等多个场景。InsightFace 是一个基于深度学习的人脸识别框架,它以其高效的性能和准确性在业界广受好评。使用 InsightFace进行人脸识别训练。 #### 硬件 建议使用NVIDIA系列GPU,因为InsightFace在GPU上的训练效率远高于CPU #### 软件 操作系统:Linux Python 库:MXNet (InsightFace的原生框架) 、NumPy、OpenCV 等 #### 数据 ##### 数据库收集 收集包含多个人脸图像的数据集。数据集应包含不同人的多张照片,以便进行训练和验证 ##### 数据处理 **人脸对齐** 使用 MTCNN 等工具对人脸进行对齐,确保每张人脸都处于相似的位置和大小。InsightFace 源码中包含 MTCNN 的实现,可以直接使用 **数据转换** 将处理后的数据转换为 InsightFace 所需的格式,包括 lst文件和 rec 文件。 #### 模型训练 修改配置文件 训练命令执行 #### 模型验证与测试 1.建立需要验证的目标集合, 2.使用InsightFace 脚本对目标集进行验证, 3.分析验证结果,对模型进行优化 #### 应用 门禁系统、监控识别等 摄像头实时捕捉人脸信息,通过模型进行识别和身份验证。 参考:[从零到一:使用InsightFace进行人脸识别训练](https://developer.baidu.com/article/detail.html?id=3342112) . #### 人脸检测 人脸检测的方法比较多,项目提供两种人脸检测方法: 一种是基于MTCNN的通用人脸检测模型, 另一种是轻量化的、快速的RFB人脸检测模型; 这个两个模型都能实现人脸检测,并同时预测人脸的五个关键点(Landmark)。 这个两个模型都能实现人脸检测,并同时预测人脸的五个关键点(Landmark)。 | 模型  | Paper | 源码 | 说明                                                         | | :---: | :---: | :--: | :----------------------------------------------------------- | | MTCNN | Paper | Link | 支持人脸检测和人脸关键点检测(5个点)通用场景人脸检测,计算量较大,适合PC服务器部署 | |  RFB  | Paper | Link | 支持人脸检测和人脸关键点检测(5个点)轻量级人脸检测,适合简单场景人脸检测,计算量较小,适合嵌入式,开发板,Android等终端部署 | 人脸关键点(Landmark):共预测五个人脸关键点,分别为:左眼中心点,右眼中心点,鼻尖中心点以及左嘴角和右嘴角。 ##### 人脸识别的实现思路 利用OpenCV的estimateAffine2D()函数估计人脸关键点(5个landmark)和参考人脸关键点(reference landmark)的仿射变换矩阵M,再根据仿射变换矩阵M矫正人脸图像 ##### 人脸搜索(1:N) 1:N人脸搜索,将一张人脸和N张人脸进行比对,找出最相似的一张或多张人脸,即1:N人脸搜索。可用作人脸签到、人脸门禁、人员信息查询、安防监控等应用场景。

  • 发表了主题帖: 【Follow me第二季第4期】任务汇总

    本帖最后由 lijinlei 于 2024-12-6 16:12 编辑 【Follow me第二季第4期】任务汇总 很高兴参加 DigiKey 和 EEWorld 联合举办的【Follow me 第二季第 4 期】活动, 经过数周的学习、思考和实践,我对 Arduino Nano RP2040 Connect 开发板有了更深刻的理解。 感谢官方的技术支持和微信群里各位大佬的指点,感谢大家的帮助,感谢工作人员对参与者的大力支持。 下面我将此次活动的任务完成情况进行汇总和报告。 所有任务已在 DigiKey得捷技术专区 板块发布,链接如下 任务一(1):【Follow me第二季第4期】Nano RP2040 Connect 开发板简介、环境搭建、工程测试 ; 任务一(2):【Follow me第二季第4期】Blink、串口打印 ; 任务二:【Follow me第二季第4期】IMU 传感器 ; 任务三:【Follow me第二季第4期】PDM 麦克风 ; 任务四:【Follow me第二季第4期】PDM 呼吸灯 . 录制视频详见:【Follow me第二季第4期】任务汇报-EEWORLD大学堂 . 代码资料详见:【Follow me第二季第4期】任务代码资料汇总 . 项目演示视频     视频节点索引如下 任务一:00:56 任务二:03:45 任务三:08:07 任务四:14:41 硬件物料 物料名称 ARDUINO NANO RP2040 CONNECT 实物图片 该开发板的 DigiKey 官网介绍 外包装 Top view Bottom view 任务成果展示 包括4个任务,即 Blink 和串口打印、IMU 传感器数据的串口打印、PDM 麦克风数据和音频数据的串口打印、PDM 音频的 LED 亮度显示。 任务一(1):Nano RP2040 Connect 开发板简介、环境搭建、工程测试 功能丰富的 Arduino Nano RP2040 Connect 将新的 Raspberry Pi RP2040 微控制器引入 Nano 外形尺寸。 借助 U-blox Nina W102 模块,充分利用双核 32 位 Arm Cortex-M0+,通过蓝牙和 WiFi 连接制作物联网项目。 使用板载加速计、陀螺仪、RGB LED 和麦克风深入研究实际项目。 使用 Arduino Nano RP2040 Connect 以最少的工作量开发强大的嵌入式 AI 解决方案。 上电效果 板载 RGB 三色 LED 循环点亮。 1. 概述 Arduino NANO RP2040 Connect 它适合 Arduino Nano 外形尺寸,使其成为具有大功能的小板。 该板的大脑是 Raspberry Pi RP2040 芯片:双核 Arm Cortex M0+,运行频率 133MHz,264KB SRAM,片外 16MB 闪存。 板载连接选项包含 u-blox NINA-W102 无线电模块、完全兼容 Arduino Cloud、配有内置传感器、麦克风和运动感应,增加了电路板的探索深度。 Arduino Nano RP2040 Connect 是 RP2040 设备升级项目和释放新项目潜力的完美选择。 2. 特点 兼容 Arduino IoT Cloud 在 Arduino 的 IoT Cloud 上使用 MKR 板,这是一种确保所有互联事物安全通信的简单快速方法。 无线模块 u-blox NINA-W102 无线电模块使其成为唯一连接的 RP2040 选项,提供完整的 WiFi 802.11b/g/n 连接, 以及蓝牙和低功耗蓝牙 v4.2 传感器 板载几个非常有用的传感器。内置麦克风用于声音激活、音频控制甚至 AI 语音识别。具有 AI 功能的六轴智能 IMU 告诉电路板移动方向,并增加了跌倒感应和双击激活。 硬件 它可能是一个小板,但 Nano RP2040 Connect 具有硬件冲击力。与已建立的 Arduino Nano 外形尺寸相匹配,使其成为各种规模项目的完美升级。 内存 借助微处理器外部的 16MB 闪存,为代码和存储需求提供了充足的空间。 引脚 可编程 I/O 引脚具有多种功能;22 个数字端口、20 个带 PWM 端口和 8 个模拟端口。 兼容 Raspberry Pi Pico 适用于智能设备的智能软件选项。支持整个 RP2040 软件生态系统。 Arduino 扩展 支持 Arduino 编程语言、IDE 2.0 和所有这些出色的库。 Python 功能 对 MicroPython 的完全支持。获取 Nano RP2040 Connect,附带用于机器视觉项目的免费 OpenMV 许可证。 Arduino 云支持 直接 Web 浏览器对 Nano RP2040 Connect 进行编程和操作。 通过 Arduino IoT Remote 手机应用程序进行即时远程控制,通过无线方式上传工程。 3. 原理图 4. 引脚 基本引脚定义 PWM、UART、SPI、IIC ADC、USB、Timer、SIO、PIO RGB LED、SJ2 WiFi NINA-W102-00B、Microphoton、FLASH、RP2040、Oscillator Micro USB、Step Down Converter MP2322GQH Nano RP2040 Connect 与 Arduino Cloud 平台兼容,可快速构建 IoT 项目。 Python 支持:该板可以使用 MicroPython 进行编程,MicroPython 是 Python 编程语言的实现,附带 Python 标准库的子集。更多信息 . 机器学习:借助项目中时钟频率为 133 MHz 的高性能节能微处理器,开始使用 TinyML、TensorFlow Lite 或 Edge Impulse 进行机器学习。库文件 . 全向麦克风 :Nano RP2040 connect 随附 MP34DT06JTR 麦克风,可实时捕获和分析声音,可创建语音界面以通过声音控制外围设备。相关文档 . 蓝牙:该板支持蓝牙,可通过蓝牙控制外围设备。库文件 ArduinoBLE . 六轴 IMU :将加速度计和陀螺仪与专用机器学习核心相结合。LSM6DSOX 库文件 . 温度传感器:LSM6DSOX 传感器还具有嵌入式传感器,可通过库直接访问。库文件 PDM . 5. 环境搭建 1.下载和安装 Ardunio IDE ; 2.安装 Arduino NANO RP2040 开发板对应的软件包。 6. 工程案例 WiFi 控制 LED Control Built-in RGB LED over Wi-Fi with Nano RP2040 Connect 代码 加载示例代码,参考如上网址,并输入本地 WiFi 名称和对应的密码 #include <SPI.h> #include <WiFiNINA.h> ///////please enter your sensitive data in the Secret tab/arduino_secrets.h char ssid[] = "xxxx"; // your network SSID (name) char pass[] = "xxxx"; // your network password (use for WPA, or use as key for WEP) int keyIndex = 0; // your network key index number (needed only for WEP) int status = WL_IDLE_STATUS; WiFiServer server(80); void setup() { pinMode(LEDR, OUTPUT); pinMode(LEDG, OUTPUT); pinMode(LEDB, OUTPUT); Serial.begin(9600); // initialize serial communication // check for the WiFi module: if (WiFi.status() == WL_NO_MODULE) { Serial.println("Communication with WiFi module failed!"); // don't continue while (true); } String fv = WiFi.firmwareVersion(); if (fv < WIFI_FIRMWARE_LATEST_VERSION) { Serial.println("Please upgrade the firmware"); } // attempt to connect to WiFi network: while (status != WL_CONNECTED) { Serial.print("Attempting to connect to Network named: "); Serial.println(ssid); // print the network name (SSID); // Connect to WPA/WPA2 network. Change this line if using open or WEP network: status = WiFi.begin(ssid, pass); // wait 10 seconds for connection: delay(10000); } server.begin(); // start the web server on port 80 printWifiStatus(); // you're connected now, so print out the status } void loop() { WiFiClient client = server.available(); // listen for incoming clients if (client) { // if you get a client, Serial.println("new client"); // print a message out the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(); // the content of the HTTP response follows the header: client.print("<style>"); client.print(".container {margin: 0 auto; text-align: center; margin-top: 100px;}"); client.print("button {color: white; width: 100px; height: 100px;"); client.print("border-radius: 50%; margin: 20px; border: none; font-size: 20px; outline: none; transition: all 0.2s;}"); client.print(".red{background-color: rgb(196, 39, 39);}"); client.print(".green{background-color: rgb(39, 121, 39);}"); client.print(".blue {background-color: rgb(5, 87, 180);}"); client.print(".off{background-color: grey;}"); client.print("button:hover{cursor: pointer; opacity: 0.7;}"); client.print("</style>"); client.print("<div class='container'>"); client.print("<button class='red' type='submit' onmousedown='location.href=\"/RH\"'>ON</button>"); client.print("<button class='off' type='submit' onmousedown='location.href=\"/RL\"'>OFF</button><br>"); client.print("<button class='green' type='submit' onmousedown='location.href=\"/GH\"'>ON</button>"); client.print("<button class='off' type='submit' onmousedown='location.href=\"/GL\"'>OFF</button><br>"); client.print("<button class='blue' type='submit' onmousedown='location.href=\"/BH\"'>ON</button>"); client.print("<button class='off' type='submit' onmousedown='location.href=\"/BL\"'>OFF</button>"); client.print("</div>"); // The HTTP response ends with another blank line: client.println(); // break out of the while loop: break; } else { // if you got a newline, then clear currentLine: currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } // Check to see if the client request was /X if (currentLine.endsWith("GET /RH")) { digitalWrite(LEDR, HIGH); } if (currentLine.endsWith("GET /RL")) { digitalWrite(LEDR, LOW); } if (currentLine.endsWith("GET /GH")) { digitalWrite(LEDG, HIGH); } if (currentLine.endsWith("GET /GL")) { digitalWrite(LEDG, LOW); } if (currentLine.endsWith("GET /BH")) { digitalWrite(LEDB, HIGH); } if (currentLine.endsWith("GET /BL")) { digitalWrite(LEDB, LOW); } } } // close the connection: client.stop(); Serial.println("client disconnected"); } } void printWifiStatus() { // print the SSID of the network you're attached to: Serial.print("SSID: "); Serial.println(WiFi.SSID()); // print your board's IP address: IPAddress ip = WiFi.localIP(); Serial.print("IP Address: "); Serial.println(ip); // print the received signal strength: long rssi = WiFi.RSSI(); Serial.print("signal strength (RSSI):"); Serial.print(rssi); Serial.println(" dBm"); // print where to go in a browser: Serial.print("To see this page in action, open a browser to http://"); Serial.println(ip); } 连接开发板与电脑,选择对应的开发板型号、目标端口,上传项目至开发板即可, 打开串口监视器,即可获取当前通信网址和 LED 状态。 效果 通过串口读取 IP 地址,手机或电脑连接相同的局域网,浏览器访问 IP 即可按钮控制对应颜色的 LED 点亮或熄灭。 任务一(2):Blink、串口打印 1. Blink 所需板载元件:红色-绿色-蓝色 LED 由原理图可知,板载 RGB 三个 LED 为低电平点亮, 因此,要实现闪灯效果,只需要令 LEDR、LEDG、LEDB 循环输出低电平即可。 此外,由开发板拓扑块图可知,板载 RGB LED 与 WiFi 模块相连,而不是直接连接 RP2040, 因此在调用 LED 时需要加入头文件 WiFiNINA.h 以实现电平输出的控制。 流程图 代码 #include "WiFiNINA.h" void setup() { Serial.begin(9600); pinMode(LEDR, OUTPUT); pinMode(LEDG, OUTPUT); pinMode(LEDB, OUTPUT); } void loop() { // Red digitalWrite(LEDR, LOW); digitalWrite(LEDG, HIGH); digitalWrite(LEDB, HIGH); delay(500); // Green digitalWrite(LEDR, HIGH); digitalWrite(LEDG, LOW); digitalWrite(LEDB, HIGH); delay(500); // Blue digitalWrite(LEDR, HIGH); digitalWrite(LEDG, HIGH); digitalWrite(LEDB, LOW); delay(500); } 效果 2. 串口打印 所用板载元件:串口通信接口,即Type-C接口和数据线 流程图 代码 void setup() { Serial.begin(9600); } void loop() { Serial.println("Hello DigiKey & EEWorld!"); delay(500); } 波特率 9600 bps 效果 任务二:IMU 传感器 IMU,即惯性测量单元(Inertial Measurement Unit),是一种用于测量和报告物体的三个基本线性运动(加速度)和三个基本角运动(角速度)的电子设备。 IMU 原理是采用惯性定律实现的。 IMU 通常包括一组加速度计和陀螺仪,他们是惯性系统的核心部件,是影响惯性系统性能的主要因素。 所用元件:板载 IMU 传感器,Micro USB 数据线。 Arduino IDE 需要安装 Arduino_LSM6DSOX 库。 1. 加速度计 加速度计是一种用于测量加速度的机电设备。 这些力可能是静态的,例如重力的连续力,或者像许多移动设备一样,动态的,用于感应运动或振动。 插图从左至右分别展示了上下、左右、前后加速度感知的情况。 快速移动开发板可在串口监视器中观察到 x, y, z 三个方向的数值曲线发生变化。 代码 #include <Arduino_LSM6DSOX.h> void setup() { Serial.begin(9600); while (!Serial); if (!IMU.begin()) { Serial.println("Failed to initialize IMU!"); while (1); } Serial.print("Accelerometer sample rate = "); Serial.print(IMU.accelerationSampleRate()); Serial.println(" Hz"); Serial.println(); Serial.println("Acceleration in g's"); Serial.println("X\tY\tZ"); } void loop() { float x, y, z; if (IMU.accelerationAvailable()) { IMU.readAcceleration(x, y, z); Serial.print(x); Serial.print('\t'); Serial.print(y); Serial.print('\t'); Serial.println(z); } } 流程图 效果 应用 跌落检测 从实验结果可以看出,当开发板发生剧烈晃动时,其加速度发生明显改变,通过设定阈值,可以判断是否发生碰撞或跌落。 步数检测 结合人体行走或奔跑时的曲线姿态,制定相应的步态检测方案,如设定曲线抖动间隔、抖动幅值,对可能的走路、跳跃、奔跑、慢跑等多场景状态进行反馈,实现步数统计。 2. 陀螺仪 陀螺仪传感器是一种可以测量和保持物体的方向和角速度的设备。 陀螺仪比加速度计更先进,因为它们可以测量物体的倾斜和横向,而加速度计只能测量其线性运动。 陀螺仪传感器也称为“角速率传感器”或“角速度传感器”。角速度以 度/秒 为单位,是物体单位时间内旋转角度的变化。 插图从左至右分别描述了绕 z 轴、绕 x 轴、绕 y 轴旋转的情况。 旋转开发板可在串口监视器中观察到 x, y, z 三个数值曲线的变化。 代码 #include <Arduino_LSM6DSOX.h> void setup() { Serial.begin(9600); while (!Serial); if (!IMU.begin()) { Serial.println("Failed to initialize IMU!"); while (1); } Serial.print("Gyroscope sample rate = "); Serial.print(IMU.gyroscopeSampleRate()); Serial.println(" Hz"); Serial.println(); Serial.println("Gyroscope in degrees/second"); Serial.println("X\tY\tZ"); } void loop() { float x, y, z; if (IMU.gyroscopeAvailable()) { IMU.readGyroscope(x, y, z); Serial.print(x); Serial.print('\t'); Serial.print(y); Serial.print('\t'); Serial.println(z); } } 流程图 效果 应用 飞机俯仰角变化,游戏模拟器,游戏手柄等 3. 温度计 温度会影响 IMU 采样数据的稳定性,因此 IMU 传感器中包含高精度温度传感器,用来对温漂进行校正。 温度漂移 TAR(Temperature Accelerometer Random Walk) 描述IMU在温度变化时性能的稳定性。温度漂移小的IMU在不同环境条件下能够提供更一致的性能。 单位: 代码 #include <Arduino_LSM6DSOX.h> void setup() { Serial.begin(9600); while (!Serial); if (!IMU.begin()) { Serial.println("Failed to initialize IMU!"); while (1); } } void loop() { if (IMU.temperatureAvailable()) { int temperature_int = 0; float temperature_float = 0; IMU.readTemperature(temperature_int); IMU.readTemperatureFloat(temperature_float); Serial.print("LSM6DSOX Temperature = "); Serial.print(temperature_int); Serial.print(" ("); Serial.print(temperature_float); Serial.print(")"); Serial.println(" °C"); } } 流程图 效果 应用 手势识别、手势控制、温漂校准等。 4. Arduino Cloud 若使用 Arduino Cloud 编译代码,则首先需要对开发板进行固件升级 若开发板出现问题需要重置或初始化,也可采用 Firmware Updater 对开发板进行重置。 注意该升级过程需要时间较长,五分钟左右,当固件下载进度条走完后,需要保持连接,等待窗口显示 Successful 字样时表明升级完成。之后下载并安装 Arduino Cloud Agent 可成功连接 Arduino Cloud . 任务三:PDM 麦克风 Nano RP2040 connect 配有 MP34DT06JTR 麦克风,可用于录制音频。 本文介绍的项目实现:当录制到响亮的噪音(如打响指)时,打开或关闭内置的 RGB LED。 所需硬件:Arduino Nano RP2040 Connect 开发板、板载 MP34DT06JTR 麦克风传感器。 Arduino IDE 需安装 PDM 库。 详见:Microphone . 1. PDM 介绍 麦克风是将物理声音转换为数字数据的组件。 环境声音振动压缩空气,导致不同位置的空气密度分布存在差异,由此带来空气分子的波动传输,鼓膜线圈受到空气波动的影响而振动,线圈切割中心磁铁产生的磁感线,并形成感应电流,该感应电流经放大电路放大为模拟信号,传递给 MCU 模数转换器 ADC ,并转化为数字信号并通过串口输出。 麦克风通常用于移动终端、语音识别系统,甚至游戏和虚拟现实输入设备。 MP34DT06JTR 传感器是一种超小型麦克风,它使用 PDM(pulse density mdulation, 脉冲密度调制)来表示具有二进制信号的模拟信号。传感器的不同值范围如下: 信噪比:64 dB 灵敏度:-26 dBFS ±3 dB 温度范围:-40 至 85°C 微机电系统或 MEMS 是使用最初为集成电路 (IC) 开发的技术在硅上蚀刻和制造的。 二十世纪 90 年代以来,MEMS 技术创造了各种传感器和其他机电设备,包括麦克风。 MEMS 麦克风的封装如图所示,麦克风和单独的 ASIC(专用集成电路)组合在同一个封装中,通过引线键合连接。 MEMS 麦克风采用类似 IC 的封装,用于表面贴装组装。声音由顶端开口进入感知器。 MEMS 数字输出 具有数字输出的 MEMS 麦克风执行 ADC,将放大的模拟音频信号转换为数字信号。 使用delta-sigma 转换来产生 PDM(脉冲密度调制)输出,如图所示。 当音频信号较高时,高脉冲(蓝色)具有较高的密度。 2. 代码 #include <PDM.h> // default number of output channels static const char channels = 1; // default PCM output frequency static const int frequency = 16000; // Buffer to read samples into, each sample is 16-bits short sampleBuffer[512]; // Number of audio samples read volatile int samplesRead; void setup() { Serial.begin(9600); while (!Serial); // Configure the data receive callback PDM.onReceive(onPDMdata); if (!PDM.begin(channels, frequency)) { Serial.println("Failed to start PDM!"); while (1); } } void loop() { // Wait for samples to be read if (samplesRead) { // Print samples to the serial monitor or plotter for (int i = 0; i < samplesRead; i++) { if(channels == 2) { Serial.print("L:"); Serial.print(sampleBuffer[i]); Serial.print(" R:"); i++; } Serial.println(sampleBuffer[i]); } // Clear the read count samplesRead = 0; } } void onPDMdata() { // Query the number of available bytes int bytesAvailable = PDM.available(); // Read into the sample buffer PDM.read(sampleBuffer, bytesAvailable); // 16-bit, 2 bytes per sample samplesRead = bytesAvailable / 2; } 3. 流程图 4. 效果 安静状态串口输出数据测试 噪声状态下的数据输出测试 动态显示由 安静 到 噪声 再到 安静 状态的数据曲线 可见噪声状态下的数据值在 10000 以上,而安静状态仅为 100,灵敏度为 100 倍,可用作环境噪声监测。 音频波形 将音响设备对准板载 PDM 传感器,串口输出捕获的音频数据。 从曲线的变化可以看出,当警报声响起时,波形发生明显变化。 任务四:PDM 呼吸灯 本文介绍 选做任务一 的完成,即 通过RGB LED不同颜色、亮度显示PDM麦克风收到的声音大小。 主要分为两部分,首先实现 RGB LED 呼吸灯,之后结合 PDM 传感器实现声控彩色 LED。 1. RGB LED 呼吸灯 根据 Arduino Nano RP2040 Connect 开发板原理图可知,板载 RGB LED 为低电平点亮 实现思路 利用正弦波信号和 analogWrite 函数实现三色 LED 的亮度循环变化,以达到 呼吸 效果。 代码 #include "WiFiNINA.h" #define led1 LEDR #define led2 LEDG #define led3 LEDB float angle1; float angle2; float angle3; void setup() { pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); angle1 = 0.0; angle2 = 0.0 + PI/3.0; angle3 = 0.0 + 2.0*PI/3.0; } void loop() { angle1 += 0.01; angle2 += 0.01; angle3 += 0.01; if(angle1 >= PI) angle1 -= PI; if(angle2 >= PI) angle2 -= PI; if(angle3 >= PI) angle3 -= PI; int a = sin(angle1)*255; int b = sin(angle2)*255; int c = sin(angle3)*255; analogWrite(led1, a); analogWrite(led2, b); analogWrite(led3, c); delay(5); } 流程图 效果 2. PDM RGB LED 将 PDM 麦克风传感器采集到的信号数据,替换前面的呼吸灯正弦信号。 这里使用音频文件 alarm1.mp3 的测试数据,将其信号强度归一化,作为 LED 亮度的基准。 代码 #include "WiFiNINA.h" #include <PDM.h> #define led1 LEDR #define led2 LEDG #define led3 LEDB float angle1; float angle2; float angle3; static const char channels = 1; static const int frequency = 16000; short sampleBuffer[512]; volatile int samplesRead; void setup() { pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); Serial.begin(9600); while (!Serial); PDM.onReceive(onPDMdata); if (!PDM.begin(channels, frequency)) { Serial.println("Failed to start PDM!"); while (1); } } void loop() { if (samplesRead) { for (int i = 0; i < samplesRead; i++) { //Serial.println(sampleBuffer[i]); int Frq = map(sampleBuffer[i], 0, 20000, 0, 16000); Serial.println(Frq); int a = 255 - (sampleBuffer[i]/10000)*255; int b = 255 - (sampleBuffer[i]/10000)*255; int c = 255 - (sampleBuffer[i]/10000)*255; analogWrite(led1, a); analogWrite(led2, b); analogWrite(led3, c); } samplesRead = 0; } } void onPDMdata() { int bytesAvailable = PDM.available(); PDM.read(sampleBuffer, bytesAvailable); samplesRead = bytesAvailable / 2; } 流程图 效果 可以看出,LED 随声音的信号强度变化,亮度发生变化。 项目源码 视频上传详见:【Follow me第二季第4期】任务汇报-EEWORLD大学堂 . 代码资料详见:【Follow me第二季第4期】任务代码资料汇总 . 项目总结 本次活动主要针对 Arduino Nano RP2040 Connect 开发板完成了四个任务,实现了对板载传感器如 IMU 、PDM 等模块的测试。 代码基于 Arduino IDE 平台编辑和编译,大部分测试函数是封装好的,只需要安装对应的库文件并调用即可,大大节省了开发时间,对于跨专业和初学者十分友好。 开发板具有多种传感器,板载资源丰富,体积小巧,便于携带,可适用的应用场景广泛,如步数检测、姿态校正、声音检测、智能语音识别等,结合无线 WiFi 和蓝牙模块功能,可实现诸多物联网应用,可为嵌入式开发提供多样化的解决方案。 再次感谢 DigiKey 和 EEWorld 对 【Follow me第二季第4期】活动的支持,感谢微信群友们的陪伴和无私帮助。在扩展任务的学习过程中,产生了许多有趣的 idea,后续将进行测试,希望继续和大家共同交流学习,创造更多有趣的 DIY 项目。 希望 Follow me 活动越办越好,也希望越来越多的电子爱好者关注和参与 DigiKey 和 EEWorld 平台举办的活动,共同学习、分享和成长,用科技创造美好未来。

  • 上传了资料: 【Follow me第二季第4期】任务代码资料汇总

  • 发表了主题帖: 【Follow me第二季第4期】PDM 呼吸灯

    # 【Follow me第二季第4期】PDM 呼吸灯 本文介绍 **选做任务一** 的完成,即 通过RGB LED不同颜色、亮度显示PDM麦克风收到的声音大小。 主要分为两部分,首先实现 RGB LED 呼吸灯,之后结合 PDM 传感器实现声控彩色 LED。 ## 呼吸灯 根据 Arduino Nano RP2040 Connect 开发板原理图可知,板载 RGB LED 为低电平点亮 ### 实现思路 利用正弦波信号和 `analogWrite` 函数实现三色 LED 的亮度循环变化,以达到 **呼吸** 效果。 ### 代码 ```c++ #include "WiFiNINA.h" #define led1 LEDR #define led2 LEDG #define led3 LEDB float angle1; float angle2; float angle3; void setup() {   pinMode(led1, OUTPUT);   pinMode(led2, OUTPUT);   pinMode(led3, OUTPUT);   angle1 = 0.0;   angle2 = 0.0 + PI/3.0;   angle3 = 0.0 + 2.0*PI/3.0; } void loop() {   angle1 += 0.01;   angle2 += 0.01;   angle3 += 0.01;   if(angle1 >= PI)     angle1 -= PI;   if(angle2 >= PI)     angle2 -= PI;   if(angle3 >= PI)     angle3 -= PI;   int a = sin(angle1)*255;   int b = sin(angle2)*255;   int c = sin(angle3)*255;   analogWrite(led1, a);   analogWrite(led2, b);   analogWrite(led3, c);   delay(5); } ``` #### 流程图 ### 效果 ## PDM 呼吸灯 将 PDM 麦克风传感器采集到的信号数据,替换前面的呼吸灯正弦信号。 ### 代码 ```c++ #include "WiFiNINA.h" #include #define led1 LEDR #define led2 LEDG #define led3 LEDB float angle1; float angle2; float angle3; static const char channels = 1; static const int frequency = 16000; short sampleBuffer[512]; volatile int samplesRead; void setup() {   pinMode(led1, OUTPUT);   pinMode(led2, OUTPUT);   pinMode(led3, OUTPUT);   Serial.begin(9600);   while (!Serial);   PDM.onReceive(onPDMdata);   if (!PDM.begin(channels, frequency)) {     Serial.println("Failed to start PDM!");     while (1);   } } void loop() {   if (samplesRead) {     for (int i = 0; i < samplesRead; i++)     {       //Serial.println(sampleBuffer);       int Frq = map(sampleBuffer, 0, 20000, 0, 16000);       Serial.println(Frq);       int a = 255 - (sampleBuffer/10000)*255;       int b = 255 - (sampleBuffer/10000)*255;       int c = 255 - (sampleBuffer/10000)*255;       analogWrite(led1, a);       analogWrite(led2, b);       analogWrite(led3, c);     }     samplesRead = 0;   } } void onPDMdata() {   int bytesAvailable = PDM.available();   PDM.read(sampleBuffer, bytesAvailable);   samplesRead = bytesAvailable / 2; } ``` 这里使用音频文件 `alarm1.mp3` 的测试数据,将其信号强度归一化,作为 LED 亮度的基准。 #### 流程图 ### 效果 可以看出,LED 随声音的信号强度变化,亮度发生变化。

  • 2024-12-05
  • 加入了学习《直播回放: DigiKey FollowMe 第二季 第4期 Arduino Nano RP2040 Connect 任务讲解》,观看 Arduino Nano RP2040 Connect 任务讲解

  • 2024-12-04
  • 回复了主题帖: 【Follow me第二季第4期】IMU 传感器

    慕容雪花 发表于 2024-12-4 16:56 大佬,流程图用啥软件画 PowerPoint   

  • 回复了主题帖: STM32全球线上峰会,STM32N6重磅发布啦!

    已报名  

  • 回复了主题帖: 【Follow me第二季第4期】任务2:PDM麦克风的学习

    大佬,你的串口数据是用什么工具采集的啊?

  • 回复了主题帖: 【Follow me第二季第4期】PDM 麦克风

    秦天qintian0303 发表于 2024-12-4 10:43 测试了一下,感觉手机播放的声音就不容易被路上,可能主频不一样,说话的声音反应就比较明显 我用手机播放警报声,需要开大点声,曲线变化才明显,比如下面这个波形的声音   显示出来是这样 我这里没有采样数更多的串口绘图工具了,不过可以用LabVIEW或MATLAB制作个串口读取工具……

  • 发表了主题帖: 【Follow me第二季第4期】PDM 麦克风

    # 【Follow me第二季第4期】PDM 麦克风 Nano RP2040 connect 配有 **MP34DT06JTR** 麦克风,可用于录制音频。 本文介绍的项目实现:当录制到响亮的噪音(如打响指)时,打开或关闭内置的 RGB LED。 所需硬件:[Arduino Nano RP2040 Connect](https://store.arduino.cc/nano-rp2040-connect) 开发板、板载 MP34DT06JTR 麦克风传感器。 Arduino IDE 需安装 PDM 库。 详见:[Microphone](https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-microphone-basics/) . ## PDM 介绍 麦克风是将物理声音转换为数字数据的组件。 环境声音振动压缩空气,导致不同位置的空气密度分布存在差异,由此带来空气分子的波动传输,鼓膜线圈受到空气波动的影响而振动,线圈切割中心磁铁产生的磁感线,并形成感应电流,该感应电流经放大电路放大为模拟信号,传递给 MCU 模数转换器 ADC ,并转化为数字信号并通过串口输出。 麦克风通常用于移动终端、语音识别系统,甚至游戏和虚拟现实输入设备。 MP34DT06JTR 传感器是一种超小型麦克风,它使用 PDM(pulse density mdulation, 脉冲密度调制)来表示具有二进制信号的模拟信号。传感器的不同值范围如下: - 信噪比:64 dB - 灵敏度:-26 dBFS ±3 dB - 温度范围:-40 至 85°C 微机电系统或 MEMS 是使用最初为集成电路 (IC) 开发的技术在硅上蚀刻和制造的。 二十世纪 90 年代以来,MEMS 技术创造了各种传感器和其他机电设备,包括麦克风。 MEMS 麦克风的封装如图所示,麦克风和单独的 ASIC(专用集成电路)组合在同一个封装中,通过引线键合连接。 MEMS 麦克风采用类似 IC 的封装,用于表面贴装组装。声音由顶端开口进入感知器。 **MEMS 数字输出** 具有数字输出的 MEMS 麦克风执行 ADC,将放大的模拟音频信号转换为数字信号。 使用delta-sigma 转换来产生 PDM(脉冲密度调制)输出,如图所示。 当音频信号较高时,高脉冲(蓝色)具有较高的密度。 ## 代码 ```c++ #include // default number of output channels static const char channels = 1; // default PCM output frequency static const int frequency = 16000; // Buffer to read samples into, each sample is 16-bits short sampleBuffer[512]; // Number of audio samples read volatile int samplesRead; void setup() {   Serial.begin(9600);   while (!Serial);   // Configure the data receive callback   PDM.onReceive(onPDMdata);   if (!PDM.begin(channels, frequency)) {     Serial.println("Failed to start PDM!");     while (1);   } } void loop() {   // Wait for samples to be read   if (samplesRead) {     // Print samples to the serial monitor or plotter     for (int i = 0; i < samplesRead; i++) {       if(channels == 2) {         Serial.print("L:");         Serial.print(sampleBuffer);         Serial.print(" R:");         i++;       }       Serial.println(sampleBuffer);     }     // Clear the read count     samplesRead = 0;   } } void onPDMdata() {   // Query the number of available bytes   int bytesAvailable = PDM.available();   // Read into the sample buffer   PDM.read(sampleBuffer, bytesAvailable);   // 16-bit, 2 bytes per sample   samplesRead = bytesAvailable / 2; } ``` ### 流程图 ## 效果 安静状态串口输出数据测试 噪声状态下的数据输出测试 动态显示由 **安静** 到 **噪声** 再到 **安静** 状态的数据曲线 可见噪声状态下的数据值在 10000 以上,而安静状态仅为 100,灵敏度为 100 倍,可用作环境噪声监测。 ### 音频波形 将音响设备对准板载 PDM 传感器,串口输出捕获的音频数据。 从曲线的变化可以看出,当警报声响起时,波形发生明显变化。

  • 2024-12-03
  • 评论了课程: EEWORLD大学堂----直播回放: DigiKey FollowMe 第二季 第4期 Arduino Nano RP2040 Connect 任务讲解

    讲得很仔细,特别是原理部分,程序代码分析得也很详细

  • 回复了主题帖: 【Follow me第二季第4期】IMU 传感器

    gtq 发表于 2024-12-3 16:02 哇塞,学到啦,超感谢!多多积累经验哈,盼着有那么一天能派上用场呢。 这款开发板的板载传感器较多,文中提到的 IMU 传感器在智能手机、手表、手环等可穿戴设备中应用较多,得益于在保持精度的前提下器件微型化技术的更新,因此还是有很多可以拓展的应用场景,如步数统计、姿态检测、速度监测等。

  • 回复了主题帖: 【Follow me第二季第4期】Blink、串口打印

    gtq 发表于 2024-12-3 15:57 哇塞,学到啦,超感谢!多多积累经验哈,盼着有那么一天能派上用场呢。 感谢关注,点灯这里还可以加上渐变功能,实现彩色呼吸灯效果,后面再更新一下

  • 回复了主题帖: 【Follow me第二季第4期】Nano RP2040 Connect 开发板简介、环境搭建、工程测试

    怀66 发表于 2024-12-3 15:25 点赞👍,写的很详细,对我这种小白很友好呀。 开发板用户手册更详细,Nano RP2040 Connect | Arduino Documentation 不过官网信息量比较大,在完成具体项目时,需要梳理出对项目有用的重点部分

最近访客

< 1/6 >

统计信息

已有81人来访过

  • 芯积分:341
  • 好友:2
  • 主题:36
  • 回复:31

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言