Der RasPi hört aufs Wort [1. Post edit, bessere Lösungen sind da]

Begonnen von Rince, 29 Mai 2013, 18:00:46

Vorheriges Thema - Nächstes Thema

Rince

Update:
Tobi macht gerade ein Modul aus der Geschichte, welches für die Sprachausgabe deulich mehr Features bietet.
http://forum.fhem.de/index.php/topic,18481.0.html
Schaut da mal vorbei, er freut sich sicher über Feedback. Es läuft auch parallel zu dieser Lösung wunderbar.


Dirk hat eine Android App, WebViewControl, gebaut, die zur Spracherkennung gleich die Google API nutzt.
Macht mehr Sinn als der Umweg über Singstar Mikrophone :)
http://forum.fhem.de/index.php/topic,10628.0.html


Kuzl schreibt gerade ein Modul, um universell Sprache zu verarbeiten. Ohne das man jeden einzelnen Befehl von Hand verdrahten muss.
http://forum.fhem.de/index.php/topic,17409.0.html



Noch ein Edit, die Links waren falsch. Danke an Franky.



Hi,

ist nix professionelles oder umwerfendes, aber es funktioniert (wenn auch langsam) und macht Spaß :)

In der c´t Hacks 2/2013 stand ein schöner Artikel über Sprachsteuerung mit dem Raspberry drin.
Ich beschreibe mal kurz die wesentlichen Schritte zur Installation:


Was man braucht:
Einen Raspberry (wobei wohl jeder Linux Rechner tut)
Passende Soundhardware (z.B. Singstar Mikros (das Blaue) => getestet, funktioniert)

Für die Softwareinstallation:

Unter SSH
sudo apt-get install sox mplayer ffmpeg

Wenn es eine Fehlermeldung gibt:
sudo apt-get update

und nochmaliges

sudo apt-get install sox mplayer ffmpeg


dann flutschts :)


in der mplayer.conf ( /etc/mplayer) folgende Zeile hinzufügen:
norlirc=yes
(sudo nano mplayer.conf)

Als nächstes benötigt man das sh Skript, welches die c´t unter der GPL veröffentlicht hat.
Quelle: www.ct.de/ch1302064

Das Skript ctvoice.sh fühlt sich im Verzeichnis
/opt/fhem/contrib
recht wohl, evtl. muss man noch mit chmod die Rechte anpassen.


Jetzt habe ich es ein kleines bisschen modifiziert:
(vermutlich hoffnungslos verschandelt, aber ich bin seit 12 Jahren aus Linux und seinen Skripten raus)

Mein Skript sieht jetzt so aus:
#!/bin/bash

# c't Hardware Hacks - Spracherkennung für den Raspberry Pi, GPL-Lizenz

count=1
lastsize=0
rec=0
first=1

# Der Soundchip des RPI erzeugt vor und nach der Wiedergabe ein Knacken. Deutlich bessere Ergebnisse liefert eine USB-Soundkarte, wie man sie bereits für rund fünf Euro bekommt. Damit mplayer die USB-Soundkarte benutzt, ändert man den Parameter "-ao alsa:device=hw=0.0" in "-ao alsa:device=hw=1.0".

function say {
mplayer -ao alsa:device=hw=0.0 -really-quiet -http-header-fields "User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22m" "http://translate.google.com/translate_tts?tl=de&q=$1";
}

sox -t alsa hw:1,0 test.wav silence 1 0 0.5% -1 1.0 1% &
sox_pid=$!

while [ $count -le 9 ]
do
   
size=$(stat --printf="%s" test.wav)

if [ $size -gt $lastsize ]
then
if [ $first -eq 0 ]
then
echo "Aufnahme!"
rec=1
else
first=0
fi
else
if [ $rec -eq 1 ]
then
echo "Abschicken"
kill $sox_pid
ffmpeg -loglevel panic -y -i test.wav -ar 16000 -acodec flac file.flac
wget -q -U "Mozilla/5.0" --post-file file.flac --header "Content-Type: audio/x-flac; rate=16000" -O - "http://www.google.com/speech-api/v1/recognize?lang=de-de&client=chromium" | cut -d\" -f12 >stt.txt
cat stt.txt
say "$(cat stt.txt)"

