Распознавание речи. Arduino

Вы думаете, чтобы распознать человеческую речь нужен как минимум мощный процессор(>100МГц), большой объем ОЗУ и памяти? Arjo Chakravarty считает иначе. Не обязательно иметь мощный процессор и большой объем памяти, достаточно обойтись Arduino. Многие хотели реализовать такое на микроконтроллере. Arjo Chakravarty написал библиотеку uSpeech как раз для этого.

Распознавание происходит в реал-тайм и побуквенно. В основном, распознавание производят путем разложения звука на фонемы с использованием FFT(Быстрым преобразованием Фурье) и еще сложнейших математических вычислений, библиотека uSpeech избегает этого, используя иные алгоритмы.

Все что нам нужно — это подключить микрофон к аналоговому порту Arduino и произвести калибровку. Как пишет автор, к  порту нужно подключить электретный микрофон с пред-усилителем. Из-за алгоритма, используемого в библиотеке, микрофон — очень важен, каждый его параметр может отразиться на распознавании. У меня не оказалось электретного микрофона, поэтому я использовал обычный с пред-усилителем на ОУ. Однако распознавание оказалось вообще никаким. Пред-усилитель работал, но на фоне был шум(хотя раньше повторял эту схему, шума не было) и «трещащий» звук. И поэтому решил собрать Hi-Fi пред-усилитель одного американца.

Правда вместо OPA2134, я использовал свой любимый К140УД6 🙂 Также вместо R7 — поставил переменный резистор, чтобы регулировать Коф. Усиления. Качество действительно хорошее, я бы сказал отличное.

Теперь перейдем к калибровке библиотеки. Скетч — Calibration.

Загружаем его, затем подключаем пред-усилитель к ArduinoA0).

 Запитываем всю нашу конструкцию и открываем Com порт.

 Молчим… 1сек. После, смотрим на значения в порту — это уровень тишины. Если значений много, то нужно определить средний уровень тишины. К примеру — 12, это число нужно поставить к #define SILENCE, в файле uSpeech.h библиотеки.

Теперь можно посмотреть на распознавание фонем. Загрузим такой скетч:

1
2
3
4
5
6
7
8
9
10
11
#include <uspeech.h> 
signal voice(A0);
void setup(){
  voice.calibrate();
  Serial.begin(9600);
}
void loop(){
  char v = voice.getPhoneme();
  if (v != 'h') Serial.println(v); //h - по умолчанию - Шум, выводим все кроме h
 
}

Фонемы(англ.), звуки речи(не путать с буквами), — хранятся в файле phoneme.cpp Вот они: s, k, z, g, v, p, e, o, i, ‘a. И у каждого есть свой параметр vowel, вычисляемый, грубо говоря, по «силе» определенной частоты и некоторым другим параметрам. А из них уже можно составить буквы.

Буква Фонема
a a
e e
i i
o o
r r,l,m,n
v v, w
p p, b
g g
z z, f, ch, j, tr, th
k k,c,qu
s s, sh
 Это также сказывается на произношении, некоторые звуки мы не произносим
«Stop»                               ———> ‘Sop’

Поняв принцип, можно русифицировать библиотеку. О фонемах можно почитать там и там. Теперь скетч ➡ . Открываем Com-порт и начинаем говорить/произносить различные слова(англ) с небольшим акцентом. «Курица! Oxygen! Вкл! Stop! » С первого раза у вас в ком порту будет всякая чепухня, нужно будет поработать со значением тишины. У меня при движении микрофона уже сыпались фонемы.

После изнурительных мучений, я подобрал значение SILENCE20 и нашел оптимальный коф.усиления. Хотя распознавание так и осталось кривоватым. Теперь сам скетч!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <uspeech.h>
signal voice(A0);
String collvoice;
 
void left(){
  voice.calibrate();
  Serial.println("left");
}
void stopp(){
  voice.calibrate();
  Serial.println("stop");
}
void right(){
  voice.calibrate();
  Serial.println("right");
}
 
void setup() {
  voice.calibrate();
  delay(100);
  Serial.begin(9600);
}
 
