Comment traiter données issues d'un capteur

Python est le langage de prédilection du Raspberry Pi

Modérateurs : Francois, Manfraid

redscreen
Messages : 26
Enregistré le : ven. 28 févr. 2020 17:05

Comment traiter données issues d'un capteur

Message par redscreen » ven. 28 févr. 2020 17:46

Bonjour à tous,

Pour mener à bien mon projet étudiant de fin d'année (simulateur de vol),

je dois pouvoir traiter des données issues d'un module IMU 5DOF (accéléromètre+gyro). (Inertial Measurement Unit).

Le matériel utilisé:
- RaspberryPi 3B+
- Module 5DOF analogique: https://www.sparkfun.com/products/retired/9268
- Arduino UNO

Pour le moment, je parviens à afficher sur le moniteur série de l'Arduino les valeurs qui m’intéressent: Les inclinaisons instantanées sur l'axe X et l'axe Y = Tangage et roulis (Pitch & Roll in english)

J'ai relié l'Arduino au Raspberry en USB, et grâce à ce script Python:

# coding: utf-8
import time
import serial
ser = serial.Serial('/dev/ttyUSB0', 115200)
while 1 :
print(ser.readline().decode())
time.sleep(0.1)

Je parviens également à lire ces valeurs via la Raspberry.

C'est maintenant que je me pose des questions et que j'aurais besoin d'avis et conseils:
Comment exploiter ces données au mieux ?

L'idée, serait une variable en RAM, en temps réel, écrite par l'accéléromètre, et lisible par un autre programme qui s'en servira pour contrôler des moteurs.
Je n'ai pas "besoin" spécialement d'écrire un fichier ou autre base de données, mais je m'interroge justement sur la manière d'être le plus efficace.

-Idée 1: enregistrer dans un fichier texte:
- je n'ai pas de connaissances en python, et pour l'instant je ne sais pas faire, je vais chercher, mais est ce que je part dans la bonne direction ?
- ce fichier devra être ouvert en écriture ET être exploitable en lecture par un autre programme qui devra récupérer ces valeurs en temps réel

-Idée 2: passer par une base de données ?
- pour l'instant je ne sais pas faire non plus, mais même question, est ce la bonne direction ?
- elle va être peuplée très, trop?, rapidement, sera elle assez rapide ?

- Une autre idée ? ...

Merci d'avance pour l'aide que vous pourriez m'apporter.
Modifié en dernier par redscreen le mar. 3 mars 2020 20:07, modifié 1 fois.

Bud Spencer
Raspinaute
Messages : 877
Enregistré le : lun. 15 août 2016 21:38

Re: Comment traiter données issues d'un capteur

Message par Bud Spencer » dim. 1 mars 2020 10:42

Je te répondrais déjà par 3 questions.

Que vient faire l'Arduino la dedans ?

Pourquoi Python ?

Quel besoin as tu en terme de persistance des données ?
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

redscreen
Messages : 26
Enregistré le : ven. 28 févr. 2020 17:05

Re: Comment traiter données issues d'un capteur

Message par redscreen » dim. 1 mars 2020 12:26

- Arduino car la carte UNO possède des entrées analogique que la Raspberry n'a pas sur ses GPIO. ( le module 5DOF est analogique ).
De plus, j'ai obtenu de l'aide pour avoir une centrale inertielle très précises en combinant Gyro, Accéléromètre, et filtre de Kalman (Axe X et Y), mais le code est destiné à Arduino.

J'ai toujours une possibilité d'utiliser un convertisseur CAN "ADS1115", en I2C, directement sur les GPIO de la Raspberry, mais à part lire les valeurs "GyroRate", je n'en tire rien de plus, et encore, ce n'est pas stable.


