Quantcast
Channel: Framboise 314, le Raspberry Pi à la sauce française….
Viewing all articles
Browse latest Browse all 1015

Kalliopé: un assistant personnel customisable

$
0
0

Kalliopé est un programme qui va vous permettre de concevoir vôtre propre assistant personnel vocal sur vôtre Raspberry Pi.

Kalliope un assistant vocal pour le Raspberry Pi

Présentation

Si vous voulez vous faire une idée de ce qu’il est possible de faire avec Kalliopé avant de poursuivre vôtre lecture, voici une petite vidéo de démonstration en français.

Le principe est simple :

  • vous choisissez un ordre,
  • vous rattachez cet ordre à une ou plusieurs actions (neurone) à exécuter

L’association entre un “ordre” et une liste de “neurone” est appelée “synapse”. Les synapses sont placés dans le cerveau (brain) de Kalliopé.

Prenons un exemple de synapse simple que vous pourriez écrire :

- name: "Hello-world"
  signals:
    - order: "dis moi bonjour"
  neurons:      
    - say:
        message: "bonjour monsieur"

Ici on décrit un synapse « hello-world », qui est exécuté sur l’ordre « dis moi bonjour » et qui active le neurone de type “say”. Ce neurone permet de faire parler Kalliopé, il prend pour paramètre un message qui sera prononcé par le bot.

Au final, quand on dit « dis moi bonjour », Kalliopé vous répond « bonjour monsieur » .

Dans cet exemple, nous utilisons le neurone “say”, qui permet de faire parler le bot.

Il existe des neurones pour toutes sortes d’utilisations. Par exemple, vous avez la possibilité de faire parler votre assistant, obtenir l’heure ou la météo, interagir avec une API, lancer un script ou même interconnecter vos synapses entre eux de façon à faire un dialogue.

Vous trouverez sur le site du projet une liste de neurones que vous pouvez utiliser. Certains sont intégrés par défaut (core neuron), d’autre sont communautaire et doivent être installés au préalable (community neuron).

Vous pouvez également créer vos propres neurones et les proposer à la communauté.

Le site propose aussi quelques exemples de vidéos par neurone en plus de leur documentation respective.

L’installation

Petite note importante avant de vous lancer dans l’installation. Vous devez disposer d’un Raspberry Pi de deuxième ou troisième génération (Rpi 2 ou Rpi 3). Le Rpi 1 avec son CPU monocœur de 700 Mhz ne tient pas suffisamment la charge est n’est tout simplement pas supporté par le projet.

Si vous souhaitez installer le projet sur un Raspberry Pi, rien de plus simple. Vous trouverez une image de Raspbian pré-compilée sur la page Github du projet (dans le menu “release”).

Décompressez cette image et clonez-la sur votre carte SD comme vous le faite habituellement pour installer un Raspberry Pi.

Vous pouvez également suivre la procédure d’installation manuelleLe projet support Ubuntu, Debian et Raspbian.

Testez que l’entrée et la sortie audio fonctionnent correctement. Pour cela, enregistrez vôtre voix:

rec test.wav

Pressez CTRL-C pour stopper l’enregistrement, puis écoutez vôtre enregistrement avec la commande suivante.

mplayer test.wav

Avec un micro-casque cela devrait fonctionner directement. Maintenant, si cela ne marche pas ou si vous souhaitez une configuration plus spécifique, comme par exemple la sortie audio sur le port jack et l’entrée sur le micro USB, suivez les instructions ci dessous.

On affiche nos périphériques de sortie

aplay -l

Ce qui donne une sortie comme celle-ci

**** List of PLAYBACK Hardware Devices ****
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  Subdevices: 7/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Headset [Logitech USB Headset], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

Ici nous pouvons identifier que nous avons

  • une sortie analogique (ou le jack est connecté) sur la carte 0 et périphérique 0 (card0,  device0)
  • une sortie audio USB sur la carte 1 et périphérique 0 (card 1, device 0)

On affiche à présent les entrées audios

arecord -l

Ce qui donne une sortie du genre

