Traçage des données en temps réel de Arduino à l’aide de Python (matplotlib)

Bonjour a tous,

Je dois actuellement extraire des données d'une jauge de contrainte (d'une balance en gros) afin d'étudier le comportement d'une pièce sur un banc de test.
J'ai donc brancher mon capteur sur mon Arduino Nano et écrit un code afin d'en retirer les données sur le port série. Jusqu'a la tout va bien.
J'utilise ensuite Python pour tracer une courbes en fonction de mon poids et du temps via le moniteur série de l'Arduino.
J'ai donc trouvé un tuto qui est exactement sur ce sujet : Traçage des données en temps réel de Arduino à l’aide de Python (matplotlib) - tubefr.com

Code C++ :

#include <HX711.h>

#define calibration_factor 7050.0 //This value is obtained using the SparkFun_HX711_Calibration sketch

#define DOUT  13
#define CLK  A0

unsigned long int milli_time;  

HX711 scale(DOUT, CLK);

void setup() {
  Serial.begin(115200);
  Serial.println("HX711 scale demo");

  scale.set_scale(calibration_factor); //This value is obtained by using the SparkFun_HX711_Calibration sketch
  scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
  
}

void loop() {
  
  Serial.print(scale.get_units(), 1); //scale.get_units() returns a float
  Serial.print(" lbs"); //You can change this to kg but you'll need to refactor the calibration_factor
  Serial.println();

delay(100); 
}

Code Python :

import sys, serial, argparse
import numpy as np
from time import sleep
from collections import deque

import matplotlib.pyplot as plt 
import matplotlib.animation as animation

    
# plot class
class AnalogPlot:
  # constr
  def __init__(self, strPort, maxLen):
      # open serial port
      self.ser = serial.Serial(strPort, 115200)

      self.ax = deque([0.0]*maxLen)
      self.ay = deque([0.0]*maxLen)
      self.maxLen = maxLen

  # add to buffer
  def addToBuf(self, buf, val):
      if len(buf) < self.maxLen:
          buf.append(val)
      else:
          buf.pop()
          buf.appendleft(val)

  # add data
  def add(self, data):
      assert(len(data) == 2)
      self.addToBuf(self.ax, data[0])
      self.addToBuf(self.ay, data[1])

  # update plot
  def update(self, frameNum, a0, a1):
      try:
          line = self.ser.readline()
          data = [float(val) for val in line.split()]
          # print data
          if(len(data) == 2):
              self.add(data)
              a0.set_data(range(self.maxLen), self.ax)
              a1.set_data(range(self.maxLen), self.ay)
      except KeyboardInterrupt:
          print('exiting')
      
      return a0, 

  # clean up
  def close(self):
      # close serial
      self.ser.flush()
      self.ser.close()    

# main() function
def main():
  # create parser
  parser = argparse.ArgumentParser(description="LDR serial")
  # add expected arguments
  parser.add_argument('--port', dest='port', required=True)

  # parse args
  args = parser.parse_args()
  
  #strPort = '/dev/tty.usbserial-A7006Yqh'
  strPort = args.port

  print('reading from serial port %s...' % strPort)

  # plot parameters
  analogPlot = AnalogPlot(strPort, 100)

  print('plotting data...')

  # set up animation
  fig = plt.figure()
  ax = plt.axes(xlim=(0, 100), ylim=(0, 1023))
  a0, = ax.plot([], [])
  a1, = ax.plot([], [])
  anim = animation.FuncAnimation(fig, analogPlot.update, 
                                 fargs=(a0, a1), 
                                 interval=50)

  # show plot
  plt.show()
  
  # clean up
  analogPlot.close()

  print('exiting.')
  

# call main
if __name__ == '__main__':
    main()

Je n'arrive pas à comprendre ou mettre le COM sur le code ci-dessus (moi je suis sur le COM13).

Si quelqu'un à déjà fait des courbes en temps réel ou à une autre solution pour crée une courbe en temps réel de données récupéré par l'Arduino ça m'aiderais beaucoup ...

mercii :smiley:

Je pense que le code python doit lire dans la liaison série, donc que tu dois émettre les valeurs mesurées dans la console via Serial.print

Salut

  parser.add_argument('--port', dest='port', required=True)

  # parse args
  args = parser.parse_args()

  #strPort = '/dev/tty.usbserial-A7006Yqh'
  strPort = args.port

Apparemment le port de com est passé sur la ligne de commande avec l'option --port.
Si tu lance le script python sans arguments, tel qu'il est codé il devrait affficher :

args.py: error: argument --port is required

Passe-lui simplement l'option comme ceci :

--port COM13
ou
--port=COM13
à ta convenance

A moins que je n'aie pas compris la question ...

@+

Bonjour Thea