- Python car dès que je cherche de la doc, tutos, etc, je tombe sur Python, rien que le script pour lire le port série "Arduino <-> Raspberry"
Si je peux utiliser un autre langage, (en C par exemple, toujours mieux vu vis à vis de l'école et du rapport de projet) peu importe, mais l'important reste le résultat.


- Quant à la persistance des données, aucun besoin justement, il faut juste pouvoir les exploiter sur le moment, du vrai temps réel


En résumé: - le "simulateur logiciel" que j'appellerai "jeu", nous envoi des données en UDP sur la position dans le jeu (axe X et Y) (là aussi on doit traiter ces données de la même façon)
- le module 5DOF nous donne la position de notre cockpit à chaque instant
- on compare "position voulue" et "position réelle", et on actionne les 2 vérins en fonction...

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: Comment traiter données issues d'un capteur

Message par BlackTom » lun. 2 mars 2020 14:33

Je n'y connais rien en python mais vu tes besoin ce qui m'est venu naturellement à l'esprit est la "Shared Memory", système de mémoire partagée entre processus.

Bud Spencer
Raspinaute
Messages : 877
Enregistré le : lun. 15 août 2016 21:38

Re: Comment traiter données issues d'un capteur

Message par Bud Spencer » lun. 2 mars 2020 19:49

Ou n'importe quel autre modèle IPC, mais y a t'il vraiment besoin de faire plusieurs programmes qui s'échangent des données ?
recevoir des données sur un socket udp et sur un port serie ca peut tenir dans un seul programme et supporter en plus l'asservissement des vérins (et c'est beaucoup plus simple que plusieurs programmes)
Quoi qu'il en soit, pas besoin de fichier d'échange et encore moins de base de données.

Perso, si j'avais ce truc a faire, j'utiliserai un mcp3204 (12 bits) qui est4 fois plus precis et au moins 100 fois plus rapide en terme de temps d'acquisition (100ksps) que les adc un Arduino uno, ce qui me permettrais de m'affranchir d'échange rs au profit d'éclanches spi beaucoup plus rapide aussi et je retranscrirais le code c qui va bien sur l'Arduino en c pour le pi pour en faire une libraire. après, un seul programme pour traiter tout ca (mais surement pas en python qui est vite très mal a l''aise pour ce genre de truc).
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

redscreen
Messages : 26
Enregistré le : ven. 28 févr. 2020 17:05

Re: Comment traiter données issues d'un capteur

Message par redscreen » mar. 3 mars 2020 20:05

Merci pour vos réponse,

BlackTom ->
"Shared Memory" est ce à quoi je pensais en premier pour rapidité, efficacité, et pas d'écriture sur la carte SD, je ne sais juste pas mettre en œuvre, mes compétences sont limitées, mais je suis en recherche constante.

Bud Spencer ->
IPC = communication inter processus c'est bien ça ?
On est 4 sur le projet, un collègue est déjà partie sur la base de donnée juste pour la partie "info du jeu" en UDP, ça fonctionne, mais nous n'avons encore rien asservi pour autant.
Un seul programme si c'est possible c'est tant mieux, on bloque simplement sur la manière d'y parvenir.
Merci pour la référence du "mcp3204", je vais regarder ce que c'est exactement.
Pour le coup on m'a fourni un CAN "ADS1115" que je pensais initialement utiliser entre Raspberry et Module 5DOF (en I2C)
Dont justement, retranscrire le code C qui va bien sur l'Arduino UNO en C pour le pi pour en faire une libraire est ce que je voulais faire, mais j'ai bloqué sur la façon d'y parvenir.

Voici le code que j'ai légèrement adapté:

/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com
e-mail : kristianl
*/

#include "Kalman.h" // Source: https://github.com/TKJElectronics/KalmanFilter

Kalman kalmanX;
Kalman kalmanY;

#define gX A0
#define gY A1

#define aX A3
#define aY A4
#define aZ A5

double zeroValue[5] = { 0 }; // gyroX, gyroY, accX, accY, accZ

double gyroXangle = 0;
double gyroYangle = 0;

// Complimentary filter
double compAngleX = 0;
double compAngleY = 0;

// Used for timing
unsigned long timer;