<span class="pl-k">****</span> List of CAPTURE Hardware Devices <span class="pl-k">****</span>
card 1: Headset [Logitech USB Headset], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice <span class="pl-c">#0: subdevice #0</span>

Ici on voit que nous avons un micro USB  en carte 1 et périphérique 0 (card 1, device 0)

On va créer un fichier de configuration de façon à ce que :

  • la sortie audio (se que Kalliopé nous dit) soit sur la sorti analogique (le port jack)
  • l’entrée audio (se que l’on dit à Kalliopé) sur le micro USB

On créé donc un fichier /home/pi/.asoundrc avec le contenu suivant

pcm.!default {
   type asym
   playback.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
   capture.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
}

Ici, playback.pcm est la sortie et capture.pcm est l’entrée.

On applique la configuration en redémarrant le service

sudo /etc/init.d/alsa-utils restart

Vous pouvez aussi ajuster le volume et la sensibilité du microphone avec la commande:

alsamixer

qui vous affichera un menu graphique. La touche F6 permettant de basculer d’un périphérique à l’autre.

Démarrage rapide

Kalliopé a besoin de deux fichiers pour fonctionner : settings.yml et brain.yml.

Pour bien débuter, vous pouvez cloner un kit de démarrage, qui vous donnera une configuration de base en français avec quelques exemples de synapses.

Si vous avez installé via l’image pré-compilée, le starter kit est déjà présent dans /home/pi.

Les fichiers de configuration utilisent la syntaxe YAML. Il est fortement recommandé d’utiliser un IDE comme VS Code ou Atom (tous deux gratuit) pour éditer ce type de fichier et détecter vos éventuelles fautes de syntaxe.

Une bonne pratique est de modifier via votre IDE les fichiers depuis votre poste de travail, puis de copier le dossier contenant toute la configuration sur votre raspberry.

Lancer Kalliopé

Une fois configuré, placez le dossier de configuration sur votre Raspberry. Placez-vous ensuite dans ce dossier et lancer le programme.

kalliope start

Une fois lancée, kalliopé sera en attente de la prononciation du “mot magique” qui permet de la réveiller.

Starting event manager
Events loaded
Starting Kalliope
Press Ctrl+C for stopping
Starting REST API Listening port: 5000
Waiting for trigger detection

Si vous avez cloné le kit de démarrage français, le mot magique est “kalliopé”. Prononcez-le, et l’état de Kalliopé devrait alors changer et attendre un ordre vocal de votre part.

2016-12-05 20:54:21,950 :: INFO :: Keyword 1 detected at time: 2016-12-05 20:54:21
Say something!

Si vous avez lancé le kit de démarrage français, l’ordre “bonjour” est présent dans le brain. Vous pouvez prononcer à haute voix “bonjour” dans votre micro et Kalliopé vous répondra.

Say something!
Google Speech Recognition thinks you said bonjour
Order matched in the brain. Running synapse "say-hello-fr"

Bonjour kalliope
Waiting for trigger detection

La configuration

Settings.yml: la configuration de Kalliopé

La configuration de kalliopé, qui se trouve dans le fichier settings.yml. La documentation complète se trouve ici.

Le trigger: moteur de réveil de Kalliopé

De la même manière que les produits commerciaux comme Alexa ou Google Assistant, Kalliopé possède un “mot-magique” aussi appelé “hot-word” afin de la réveiller.

Le hot-word est géré par un  programme en écoute permanente. Kalliopé supporte actuellement un seul moteur: Snowboy.

Vous pouvez changer le hot word en vous connectant au site Snowboy et en créant votre propre mot magique,  ou en améliorant un mot existant comme par exemple celui créé pour Kalliopé.

La création ou l’amélioration d’un hot-word génère un modèle sous la forme d’un fichier binaire. Une fois le modèle téléchargé, vous devez le placer dans le dossier de votre configuration et vous devez préciser le chemin vers ce dernier dans la configuration de Snowboy.

# on précise que l’on va utiliser snowboy comme moteur
default_trigger: "snowboy"

# configuration de snowboy
triggers:
 - snowboy:
     pmdl_file: "trigger/mon_model.pmdl"