Si tu connais et utilises Excel, j'ai cette solution.

Cordialement
jpbbricole

Ya Processing aussi...

dans le fichier python

#strPort = '/dev/tty.usbserial-A7006Yqh'
strPort = args.port

strPort = COM13 ?

analogPlot = AnalogPlot(COM13, 100)

Comme je l'ai dit plus haut, on peut passer le port COM en argument sur la ligne de commande.

  programme.py --port COM13

Mais on peut le fixer dans le code. C'est cependant moins pratique.

  strPort = COM13 ?   # NON
  # plutôt comme ceci, entre quotes
  strPort = "COM13"
  #ou
  strPort = 'COM13'

En python les simples quotes sont équivalentes aux doubles quotes.

@+

Je n'ai toujours pas compris sur quelle ligne mettre mon COM13 ...
quand je rentre cela :

def main():
  # create parser
  parser = argparse.ArgumentParser(description="LDR serial")
  # add expected arguments
  parser.add_argument('--port=', dest='port', required=True)

  # parse args
  args = parser.parse_args()
 
  #strPort = '/dev/tty.usbserial-A7006Yqh'
  strPort = 'COM13'

  print('reading from serial port %s...' % strPort)

  # plot parameters
  analogPlot = AnalogPlot(strPort, 100)

  print('plotting data...')

j'ai l'erreur usage: testgraph.py [-h] --port= PORT
testgraph.py: error: the following arguments are required: --port=

Peut-être en t'inspirant de ceci : Display analog data from Arduino using Python (matplotlib animation) ?

Le port est passé en argument, par exemple :

python ldr.py --port COM4

le code vient du lien GetHub, mais ca ne m'aide pas a introduire mon COM ...
"python ldr.py --port COM4" me crée une erreur de syntaxe lorsque je l'introduit dans le code. Même chose quand c'est dans le shell

--port= PORT

Sans espace de part et d'autre de '=' ou comme dit lesept : --port COM4

me crée une erreur de syntaxe

Oui, laquelle ?

@+

Thea:
"python ldr.py --port COM4" me crée une erreur de syntaxe lorsque je l'introduit dans le code

C'est dans le shell, en ligne de commande, qu'il faut écrire ça.

Mais c'est accessoire, l'important c'est ces lignes de code Python :

  # create parser
  parser = argparse.ArgumentParser(description="LDR serial")
  # add expected arguments
  parser.add_argument('--port', dest='port', required=True)

  # parse args
  args = parser.parse_args()
  
  #strPort = '/dev/tty.usbserial-A7006Yqh'
  strPort = args.port

Elles utilisent la bibli Argparse qui me semble bien compliquée pour ce qu'on veut en faire. L'erreur vient peut-être du fait que tu ne l'as pas installée...

Si le port reste toujours le même, il est plus simple de le mettre en dur dans le code. On retrouve StrPort ici :

# constr
  def __init__(self, strPort, maxLen):
      # open serial port
      self.ser = serial.Serial(strPort, 9600)

La commande serial.Serial est expliquée ici. Tu peux notamment tester ceci :

with serial.Serial() as ser:
    ser.baudrate = 19200
    ser.port = 'COM1'
    ser.open()
    ser.write(b'hello')

et voir si ça envoie bien quelque chose dans le port. Pas facile de comprendre tout ça : à mon avis tu devrais faire un script pour comprendre le fonctionnement de la liaison série et de la communication avec l'Arduino, à l'exception de toute autre chose. Une fois que ça sera compris, tu ajoutes le code dans ton code complet.

me crée une erreur de syntaxe

Attendons patiemment que Thea veuille bien nous communiquer l'erreur de syntaxe 8)

Alors voilà pour répondre au sujet :
j'ai changer de code, ce code ne marchant pas j'ai chercher a faire quelque chose de plus simple et plus compréhensible.
Je met le code Python que j'ai utilisé ci dessous (il marche très bien et m'affiche les données de mon port série)

# Programme à exécuter avec Python3

#
# Ce programme fait la lecture de la ligne série.
# Les données attendues sont des lignes de la forme :
# "temps_milliseconde \t tension"
# Quand on reçoit juste un "-1", c'est la marque du STOP.
#
from serial import Serial
import numpy as np

# Connexion au port série :
# Nom du port série  (le nom est dans la barre de titre du 'Moniteur') :
#   Sous Windows : "COM3" ou "COM4"....
#   Sous Linux   : "/dev/ttyACM0" (ou voir barre de titre fenêtre Moniteur)
#   Sous Mac OS X: voir barre de titre fenêtre Moniteur
#
# Paramètres : Nbre de bits de données -> 8
#              Nbre de bit STOP        -> 1
#              Parité                  -> Sans (None)
#              Vitesses                -> 9600, 14400, 19200, 28800, 38400,
#                                         57600, or 115200
#

