基于树莓派和LD3320模块的语音识别控制

人工智能61

目录

树莓派模块

1-wiringPi库的安装

2-串口相关API函数说明

3-代码的编写

LD3320模块

1-代码分析

树莓派模块

本项目所用到的树莓派型号是3B,烧写的固件是官方的固件。固件版本是:2019-04-08-raspbian-stretch.img。也可以从官网或百度云下载。百度云下载地址,提取码:fdnb。下载后用烧写工具烧写好后,插入树莓派即可。

可以通过以下方式登录树莓派:

  1. 通过HDMI线连接树莓派和带有HDMI接口的显示器进行登录
  2. 修改已经烧写好的固件中的配置文件来进行串口登录
  3. 通过SSH方式登录
  4. 树莓派安装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进行交叉连接。

基于树莓派和LD3320模块的语音识别控制

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项-回车

基于树莓派和LD3320模块的语音识别控制

选择第2项-回车

基于树莓派和LD3320模块的语音识别控制

选择Yes-回车

基于树莓派和LD3320模块的语音识别控制

选择Ok-回车

基于树莓派和LD3320模块的语音识别控制

选择Finsh-回车-退出

基于树莓派和LD3320模块的语音识别控制

至此SSH登录就设置好了,如果觉得默认登录密码记不住可以选择第一项:Change User Password进行修改登录密码。

1-wiringPi库的安装

wringpi在raspbian系统中都有预装,如果想要重新安装和更新可以执行以下命令:

sudo apt-get install wringpi
sudo apt-get upgrade wiringpi

安装成功后可以通过:gpio -v来查看wiringPi库的版本信息。

基于树莓派和LD3320模块的语音识别控制

还可以通过命令:gpio readall 来查看树莓派的外部引脚

基于树莓派和LD3320模块的语音识别控制

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-代码分析

基于树莓派和LD3320模块的语音识别控制

sRecog[DATE_A][DATE_B]中的二维数组就是要添加的口令,所说的口令,拼音打出用空格隔开即可,而pCode[DATA_A]数组中存放的是宏定义,在User_handle(nAsrRes);函数中能用到,可以根据,添加的口令,进行修改宏定义。 基于树莓派和LD3320模块的语音识别控制

根据口令识别的结果可通过串口进行打印,用户可以自己修改 。

最后要说明的几点:

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:一个将数字映射为文本的字典

# &#x6839;&#x636E;&#x5B57;&#x7B26;&#x5212;&#x5206;
# &#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5206;&#x8BCD;&#x5668;
>>> tokenizer = tf.keras.preprocessing.text.Tokenizer()
>>> text = "hello tensorflow"
# &#x6839;&#x636E;&#x6587;&#x672C;&#x66F4;&#x65B0;&#x5185;&#x90E8;&#x5206;&#x8BCD;&#x5668;&#x4FE1;&#x606F;
>>> tokenizer.fit_on_texts(text)
# &#x83B7;&#x53D6;&#x914D;&#x7F6E;&#x4FE1;&#x606F;
>>> 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}'
}
# &#x83B7;&#x53D6;word_index&#x5B57;&#x5178;
>>> tokenizer.word_index
'{"l": 1, "o": 2, "e": 3, "h": 4, "t": 5, "n": 6, "s": 7, "r": 8, "f": 9, "w": 10}'
# &#x6709;&#x65F6;&#x9700;&#x8981;&#x83B7;&#x53D6;&#x8BCD;&#x6C47;&#x8868;&#x7684;vocab_size
>>> vocab_size = len(tokenizer.word_index)
>>> vacab_size
10
# &#x6587;&#x672C;&#x8F6C;&#x6362;&#x6210;&#x5E8F;&#x5217;
>>> seq = tokenizer.texts_to_sequences("hello")
>>> seq
[[4], [3], [1], [1], [2]]
# &#x5E8F;&#x5217;&#x8F6C;&#x6362;&#x6210;&#x6587;&#x672C;&#xFF0C;&#x4E09;&#x79CD;&#x60C5;&#x51B5;
>>> 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']

# &#x6839;&#x636E;&#x5355;&#x8BCD;&#x5212;&#x5206;
>>> 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}
# &#x8BBE;&#x7F6E;&#x53C2;&#x6570;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

-->

  • 目录
  • 本页二维码