if [[ $(cat stt.txt) =~ "Befehl" ]]
then
echo "Sprachbefehl erkannt!"
say "Sprachbefehl erkannt! Ich könnte jetzt einen beliebigen Shell-Befehl ausführen!"
# mach was
elif [[ $(cat stt.txt) =~ "Wetterbericht" ]]
then
echo "Wetterbericht erkannt!"
say "Ich würde Dir jetzt den Wetterbericht vorlesen! Bring es mir bei!"
# mach was
elif [[ $(cat stt.txt) =~ "Licht an" ]]
then
echo "Licht an"
say "Ich schalte das Licht an"
/opt/fhem/fhem.pl 7072 "set OG_wz_ws_LICHT on"

elif [[ $(cat stt.txt) =~ "Licht aus" ]]
then
echo "Licht aus"
say "Ich schalte das Licht aus"
/opt/fhem/fhem.pl 7072 "set OG_wz_ws_LICHT off"
else
echo "Kein Kommando erkannt..."
fi

sleep 1
bash ctvoice.sh
else
echo "Stille..."
fi
rec=0
fi

lastsize=$size

sleep 1

done




Was es jetzt kann:
Mit "Licht an" mein Wohnzimmerlicht einschalten oder mit "Licht aus" ausschalten :)
Ansonsten plappert es fröhlich alles nach, was man ihm sagt.

Gestartet wird es mit ./ctvoice.sh

Anmerkung:
Wenn man nix hört (alles was nach "say" kommt, sollte man hören), muss man auf dem Rapberry evtl. noch die Soundausgabe umstellen auf den analogen Ausgang:
sudo amixer cset numid=3 1
http://www.raspberrypi-spy.co.uk/2012/06/raspberry-pi-speakers-analog-sound-test/

Was man draus machen könnte:
Eine Perl Version die direkt von FHEM aus läuft, wäre schick.
Dann könnte man vielleicht über Attribute Sprachbefehle für einzelne Devices definieren.
Und man wäre nicht darauf angewiesen, den erkannten Text in eine Datei zu speichern. Sondern evtl. könnte man daraus direkt Befehle generieren...

Wo liegt jetzt der Sinn?
Ich glaube, es ist eine gute Ausgangsbasis für eigene Spielereien :)
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Rince

Sodala.
Ich habe noch ein bisserl gespielt.

Ein großer Nachteil von obigem Skript ist, dass alles über die arme SD Karte abläuft. Das hat zwei Nachteile:
1. langsam
2. SD Karten sind nicht gebaut für unendlich viele Schreibzyklen

Lösung:
Wir brauchen eine RAMDISK

Bekommt man so:
in der /etc/fstab folgenden Eintrag hinzufügen (sudo nano /etc/fstab)

tmpfs           /tmp              tmpfs   defaults,noatime,mode=1777 0       0

Damit wird das /tmp Verzeichnis zukünftig auf eine Ramdisk gemounted.

Jetzt im Skript von oben folgende Anpassungen vornehmen:

Alles was als Datei gespeichert wird, um die Pfadangabe /tmp/ ergänzen:

Aus test.wav wird /tmp/test.wav
aus file.flac wird /tmp/file.flac
aus stt.txt wird /tmp/stt.txt

Überall wo das Zeug auftaucht...

=> Die Reaktion wird wesentlich schneller
=> unsere SD Karte wird nicht weiter belastet :)
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Puschel74

Hallo,

auch wenn keine Reaktionen kommen - bleib bitte dran.
Ich verfolge den Beitrag weil ich den geinal finde - auch wenn es nur eine Spielerei ist ;-)

Grüße
Zotac BI323 als Server mit DBLog
CUNO für FHT80B, 3 HM-Lan per vCCU, RasPi mit CUL433 für Somfy-Rollo (F2F), RasPi mit I2C(LM75) (F2F), RasPi für Panstamp+Vegetronix +SONOS(F2F)
Ich beantworte keine Supportanfragen per PM! Bitte im Forum suchen oder einen Beitrag erstellen.

Rince

:)

Logisch :)
Wobei das Umsetzen als Perl Skript wohl länger dauert, wenn es überhaupt klappt :)

Ich muss mir mal näher ansehen, wie die Jungs von Asterisk das ganze gelöst haben mit den Frage / Antwort - spielchen
Ich denke, in Perl wäre es besser aufgehoben als in einem SH Skript.