void setup() {
analogReference(EXTERNAL); // 3.3V
Serial.begin(115200);
delay(100);//wait for the sensor to get ready

// Calibrate all sensors in horizontal position
for (uint8_t i = 0; i < 100; i++) { // Take the average of 100 readings
zeroValue[0] += analogRead(gX);
zeroValue[1] += analogRead(gY);
zeroValue[2] += analogRead(aX);
zeroValue[3] += analogRead(aY);
zeroValue[4] += analogRead(aZ);
delay(10);
}
zeroValue[0] /= 100;
zeroValue[1] /= 100;
zeroValue[2] /= 100;
zeroValue[3] /= 100;
zeroValue[4] /= 100;
zeroValue[4] -= 102.3; // Z value is -1g when facing upwards - Sensitivity = 0.33/3.3*1023=102.3

kalmanX.setAngle(0); // Set starting angle
kalmanY.setAngle(0);
timer = micros(); // start timing
}

void loop() {
double gyroXrate = -((analogRead(gX) - zeroValue[0]) / 1.0323); // (gyroXadc-gryoZeroX)/Sensitivity - in quids - Sensitivity = 0.00333/3.3*1023=1.0323
gyroXangle += gyroXrate * ((double)(micros() - timer) / 1000000); // Without any filter

double gyroYrate = -((analogRead(gY) - zeroValue[1]) / 1.0323);
gyroYangle += gyroYrate * ((double)(micros() - timer) / 1000000);

double accXval = (double)analogRead(aX) - zeroValue[2];
double accYval = (double)analogRead(aY) - zeroValue[3];
double accZval = (double)analogRead(aZ) - zeroValue[4];

// Convert to 360 degrees resolution
// atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
// We are then convert it to 0 to 2π
double accXangle = (atan2(accXval, accZval));
double accYangle = (atan2(accYval, accZval));

/* You might have to tune the filters to get the best values */
compAngleX = (0.98 * (compAngleX + (gyroXrate * (double)(micros() - timer) / 1000000))) + (0.02 * (accXangle));
compAngleY = (0.98 * (compAngleY + (gyroYrate * (double)(micros() - timer) / 1000000))) + (0.02 * (accYangle));
double xAngle = kalmanX.getAngle(accXangle, gyroXrate, (double)(micros() - timer));
double yAngle = kalmanY.getAngle(accYangle, gyroYrate, (double)(micros() - timer));

timer = micros(); // reset timing

Serial.print(xAngle,15); Serial.print("\t");
Serial.print(yAngle,15); Serial.print("\tEquivalent degrès:\t");
Serial.print(xAngle*RAD_TO_DEG); Serial.print("\t");
Serial.print(yAngle*RAD_TO_DEG); Serial.print("\t");
Serial.print("\n");

delay(10);
}
Modifié en dernier par redscreen le lun. 22 juin 2020 12:39, modifié 1 fois.

Bud Spencer
Raspinaute
Messages : 877
Enregistré le : lun. 15 août 2016 21:38

Re: Comment traiter données issues d'un capteur

Message par Bud Spencer » mer. 4 mars 2020 11:19