Le player: le programme qui va gérer la sortie audio

Kalliopé propose de changer le programme qui gère la sortie audio. Par défaut la configuration est sur « mplayer ». Vous pouvez laissez ce réglage ou le modifier si vous le souhaitez.

default_player: "mplayer"

# players configuration
players:
 - mplayer: {}

Speech to text: conversion audio→ texte

Kalliopé à besoin de transformer un ordre vocal en texte afin de l’analyser et d’exécuter les actions en fonction.

Par défaut, le moteur Speech To Text (STT) utilisé est celui de google, dans le cloud donc. Celui-ci propose d’excellent résultats. Cependant, pour les personnes soucieuses du respect de la vie privée, Kalliopé propose un moteur auto-hébergé et non connecté nommé CMUSphinx.

La liste des moteurs est disponible dans la documentation. Chacun de ces moteurs a sa propre configuration, les paramètres changent donc suivant celui que vous aurez choisi.

Pour google par exemple, en suivant sa documentation, sa seule configuration est le langage à utiliser.

# on précise que l'on souhaite utiliser google comme STT
default_speech_to_text: "google"

# configuration de google STT
speech_to_text:
 - google:
     language: "fr-FR"

Text to speech: conversion texte → audio

Dans l’autre sens, quand Kalliopé doit vous parler, son algorithme génère du texte qui faut convertir en audio. Il s’effectue au travers d’un moteur de Text To Speech (TTS).

Là aussi, Kalliopé propose plusieurs moteurs. Connectés ou non connectés. Pour certains, il est même possible de choisir la voix.

La syntaxe de configuration est toujours la même, on choisie le moteur que l’on va utiliser

default_text_to_speech: "nom_de_mon_moteur_tts"

Et on le configure

text_to_speech:
 - nom_du_tts:
     parametre: "valeur"

Par exemple, avec le moteur par défaut Pico2wav, qui est non connecté et offre un résultat très correct en français, la configuration sera la suivante:

default_text_to_speech: pico2wave
text_to_speech:
 - pico2wave:
     language: "fr-FR"

Une fois encore, référez-vous à la documentation pour connaître la liste les moteurs disponibles, ainsi que le nom des paramètres à leur fournir pour leur bon fonctionnement.

Wake up answers

Vous aurez remarqué dans la vidéo de démonstration, que Kalliopé prononce une phrase lorsque qu’elle a détecté le hot-word, afin de prévenir qu’elle est prête à écouter votre ordre.

La liste des phrases aléatoires qu’elle prononcera est à placer dans la variable « random_wake_up_answers ».

random_wake_up_answers:
  - "Oui monsieur?"
  - "Je vous écoute"
  - "Monsieur?"
  - "Que puis-je faire pour vous?"
  - "J'écoute"
  - "Oui?"

Synapse par défaut

Le synapse par défaut est le synapse qui sera exécuté si Kalliopé n’a pas compris votre ordre.

On le précise comme suit

default_synapse: "default-synapse"

Bien évidement, ce synapse doit être existant dans le cerveau (brain) de votre bot, nous reviendrons dessus plus tard. Par exemple:

- name: "default-synapse"
  signals:
    - order: "default-synapse"
  neurons:
    - say:
        message:
          - "Je n'ai pas compris vôtre ordre"
          - "Je ne connais pas cet ordre"
          - "Veuillez renouveler votre ordre"
          - "Veuillez reformuler s'il vous plait"
          - "Je n'ai pas saisi cet ordre"

Quand on fourni une liste de phrases au neurone « say » ce dernier prendra l’une d’elles aléatoirement, afin de donner un coté plus humain à votre bot.

Les Variables globales

La variable « var_files » permet de spécifier un ou plusieurs fichiers contenant des variables qui peuvent être utilisées dans les paramètres de vos neurones.

Par exemple, je définis un fichier de variables dans mon fichier settings.yml comme ci-dessous :

var_files:
  - variables.yml

Il faut bien sûr créer ce fichier à la racine du dossier de configuration. Et placer par exemple dans ce fichier une variable.

