目录
树莓派模块
本项目所用到的树莓派型号是3B,烧写的固件是官方的固件。固件版本是:2019-04-08-raspbian-stretch.img。也可以从官网或百度云下载。百度云下载地址,提取码:fdnb。下载后用烧写工具烧写好后,插入树莓派即可。
可以通过以下方式登录树莓派:
- 通过HDMI线连接树莓派和带有HDMI接口的显示器进行登录
- 修改已经烧写好的固件中的配置文件来进行串口登录
- 通过SSH方式登录
- 树莓派安装xrdp开源工具,然后可以通过windows工具或其他工具来发访问Linux远程桌面
以上第3,第4种方式都是基于第一或者第二种方式登录之后进行设置才可以的。由于串口登录和SSH登录比较常见,下面我们简单说下这两种登录方式。
1-串口方式登录
1-1:修改配置:用读卡器打开SD卡根目录的"config.txt"文件,将以下内容添加在文件的最后并保存
dtoverlay=pi3-miniuart-bt
1-2:然后再修改根目录的"cmdline.txt",将里面的内容全部替换成以下内容,为了安全起见,请先备份好这个文件的原内容,再将里面的内容替换成下面的内容。
dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
1-3:修改好上面的配置文件后,把内存卡插入树莓派上电后即可用串口方式登录。
1-4:下载软件 SecureCRT或MobaXterm 。
1-5:USB -TTL 转换模块的VCC,GND分别连接到树莓派的VCC和GND,USB -TTL 转换模块的RXD,TXD分别和树莓派的RXD和TXD进行交叉连接。
1-6:打开安装好的SecureCRT软件,选择串口连接。波特率默认115200,选择自己的USB-TTL模块对应的com口即可。用户名默认为:pi 密码默认为:raspberry
2-SSH方式登录
2-1:在树莓派的Linux命令终端输入以下命令:
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
2-2:在文件末尾添加以下内容,并替换掉ssid_name以及password (需要连接的无线名称和密码)
network={
ssid="ssid_name"
psk="password"
key_mgmt=WPA-PSK //加密方式,可不写
}
2-3:保存并退出:Ctrl + o 保存 Ctrl + x 退出。
2-4:重启树莓派让上面的修改生效,输入命令即可重启:sudo reboot
2-5:由于路由器默认开启了DHCP模式,每次开启树莓派ip地址可能会变掉,所以我们应该固定ip地址,这样保证每次开机ip地址都是不变的,保证了第一次设置的SSH登录方式不会失效。
2-6:输入以下命令:
sudo nano /etc/rc.local
2-7:在 exit 0 上面添加:
ifconfig wlan0 192.168.3.18 红色字段为你想要固定的ip地址,根据自己的网段进行修改
2-8:保存并退出:Ctrl + o 保存 Ctrl + x 退出。
2-9:这样设置并不能登录树莓派,我们还要进行下一步的设置。
2-10: 输入命令 : sudo raspi-config 回车按下。
选择第5项-回车
选择第2项-回车
选择Yes-回车
选择Ok-回车
选择Finsh-回车-退出
至此SSH登录就设置好了,如果觉得默认登录密码记不住可以选择第一项:Change User Password进行修改登录密码。
1-wiringPi库的安装
wringpi在raspbian系统中都有预装,如果想要重新安装和更新可以执行以下命令:
sudo apt-get install wringpi
sudo apt-get upgrade wiringpi
安装成功后可以通过:gpio -v来查看wiringPi库的版本信息。
还可以通过命令:gpio readall 来查看树莓派的外部引脚
2-串口相关API函数说明
函数原型返回值说明
int serialOpen(char* device, int baud);
成功返回串口文件描述符,失败返回-1
d evice:串口的地址,在Linux中就是设备所在的目录。
默认一般是"/dev/ttyAMA0"。
baud:波特率
void serialClose(int fd);
无
关闭fd关联的串口
void serialPutchar(int fd,
unsigned char c);
无
fd:serialOpen()函数返回的文件描述符
c:要发送的数据
发送一个字节的数据到串口
void serialPuts(int fd, char* s);
无
fd:serialOpen()函数返回的文件描述符
s:发送的字符串,字符串要以'\0'结尾。
发送一个字符串到串口
void serialPrintf(int fd,char*message, ...);
无
fd:serialOpen()函数返回的文件描述符
message:格式化的字符串
像使用C语言中的printf一样发送数据到串口
int serialDataAvail(int fd);
串口缓存中已经接收的,可读取的字节数,-1代表错误
fd:serialOpen()函数返回的文件描述符
获取串口缓存中可用的字节数。
int serialGetchar(int fd);
返回:读取到的字符
从串口读取一个字节数据返回。
如果串口缓存中没有可用的数据,则会等待10秒,如果10秒后还有没,返回-1
所以,在读取前, 最 好通过serialDataAvail判断下。
void serialFlush(int fd);
无
刷新,清空串口缓冲中的所有可用的数据。
* size_t write (int fd,const void * buf,**
size_t count);
返回:实际写入的字符数,错误返回-1
fd:文件描述符,buf:需要发送的数据缓存数组,count:发送buf中的前count个字节数据。
这个是Linux下的标准IO库函数,需要包含头文件#include
当要发送到的数据量过大时,wiringPi建议使用这个函数。
* size_t read(int fd,void * buf ,size_t count);**
返回:实际读取的字符数。
fd:文件描述符,buf:接受的数据缓存的数组,count:接收的字节数。
这个是Linux下的标准IO库函数,需要包含头文件#include
当要发送到的数据量过大时,wiringPi建议使用这个函数。
除了上面的串口相关的函数之外,本模块还用到了硬件初始化函数和GPIO控制函数,
下面列出常用的硬件初始化函数和GPIO初始化函数,更多关于wiringPi库函数请参考: http://wiringpi.com/reference/
函数原型返回值
int wiringPiSetup (void) ;
成功:返回执行状态
失败:返回 -1
①使用这个函数初始化树莓派引脚时,程序使用的是wiringPi引脚编号,引脚编号为0~16。需要root权限
int wiringPiSetupGpio(void);
成功:返回执行状态
失败:返回 -1
①当使用这个函数初始 化树莓派引脚时,程序中使用的是BCM GPIO 引脚编号表。
需要root权限
void pinMode(int pin, int mode);
①pin:所配置的引脚,mode:指定引脚的I/O模式
引脚的模式有:INPUT、OUTPUT、PWM_OUTPUT或GPIO_CLOCK。注意只有wiringPi引脚1 (BCM_GPIO 18)支持PWM输出,只有wiringPi引脚7 (BCM_GPIO 4)支持CLOCK输出模式。
②在Sys模式下,此功能无效。如果需要更改pin模式,那么可以在启动程序之前在脚本中使用gpio程序进行更改。
void digitalWrite (int pin,int value);
①pin:所配置的引脚,value:配置引脚的电平。(可取的值为HIGH(高电平)或LOW(低电平))。
②在这之前该引脚必须配置为输出模式。
int digitalRead (int pin) ;
引脚上的电平值(0或1)
pin:所要读取的引脚。
void pwmWrite (int pin, int value) ;
①pin:所配置的引脚,value:配置引脚的PWM值。
②将给定引脚的值写入PWM寄存器。树莓派有一个板载PWM引脚。pin只能是wiringPi 引脚编号下的1脚(BCM下的18脚)。
void pullUpDnControl (int pin, int pud) ;
①pin:所配置的引脚(该引脚应被置为输入),pud:拉电阻模式。
拉电阻的模式有:PUD_OFF(不设置上拉/下拉)、PUD_DOWN(引脚电平拉到地面)、PUD_UP(引脚电平拉到3.3V)。
②与Arduino不同的是,BCM2835(支持上拉和下拉更丰富)有上拉和下拉的内部电阻(约50KΩ)。
③该函数在Sys模式下对树莓派的GPIO管脚没有影响。如果您需要激活上拉/下拉,那么您可以在启动程序之前在脚本中使用gpio程序。
3-代码的编写
#include <stdio.h>
#include <stdlib.h>
#include <wiringpi.h>
#include <wiringserial.h>
#include <string.h>
#include <unistd.h>
#define SWITCH 7
int main()
{
int fd;
int nRead = 0;
char *getString = NULL;
getString = (char *)malloc(sizeof(char)*25);
memset(getString,'\0',25);
if(wiringPiSetup() == -1)
{
printf("Setup wiringPi fail\n");
exit(-1);
}
if((fd = serialOpen("/dev/ttyAMA0",9600)) == -1)
{
printf("Open seial fail\n");
exit(-1);
}
pinMode(SWITCH,OUTPUT);
while(1)
{
nRead = read(fd,getString,25);
if(nRead > 0 && (strcmp(getString,"Received")) != 0)
{
if((strcmp(getString,"CODE_DKD")) == 0)
{
digitalWrite(SWITCH,HIGH);
printf("Open successfully\n");
}
if((strcmp(getString,"CODE_GBD")) == 0)
{
digitalWrite(SWITCH,LOW);
printf("Closed successfully\n");
}
memset(getString,'\0',25);
nRead = 0;
}
else
{
memset(getString,'\0',25);
}
}
return 0;
}
</unistd.h></string.h></wiringserial.h></wiringpi.h></stdlib.h></stdio.h>
LD3320模块
LD3320是非特定人语音识别芯片,即语音声控芯片。最多可以识别50条预先内置的指令。
工作模式
普通模式:直接说话,模块直接识别。
按键模式:按键触发开始ASR进程。
口令模式:需要一级唤醒词(类似于苹果手机中的唤醒Siri,"嗨,Siri"。)
1-代码分析
sRecog[DATE_A][DATE_B]中的二维数组就是要添加的口令,所说的口令,拼音打出用空格隔开即可,而pCode[DATA_A]数组中存放的是宏定义,在User_handle(nAsrRes);函数中能用到,可以根据,添加的口令,进行修改宏定义。
根据口令识别的结果可通过串口进行打印,用户可以自己修改 。
最后要说明的几点:
1-树莓派在读取串口缓冲区的字符时,如果超过8个字符时,会造成判断失误。
解决方案有两种:
1.修改User_handle()函数发送到串口缓冲区中字符的长度应
Original: https://blog.csdn.net/weixin_40471635/article/details/125731931
Author: Davide-
Title: 基于树莓派和LD3320模块的语音识别控制
相关阅读1
Title: tf.keras.preprocessing.text.Tokenizer函数
tf.keras.preprocessing.text.Tokenizer(
num_words=None,
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
lower=True,
split=' ',
char_level=False,
oov_token=None,
document_count=0,
**kwargs
)
</=>
Tokenizer函数用于创建一个分词器对象tokenizer。参数num_words用于表示应该保持的最大的单词数,如果小于输入数据所构成词汇表的不同的单词数,则一部分单词会被删掉。默认值为None,表示应该保持的单词数和词汇表的一致。
参数filters表示应该被过滤的单词或者字符。参数lower表示输入的大写字母是否应该转换成小写字母,默认为True。参数split表示用于分词的分隔符,默认情况下,文本变成以空格分隔的单词序列(单词可能包括 ' 字符)。
参数char_level表示是对一个单词进行分割还是对一个字符进行分割,默认为True,表示对一个字符进行分割。
参数oov_token如果给定,它将被添加到 word_index 并用于在 text_to_sequence 调用期间替换词汇表外的单词。
tokenizer对象具有以下常用属性和方法:
1、fit_on_texts(texts):根据文本来更新内部词汇表,如果texts为一个字符串,那么结果都将按照字符划分;如果texts为一个字符串列表,比如["hello world"]、["hello", "world"],则如果设置char_level=False,分词器按照单词划分。
2、get_config():获取分词器的配置,里面有word_index、index_word、word_docs、index_docs、document_count、word_counts等常用的信息。
3、sequences_to_texts(sequences):将数字序列转换成文本,形式为[[4], [3], [2], [2]]或者[[4, 3, 2, 2]]的列表或者numpy数组。
4、texts_to_sequences(texts):将文本转换成数字序列。
5、word_index:一个将文本映射为数字的字典
6、index_word:一个将数字映射为文本的字典
# 根据字符划分
# 创建一个分词器
>>> tokenizer = tf.keras.preprocessing.text.Tokenizer()
>>> text = "hello tensorflow"
# 根据文本更新内部分词器信息
>>> tokenizer.fit_on_texts(text)
# 获取配置信息
>>> config = tokenizer.get_config()
>>> config
{
'num_words': None,
'filters': '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
'lower': True,
'split': ' ',
'char_level': False,
'oov_token': None,
'document_count': 16,
'word_counts': '{"h": 1, "e": 2, "l": 3, "o": 3, "t": 1, "n": 1, "s": 1, "r": 1, "f": 1, "w": 1}',
'word_docs': '{"h": 1, "e": 2, "l": 3, "o": 3, "t": 1, "n": 1, "s": 1, "r": 1, "f": 1, "w": 1}',
'index_docs': '{"4": 1, "3": 2, "1": 3, "2": 3, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1}',
'index_word': '{"1": "l", "2": "o", "3": "e", "4": "h", "5": "t", "6": "n", "7": "s", "8": "r", "9": "f", "10": "w"}',
'word_index': '{"l": 1, "o": 2, "e": 3, "h": 4, "t": 5, "n": 6, "s": 7, "r": 8, "f": 9, "w": 10}'
}
# 获取word_index字典
>>> tokenizer.word_index
'{"l": 1, "o": 2, "e": 3, "h": 4, "t": 5, "n": 6, "s": 7, "r": 8, "f": 9, "w": 10}'
# 有时需要获取词汇表的vocab_size
>>> vocab_size = len(tokenizer.word_index)
>>> vacab_size
10
# 文本转换成序列
>>> seq = tokenizer.texts_to_sequences("hello")
>>> seq
[[4], [3], [1], [1], [2]]
# 序列转换成文本,三种情况
>>> texts = tokenizer.sequences_to_texts([[4],[3],[1],[1],[2]])
>>> texts
['h', 'e', 'l', 'l', 'o']
>>> texts = tokenizer.sequences_to_texts([[4, 3, 1, 1, 2]])
>>> texts
['h e l l o']
>>> texts = tokenizer.sequences_to_texts(np.array([[4, 3, 1, 1, 2]]))
>>> texts
['h e l l o']
# 根据单词划分
>>> tokenizer = tf.keras.preprocessing.text.Tokenizer()
>>> texts = "hello tensorflow"
>>> text_list = tf.keras.preprocessing.text.text_to_word_sequence(texts)
>>> text_list
['hello', 'tensorflow']
>>> tokenizer.fit_on_texts(text_list)
>>> tokenizer.texts_to_sequences(text_list)
[[1], [2]]
>>> texts = "hello tensorflow"
>>> tokenizer.texts_to_sequences([texts])
[[1, 2]]
>>> tokenizer.word_index
{'hello': 1, 'tensorflow': 2}
# 设置参数oov_token
>>> tokenizer = tf.keras.preprocessing.text.Tokenizer(oov_token="mask")
>>> tokenizer.fit_on_texts(text_list)
>>> tokenizer.sequences_to_texts([[1, 2]])
['mask hello']
>>> tokenizer.word_index
{'mask': 1, 'hello': 2, 'tensorflow': 3}
</=>
Original: https://blog.csdn.net/weixin_49346755/article/details/124558309
Author: 不负韶华ღ
Title: tf.keras.preprocessing.text.Tokenizer函数
相关阅读2
Title: 关于 FLOPS、FLOPs、参数量的相关计算
最近找到一些计算FLOPs的文章,奈何全是水文,讲都讲不清楚,完完全全的究极缝合怪。因此,这里准备彻底搞懂。
FLOPS:全称:FLoating point Operations Per Second的缩写,即 每秒浮点运算次数,或表示为计算速度。是一个衡量 硬件性能的指标。通俗点讲 显卡算力,对应英伟达官网的那些:GPU算力排行榜。
这才是本文的重点,FLOPs:FLoating point OPerationS 即 浮点计算次数,包含乘法和加法,只和模型有关,可以用来衡量其复杂度。多提一嘴,论文里面的FLOPs有的计算也并不明确,包括很多 Github 开源代码里面采用的 MACs,也就是考虑一次乘+加法运算为一次 MAC,粗略换算的话:FLOPs = 2 × MAC \text{FLOPs} = 2\times\text{MAC}FLOPs =2 ×MAC。建议发表的论文还是按照 FLOPs 来给出,因为我看的大部分文章都是用的这个,而不是 MACs。
2.1 2D 卷积运算
就单纯的 2D 卷积而言,举例:
Conv2d ( C o u t , C i n , k e r n e l = K , s t r i d e = S , p a d d i n g = P , b i a s = F a l s e ) \text{Conv2d}(C_{out}, C_{in}, kernel= K, stride= S, padding= P, bias=False)Conv2d (C o u t ,C i n ,k e r n e l =K ,s t r i d e =S ,p a d d i n g =P ,b i a s =F a l s e )
输入 Feature map:( B , C i n , W i n , H i n ) (B, C_{in}, W_{in}, H_{in})(B ,C i n ,W i n ,H i n ),输出 Feature map:( B , C o u t , W o u t , H o u t ) (B, C_{out}, W_{out}, H_{out})(B ,C o u t ,W o u t ,H o u t ),计算如下:
FLOPs = ( 2 × C i n × K 2 − 1 ) × W o u t × H o u t × C o u t \text{FLOPs}=\left(2\times{C_{in}}\times{K}^2-1\right)\times{W_{out}}\times{H_{out}}\times{C_{out}}FLOPs =(2 ×C i n ×K 2 −1 )×W o u t ×H o u t ×C o u t
注意 (.) 里面的 -1 ,如果 bias = True,则不需要 -1。将 (.) 拆分为 乘法和加法:
FLOPs = [ ( C i n × K 2 ) + ( C i n × K 2 − 1 ) ] × W o u t × H o u t × C o u t \text{FLOPs}=\left[\left({C_{in}}\times{K}^2\right)+\left({C_{in}}\times{K}^2-1\right)\right]\times{W_{out}}\times{H_{out}}\times{C_{out}}FLOPs =[(C i n ×K 2 )+(C i n ×K 2 −1 )]×W o u t ×H o u t ×C o u t
第一个 (.) 里面是乘法,第二个是加法,如果 n 个数相加,做 n - 1 次加法运算,因此当 bias = True 时,刚好和 -1 抵消掉。
参数量的计算要简单些:Paras = K × K × C i n × C o u t + C o u t \text{Paras}=K\times{K}\times{C_{in}}\times{C_{out}}+C_{out}Paras =K ×K ×C i n ×C o u t +C o u t
同样注意:如果 bias = True,+ C o u t +C_{out}+C o u t ,如果 bias = False,去掉 + C o u t +C_{out}+C o u t 。
2.2 全连接层
线性全连接层,举例:
L i n e r ( C i n , C o u t , b i a s = F a l s e ) Liner(C_{in}, C_{out}, bias = False)L i n e r (C i n ,C o u t ,b i a s =F a l s e ),输入 sequence :( B , n u m , C i n ) (B, num, C_{in})(B ,n u m ,C i n ),输出 sequence:( B , n u m , C o u t ) (B, num, C_{out})(B ,n u m ,C o u t ),计算如下:
$FLOPs = ( 2 × C i n − 1 ) × C o u t \text{FLOPs}=\left(2\times{C_{in}}-1\right)\times{C_{out}}FLOPs =(2 ×C i n −1 )×C o u t
其中 2 2 2 代表乘法和加法。同上,当 bias = False 时,-1,bias = True时,无 -1。
全连接层参数:Paras = C i n × C o u t + C o u t \text{Paras}={C_{in}}\times{C_{out}}+C_{out}Paras =C i n ×C o u t +C o u t
同样注意:当 bias = True,+ C o u t +C_{out}+C o u t ,当 bias = False,去掉 + C o u t +C_{out}+C o u t 。
2.3 BatchNorm2D 层
对于每一个通道来说,可学习的参数有 2 个,动量 γ \gamma γ、动量偏移 β \beta β。
2.4 激活层
对于 ReLU 来说,由于其本身性质,不涉及 MAC 运算,因此只考虑 FLOPs。而FLOPs 相对来说较小,所以一般不计算或者想其他办法计算。提一嘴,在推理时哪会用得到sigmoid呢。
激活层没有参数。
3.1 thop
pip install thop
使用举例:
from torchvision.models import resnet50
from thop import profile
model = resnet50()
flops, params = profile(model, input_size=(2, 3, 200,280))
print("FLOPs=", str(flops / 1e9) + '{}'.format("G"))
print("FLOPs=", str(flops / 1e6) + '{}'.format("M"))
自定义计算规则举例:
from thop import profile
class YourModule(nn.Module):
def count_your_model(model, x, y):
flops, params = profile(model, input_size=(2, 3, 200,280),
custom_ops={YourModule: count_your_model})
print("FLOPs=", str(flops / 1e9) + '{}'.format("G"))
print("FLOPs=", str(flops / 1e6) + '{}'.format("M"))
优点:对于某个层的调试来说,很方便,比如 nn.Conv2D。
缺点:自定义的层,如 nn.Sequential()、nn.ModuleList() 这些容器层计算不了,需要自定义规则。
3.2 ptflops
pip install ptflops
使用举例:
import torch
from torchvision.models import resnet50
from ptflops import get_model_complexity_info
model = resnet50()
macs, params = get_model_complexity_info(model, (3, 200, 280), as_strings=True,
print_per_layer_stat=True, verbose=True)
print("FLOPs=", str(flops / 1e9) + '{}'.format("G"))
print("FLOPs=", str(flops / 1e6) + '{}'.format("M"))
优点:对于某个层的调试来说,很方便,比如 nn.Conv2D这些。另外 print_per_layer_stat = True 可以打印每一层的结构
缺点:自定义的层,如 nn.Sequential()、nn.ModuleList() 这些容器层计算不了。另外输入没有 batch维度,给出 shape 即可。
3.3 其他
写在后面
CSDN 灌水的人太多了,关键很多是错的,无语~~
Original: https://blog.csdn.net/qq_38929105/article/details/123847385
Author: 乄洛尘
Title: 关于 FLOPS、FLOPs、参数量的相关计算
相关阅读3
Title: 为博客园文章添加 “分享到Teams”按钮
概要:在博客园的博客页面底部增加"分享到Teams"的按钮
作者:陈希章
来源:《Microsoft Teams 平台完全手册》
网址:https://teamsplatform.code365.xyz
-->