Habe mir übrigens grade mal die CPU Auslastung angesehen:
SOX braucht so etwa 6% (der läuft quasi immer), der mplayer, wenn er mal läuft (passiert ja nur bei der Sprachausgabe), so um die 25%.


Als Zwischenfazit:
Was etwas stört, dass man nicht unbedingt weiß, was das Skript grade tut. Wenn es grade am Konvertieren / Upload / Download /Wiedergabe ist, reagiert es nicht auf die Spracheingabe. Ist ja logisch, wird ja SOX mit kill abgeschossen wenn es anspringt.
Bei fehlerhaftem Ansprechen von SOX (es meint also man sagt etwas, obwohl man das gar nicht tut) kann man sagen was man will, es wird nicht gehört. Ohne die Konsole im Fenster bekommt man es aber nicht mit.


Jedenfalls ist das blaue SingStar Mikrofon ziemlich gut geeignet.
=> es ist in Sekunden ein/ausgeschaltet
solange es aus ist, reagiert SOX ja auf nix :)


Lang die Rede, kurz der Sinn:
Man sollte mal versuchen, das Skript auf Perl zu portieren :)
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Rince

Hi,

ich pfusche mich so weiter durch :)

Hab jetzt ein eigenes sh Skript gebastelt, welches sich say.sh nennt.

#!/bin/bash
mplayer -ao alsa:device=hw=0.0 -really-quiet -http-header-fields "User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22m" "http://translate.google.com/translate_tts?tl=de&q=Test";


Wenn man dieses so aufruft, erzählt der mplayer brav "Test"

Wenn man das Test durch $1 ersetzt, kann man das Ding mit einem Parameter aufrufen, der vorgelesen wird:

./say.sh "Dieser Text wird jetzt vorgelesen"



Ob das irgend wem was nützt, kann ich nicht sagen.

Ich persönlich fände ja Sprachfeedbacks sehr angenehm. Jetzt muss ich einen Eintrag in die 99Utils basteln, der mir das Skript mit dem Parameter aufruft.
sub say($)
{
/opt/fhem/contrib/say.sh @_;
}


Ist es schon mal nicht :)
ZitatTransliteration pattern not terminated at ./FHEM/99_Utils.pm line 237.




Edit:
So gibts beim Speichern schon mal keine Fehlermeldung:
sub say($)
{
my ($Text) = @_;
"/opt/fhem/contrib/say.sh $Text";
}
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Rince

Wieder einen Schritt weiter:

So wird das Skript zumindest ausgeführt:

sub say($)
{
my $Text = @_;
#sh /opt/fhem/contrib/say.sh;
`/opt/fhem/contrib/say.sh $Text`;
}


Was jetzt noch nicht geht, ist die Parameterübergabe.

Schlau wäre, wenn man FHEM sagen könnte:
say Irgend etwas

Das Irgend etwas muss dann in " " rein und als Parameter an das say.sh Skript übergeben werden:

/opt/fhem/contrib/say.sh "Irgend etwas"


Wie geht denn das jetzt???
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Puschel74

Hallo,

evtl. muss noch ein sudo vorn dran wenn fhem nicht auf die audiohardware zugreifen darf und evtl. noch ein
system vorne dran.
Also evtl. so:
`system /opt/fhem/contrib/say.sh $Text`

Mal ohne sudo.

Grüße
Zotac BI323 als Server mit DBLog
CUNO für FHT80B, 3 HM-Lan per vCCU, RasPi mit CUL433 für Somfy-Rollo (F2F), RasPi mit I2C(LM75) (F2F), RasPi für Panstamp+Vegetronix +SONOS(F2F)
Ich beantworte keine Supportanfragen per PM! Bitte im Forum suchen oder einen Beitrag erstellen.

Rince

Danke Puschel,

das Problem mit der Audio Hardware habe ich so gelöst:

sudo gpasswd -a fhem audio
+ Neustart

=> Damit darf der User fhem die Audio Hardware benutzen :)


Also langsam zum Testen:
Wir lassen das say.sh Skript wie es ist (es erwartet als Parameter also den Text, der gesprochen werden soll; dieser muss in  " " stehen wenn es mehre Wörter sind)

in der 99 sieht es zum Testen so aus:
`/opt/fhem/contrib/say.sh Hallo`;      (<= das bedeutet, der Parameter wird ignoriert, es wird immer Hallo übergeben)