name: Batman

Je peux à présent utiliser cette variable dans mes synapses. Pour cela j’utilise une double parenthèse afin de faire comprendre au moteur qu’il s’agit d’une variable.

---
  - name: "qui-je-suis"
    signals:
      - order: "comment je m'appelle"
      - order: "qui suis-je"
    neurons:
      - say:
          message:
            - "Vous êtes {{name}}"

Ces variables seront très pratiques pour réutiliser un même paramètre. Par exemple, si vous avez plusieurs synapses utilisant le neurone URI (qui permet de faire des appels à un service web), vous pouvez spécifier l’adresse de l’URL dans une variable et utiliser cette dernière dans vos synapses. Ainsi, si l’URL change, vous aurez besoin de changer l’adresse qu’a un seul endroit.

- name: "synapse1"
  signals:
    - order: "fais ceci"
  neurons:
    - uri:
        url: "http://{{address}}/action1" 
        method: POST  
  
- name: "synapse2"
  signals:
    - order: "fais cela"
  neurons:
    - uri:
        url: "http://{{address}}/action2" 
        method: POST

brain.yml: le cerveau de Kalliopé

On va à présent faire un tour sur la partie la plus importante de Kalliopé: son cerveau.

Comme expliqué plus haut, le cerveau (brain) de kalliopé est composé de synapses. Un synapse est l’association d’un signal avec une liste de neurones.

Les signaux

Il existe actuellement deux types de signaux. Un ordre (order) ou un événement planifié (event).

Les ordres

Un ordre est une phrase capturée par le microphone et envoyé au moteur STT afin d’être analysé.

Voici un ordre simple:

signals:
    - order: "effectue cette action"

Nous allons voir maintenant un ordre avec argument. Les arguments sont important pour améliorer la flexibilité de votre bot.

Prenons un exemple avec le neuron Wikipédia qui, bien-sur, permet d’effectuer une recherche sur Wikipédia.

Nous pouvons créer un synapse avec un ordre simple comme ceci

- name: "wikipedia-search"
  signals:
    - order: "cherche sur wikipédia Obama"    
  neurons:
    - wikipedia_searcher:
        language: "fr"        
        query: Obama
        say_template: "résultat de la recherche: {{ summary }}"

Le synapse va fonctionner, mais cela revient à coder en dur chaque recherche que l’on voudrait effectuer. Pas franchement top pour impressionner les amis. C’est là qu’interviennent les arguments.

Modifions notre synapse

- name: "wikipedia-search"
  signals:
    - order: "cherche sur wikipédia {{ma_recherche}}"    
  neurons:
    - wikipedia_searcher:
        language: "fr"        
        query: "{{ma_recherche}}"
        say_template: "résultat de la recherche: {{ summary }}"

On ajoute un argument à l’ordre. Kalliopé va donc insérer tout ce qui est dit après la phrase « cherche sur wikipédia » dans une variable nommée « ma_recherche ».

Cette variable est ensuite utilisable dans les paramètres des neurones. Ici je le donne au paramètre « query », qui est le nom de la page recherchée.

Les events

Le second type de signal est l’événement planifié (event).

Ce type de signal permet de planifier un événement suivant une fréquence. Par exemple, je veux que Kalliopé me dise bonjour tous les matins à 7h30 en semaine, qu’elle me donne l’heure et me lance ma web radio préférée. Je vais créer un synapse comme celui ci:

- name: "wake-up"
  signals:
    - event:
        hour: "7"
        minute: "30"
        day_of_week: "1,2,3,4,5"
  neurons:
    - say:
        message:
          - "bonjour"
    - systemdate:
        say_template:
          - "il est {{ hours }} heures et {{ minutes }} minutes"
    - shell: 
        cmd: "mplayer http://192.99.17.12:6410/"
        async: True

La liste complète des paramètres utilisables par un event sont dans la documentation.

Les neurones

Le dernier point, les neurones. Un neurone est un module, ou plugin, qui va effectuer une action.

Syntaxe

Vous pouvez définir autant de neuron que nécessaire dans un seul synapse sous forme de liste.