void loop() {
char phoneme = voice.getPhoneme();
 
if( phoneme != 'h'){ //Убираем "Шум"
 
collvoice = denoise(phoneme,collvoice); //Еще раз убираем шум и передаем фонемы, объединяя их.
Serial.println(phoneme); //(Можно убрать) Выводим фонемы.
}
else { //Очень хитрый аглоритм
int i[3],j,min,x;
i[0] = umatch(collvoice,"sop"); //stop
i[1] = umatch(collvoice,"ez"); //left
i[2] = umatch(collvoice,"i"); //right
//find the lowest number
while(j<0){
if(i[j]<min){
x = j;
min = i[j];
}
j++;
}
if(x == 0){ //Stop!
stopp();
}
if(x == 1){ //Left!
left();
}
if(x == 2){ //Right!
right();
}
}
}

Нужно загрузить скетч, запустить программу, затем открыть Com-порт, подождать 1 сек., сказать «ЛЭФТ«/»РАЙД«/»СТОП«. Если все настройки верны, и вы хорошо говорите по-английски, то шанс 80%, что распознается. Здесь можно заметить распознавание по фонемам: Stop -> sop, left -> ez, right -> i.

Из-за особенностей моего пред-усилителя, либо моей криворукости — у меня не получилось добиться полного распознавания. Я написал свою, программу распознавания по 3-м фонемам, которая управляет светодиодом.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <uspeech.h>
signal voice(A0);
char collvoice[3];
int ins = 0;
char charOn[13], charOff[13];
 
void setup() {
  voice.calibrate();
  delay(100);
  Serial.begin(9600);
  pinMode(13, OUTPUT); 
 
  while(!Serial.available()) {
    char phoneme = voice.getPhoneme();
    if (phoneme != 'h') Serial.println(phoneme);
  }
  siln = Serial.read();
  Serial.println("Enter thee 'on' char...");
 
  while(1) {
    if (Serial.available()>0) {charOn[ins] = Serial.read(); ins++;}
    if (ins>=3) {ins=0; break;}
  }
 
  Serial.println("Enter thee 'off' char...");
 
  while(1) {
    if (Serial.available()>0) {charOff[ins] = Serial.read(); ins++;}
    if (ins>=3) {ins=0; break;}
}
  Serial.println(charOn);
  Serial.println(charOff);
  Serial.println("Well...Done");
}
 
void loop() {
  char phoneme = voice.getPhoneme();
 
  if( phoneme != 'h') {
    collvoice[0] = phoneme;
    while(1) {
      phoneme = voice.getPhoneme();
      if (phoneme != 'h') collvoice[1] = phoneme; break;
    }
    while(1) {
      phoneme = voice.getPhoneme();
      if (phoneme != 'h') collvoice[2] = phoneme; break;
    }
  }
  if ( collvoice[0]==charOn[0] && collvoice[1]==charOn[1] && collvoice[2]==charOn[2] ) digitalWrite(13, HIGH);
  if ( collvoice[0]==charOff[0] && collvoice[1]==charOff[1] && collvoice[2]==charOff[2] ) digitalWrite(13, LOW);
 
  collvoice[0]=0; collvoice[1]=0; collvoice[2]=0;
}

Итак, после загрузки программы открываем Com-порт и молчим. Затем мы можем что-то говорить, а на экран будут выводится фонемы. Если отправить что-нибудь в Com-порт, программа спросит фонемы для ВКЛ светодиода на 13-ом пине. (вводить нужно 3 анг. фонемы) После, попросит ввести фонемы для ВЫКЛ светодиода. Теперь включается само распознавание.
Этот способ работает вполне удовлетворительно. Конечно, у автора, по идее, должно работать гораздо лучше, чем у меня. У него применяются различные фильтры, псевдо-шумоподавители в программе. Документацию к библиотеке можно почитать там ->/. Итоги…

Достоинства:

  • Низкие требования к ЦПУ и ОЗУ
  • Потоковое распознавание
  • Малый размер библиотеки (~4.5кб)

Недостатки:

  • Микрофонозависимость Специфические требования к входному сигналу
  • Точность распознавания <90

 

Распознавание голоса на Arduino — великолепная идея, можно, к примеру, зажигать лампочки одним словом, включать моторы свистом и т.п.


Как только найду электретный микрофон и пред-усилитель для него — результат будет лучше… наверное…

Удачи! 🙂

PS: Отличную идею для улучшения распознавания предложил mihtm. (см. ниже).

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Bug Report
Локализовано: шаблоны Wordpress