Tippt man in die FHEM Befehlszeile jetzt ein:
{say "huhu"}

dann kommt brav die Ansage Hallo aus den Lautsprechern :)


=> Das sh Skript prinzipiell läuft, sonst täte man nix hören :)



2ter Schritt:
Jetzt will ich den Text den ich als Parameter angebe, vorgelesen bekommen, der $ muss also ausgewertet werden.

Schreibe ich jetzt:
sub say($)
{
my $Text = @_;
`/opt/fhem/contrib/say.sh $Text`;
}


Und rufe es auf mit
Zitat{say "huhu"}

Dann spricht das Skript: EINS (???????)
Offenbar wird aus $Text in FHEM als Skriptparameter eine 1 ????


Wie bekomme ich jetzt diesen $Text wirklich 1:1 als Skriptparameter übernommen???
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Puschel74

Hallo,

sub say($)
{
my $Text = @_;
`/opt/fhem/contrib/say.sh $Text`;
}


steht in @_ wirklich der Text?
Versuchs mal mit $EVTPART0 oder $EVTPART1 (ich hab grad nicht im Kopf was passt - evtl. mal loggen lassen)
Oder lass mal $Text loggen.

Die 1 (EINS) vermute ich mal ist der Rückgabewert der Funktion.

Grüße

Edith:
Die Übergabe einer Variable habe ich in meiner Sub so gelöst:
"sudo i2cget -y 1 ".$sensor." 0x00 w &"
Zotac BI323 als Server mit DBLog
CUNO für FHT80B, 3 HM-Lan per vCCU, RasPi mit CUL433 für Somfy-Rollo (F2F), RasPi mit I2C(LM75) (F2F), RasPi für Panstamp+Vegetronix +SONOS(F2F)
Ich beantworte keine Supportanfragen per PM! Bitte im Forum suchen oder einen Beitrag erstellen.

Rince

Danke für die Hilfe.
Der Tipp, dass @_ nicht den Inhalt der Variablen bekommt war völlig richtig.


So funktioniert es:
sub say($)
{
my $Text = @_;
`/opt/fhem/contrib/say.sh "@_"`;
}


Mit
{say "hier muss man den Text den man gesagt haben will reinschreiben"}
(in der FHEM Befehlszeile)
Plappert die Kiste jetzt los :)



Meinst du es lohnt sich, das in den Codeschnippseln zu posten?
Also mplayer installieren, Rechte setzen, die say.sh und den Eintrag in die 99 Utils?
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Steffen

Hallo!

Habe mir das jetzt auch auf den pi installiert und "say" klappt über Pi und fhem gut, danke nochmal dafür.

Würde das gerne auch mit dem Singstar Mikro versuchen, aber bevor ich das jetzt irgendwo hole, wollte ich noch fragen wie man den Befehl dann aktiviert(bestimmter Satz) oder hört es immer zu??

Mfg Steffen

Rince

Also, prinzipiell hört es immer zu und wenn ein gewisser Schwellwert überschritten wird, zeichnet er auf. Das permanente Zuhören braucht etwas CPU Last, das habe ich irgendwo vorne gepostet glaube ich :)

Das eigentliche Problem ist, dass solange man das Skript aus SSH heraus startet, wenn man die SSH Konsole schließt das Teil im Hintergrund weiterläuft, aber wohl nicht mehr aktiv an der Hardware rumfummeln kann.

Vermutlich müsste man das ganze Skript direkt aus FHEM heraus lostreten, aber ich bin kein Linux Freak.

Probiere es einfach mal aus. Wenn du die Fstab modifizierst und das Skript mit meinen Änderungen benutzt, wird die Geschwindigkeit durchaus akzeptabel, da dann das ganze in einer RAMDisk läuft. Dürfte auch sehr viel schonender für die SD Karte sein, da es keine Schreib/Lesezugriffe mehr generiert.


Versuch es mal und schreib mir, wie es dir gefallen hat. Es funktioniert übrigens nur mit dem blauen Mikrofon! (Liegt daran, dass von den beiden Mikros eines nur links und eines nur rechts überträgt (jedes ist also mono) und Google nur 1 Kanal auswertet. Den vom blauen Mikro :)


Meine Idee, wenn es noch jemand anderen interessiert:
Im Moment ist das ganze ja hart verdrahtet. Jede Funktion braucht also einen Befehl, der genau so an FHEM übergeben wird, um ihn aus zu führen.