C’est ça (IPC) InterProcess Communication (Communication Entre Processus).
Il y plusieurs façons de d’echanger des données entre différents processus (si si c'est possible :roll: ) . Le shared memory en est une, mais il y a d’autres méthodes suivant le système et si l’on a besoin d’échange unidirectionnel ou bidirectionnel rapide ou pas. Suivant les cas, ce n'est pas forcement facile a mettre en œuvre, mais certaines méthodes reste malgré tout plutôt accessible.

Si je comprends bien, vous avez 2 points d’entrées de données (des donnés arrivent sur un socket UDP et d’autres arrivent sur un port série) et une synthèse de l’association de ces données doit définir des états de sortie. Votre approche est de faire un programme qui reçoit des données du port série, un autre qui reçoit celles du socket UDP , ces données sont écrite dans une base de données et un troisième programme qui vient lire ces données pour définir des sorties.

Je ne connais pas la vitesse des flux sur le socket UDP et sur le port série, mais s’il est important, vous allez avoir de gros problèmes. Déjà vous allez effondrer les performances en mettant à jour systématiquement vos enregistrements et en plus vous allez bousiller la SD en rien de temps. Avec des échanges IPC, cela résoudrait en partie le problème, mais cela sous-entend qu’en plus d’échanger des données, vos process devraient aussi s’échanger des signaux pour être synchronisés et là, ça risque de vite se compliquer pour vous (surtout avec du shared memory et en plus en python).

Pour simplifier tout ça et rendre le truc beaucoup plus rapide (et donc plus près du temps réel dont vous avez besoin), il suffit de faire un seul programme qui reçoit toutes les données (udp, serie (ou adc) ….) et qui fasse l’asservissement des vérins. Ça vous permettrait de stocker directement vos donnés entrantes dans des variables et de les avoir disponible pour la procédure de calcul d’asservissement.

Globalement, comment faire un programme qui fasse tout ça à lui tout seul :

De façon séquentielle synchrone :
Open UDP-> Wait Data-> Data=>Var->Close UDP
Open SERIAL-> Wait Data-> Data=>Var->Close Serial
Data => Procédure d’asservissement synchrone
Retour Open UDP

Facon Multithread :
Open UDP, Open Seral=>Data
Lock Data => Procédure d’assertivement asynchrone
Unlock Data
Asservissement
Retour Lock Data

La première est simple à mettre en œuvre même avec python mais est forcement lente
La seconde est beaucoup plus rapide, mais demande de faire du multithreading. C’est là que ça commence à se gâter avec Python et que tout se complique parce que même s’il existe de quoi faire, ce langage n’est absolument pas fait pour ça.

Pour vous, l’idéal, ce serait de pouvoir faire de l’asynchrone sans avoir besoin de gérer de multithreading ou de multiprocessing. Il y a au moins 2 Concept qui font ça très bien et qui sont très performant sur un PI (aucune comparaison avec Python).

Le premier, c’est NodeJS (Javacript). En tant que débutant, c’est le meilleur choix que vous pouvez faire. Recevoir en même temps des donnés en provenance d’un socket UDP et d’un port série, c’est tout au plus 10 à 20 lignes de code simple, déclarations variables inclus. Pour quelques lignes de plus vous pouvez avoir une gestion évènementielle de l’asservissement et en ajoutant quelques lignes encore, vous pouvez même suivre tout ça en temps réel et même graphiquement depuis vos navigateur web. NodeJS prend nativement en charge tous les protocole réseaux et il existe des npm (librairie) pour la gestion des ports série et des GPIO du PI.
Tuto NodeJS : viewtopic.php?f=44&t=3033
Npm pour port série : https://www.npmjs.com/package/serialport
Npm pour GPIO : https://www.npmjs.com/package/rpio

Le second, c’est .Net Core (C# de préférence mais aussi VB.Net ou F# ). Beaucoup plus robuste et plus pro que NodeJS, mais un peu plus difficile à aborder pour des débutants. Il y a déjà tout ce qu’il faut dedans aussi bien pour les protocoles réseaux, les port série et les gpio :
Tuto .Net Core (liens officiels en début de tuto) : viewtopic.php?f=42&t=5449

Après, tout dépend du traitement qu’il y a faire sur les données entrantes avant de les renvoyer à la procédure d’asservissement. Si c’est conséquent, .Net Core s’en sortirait sans doute beaucoup mieux que NodeJS , mais déjà avec NodeJs vous arriverez a un niveau de performance sans commune mesure avec ce que vous auriez fait en python et avec considérablement moins de code.
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

redscreen
Messages : 26
Enregistré le : ven. 28 févr. 2020 17:05

Re: Comment traiter données issues d'un capteur

Message par redscreen » mer. 4 mars 2020 22:00

Un énorme merci pour le temps que tu prends à me répondre et expliquer en détail !
Je vais lire tes tutos NodeJS & Npm pour voir si j'arrive à exploiter ce conseil.
.Net Core en C# à voir également, mais si tu dis que c'est "moins facile"...
ne connaissant aucun des 2 langages, (NodeJS / C#), il me faut aller au plus simple, sans aller droit dans le mur, le projet devant être clôturé fin avril.
J'ai juste quelques bases en C/C++ et Javascript...
Modifié en dernier par redscreen le dim. 8 mars 2020 20:51, modifié 1 fois.

Bud Spencer
Raspinaute
Messages : 877
Enregistré le : lun. 15 août 2016 21:38

Re: Comment traiter données issues d'un capteur

Message par Bud Spencer » jeu. 5 mars 2020 10:49

Je pense que NodeJs est de loin le meilleur choix pour vous et ce genre de projet tout comme je pense que python est parmi les pires pour ça. Si tu lis le tuto NodeJs, tu vas voir que je l’ai surtout orienté ‘web dynamique’ parce que c’était l’idée de départ, mais ce n’est qu’une facilité offerte par NodeJs et rien n‘oblige de faire du ‘web’ avec. J’ajoute même que NodeJs est typiquement fait pour répondre très facilement aux contraintes d’un projet comme le vôtre. Pour ce qui est de .Net Core avec C#, ce n’est pas trop le langage qui plus difficile à aborder (il est très ‘standard’), mais plutôt que ça demande un minimum de compréhension sur les concepts de la programmation objet et la gestion des références y est un peu plus complexe. Cela dit, que ce soit python, javascript et même c++, java et la plupart des langages les plus utilisés, ils ne dévoilent toutes leurs flexibilités que quand on les aborde avec une approche poo, mais ça c’est un autre débat.

Je ne me souviens pas avoir parler d’UDP ou de port série dans ce tuto donc j’ai ajouté un petit exemple simpliste de serveur udp pour recevoir des données, mais en cherchant un peut tu vas trouver des milliers d‘exemples sur le net. Je regarderais ce wk si j’ai de quoi câbler un port série et j’ajouterais un petit exemple serial port
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

redscreen
Messages : 26
Enregistré le : ven. 28 févr. 2020 17:05

Re: Comment traiter données issues d'un capteur

Message par redscreen » dim. 8 mars 2020 20:47

Merci Bud, une chose est sure, tu maîtrises ton sujet, j'aimerais bien t'avoir dans mon équipe pour notre projet :D :lol:
J'ai commencé à lire ton tuto NodeJs, j'ai fait des essais sur un pc sous debian buster,
les versions dans les dépots sont anciennes: node v10... et npm v5... -> problème de compatibilité quand on installe tel quel,
avec ces 3 lignes j'ai réussi à avoir les dernières versions:
apt-get install curl software-properties-common
curl -sL https://deb.nodesource.com/setup_13.x | sudo bash -
apt-get install nodejs

J'ai regardé:
- l'exemple serveur web (lecon1)
- l'exemple lecture d'une valeur sur un graphique avec jquery (lecon4) -> çà c'est top pour mon collègue qui doit gérer les instruments de bord (j'avais déjà trouvé une base jquery de même type que ton exemple); il travaille actuellement en php/ajax, mais je vais lui parler de NodeJs également du coup !
- et je suis donc passé page 20 à ton exemple UDP, un peu de mal à digérer la partie poo mais ça va venir :D
j'ai juste testé que je récupèrai bien les valeurs avec uniquement le 1er "server.js" (client.js étant déjà créé dans le jeu) -> et quand je retire le "prefix", cela fonctionne ! :

// Serveur UDP
const dgram = require("dgram");
const udpserver = dgram.createSocket("udp4");

udpserver.on('error', (err) => {
console.log(err.stack);
udpserver.close();
});

udpserver.on("message", (data) => {
let rx = data.toString('utf8');
console.log("server receive " + rx.substring());
});

udpserver.on("listening", () => {
console.log("UDP server listening on port " + udpserver.address().port);
});

udpserver.bind(55278, '0.0.0.0');


Je vais donc étudier la suite ! (ah oui, au passage, il n'y a pas la version .zip pour fainéant ? :? ;) )

Sinon en parallèle on a avancé tout de même, on a combiné ma partie "Module 5DOF" + la partie "PWM" du collègue pour actionner les vérins sur la même Arduino UNO:
pour l'instant le vérin avance ou recule suivant l'inclinaison du module IMU, il nous reste à lire la Base de données qui contient les valeurs UDP (qui s'enregistrent bien toutes les 100ms), puis coder une partie "comparaison", et enfin inclure un "PID"..., avec une interface Web pour régler le gain.

Même si je sais maintenant que ce n'est pas la meilleure solution (bdd), dur de dire à tout le monde: STOP arrêtez ce que vous faite c'est nul :lol: , NodeJs c'est mieux :P
J'en suis convaincu, donc je vais étudier ça en parallèle et voir si j'avance plus efficacement !

Répondre

Retourner vers « Python »