Lundi, octobre 20 2014

Seeedstudio Limite Perçage

Petit article juste pour vous parler d’un bonne surprise, la qualité des pcb SeeedStudio semble avoir été vraiment améliorée, vous pouvez voir en exemple un des plus « mauvais » via que j’ai trouvé sur ce batch.

Le via doivent normalement avoir une pastille de 0.8mm mais avec une pastille à 0.6mm et un perçage à 0.3mm le via est correcte.

Pour comparaison voila une analyse d’un PCB produit par le même service en 2012.

seeedstudio_2012_analysis


Classé dans:Microscope, PCB

Vendredi, octobre 17 2014

Régulation PID, faire de jolis graphiques 2/2

La dernière fois, je vous ai présenté une méthode essentiellement graphique, permettant une bonne approximation des paramètres PID de votre régulateur. Encore faut-il savoir comment tracer les graphiques, c’est ce que je me propose de vous expliquer dans cet article. Il est bien sûr évident que cette méthode peut être utilisée pour tracer des graphiques à partir de tout type de données, du moment que les données arrivent via un port série.
La première étape va consister à récupérer les données du port série sur un PC. Comme souvent, il existe plusieurs façon de faire, je vous présenterais celle que j’ai utilisé ici : un petit script python.

import serial
import sys

serialport = serial.Serial("/dev/ttyACM0", 9600, timeout=1)
line = []

while True:
    for c in serialport.read():
        line.append(c)
        if c == '\n':
            for s in line:
                f=open('myfile','a')
                sys.stdout.write(s)
                f.write(s)
                f.close
            line = []
            break

serialport.close()

Ce petit script prend les caractères arrivant sur le port série, jusqu’au caractère marquant la fin de ligne, puis écrit la ligne dans un fichier. Ca peut éventuellement suffire pour tracer un graphique… Mais dans ce cas précis, on veut pouvoir également envoyer une consigne au régulateur, la modifier afin de suivre la réaction de ce dernier. Problème : comment envoyer des données sans perturber la lecture des données et ne pas sauter de mesure ?
Là encore, il y a plusieurs méthodes possibles, certaines plus complexes que d’autres. Celle que j’ai choisi permet de garder un programme simple, mais ne fonctionnera que sous linux. Ceux sous un autre système chercheront du côté du multithreading…

import serial
import sys
import select

serialport = serial.Serial("/dev/ttyACM0", 9600, timeout=1)
line = []

while True:
        while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
          ligne = sys.stdin.readline()
          if ligne:
            print(ligne)
            serialport.write(ligne+'\r\n')
          else: # an empty line means stdin has been closed
            print('eof')
            exit(0)
        else:
            for c in serialport.read():
                    line.append(c)
                    if c == '\n':
                        for s in line:
                                f=open('myfile','a')
                                sys.stdout.write(s)
                                f.write(s)
                                f.close
                                line = []
                    break

serialport.close()

Ah oui, j’ouvre et ferme le fichier a chaque écriture, de manière à pouvoir accéder aux données pendant l’exécution du programme. Ca permet de visualiser l’évolution du graphique en temps réel.
Les données sont envoyées par le microcontrolleur sous la forme . Je n’ai pas de mesure de temps, chaque échantillon étant pris à un intervalle d’une seconde, il suffit de compter les échantillons pour connaître le temps écoulé.

Passons maintenant au graphique lui-même. Pour cela, j’utilise le logiciel Gnuplot, outil très puissant de tracé de courbes dont nous n’utiliseront ici qu’une infime quantité de ses possibilités. Lorsque vous lancez gnuplot en ligne de commande, vous vous retrouvez avec un shell dans lequel vous pourrez lancer vos commandes gnuplot.

plot 'myfile' using 1 smooth bezier title 'temperature', 
'myfile' using 2  with line title 'CO', 
'myfile' using 3 with line title 'Setpoint'
 plot 'myfile' using 1 

Indique que l’on va tracer une courbe correspondant aux données de la première colonne du fichier

smooth bezier

Indique que l’on veut lisser les données. Il n’est pas toujours intéressant de lisser les données, par exemple, ici la colonne 3 correspondant à la consigne n’est pas lissée, ce qui permet de visualiser le moment exact du changement de consigne. Par contre, dans le cas des valeurs mesurées, cela permet de s’affranchir du bruit sur les mesures.

title 'temperature'

Légende de la courbe. Pratique dès qu’il y a plus d’une courbe.

Par défaut, gnuplot va afficher le résultat à l’écran. Pour pouvoir enregistrer le résultat dans un fichier il faut taper les instructions suivantes :

set terminal "png"
set output "monfichier.png"
replot

Nota : avec la commande replot (ou avec le bouton replot de la gui), vous pouvez rafraîchir les données affichées, de manière à visualiser en continu vos données…
Il y a certainement plein d’amélioration possibles à ma méthode, mais je vous la présente car elle a le mérite d’être simple et rapide à mettre en oeuvre, tout en fournissant de bons résultats :)

Jeudi, octobre 2 2014

De la déco avec des éléments de Stick Arcade

  • Quand on a sous la main la collection de toutes les Ball Top Seimitsu et qu'on se dit que c'est trop bête de ranger celles dont on ne se sert pas sur les sticks de la borne, on imagine alors une sorte de présentoir percé de vis bien alignées, sur lesquelles on viendrait visser les boules, juste comme ça, pour faire joli…

Et puis on fait un proto en bois vite fait, et de constater que pour faire quelque chose d'esthétique qui se marie bien avec le design de la borne, il va y avoir du boulot, sans compter qu'on aimerait bien que tout ça s'illumine de mille feux !
Et c'est alors que : l'imprimante 3D !

Bah oui, ça serait bien dommage de ne pas remettre en question toutes les techniques que j'employais jusqu'alors afin d'en rajouter une dans le shaker et voir quoi de bon pourrait en sortir !


Concept et principe :

dsc00332.jpg

  • Voici une photo des croquis vite fait, qui m'ont servi lors de la réflexion, puis de la modélisation 3D.

Il s'agit donc de concevoir une surface percée de 9 trous qui accueilleront des vis à têtes hexagonales. Face à chaque trous, une fente permettra le passage de LEDS électronique.
Sous la structure, une gorge est formée par y passer les câbles.
Une glissière viendra refermer l'objet, et servira également de butée pour maintenir les LEDS en place, dont on aura plié les pattes.

La principale contrainte a été de calculer les dimensions de l'objet pour un compromis entre harmonie des formes de celui-ci, et sa bonne tenue sur la surface d'impression maximale de 19,4x19,4cm.
J'ai donc coupé l'objet en deux parties qu'il faut assembler avec de la colle.

Étant donné que je voulais essayer le soft, c'est cette fois sur Sketchup 8 que j'ai modélisé mon idée.
C'est pas libre et en plus ça ne fonctionne pas sous GNU/Linux, sauf via PlayOnLinux. Cependant le module d'export en .STL ne fonctionne pas, j'ai été obligé de passer par un Windows®.
Voici les plugins qui m'ont été utiles, disponibles en annexe :

- TT_Lib2-2.9.9, nécessaire au fonctionnement de tt_solid_inspector-1.2.0, outil permettant d'examiner le modèle 3D à la recherche de surface « non-manifold ».
Il s'agit de faces qu'on aurait oublié de fermer lors de la modélisation, qu'il faut alors corriger, car une fois le modèle exporté en STL et chargé dans le logiciel d'impression, celui-ci refusera d'imprimer un volume ouvert (genre un cube à 7 faces), ou une surface plane (un simple rectangle sans épaisseur).
- sketchup-stl-2.1.4, pour exporter le modèle 3D en fichier imprimable.


Impression 3D :

  • L'impression doit donc être faite en disposant l'objet en diagonale dans un carré de 194x194mm.

Avec les quatre fichiers STL que j'ai généré[1], rien à faire, chaque objet est déjà placé dans la diagonale.
Rien de spécial à dire sur les réglages d'impression…

  • Mais une précaution à prendre, pour décoller les objets de la surface d'impression : attendre le refroidissement complet !