La syntaxe est la suivante:

neurons:
  - neuron_name_1:
      parameter1: "value1"
      parameter2: "value2"
  - neuron_name_2:
      parameter1: "value1"
      parameter2: "value2"

Paramètres en entrée

Un neuron demande parfois des paramètres en entrée pour fonctionner. Nous trouverez la liste des paramètres dans la documentation du neurone. Certains sont obligatoires, d’autres optionnels.

Un paramètre peut être donné directement dans la configuration du synapse en dur comme ici

- name: "turn-on"
  signals:
    - order: "allume le couloir"
  neurons:
    - hue:
        bridge_ip: "192.168.0.7"
        group_name:  "couloir"
        state: "on"

Ici, les paramètres « bridge_ip », « group_name » et « state » sont codés en dur dans le synapse.

Un paramètre peut être récupéré depuis l’ordre comme ceci:

- name: "turn-on"
  signals:
    - order: "allume le {{ group_name }}"
  neurons:
    - hue:
        bridge_ip: "192.168.0.7"
        group_name:  "{{ group_name }}"
        state: "on"

Ou alors récupéré depuis une variable globale comme cela:

- name: "run-simple-sleep"
    signals:
      - order: "endors toi un moment"
    neurons:
      - sleep:
          seconds: "{{variable}}"

Note importante: Si vous utilisez une variable dans un paramètre, il est obligatoire d’utilisez les doubles guillemets pour encadrer celui-ci.

Cette syntaxe est fausse:

seconds: {{variable}}

Celle-ci est correct:

seconds: "{{variable}}"

Paramètres en sortie

Kalliopé est un framework, cela signifie que c’est un outil pour concevoir votre assistant comme vous le souhaitez.

Chaque neuron possède sa propre configuration en entrée, et retourne des variables en sortie que vous pouvez utiliser pour concevoir votre réponse.

Prenons un exemple avec le neurone « systemdate », qui permet de donner l’heure. La documentation de ce neurone vous donne la liste des variables qui seront retournées et que vous pouvez utiliser dans votre template.

Les variables instanciées qui serons retournées sont: hours, minutesweekdaymonthday_month et year.

On peut donc écrire un synapse avec un template de réponse comme suit:

- name: "say-local-date"
  signals:
    - order: "quelle heure est-il"
  neurons:
    - systemdate:
        say_template:
          - "il est {{ hours }} heure et {{ minutes }} minute"

Ici, Kalliopé instanciera les valeurs « hours » et « minutes » au moment de l’appel et les transmettra au template « say_template ».

Ce qui donnera un résultat du genre: « il est 9 heures et 32 minutes ».

Vous pouvez donc choisir ce que va répondre vôtre bot, et ce dans la langue que vous souhaitez du moment que le moteur TTS gère cette langue.

Le templating est basé sur un moteur nommé Jinja. Ce moteur vous permet de jouer avec les variables comme bon vous semble.

Par exemple, je modifie mon synapse pour que cette fois il utilise un fichier de template.

- name: "gouter-time"
  signals:
    - order: "est-ce qu'il est l'heure du goûter"
  neurons:
    - systemdate:
        file_template: "gouter.j2"

Je créé à présent un fichier gouter.j2 avec le contenu suivant

{% if hours|int() in range(16,17) %}
oui il est l'heure
{% else %}
non pas encore
{% endif %}

J’utilise ici le moteur et sa structure de contrôle afin de faire dire à mon bot si il est l’heure du goûter en fonction des variables retournées par le neurones au moment de l’utilisation.

Jinja propose de multiples structures de contrôles (if, else, boucle for, etc..) vous permettant de créer un nombre illimité de combinaison pour les réponses de vôtre assistant.

Infos et liens utiles

Ce petit article est une introduction au projet et ne couvre pas l’ensemble des possibilités du programme. Si vous voulez en savoir plus, Kalliopé propose:

Sources

Cet article Kalliopé: un assistant personnel customisable a été publié en premier sur Framboise 314, le Raspberry Pi à la sauce française.....


Viewing all articles
Browse latest Browse all 1015

Trending Articles