Das erscheint mir weitgehend umständlich.

Ich fände es wesentlich eleganter, wenn man die Textdatei mit Perl etwas intelligenter nach Schlagwörtern durchsuchen könnte :)
Also in etwa so:

Taucht in der Textdatei ein "mache", "schalte", "stelle" oder sowas auf, dann wird es prinzipiell ein "set" Befehl, der wird $1

Wenn das so ist, benötigen wir noch weitere Parameter:
Wo?
Welches Gerät?
Was ist damit zu tun?

Also Schlagwörter für wo:
"alle", überall", "Wohnzimmer", "Schlafzimmer" etc...

Welches Gerät Schlagwörter:
"Heizung", "Licht" etc...

Was:
"Grad" (dann den Zahlenwert davor nehmen), "an", "aus", "Prozent" (wieder Zahlenwert davor), "wärmer" (dann aktuellen Wert abfragen und um 10% erhöhen) (ebenso wie bei kälter, kauter, leiser, heller, dunkler etc...)



Ich denke, auf diese Weise müsste man einen FHEM Befehl erzeugen können.


set OG_wz_WS_LICHT on
=Obergeschoss_Wohnzimmer_Wandschalter_ LICHT

Mach das Licht im Wohnzimmer an

Mach im Wohnzimmer das Licht an

Mach bitte im Wohnzimmer das Licht an...

Bei fehlen von einem Parameter könnte man nachfragen lassen: Welches, welche...


Ich denke, dieser Weg sollte mit Perl gut beschreitbar sein :)



Nachtrag:
Auf diese Weise müsste auch ein Kommando ausgewertet werden können, um das ganze überhaupt auszulösen. Hat deine Haussteuerung schon einen Namen? Dann könntest du dir einen ausdenken :)

Was das imme rzuhören betrifft, das ist mit den Singstar Mikrofonen recht gut, da man die in Sekunden Aus/Eingeschaltet hat. So gibt es wenig Fehlalarme :)
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Steffen

Ja das wäre eine gute Idee...aber ich würde die Befehle mit einem Start befehl absichern, man stelle sich vor bei einer Party wo der Kumpel erzählt wie es das "Licht aus" gemacht hatte und alle auf einmal im Dunkeln stehen ist auch nicht das wahre;-)

Aber ich finde das sehr sehr spannend vielleicht mal das Haus per Sprach befehl zu steuern.

Mfg Steffen

 

Rince

Deshalb ja die Frage, ob diene Haussteuerung schon einen Namen hat :)

"Computer" ist blöd, kommt viel zu oft vor :)

"Susi" oder so ist viel praktischer (wenn nicht zufällig Familienangehörige so heißen :) )

Das wäre quasi die Voraussetzung, damit überhaupt weiter die Sprache abgearbeitet wird.


Aber mach dir wegen der Party nicht zu viel Kopf, ich kann mir nicht vorstellen, dass bei dem Hintergrundlärm noch irgend etwas erkannt wird ;)



Der riesige Vorteil liegt aber darin, dass du so im Endeffekt Befehle definieren kannst, für die du keinen Schalter/Aktor brauchst (weil die sind ja meist eher teuer).

So ein:
"Ich fahre in den Urlaub, tschüss"

Ist dahingehend einfach billig :)
(und alle Verbraucher gehen aus, eine Anwesenheitssimulation läuft an, die Bewegungsmelder werden scharf...)
Wer zu meinen Posts eine Frage schreibt und auf eine Antwort wartet, ist hiermit herzlich eingeladen mich per PN darauf aufmerksam zu machen. (Bitte mit Link zum betreffenden Thread)

Steffen

Guten Morgen!

Komme da nicht weiter, wie bekomme ich das "say" in diesen Code:

define SteffenZuHause notify Steffen {\
  my $Steffen1 = ReadingsVal("Steffen","state","99");;\
  my $L1 = Value("SteffenHome");;\
  if((($Steffen1) eq "present") && (($L1) eq "off")){\
    fhem("set say "Hallo Steffen alles Online"");;\
    fhem("set SteffenHome on");;\
  }\
else {\
  if((($Steffen1) eq "absent") && (($L1) eq "on")){\
    fhem("set SteffenHome off");;\
  }\
}}


so funktioniert es leider nicht?

Mfg Steffen