dsc00270.jpg dsc00271.jpg
dsc00272.jpg dsc00275.jpg
dsc00276.jpg Trop pressé, j'ai eu la mauvaise idée de décoller les trois premiers objets quelques minutes après la fin de l'impression (j'imprimais en PLA, à froid sur de la bande de masquage), chose pas facile du tout d'ailleurs, et de constater qu'ils se sont tordu, surement sous la contrainte mécanique que j'ai exercé en tirant fortement dessus…
Pour rattraper ça, j'ai donc du jouer du décapeur thermique, et placer les objets sous presse pour leurs redonner une forme bien droite.
Pour le quatrième objet ce fut inutile, étant donné que ça ne s'est pas produit car je lui avait laissé le temps de refroidir.
Le décapeur fait ramollir très vite le plastique, il faut donc y aller mollo, et la presse doit être dosée, pour ne pas écraser l'objet, sinon les déformations pourraient se révéler très gênante, soit la surface se dégrade, et ça se voit sur les photos ou l'on y voit le plastique qui s'est affaissé sur le nid d'abeille sous l'effet de la chaleur, soit l'objet se déforme, et avec la glissière à faire passer, où les vis à rentrer, ça ne va pas être simple.

Essais :

  • Avant d'aller plus loin, j'ai procédé à un assemblage complet de tous les éléments, afin de vérifier la cohérence de l'ensemble.

dsc00268.jpg dsc00267.jpg
dsc00259.jpg dsc00258.jpg
dsc00266.jpg dsc00265.jpg dsc00264.jpg Les vis font 15mm de long et 6mm de diamètre.
C'est à ce moment là qu'on ressent la satisfaction du créateur, contemplant le bidule complexe et pourtant pas très utile, en se disant que oué, ça l'fait, les calculs étaient exact, la preuve devant soit.

Réalisation :

Les trucs imprimés c'est bien, mais l'aspect de surface n'est pas jolie.

  • De plus, forcements la jointure entre les deux parties est très visible, donc après avoir collé les deux parties à la cyanolite, allons-y pour un masticage en règle, avec du mastic standard de marque Sintofer.

Puis étape de ponçage, avec du papier abrasif 400 et de l'eau, ça évite d'avoir de la poussière partout et rend le ponçage plus doux.
dsc00280.jpg dsc00281.jpg

  • Ensuite une sous-couche blanche à la bombe avant de peindre l'ensemble à l'aérographe, avec la même peinture acrylique brillante que j'ai utilisé pour la borne, diluée au liquide vitre (oui oui, le truc bleu nettoyant).

dsc00283.jpg dsc00284.jpg

Câblage électrique :

  • L'éclairage est réalisé avec des LEDs crystal blanche qui éclaire à 1000mcd pour 3,2V@20mA.

dsc00286.jpg dsc00291.jpg

  • J'avais prévu de les disposer à la verticale, et de peindre en noir le dessus pour éviter d'éclairer le plafond, mais finalement, après essais, l'illumination rend mieux avec la LED orientée vers le centre de la boule, on arrange ça en courbant les pattes comme ceci :

dsc00299.jpg dsc00302.jpg

  • Le câblage en lui même peut être facilement calculé à l'aide de cet outil en ligne très pratique, sachant que le montage sera alimenté par le 12V provenant de la borne.

J'ai donc câblé en parallèle 3 lots de 3 LEDs en série, chacun protégés par une résistance de 120Ω.
Les câbles sont placés dans la gorge et les pattes des LEDs replié dans celle-ci, pour qu'à la fermeture de la glissière elles soient en appuie à la bonne hauteur pour éclairer la boule.
dsc00303.jpg dsc00306.jpg

Mise en situation :

  • Voila, donc on peut par exemple poser le bidule sur le dessus de la borne, et le raccorder au 12V de celle-ci.

dsc00326.jpg dsc00307.jpg dsc00324.jpg

Hop, on va allumer la lumière… Oula, ça éclaire fort, voyons ça dans le noir en réglant correctement d'exposition de l'appareil photo :
dsc00317.jpg dsc00308.jpg dsc00318.jpg dsc00319.jpg dsc00320.jpg

Note

[1] voir en annexe, ci-dessous ou sur thingiverse

Mardi, septembre 30 2014

Le poulailler connecté

Grâce à une campagne de test concernant la réduction des déchets à laquelle j'ai participé, j'ai maintenant 2 poules et un poulailler (c'est d'ailleurs le nom de la campagne : « deux poules et un poulailler pour réduire nos déchets »).
L'opération consistait à nourrir les poules avec un maximum de déchets qui finissent habituellement dans les poubelles ou au mieux dans un composteur. Plus de 6 mois se sont écoulés, l'opération est maintenant finie et je trouvais qu'il serait intéressant, et surtout amusant de mettre quelques capteurs dans le poulailler.

Cot ! Les poules

Après la brosse à dents connectée ou le produit qui va totalement révolutionner vos WC : SmartPQ, le premier rouleau de papier de toilette connecté et surfant sur cette vague de bullshit créativité, j'ai décidé de me lancer dans une première mondiale : ami des gallinacées, je vous présente le premier poulailler connecté !

Twitter @LaVieDePoule

Fonctionnalités du bestiau :

  • Prise de photos infrarouges, détection des œufs
  • Prise de température / luminosité / capteur mouvement / capteurs d'ouverture
  • Surveillance de la température, de la tension d'alimentation et du courant consommé
  • Alertes via Twitter et SMS (API Free Mobile) si certains seuils sont dépassés (température, alimentation)

Le tout est articulé autour d'un Raspberry et d'une carte fille RaspiO'Mix armée de capteurs :

  • Température de l'enceinte du poulailler
  • Température de chaque nid
  • Détecteur de mouvement IR
  • Caméra infrarouge
  • Capteur de luminosité extérieure
  • 3 capteurs sur tous les accès du poulailler (basse-cour, porte jardin et collecteur d’œufs)
  • Mesure de la tension d'alimentation et du courant consommé

2 sorties sont également pilotables et sont utilisées pour la led témoin et le relais pilotant les leds infrarouges.

Le tout en 1 image :
Vue des entrées / sorties

Le poulailler

Le poulailler fourni dispose de 2 portes et d'un collecteur d'oeufs, sur l'un des côtés disposant d'une ouverture, on vient coller une sorte de cage (à gauche sur la photo) disposant elle-même d'une ouverture permettant l'accès à une éventuelle basse-cour, dans la configuration actuelle, la seconde porte sert à faire sortir les poules directement dans le jardin (la porte devant).

Vue du poulailler

Le boitier et son logement

Afin de protéger l'électronique contre les conditions qui seront certainement rudes en pleine hiver, le tout est logé dans un boitier de répartition étanche.

Le coffret étanche

Détails techniques

Caméra

J'utilise Pi Noir (la caméra officielle de Raspberry sans le filtre infra-rouge), elle est placée face aux 2 nids et sa focale permet d'avoir une vue parfaite sur les poulettes.
Pour les photos de nuit, un relais pilote un anneau de leds infra-rouges permettant d'illuminer le poulailler afin d'y voir quelque chose et ça marche plutôt bien :

ir_0.jpg ir_1.jpg ir_2.jpg ir_3.jpg

La caméra (en noir à droite) et l'éclairage infrarouge situé à la verticale des nids
Le capteur infrarouge à gauche et la caméra Noir à droite Les leds infrarouges

Le principal intérêt de la caméra concerne la détection des œufs via OpenCV. À intervalle régulier, je prends une photo de la scène et je cherche des éventuels œufs après une succession de traitement sur l'image.

Pour le moment, la détection des œufs ne se lance pas automatiquement car j'ai encore un peu de développement à faire pour améliorer l’algorithme qui affiche pour le moment un taux de succès inférieur à 42%.

Pour pouvoir tester l'algorithme de détection, j'ai collecté des images en provenance de la caméra sur lesquelles apparaissent ou non des œufs, le nom des fichiers indique le nombre d’œufs présents (0_1.jpg signifie 1 œuf, il faut ignorer le premier numéro), ainsi, une fois l'image analysé, je peux savoir si le résultat est bon ou non, si tous les œufs de l'image ont bien été trouvés.

Un exemple d'utilisation :

$ python eggcounter.py --export=tests/export tests/with/*
Open tests/with/0_1.jpg [Ok]
Open tests/with/10_2.jpg [Error]
Open tests/with/11_1.jpg [Ok]
Open tests/with/11_2.jpg [Error]
Open tests/with/1_1.jpg [Ok]
Open tests/with/12_2.jpg [Error]
Open tests/with/13_1.jpg [Error]
Open tests/with/14_1.jpg [Error]
Open tests/with/15_1.jpg [Error]
Open tests/with/16_1.jpg [Error]
Open tests/with/2_1_highlight.jpg [Ok]
Open tests/with/3_1.jpg [Ok]
Open tests/with/5_1.jpg [Ok]
Open tests/with/6_1.jpg [Ok]
Open tests/with/7_2_highlight.jpg [Error]
Open tests/with/9_2_highlight.jpg [Error]
Result: 42% (7/16)
- Extra egg detected : 5
- Missed egg : 11
$ 

42%, ce n'est pas énorme mais c'est une partie que j'ai peu travaillé, le score ne peut donc que s'améliorer...

Notez que je lance le même test avec des images sans œuf, le résultat est meilleur (84%) mais ça signifie tout de même que j'arrive à trouver des œufs ou il n'y en a pas ;).

Enfin, je n'ai pas testé d'image depuis que l'éclairage infrarouge est installé, je pense que ça devrait grandement améliorer la détection.

Les différentes étapes de la détection
  1. On part de l'image source
  2. On applique 2 transformations (erode et dilate)
  3. Modification des niveaux (threshold)
  4. Le résultat : 1 œuf trouvé !

Tout ceci en image :
Image originale Erode / Dilate Niveaux Oeuf trouvé !

Capteur de mouvements IR

L'idée est de repérer les mouvements dans l'enceinte du poulailler afin de savoir si les poules sont dedans ou non lorsque les portes extérieures sont ouvertes.

J'utilise un capteur de type PIR HC-SR501 qui possède 3 broches, 2 d'alimentation et une de sortie. Le souci avec ce module est qu'il possède son propre régulateur 3V (à faible chute de tension), or, je l'alimente en 5V et j'ai besoin d'une tension de sortie de 5V pour un niveau haut (3V pourrait être pris un niveau indéterminé par l'adaptateur de niveau de la carte RaspiO'Mix #vécuInside, enfin, je ne souhaite pas le brancher en direct sur les IO du Raspberry), j'ai donc ajouté un petit montage avec un transistor et une résistance faisant office d’adaptateur de niveau (0V -> 5V, 3V -> 0V).

Le capteur étant assez sensible et surtout pas vraiment adapté à ce genre d'utilisation (dans un environnement aussi confiné), le logiciel gère en grande partie ces insuffisances (debouncer et compteur d'impulsions) et ça marche assez bien.

Le capteur infrarouge à gauche et la caméra RaspberryPi Noir à droite
Le capteur infrarouge à gauche et la caméra Noir à droite

Capteur de luminosité extérieure

Il est constitué d'une simple photorésistance (LDR) dont le principe de fonctionnement est une modification de la résistance à ces bornes proportionnellement à la luminosité (plus l'éclairage est fort, plus la résistance diminue).
Ce capteur permet de donner des indications sur l'heure de coucher des poules car le rythme de ces dernières est totalement calé sur celui du Soleil, d’où l'expression se coucher à l'heure des poules...

La LDR est utilisée avec un pont diviseur de tension qui attaque directement une des entrées analogiques de la carte RaspiO'Mix.

La LDR (à droite) et un capteur de température à gauche (DS1820)
La LDR (à droite) et un capteur de température à gauche

Capteur d'accès

Aux nombres de 3 et disposés sur toutes les ouvertures, ils sont simplement constitués de microswitch, à chaque changement d'état de l'entrée associée au capteur, un évènement est généré dans le code et une action est générée (Tweet, etc...), l'anti-rebond est géré du côté logiciel.

Les accès :

  • Basse court
  • Jardin entier
  • Collecteur d’œufs

Capteur d'ouverture basse-cour Capteur d'ouverture jardin Capteur d'ouverture collecteur oeufs

Mesures de tension et courant

La valeur de la tension d'entrée (environ 12V) juste avant le régulateur à découpage est présentée via un pont diviseur de tension à une des entrées analogiques du RaspiO'Mix, la seconde mesure concernant la courant consommé par le dispositif.

Le but serait d'alimenter le tout par panneau solaire, cela explique aussi la présence de ces 2 capteurs.

Les sondes de température

Le capteur de l'enceinte du poulailler est basé sur un simple LM35 connecté sur une des entrées analogiques de la carte RaspiO'Mix, au niveau logiciel, je lis très simplement cette valeur, la multiple par 100 (1 degré = 10mV) et l'affiche dans les log ou la publie sur Twitter, dans la configuration (config/general.py), il est possible de définir des seuils acceptables (3 à 32 degré C) au delà desquels une alerte est générée et un twit m'est directement adressé pour me prévenir que mes poulettes ont froid ou trop chaud...

La prise de température de chaque nid est un peu plus complexe que pour l'enceinte, la distance entre le Raspberry et les nids étant plus grande, un capteur analogique n'aurait pas été très précis, ainsi, afin d'avoir une mesure fiable, j'ai utilisé des sondes OneWire DS1820 qui autorisent de grande distance de câble sans perte de précision (et puis de toute façon, je n'avais plus d'entrées analogiques disponibles).

Ces 2 sondes n'utilisent que 2 fils (mode parasite), la masse et le tension / signal :

Screen_Shot_2014-08-13_at_14.45.41.png

Au niveau logiciel, afin de fonctionner correctement, ces sondes ont besoin des modules suivants avec les paramètres qui vont bien :

$ modprobe w1-gpio gpiopin=25 pullup=1
$ modprobe w1-therm
$ 

Une fois les modules chargés, ils vont "pouller" à intervalle régulier le bus afin de découvrir et lire les valeurs des capteurs connectés sur le bus, pour les découvrir, une petite commande suffit :

$ ls /sys/bus/w1/devices/
10-0008008ba2a9  10-0008008bceb5  w1_bus_master1
$ cat 10-0008008ba2a9/w1_slave
1b 00 4b 46 ff ff 01 10 23 : crc=23 YES
1b 00 4b 46 ff ff 01 10 23 t=13687
$ 

Le fichier w1_slave contient toutes les informations qui nous intéressent, il est à parser et c'est la fonction read_w1_temperature qui s'en occupe et pas folle la guêpe, en cas d'erreur de lecture sur le bus, une nouvelle tentative est faite...

Durant le développement, un bug dans le pilote w1 foutait la brouille sur le bus i2c et empêchait la caméra de fonctionner correctement, il n'était donc pas possible d'utiliser conjointement la caméra et les capteurs de température, si je voulais utiliser ces derniers, j'étais obligé de rebooter en activant les modules, la caméra devenant alors inaccessible, pour l'avoir, il fallait rebooter et bien entendu, ne pas charger les modules w1 (plus d'informations à propos de ce problème sur GitHub).

J'ai réglé ces problèmes avec cet ordre dans /etc/modules :

$ cat /etc/modules
# First load onewire driver before i2c module
w1-therm
w1-gpio gpiopin=25 pullup=1

i2c-bcm2078
i2c-dev
#spi-bcm2708
rtc-ds1307
#snd-bcm2835
bcm2708_wdog

cuse
$ 

Note à propos des capteurs de température des nids : en suivant leurs températures, il est possible de détecter la présence des poules sur les nids, plus pratique et précis qu'un système mécanique.

Le logiciel

C'est Python qui fait tout le travail de notifications, de surveillance, etc... J'utilise également Motion pour streamer « en live » le flux vidéo en provenance de la caméra afin de voir ce qui se passe dans le poulailler.

Pour tout le reste, c'est Python qui intervient, j'utilise, entre autre, les modules suivants :

  • OpenCV pour la recherche des œufs
  • Twython pour l'interfaçage avec Twitter

2 threads sont utilisés, un s'occupe d'envoyer les notifications dans la console, surveiller les seuils d'alertes, générer les messages et les envoyer sur Twitter et un autre thread gère les réponses aux messages qui sont adressés au compte Twitter, oui, je l'admet, mes poules n'ont pas le temps de répondre à tous les messages et un CM automatisé leur permet de vaquer à leurs occupations tranquillement !

J'ai également développé un bout de code qui permet de former des phrases de manières plus ou moins « aléatoire » afin de ne pas toujours envoyer les mêmes messages sur Twitter.

Voici un exemple d'utilisation très simple; imaginez que vous vouliez pouvoir dire bonjour à quelqu'un via l'appel à un programme mais d'une manière originale à chaque fois, par exemple :

Nous avons d'un côté "Bonjour", "Salut" et "Hello", de l'autre "toi", "l'ami" et "vous", en combinant tout ça, on obtient ces bouts de phrases :

  • Bonjour toi / Bonjour l'ami / Bonjour vous
  • Salut l'ami / Salut toi / Salut vous
  • Hello vous / Hello toi / Hello l'ami

Vous avez compris le principe, on combine plusieurs morceaux de phrases ensemble afin d'en former d'autres.

Pour automatiser ça avec ma lib, on créé un tuple de tuple ainsi :

test = (
    '{0} {1}',
    (
        ( 5, 'Bonjour'),
        ( 2, 'Salut' ),
        ( 2, 'Hello' ),
    ),
    (
        ( 1, 'toi'),
        ( 5, 'l\'ami{0}', (
            ( 1, ', il est %hour% !'),
            ( 1, '!'),
        )),
        ( 5, 'vous'),
    )
)

Notes :

  • Le premier élément du tuple '{0} {1}' est une règle de formatage "format", le second élément et le troisième correspondent aux éléments que l'on va « sélectionner aléatoirement » et venir remplacer dans dans le premier élément.
  • Le second tuple est constitué de sous tuple contenant un nombre (la pondération) et une chaine de caractère, plus la valeur de la pondération est élevée et plus la chaine de caractère associée à de chance d'être choisi.
  • Vous pouvez imbriquez autant de tuple que vous le souhaitez pour créer des phrases très complexes

Après, il suffit de faire un appel à la fonction speak avec notre tuple en paramètre et elle nous généra une phrase automagiquement.

speak(test, hour='11h')

Vous noterez au passage que vous pouvez injecter des paramètres nommés qui serviront à remplacer leur équivalent sous la forme %param% dans les chaines de caractères.

Et voici ce que ça donne :

$ python speak.py
Bonjour l'ami, il est 11h !
$ python speak.py
Bonjour vous
$ 

Si vous voulez jouer avec cette lib, vous la trouverez dans lib/speak.py.

Les pistes d'évolutions

  • Améliorer la détection des œufs
  • Générer des stats (pontes, températures, éclairage, etc...)
  • Reconnaitre chaque poule (les lier à la production d’œufs)
  • Autonomie énergétique du dispositif

Le tout est disponible sur GitHub sous une licence évidemment libre : GitHub / hugokernel / LaVieDePoule

Mercredi, septembre 24 2014

Holster pour ordi-phone

  • Comme je l'évoquais alors, me voilà avec un ordi-phone FirefoxOS et pas de poche assez grande pour le transporter en sécurité.

Les téléphones qu'on a d'abord voulu si petit pour tenir dans une poche, deviennent maintenant si gros que ça en devient ridicule, dixit l'itruc6 qui fait les frais à juste titre de pas mal de moqueries.

  • De taille raisonnable, le ZTE Open C ne rentre pourtant pas dans le compartiment prévu de mon holster, et pas question de le placer dans une poche de pantalon, avec le risque trop important d'abîmer l'écran ou de s'asseoir dessus dans le cas de la poche arrière, sans compter la perte facile de l'engin.

Maintenant que je me penche sur le sujet, je dénote que ces appareils sont plus souvent dans les mains de leurs propriétaires que rangés, et j'en viens à me demander si la grande utilisation de ceux-ci ne serait pas incitée par leur encombrement excessif, genre on ne sait pas ou le mettre, donc on l'a à la main, donc bah on l'allume et on se laisse aller…
Hum, de la à dire que ce serait une stratégie des fabricants pour pousser à la consommation de contenu, je vous laisse réfléchir la dessus…

Bien, partant de ce problème de transport, je me suis mis en quête d'une solution, d'abord en cherchant à remplacer mon holster, par un autre modèle plus adapté mais que je n'ai pas trouvé, puis je me suis laissé gagner par l'idée de modifier mon holster d'une manière relativement simple, en fabriquant un nouveau compartiment !

  • Pour référence, voici un holster, sans modifications :

dsc00176.jpg On dispose d'un compartiment portefeuille, et en dessous d'un plus petit pour le téléphone, donc trop petit pour un ordi-phone.
Une fois porté, on a donc un côté utile à droite et du côté gauche une sangle qui passe sur l'aisselle, ce qui crée un déséquilibre, car le poids des affaires à droite tire et tend à ce que la sangle colle bien sous le bras ce qui n'est ni esthétique avec un T-shirt ou une chemise un peu large, ni agréable.
Nous verrons donc que ma bidouille permet au passage de résoudre ce défaut de conception de la part du fabriquant.
dsc00180.jpg dsc00181.jpg

PatronHolster.jpg

Le patron :

  • C'est donc après quelques heures de réflexions intense et de tests à base de modèles en papier que j'ai élaboré le patron du compartiment, que voici :

Le scan est à 300dpi, donc normalement imprimable directement à l'échelle 1 sur un A4, sinon, j'ai tracé un petit centimètre de référence.

  • Sur du tissus épais type jean, j'ai tracé et découpé le rabat de fermeture deux fois et la forme principale 3 fois. L'une d'elle est tronquée, ce sera la face avant.

Le plus dur est fait, passons à la couture !

dsc00135.jpg dsc00137.jpg

Le rabat de fermeture :

  • Il s'agit de coudre la partie douce d'une bande velcro sur une des deux formes en tissus, puis de la recouvrir de la seconde et de les assembler par une couture sur la marge, c'est à dire le long du trait blanc.
  • Puis on retourne l'ouvrage comme une chaussette, et le velcro réapparaît, tandis que les coutures disparaissent à l'intérieur.

dsc00138.jpg dsc00139.jpg

  • Ensuite on va assembler et coudre le rabat sur l'une des trois formes principale, le long du trait blanc vertical, comme ceci :

dsc00141.jpg dsc00142.jpg

La face arrière :

  • J'ai ensuite disposé un chiffon micro-fibre sur ce qui constitue donc la face arrière de la pochette.

Une fois taillé, il est cousu tout le long du trait blanc.
dsc00143.jpg dsc00144.jpg

dsc00148.jpg

La face avant :

  • La bande velcro accrocheuse est cousue sur la face avant, côté extérieur donc.

  • Une fois retourné, je dispose un morceau de tapis de souris qui me restait de mon étuis pour Nintendo DSi, recouvert aussi d'un chiffon micro-fibre, assemblés avec une couture sur la marge en blanc.

Ce qui devrait donc molletonner efficacement la pochette.

dsc00145.jpg dsc00146.jpg

Assemblage :

À ce stade on peut donc disposer la face avant sur l'arrière et mettre en situation l'ordi-phone afin de vérifier la cohérence de l'ensemble.
dsc00149.jpg dsc00150.jpg
À l'aide d'épingle, les deux faces sont maintenues le long du trait blanc. dsc00151.jpg dsc00152.jpg

Maintenant, il doit rester une partie principale inusitée dont on ne saurait à priori que faire, et de se dire également qu'on cousant nos deux faces tel quel, les coutures seraient non seulement visible, mais en plus le tissus resterait ainsi sujet à l’effilochage…

  • On va donc recouvrir le tout par le dessus avec cette dernière pièce de tissu, et coudre ces trois couches d'un coup, tout le long de la marge en blanc, mais attention à ne pas coudre le rabat, ni le haut de la pochette (là ou c'est étroit) !!!

dsc00153.jpg dsc00155.jpg

  • Car et c'est là qu'est toute l'astuce, on va se servir de cet endroit pour retourner l'ouvrage façon chaussette.

C'est pas facile, mais on y arrive tranquillement…
Pour récapituler, la troisième pièce est cousue par dessus la face avant, et une fois retournée se retrouve en face arrière, les coutures liant les trois couches se retrouvant à l'intérieur de la cavité ainsi créée par les deux faces arrières… je ne sais pas si c'est très clair, mais bon…
dsc00157.jpg dsc00159.jpg
Auparavant, on aura pris soins de coudre des rabats en haut et sur le devant, comme on l’aperçoit sur la photo avec la machine à coudre, et comme on le comprendra mieux sur les photos suivantes.

Finitions :

  • La pochette est quasi terminée, mais il reste une cavité béante formée par cette troisième couche de tissus retourné qu'on a rajouté et qu'on va mettre à profit pour y glisser un peu de mousse afin de molletonner l’arrière.

Pas facile non plus à disposer, mais on y arrive, aidé si besoins de petite pinces brus-selle ou à épiler.
dsc00160.jpg dsc00161.jpg

  • Une fois en place, il reste à fermer le trou en cousant les marges repliées sur l'intérieur.

Attention à ne pas coudre les rabats de l'étape précédente ! En effet, à cet emplacement doit passer la sangle du holster, comme en attestent les clichés de la dernière étape.
dsc00162.jpg dsc00163.jpg
dsc00164.jpg dsc00165.jpg

Mise en place sur le holster :

  • Il reste donc à faire passer la sangle depuis le haut jusque devant, à l'intérieur de la cavité ou siège la mousse.

Le nouveau compartiment peut coulisser sur la sangle pour être ajusté comme on veut.
dsc00168.jpg dsc00170.jpg dsc00173.jpg dsc00174.jpg

  • Et donc comme je l'évoquais, avec ce nouveau compartiment à gauche, le holster s'en trouve équilibré et la sangle ne vient plus serrer sous le bras :)

dsc00182.jpg dsc00184.jpg dsc00185.jpg dsc00186.jpg

  • Voilà un ordi-phone bien protégé dans son compartiment molletonné, à portée de la main, et pratique avec le chiffon micro-fibre qui nettoie l'écran à chaque rangement.

Seul regret, j'aurais dû molletonner un peu aussi le rabat de fermeture pour mieux protéger la tranche.

Dimanche, septembre 21 2014

Une Borne d'Arcade maison, nouveau système informatique

AdvanceMame3.png

  • Refonte de mon tuto précédent, je propose ici une méthode d'installation rapide et simple d'un système Arcade@15kHz fonctionnel branché sur un téléviseur, avec AdvanceMenu, plusieurs versions de mame64 bits et AdvanceMame.

Ce système pèse environ 1,4Gio, sans superflu, il démarre très vite, idéal pour être utilisé en borne, sans clavier ni souris.

Prérequis :

- Une télévision cathodique.
- Un adaptateur VGA vers péritel, ou un autre bricolé vite fait.
- L'image iso de debian 7 netinstall depuis debian.org.
- L'archive d'installation ArcadeDebian7Install que j'ai scripté.
- Une connexion internet.
- Un ordinateur 64 bits avec une carte graphique ATI de préférence. (pour une carte nVidia éditer de fichier xorg.conf présent dans l'archive en remplaçant radeon par nouveau)

Si on souhaite utiliser un écran d'ordinateur plutôt qu'une télévision, supprimer le fichier xorg.conf de l'archive d'install.

Installation du système d'exploitation :

  • Générer une clé USB bootable depuis d'image iso avec la commande dd.

sdX correspondant à la clé cible, attention à ne pas se tromper !

dd if=debian-7.6.0-amd64-netinst.iso of=/dev/sdX bs=4M; sync
  • Démarrer l'ordi 64bits sur la clé, et suivre les instructions d'installation.

Pour simplifier les choses et permettre la configuration correcte de l'étape d'installation Arcade, indiquer ceci à l'installeur :

- Mot de passe root : arcade
- Nom d'utilisateur : arcade
- Mot de passe utilisateur : arcade

Partitionner de préférence :

- Une première partition de 2Gio minimum, point de montage racine /
- Une seconde partition pour stocker, point de montage /home

À l'étape d'installation des packets, lorsque la question sera posée, décocher :

- environnement de bureau debian
- serveur d'impression


Installation du système Arcade :

Cette étape va permettre d'installer ceci :

- Le minimum requis du serveur graphique Xorg et sonore Alsa.
- Un Linux 3.2.55 patché@15kHz.
- AdvanceMenu 2.7.
- AdvanceMame 1.2.
- Mame 64 bits 0.143u9 patché SH3, Hiscore et nonag screen.
- Mame 64 bits 0.148 patché SH3, Hiscore et nonag screen.
- Mame 64 bits 0.152 patché SH3, Hiscore et nonag screen.
- UME 64 bits 0.153.
- L'ensemble des fichiers de configurations (xorg.conf@15kHz, mame.ini, advmame.rc, advmenu.rc, hiscore.dat…) et quelques scripts de commandes utiles à AdvanceMenu.

  • Copier ArcadeDebian7Install.tar.gz dans /home/arcade/ et décompresser l'archive tar :
tar xvf ArcadeDebian7Install.tar.gz
  • Se placer dans le dossier ArcadeDebian7Install :
cd ArcadeDebian7Install
  • Se loguer en root :
su
  • Exécuter le script d'installation :
./install.sh

Aller se promener, plus ou moins longtemps selon la puissance de l'ordinateur et du débit internet…

  • À la fin, sera affiché la page de configuration Alsa, régler alors les barres-graph de volume comme souhaité, et frapper échap pour sortir.

Copier des roms dans ./advance/RomTATE (jeux verticaux) et ./advance/RomYOKO (jeux horizontaux)
Pour avoir des vignettes dans AdvanceMenu, il faudra remplir les dossiers ./advance/snap et ./advance/titles.

  • C'est terminé, taper starx pour tester.

Au prochain reboot, l'interface se lancera automatiquement.

Il se peut que la carte graphique n'aime pas le modeline par défaut 320x240, dans ce cas, effectuer les changements en éditant le fichier /etc/X11/xorg.conf.

Fonctionnalités :

Voir aussi la démo vidéo.

- Presser une fois le bouton power de l'ordi et le système s'éteindra proprement en 5 à 10 secondes.

Via AdvanceMenu,

- Possibilité par le menu Emulator de choisir la version de Mame désirée.
- Possibilité par le menu Command de passer en TATE ou en YOKO
- Possibilité par le menu Command de copier de nouvelles roms depuis une cléUSB contenant les dossiers RomTATE et/ou RomYOKO remplis.
- Possibilité par le menu Command de passer l'affichage graphique en mode overlay (320x240, bon compromis) ou true15kHz.

Pour true15kHz, il faudra remplir le fichier /etc/X11/xorg.conf de quelques modelines fonctionnels correspondant à la résolution des jeux.

Le mot de la fin :

Bon, je commence à être rodé pour ce genre d'installation, avec 4 bornes à mon actif dont j'assume la maintenance.
Si j'avais le temps je me lancerais bien dans la confection d'un LiveCD avec installeur complet, histoire de voir comment on fait pour créer sa propre distribution GNU/Linux et de pérenniser l'installation hors connexion internet !
Un jour peut-être…

Mercredi, septembre 17 2014

Pi Camera et Streaming video

dsc00329.jpgMe revoici donc, après les petits ennuis matériel évoqués, avec l'idée de streamer la caméra du Raspberry en IP.


dsc00330.jpgAu boîtier imprimé j'ai ajouté des lentilles magnétiques pour ordi-phone, afin de disposer d'un grand angle ou d'un fish-eye.

Rappel vite fait de la base pour l'installation :

Télécharger Raspbian http://www.raspberrypi.org/downloads

  • Déployer l'image sur une carte SD depuis un système GNU/Linux, avec la commande DD

(attention, of=/dev/sdx à adapter pour la cible de votre carte SD, en cas d'erreur risque d'effacement d'un disque dur non désiré)

dd bs=4M if=2014-06-20-wheezy-raspbian.img of=/dev/sdx; sync
  • Placer la carte SD dans le Raspberry, et le mettre sous tension.

raspi-config s'exécute tout seul au premier démarrage, configurer tous les trucs comme on veut, avec cette fois la partie camera à activer !
Pour exécuter raspi-config plus tard, simplement :

raspi-config

Après le reboot, pour rappel :

Login : pi
Pass : raspberry

Effectuer les mises à jour :

sudo apt-get install rpi-update
sudo rpi-update
sudo apt-get update
sudo apt-get upgrade



Installer la clé wifi TL-WN725N-v2 :

  • Attention, par rapport au moment ou j'ai écris l'article sur la clé wifi TL-WN725N-v2, la dernière Raspbian (2014-06-20-wheezy-raspbian) n’intègre toujours pas le module pilote, mais je constate qu'il est désormais impossible d'installer les headers nécessaires à la compilation du module !

En effet, quand on demande l'installation des headers, ce sont donc ceux du noyau en fonctionnement (uname -a) qui sont requis, et celui-ci est donc un noyau fournit par la fondation Raspberry, sans les headers, hahaha, sinon c'est pas drôle -_-
Il faut donc installer un autre noyau et ses headers, soit en le choisissant manuellement avec :

sudo aptitude search linux-headers

puis,

sudo apt-get install linux-image-3.10-3-rpi linux-headers-3.10-3-rpi
  • Soit en utilisant le métapacket suivant qui dans le cas présent installe automatiquement le kernel 3.10-3-rpi
sudo apt-get install linux-image-rpi-rpfv linux-headers-rpi-rpfv
  • Bien, mais il faut savoir, qu'un chargeur de démarrage tel que Grub n'existe pas sous Raspbian, alors tout se passe dans le fichier texte /boot/config.txt qu'il suffit d'éditer pour y ajouter ces lignes pour la prise en compte du noyaux qu'on vient de mettre. (Sinon le Raspberry continuera de démarrer sur le noyau par défaut kernel.img).
kernel=vmlinuz-3.10-3-rpi
initramfs initrd.img-3.10-3-rpi followkernel

Tester la camera :

  • Cette commande devrait afficher une image (oui même sur la console tty) durant quelques seconde, puis enregistrer le fichier image.jpg.
raspistill -o image.jpg



Monter un dossier réseau, pour enregistrer les vidéos :

  • Même démarche qu'avec le médiacenter, bien pratique d'avoir un serveur ou un nas.
sudo apt-get install sshfs
  • Créer une paire de clés publiques/privées pour l'utilisateur sur le client :
ssh-keygen -t dsa

Laisser le chemin par défaut (/home/pi/.ssh/id_dsa) et laisser la demande de passphrase vide.

  • Copier la clé publique sur le serveur :
sudo ssh-copy-id -i /home/pi/.ssh/id_dsa.pub "-p N°-de-port pi@IP-du-serveur-nas"
  • Ajouter l'utilisateur au groupe fuse (se déloguer/reloguer pour prise en compte) :
sudo usermod -a -G fuse pi
  • Écrire le script cameraONsvr.sh :
nano ~/cameraONsvr.sh
#!/bin/sh
sshfs -p N°-de-port pi@IP-du-serveur:/home/LOGIN-sur-serveur/video /home/pi/camera
  • Créer le dossier camera
mkdir ~/camera
  • Rendre le script exécutable :
sudo chmod +x ~/cameraONsvr.sh
  • Pour exécuter le script à chaque démarrage, éditer le fichier /etc/rc.local et avant la ligne exit 0, inscrire ces deux lignes :
sleep 8
su -l pi -c "/home/pi/cameraONsvr.sh &"



Le Stream sur IP :

J'ai testé plusieurs logiciels, les plus emblématiques étant Motion, MJPG-Streamer, VLC ou encore GStreamer.

- Motion, utilisé avec sa version spéciale Pi, reste très lent et la visualisation du stream sur le réseau via un navigateur est inexploitable pour qui a besoin une image fluide.
- VLC ça marche, j'en parle plus bas.
- GStreamer, pas réussit à le faire fonctionner.
- MJPG-Streamer, dans sa version standard fonctionne mieux que motion, mais malgré un réglage correct, le débit d'image restitué à l'écran est très lent.

Mais il existe une version spéciale Pi qui marche très bien, c'est celle que je vais décrire tout de suite.

Le Stream sur IP live avec MJPG-Streamer raspicam :

  • Installer MJPG-Streamer raspicam
sudo apt-get install libjpeg8-dev imagemagick libv4l-dev libjpeg62-dev cmake
git clone https://github.com/jacksonliam/mjpg-streamer.git ~/mjpg-streamer
cd ~/mjpg-streamer/mjpg-streamer-experimental
make clean all
sudo mv ~/mjpg-streamer/mjpg-streamer-experimental /opt/mjpg-streamer
sudo rm -rf ~/mjpg-streamer
  • Une commande pour streamer, qu'on pourra intégrer dans un script à mettre au démarrage (voir plus haut) :
LD_LIBRARY_PATH=/opt/mjpg-streamer/ /opt/mjpg-streamer/mjpg_streamer -i "input_raspicam.so -fps 25 -q 10 -x 640 -y 480" -o "output_http.so -p 9000 -w /opt/mjpg-streamer/www" &
  • Pour sortir sur le web, il faudra bien entendu ouvrir le port 9000 (ou autre au choix) sur le routeur ou la machin-box.
  • Pour intégration à un site web, créer la page de visualisation du flux :

stream.html :

<img src="http://IP-Publique:9000/?action=stream" />

Et voici le code à intégrer à sa home page pour avoir une vignette cliquable vers la page de visualisation du flux :

<a href="http://DOMAINE.com/stream.html"><img src="http://IP-Publique:9000/?action=snapshot"  width="270" height="190" /></a>

Vous pouvez, si au moment de lire ces lignes il est en marche, avoir un aperçu du flux et le visionner en cliquant sur la vignette de la colonne de droite.
Ça fonctionne très bien alors que le Pi est câblé en RJ45. En Wifi, l'image saccade, cf la suite.

Le Stream sur IP live, à haut débit, avec VLC :

J'ai testé deux résolution HD avec cette commande @15MBits/s, avec mon matériel réseau filaire (Routeur 100Még)

  • Test en HD 1920x1080 25p :
raspivid -t 0 -w 1920 -h 1080 -fps 25  -b 15000000 -o - | cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8555}' :demux=h264

Pour lire le flux, depuis un ordi du réseau, ouvrir VLC sur http://Adresse-ip-Raspberry:8555

Ça passe, mais l'image saute de temps en temps sur les mouvements rapides.

  • Test en HD 1280x720 25p :
raspivid -t 0 -w 1280 -h 720 -fps 25  -b 15000000 -o - | cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8555}' :demux=h264

Ça passe très bien :)

  • Les mêmes tests en Wifi se sont révélés désastreux (en b/g).

En effet, j'ai découvert que contrairement à ce que j'avais cru comprendre cette clé wifi est limite-limite en terme d'alimentation autonome sur le port USB du Pi.
Quelque soit le débit demandé pour le flux vidéo, sans alimentation externe, la clé wifi perd rapidement 30 à 40% de qualité de signal, voire le contrôleur wlan0 s'arrête, plus de réseau nous dit ifconfig !
Avec l'alimentation externe, en baissant le flux@2MBits/s ça passe pour du 1280x720@25p, mais l'image saccade quelque peu.
Le flux@1MBits/s passe bien, mais forcément l'image est crade.

Voilà, donc pour une utilisation légère du wifi, la clé TL-WN725N-v2 se suffit à elle-même, sinon il faut l'alimenter. À voir pour faire des tests de débit en b/g/n, mais l'ancien routeur qui en était capable me fait des misères, alors bon…


Enregistrer le Stream sur IP live, à haut débit, avec VLC :

Ça se passe en deux commandes :

  • Une pour générer le stream :
raspivid -t 0 -w 1280 -h 720 -fps 25  -b 15000000 -o - | cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8555}' :demux=h264
  • L'autre à lancer depuis une autre machine pour le sauver :
cvlc http://IP-du-Raspberry:8555/ --sout file/ts:fichier.mp4



Enregistrer directement la vidéo sur un partage réseau :

Donc sur le montage sshfs vu précédemment :

raspivid -t 0 -w 1280 -h 720 -fps 25 -b 15000000 -o camera/vid.mp4



Aller plus loin et streamer de l'audio ?

J'ai une carte son USB et les quelques tests pour streamer le microphone ont été très peu concluants.
Je n'ai pas réussi à sortir une commande qui puisse streamer audio et vidéo en même temps, ni même une commande pour l'audio seul qui soit vraiment intéressante.

  • Pour lister les cartes son disponibles :
cat /proc/asound/cards

Montre la carte bcm2835 en premier de la liste.
Pour indiquer au système qu'elle carte son mettre en premier, éditer /etc/modprobe.d/alsa-base.conf et remplacer

options snd-usb-audio index=-2

par

options snd-usb-audio index=0

Au prochain démarrage du Pi, cat /proc/asound/cards devrait montrer USB-Audio en premier.

  • Vérifier les périphériques de capture disponibles :
arecord -l
  • Enregistrer le micro en qualité CD :
arecord -f cd output.wav
  • On peut relire le fichier avec mplayer ou bien :
aplay output.wav


  • Pour streamer un fichier :
cvlc "fichier.mp3" --sout '#http{mux=mp3,dst=:8555}' --loop

En écoute sur un client du réseau avec VLC sur http://IP-du-Raspberry:8555

  • Pour streamer sur un client en SSH !!
arecord -D plughw:1,0 -f dat | ssh -C user@IP-du-client aplay -f dat

Le son sort directement des enceintes du client, c'est beau !

Dimanche, septembre 14 2014

Régulation PID, comment la régler 1/2

Étant donné que je n’ai pas encore tout le matériel nécessaire pour avancer mes autres projets, j’en profite pour approfondir un peu mes connaissances théoriques et vous en fait donc profiter. Mon projet ici est simple : Réguler une température à l’aide d’un microcontrolleur, mais surtout bien comprendre les tenants et aboutissements des différents réglages et algorithmes. L’objectif ici n’est pas tant d’expliquer ce qu’est un PID, ni comment il fonctionne dans le detail, mais plutôt de donner une méthode permettant d’approximer les coefficients du régulateur.

Je profiterais de cette expérience pour faire 2 articles :
– celui-ci concernant le PID a proprement parler
– Un second article concernant ma méthode pour obtenir des courbes « lives » a partir d’un arduino et d’un PC.

Qu’es-ce qu’un PID

Pour simplifier, on parlera dans la suite de régulation de température, vu que ce sera notre application ici. Mais bien entendu, une régulation peut se faire sur tout type de process (vitesse, débit, etc…)

Lorsqu’on veut réguler la température (d’une pièce par exemple), l’idée la plus simple qui nous vient à l’esprit est de faire une régulation tout-ou-rien : on est en dessous d’un premier seuil, on allume la chauffe, on passe au dessus d’un second seuil on coupe la chauffe. Cette façon de procéder, bien que très simple à implémenter n’est pas exempte de défauts. Par exemple, la température ne sera jamais vraiment constante, même si elle l’est « en moyenne ». Selon le process, on peut arriver à fortement dépasser les consignes (dans un sens ou dans l’autre), en fonction de l’inertie du système, ce qui peut s’avérer nuisible selon ce qu’on veut faire. Enfin, la puissance du chauffage oscillera entre 0 et 100%, ce qui engendre une surconsommation non négligeable.

La régulation PID permet de coller « au plus juste » à la consigne, en évitant les dépassement (en fonction du réglage comme on le verra plus loin), et en n’utilisant que la puissance nécessaire à aller à la consigne et à la maintenir. Concrètement, dans PID, on a P pour Proportionnelle, D pour Dérivée, et I pour Intégrale. Si comme moi vous avez tendance à être un peu allergique aux maths, vous commencez déjà à vous sauver, mais en fait c’est moins compliqué que ça n’en a l’air.

Proportionnelle : c’est ni plus ni moins que la différence entre votre consigne et la valeur actuelle, multiplié par un coefficient de proportionnalité (Que vous définissez, cf la suite). Dans la suite, nous l’écrirons Kc.e(t)

Intégrale : A chaque mesure, vous allez avoir une différence avec la consigne (en plus ou en moins). Prenez cette erreur à chaque pas de temps, multipliez la par un (autre) coefficient de proportionnalité, et additionnez le résultat de tous les pas de temps depuis le début, et vous avez la fameuse composante Intégrale. Dans la suite, l’intégrale sera notée de la façon suivante :

I

 

Dérivée : Son rôle est de « freiner » l’augmentation de puissance calculée en fonction de P et de I, de manière à ne pas dépasser la consigne. C’est une façon de prendre en compte la vitesse d’évolution de notre process. Pour le calculer, vous faites erreur actuelle – erreur précédente, multiplié par un troisième coefficient de proportionnalité

La formule globale de notre régulateur PID peut donc s’écrire de la manière suivante (plus d’explications plus bas):

Formule PID, source ControlGuru

Formule PID, source ControlGuru

 

Description du materiel

Le montage utilisé pour ces essais est très simple : un arduino, un bouton 2 positions, 1 capteur de température (DS18B20), un mosfet (IRF540), une résistance 10Ω 5W, et une alimentation 12V. Le mosfet est piloté par une sortie PWM de l’arduino, et gère le courant (puissance) transitant dans la résistance. Cette dernière chauffe donc plus ou moins en fonction du ratio PWM (la tension change) envoyé par l’arduino. Le bouton 2 positions sert à passer d’une première consigne (valeur PWM 5 sur 255) à une seconde (valeur PWM 30 sur 255) Un petit programme python enregistre les valeurs dans un fichier texte sur le pc (via connection série), et j’utilise GnuPlot pour avoir une jolie courbe et pouvoir lire les valeurs des graphiques plus facilement que directement sur l’image. Par la suite, j’ai ameliore mon script python, de maniere a pouvoir saisir une consigne via le port serie, tout en continuant de logguer.

Montage régulation de température

Montage régulation de température. Attention, utiliser une résistance 5W mini !

Méthode de réglage

Je vous présente une méthode « graphique » directement tirée du site Control Guru, appliquée à mon montage de test. Ce site étant en anglais et très dense en informations, j’ai essayé de résumer les points essentiels ici. J’invite ceux qui voudraient approfondir le sujet à consulter ce site qui est une véritable mine d’information sur la régulation.

Définitions

  • PV : Process Variable, cela correspond à la grandeur mesurée que l’on veut influencer (la température dans cet exemple)
  • CO : Controller Output , la sortie de notre contrôleur en %. (Correspond à l’ouverture de la vanne par exemple)
  • Kp : Gain du process
  • Kc : Gain du contrôleur

Introduction

Une fois modélisé notre boucle de contrôle, nous allons pouvoir en déterminer les trois paramètres principaux par expérimentation, mesure et calcul (et oui…)

Les paramètres à déterminer sont :

  • Kp : la Gain du process, ou, dit autrement, le sens et la grandeur de la variation de la sortie
  • Tp : Constante de temps du process, ou la vitesse de variation quand le process a commencé à répondre
  • θp : (Prononcer Thêta P) Temps mort du process, ou le délai écoulé avant que le process commence à réagir
Boucle de contrôle

Boucle de contrôle

Afin de pouvoir déterminer nos trois paramètres, il va nous falloir mesurer la façon dont réagis notre système. La première étape consiste donc à fixer une première valeur de sortie à notre contrôleur (CO) et attendre que la variable de sortie (PV, ici la température) soit bien stabilisée.

Une fois cette dernière stable, nous allons modifier la valeur de sortie du contrôleur (CO), et mesurer le changement obtenu sur la variable de sortie (PV). On logue bien sûr tout ça dans un fichier, avec les pas de temps, de manière à pouvoir également déterminer le temps mort du process (θp) et la vitesse de variation (Tp).

On se sert ensuite de ces mesures pour tracer un joli graphique, qui nous servira à déterminer nos différentes valeurs.

La formule permettant de déterminer notre premier paramètre Kc, qui est à la base du calcul de nos 3 paramètres (cf. formule) est la suivante :

source : controlguru

source : controlguru

pour déterminer Kc, nous devons donc commencer par déterminer différentes variables intermédiaires.

Calcul de Kp

Kp décrit le sens et de combien va évoluer PV, en fonction d’un changement de consigne (CO) Le cacul de Kp est assez simple : Kp=ΔPV/ΔCO ou en d’autre termes Kp = (différence en PV initial et PV final)/(différence entre consigne initiale et finale)

Sur mon prototype, j’ai modifié la consigne initiale (1,96%) pour une nouvelle consigne à 11.76%, voici la courbe obtenue:

Graphique test du système.

Graphique test du système.

En appliquant la formule ci-dessus, on a donc Kp = (79-40)/(11,76-1,96) = 39/9.8 = 3.98°C/%

Détermination de la constante de temps Tp

Tp représente le « en combien de temps » le système va réagir. En partant du même graphique, on va déterminer quand l’état de sortie vaut 63% de δPV, à partir du moment où le système à déjà commencé à réagir (et pas à partir du moment où on a modifié la consigne. Pour ceux qui se demandent d’où sort les 63%, vous trouverez toutes les explications (attention, maths !) dans ce pdf Fichier:Derive63rule.pdf On regarde donc le nombre de secondes écoulées lorsqu’on arrive à 63% de δPV (ici 39*0.63 + 40 = 64.6°C), et le nombre de secondes écoulées au moment où le système commence à réagir (très rapide ici)

Détermination de la constante de temps TP

Détermination de la constante de temps Tp

On obtiens donc Tp = 172 secondes.

Le cas de θp

θp représente le temps mort avant réaction. Dans une boucle de contrôle, il n’est jamais très bon d’avoir du temps mort, et il sera nécessaire d’avoir cette valeur la plus faible possible. Du fait de mon montage de test, le temps mort est ici très réduit. Ceci s’explique facilement par la position du capteur (sur la résistance), et par le faible volume à chauffer (uniquement la résistance. De manière assez intuitive, on imagine très bien que plus le volume à chauffer sera important, plus le temps mort sera long, pour une même puissance de chauffe.

Si on reprend le graphique ci-dessus, on vois que la consigne a été modifié à T1=195s, et j’ai décider de compter le début de la réaction à T2=198s. On a donc θp = T2 – T1 = 198 – 195 = 3s.

Il est intéressant de noter ici que mon échantillonnage étant d’une seconde, la valeur de θp peut être sensiblement erronée, du fait de sa faible durée dans ce cas.

Application à un modèle PID

Formule PID, source ControlGuru

Formule PID, source ControlGuru

Avec:

  • CO = Sortie du contrôleur (Controller Output)
  • CObias = biais du contrôleur, ou valeur nulle.
  • e(T) = Erreur actuelle, définie par SP – PV
  • SP = consigne (Set Point)
  • PV = valeur actuelle du process (Process Value)
  • Kc = Gain du controlleur, un paramètre de réglage
  • Ti = reset time, un paramètre de réglage
  • Td = derivative time, un paramètre de réglage

Calcul du gain et du reset time

Il faut tout d’abord se demander si notre process accepte un dépassement de consigne ou pas, ainsi que la vitesse à laquelle ce process doit réagir.

  • Si on accepte un dépassement, on peut choisir un réglage agressif, et donc un faible temps de réponse (petit Tc). Dans ce cas, Tc est le plus grand de 0.1Tp ou 0.8θp
  • Modéré, le contrôleur produira peu ou pas de dépassement. Dans ce cas, Tc est le plus grand de 1.Tp ou 8.θP
  • Conservateur, le contrôleur ne produira pas de dépassement, mais ira doucement, Tc sera le plus grand de 10.Tp ou 80.θP

Une fois décidé du comportement de notre contrôleur, et calculé Tc, on peut calculer le gain du contrôleur et le reset time grâce à la formule suivante :

source : controlguru

source : controlguru

Pour mon application, je vais choisir un comportement modéré. Tc sera donc la plus grande valeur de 1.Tp ou 8.θP (1×172 ou 8*3=24). On retiendra donc Tc = 172.

J’obtiens donc Kc = (1/3.98)*(172/(3+172)) = 0.25%/°C et Ti = 172

Je peux maintenant calculer mes deux principaux paramètres P et I :
P = Kc = 0.25
I=Kc/Ti. = 0.25/172 = 0.00145
Reste à déterminer D. la formule permettant de déterminer D est Kc.Td, avec Td = (Tp.θP)/(2Tp+θP) = (172*3)/(344+3) = 1.487. D vaut donc 0.25*1.487=0.372
D = 0.372

Mise en application

Reste maintenant à confronter les calculs à la réalité. Voici le code Arduino que j’ai utilisé pour effectuer mes tests :

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

unsigned long lastTime;
double Input, Output, Setpoint;
double ITerm, lastInput;
double kp, ki, kd;
int sampleTime = 1000; //1 sec
double outMin, outMax;
String cmd = String("");
double tfirst = 0;

float printTemperature(DeviceAddress deviceAddress)
{
  sensors.getAddress(insideThermometer, 0);
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(tempC);
  Serial.print(" ");
  Serial.print(Output);
  Serial.print(" ");
  Serial.println(Setpoint);
  return(tempC);
}

void compute()
{
  unsigned long now = millis();
  int timeChange = (now-lastTime);
  if(timeChange>=sampleTime)
    {
      //get the new input value
      Input = getInput();
      //compute all working error variables
      double error = Setpoint - Input;
      ITerm+=(ki * error);
      if(ITerm>outMax) ITerm = outMax;
      else if(ITerm < outMin) ITerm = outMin;
      double dInput = (Input - lastInput);
      
      //compute PID output
      Output = kp * error + ITerm - kd * dInput;
      if(Output > outMax) Output = outMax;
      else if(Output < outMin) Output = outMin;
      
      //remember some variables for next round
      lastInput = Input;
      lastTime = now;
    }
}

void setTunings(double Kp, double Ki, double Kd)
{
  double sampleTimeInSec = ((double)sampleTime)/1000;
  kp = Kp;
  ki = Ki * sampleTimeInSec;
  kd = Kd / sampleTimeInSec;
}

void setSampleTime(int NewSampleTime)
{
  if (NewSampleTime>0)
  {
    double ratio = (double)NewSampleTime / (double)sampleTime;
    
    ki *= ratio;
    kd /= ratio;
    sampleTime = (unsigned long)NewSampleTime;
  }
}

void setOutputLimits(double Min, double Max)
{
  if(Min > Max) return;
  outMin = Min;
  outMax = Max;
  
  if(Output > outMax) Output = outMax;
  else if (Output < outMin) Output = outMin;
  
  if(ITerm>outMax) ITerm = outMax;
  else if(ITerm<outMin) ITerm = outMin;
}

double getInput(void)
{
  sensors.setResolution(insideThermometer, 9);
  sensors.requestTemperatures(); // Send the command to get temperatures
  return printTemperature(insideThermometer);
}

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  Setpoint = 0; //on fixe 0 de consigne
  double P, I, D;
  P = 0.25;
  I = 0.00145;
  D = 0.372;
  int STime = 1000;
  setTunings(P,I,D);
  setSampleTime(STime);
  setOutputLimits(0, 255);
}

void loop(){
compute();
analogWrite(3,Output);
if (Serial.available() > 0) {
      char SerialInByte;
      SerialInByte = Serial.read();
      
      if(SerialInByte==13){
        Setpoint = cmd.toInt();
        cmd = String("");
      }
      else
      {
        cmd += String(SerialInByte);
      }
  }
}

et voici le résultat :

Test des paramètres PID calculés

Test des paramètres PID calculés

Conclusion :

J’ai utilisé des paramètres « conservateurs » afin d’éviter de dépasser la consigne. On peut voir qu’effectivement la consigne n’est jamais dépassée, au prix d’une montée en charge un peu lente. En partant sur cette bonne base, il est maintenant facile d’affiner si l’on souhaite quelque chose d’un peu plus réactif, il reste de la marge de manœuvre.

Sources :

La régulation étant un vaste domaine, j’ai trouvé beaucoup d’informations pertinentes sur les deux sites suivants. Si vous n’êtes pas anglophobe, et désirez approfondir le sujet, je vous les recommandes chaudement !
ControlGuru
brettbeauregard.com, auteur d’osPID

- page 1 de 39