serPort = Serial('COM13', baudrate=19200, timeout=None)
print(serPort) # juste pour voir...

# liste de lecture des données:
lu = []

# Boucle de lecture des données sur le port série :
while True:
    data = serPort.readline()   # Lire une ligne de données
    data = data.strip()         # Enlever les caractères de fin de ligne
    if data == b'-1':           # "-1 : Bouton poussoir STOP appuyé
        serPort.close()         # Fermer le port série
        print("Fin")
        break                   # sortir de la boucle while
    if data is not "":          # on a lu quelquechose
        print(data)
        data = list(map(float,data.split())) # conversion en liste de flottants
        print(data)             # contrôle à l'affichage...
        if len(data) > 1:       # plusieurs données sont lues (dont le temps),
            lu.append(data)     # tout va bien, on ajoute les données à la liste 'lu'.

#
# à partir d'ici, on est sorti de la boucle de lecture des données            
#
lu = np.array(lu)               # convertir la liste en objet ndarray
X  = lu[:,0]                    # colonne 0 : vecteur temps 
Y  = lu[:,1]                    # colonne 1 : tension 

#
# Écriture des données dans un fichier
#
from time import strftime
uniqFileName = "Data_"+strftime("%Y-%m-%d_%H-%M-%S")+".txt"
fOut = open(uniqFileName, "w");
LF = "\r\n"                     # LineFeed : saut de ligne ASCII
fOut.write("#Temps [ms]\tTension [V]"+LF)
for x, y in zip(X,Y):
    fOut.write("{:f}\t{:f}{:s}".format(x, y, LF))
fOut.close()

#
# tracé de la courbe
#

import matplotlib.pyplot as plt

plt.figure()
plt.grid(True)
plt.xlabel("Temps [s]")
plt.ylabel("Tension [V]")
plt.ylim(-5,30)
plt.plot(X/1000, Y, "o-b")

plt.show()

au niveau du code Arduino j'ai cela :

#include <HX711.h>

//
// Programme pour faire la lecture de la rension sur le curseur du potentiomètre :
// Écriture sur le port série du temps d'acquisition et de la tension
// correspondante en Volts.
//

#define calibration_factor 7050.0 //This value is obtained using the SparkFun_HX711_Calibration sketch

#define DOUT  13
#define CLK  A0

HX711 scale(DOUT, CLK);

//********************
// variables globales:
//********************
int etat ;             //  l'état de l'automate
int oldEtatBP;         // mémorisation état BP (Bouton Poussoire)
unsigned long int t0;  // mesure des temps écoulés

//********************
// constantes
//********************

// Etats
const int WAIT  = 0;
const int START = 1;
const int STOP  = 2;

// numéros des entrées/Sorties
const int pinBP = A3;           // Bouton poussoir

void setup() {
  
  // Initialisation de l'objet 'Serial' pour écrire les données sur la liaison USB
  Serial.begin(19200);    // 19200 : vitesse de tranfert des octets
                          // ARDUINO -> PC par liaison USB
  //
  // Configuration des E/S numériques
  //
  pinMode(pinBP, INPUT);
  
  // Initialisation des variables globales
  //  
  oldEtatBP = LOW;
  etat = WAIT;
  scale.set_scale(calibration_factor); //This value is obtained by using the SparkFun_HX711_Calibration sketch
  scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
}


void loop() 
{ 
  int etatBP = digitalRead(pinBP);  // lecture du bouton ON/OFF
    
  if (oldEtatBP == LOW && etatBP == HIGH)  // Evénement "BPA" (Bouton Poussoire Appuyé)
  { 
    if (etat == WAIT || etat == STOP)
    {
      // Transition WAIT -> START ou STOP -> START:
      t0 = millis();   // nombre de millisecondes écoulées depuis le lancement du programme
      etat = START;

    }
    else  // Transition START -> STOP
    {
      Serial.println(-1);  // Envoyer la LIGNE conetant juste"-1" sur le port série pour dire au PC : FIN !
      etat = STOP;
    }
  }
  oldEtatBP = etatBP;  // Mémoriser l'état du bouton poussoir pour le procain tour de boucle
  
  if (etat == START)
  {
    unsigned long t1 = millis();    // temps écoulé depuis le début du programme
    //
    // Écriture du temps et de la tension (séparés par une tabulation) sur la liaison série
    //
    Serial.print(t1-t0);
    Serial.print("\t");
    Serial.println(scale.get_units(), 1); 
    Serial.flush();
    delay(50);
  } 
}

j'ai fait un montage simple avec un Bouton Poussoir : je clique sur le bouton, je print mes données puis je rappuie fin du print.

Seule chose que je n'arrive pas encore a faire c'est afficher en temps réel sur Python ... Work in progress