Répondre à la discussion
Affichage des résultats 1 à 13 sur 13

Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

  1. #1
    zepeu

    Question Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Bonjour à tous !

    Je me suis mis à la robotique il y a quelques temps et je commence à avoir un joli robot qui gigote bien via un des fameux Raspberry pi 3.
    Seulement, des soucis se posent lors de l'émission et la réception des données pour le contrôler.
    Google ne m'a pa aidé :'(

    Contexte de la question :

    Le robot est constitué de 4 jambes avec :
    - 2 moteurs sur chaque jambe.
    - 2 capteurs de rotation (1 par moteur)
    - 2 capteurs de fin de course (à droite et à gauche)

    Mon but est de concevoir un programme me permettant de contrôler le robot (étonnant, je sais -_- ).

    Pour ce faire, j’ai créé deux threads, un de lecture des données (provenant des capteurs de vitesse et fin de course) et un d’envoi de données (pour commander les moteurs).

    Le but est d’avoir ces deux threads qui tournent en continu en background pendant que le programme fait ses petits calculs pour définir quand un moteur doit tourner ou non.


    Explication légèrement plus aprofondie :

    Pour envoyer et recevoir les données, j’utilise des registres à décalage 16 bits.

    Au cas où, j’explique :
    L’envoi des données se fait via un registre a décalage « Série Parallele 16 Bit » (composant 74hc595) qui comporte 3 broches :
    - « Entrée série » (SER) ou les bits a afficher en sortie seront envoyés 1 a 1
    - « horloge » (CLK) qui permet l’enregistrement du bit en cours et le passage au suivant
    - « horloge de registre » (RCLK) qui permet de charger toutes les données vers les sorties pour les afficher d’un coup

    Le fonctionnement est simple :
    0) Les bits de données à envoyer sont définis par le programme
    1) Le premier bit est chargé à l’entrée série
    2) Un front montant est donné sur l’entrée CLK
    3) Le bit suivant est chargé sur l’entrée série
    4) Un front montant est donné sur l’entrée CLK
    5) Etc.
    6) On répète l’opération pour tous les bits (16x au total, donc)
    7) Un front descendant est envoyé sur RCLK pour charger toutes les données sur les sorties du registre d’un coup
    8) Tadaaa, les moteurs tournent comme on leurs a demandé 


    La réception des données se fait via un autre registre à décalage, mais « Parallèle vers série 16 bit » (composant 74hc165) avec aussi 3 broches, 2 entrées et une sortie :
    - La broche de « chargement des données » (PL) permettant de charger l’intégralité des données parallèles dans le registre,
    - la broche « Série » (SER) ou les bits sont lisibles un par un,
    - l’entrée « horloge » (CLK) qui permet de passer au bit suivant.

    Le fonctionnement n’a rien de compliqué non plus :
    1) Un front est envoyé sur PL (actif à l’état bas) qui fait charger les données présentes en entrée des 16 bit dans le registre (tout est sauvegardé d’un coup)
    2) Le dernier bit est immédiatement disponible sur la sortie SER (la lecture se fait à l’envers, du dernier vers le 1er)
    3) On lit le bit
    4) Un front sur l’entrée CLK permet de charger le bit suivant
    5) On lit le bit suivant
    6) Front sur CLK
    7) Etc
    8) On répète les actions 16x au total
    9)
    Donc, ces deux threads vont passer leur temps à envoyer de données pour l’un et recevoir des données pour l’autre.

    Normalement, je peux atteindre une fréquence avoisinant les 70Khz, ce qui est amplement suffisant pour lire les données des capteurs de vitesse des moteurs (ça ne tourne pas trop vite), mais la lecture des ces capteurs semblait complètement fausse…

    Du coup, j’ai dégainé mon oscilloscope (pas d’inquiétude, tout sera expliqué) et je me suis branché sur les entrées CLK et PL du registre à décalage…

    Lors de la réception des données, j’ai pu remarquer que le thread de lecture n’est exécuté que la moitié du temps (grosso merdo) via ceci :

    P1230604.JPG

    On visualise le chargement des 16 bit :
    - en bas, l'entrée PL qui permet de charger les 16 bit dans le registre,
    - puis en haut les 16 fronts d'horloge pour lire les 16 bit (du dernier vers le 1er)

    P1230607.JPG

    Ici, en augmentant l’échelle de temps, on voie que l’opération est répétée plusieurs fois d’affilée, c’est joli…

    Si on regarde de façon bien plus large en augmentant l’échelle de temps, on remarque ceci :

    P1230611.JPG

    On retrouve bien nos fronts sur PL et CLK tout du long, mais avec un gap d’environ 3.5ms de temps a autres ce qui n’est pas acceptable (l’écran d’oscillo arrête pas de sauter, j’ai fait de mon mieux pour vous faire une belle image)

    J’ai donc fait mes petites investigations… Il se trouve que si j’arrête l’exécution du thread envoyant les données sur le robot, il n’y a plus ces interruptions de lecture des signaux… J’imagine facilement que l’exécution de ce thread d’écriture perturbe l’exécution du thread de lecture.

    Je n’ai aucune idée de ce qui pourrait arranger les choses, j’ai essayé de « vider » le thread en mettant tout le programme en commentaire, rien n’y fait…
    Vous trouverez ci-joint mon code (d’ailleurs, si je peux me passer de ces déclarations de variable globales à la chaine, ça m’arrangerai  )

    Les parties vous intéressant sont les classes « writing » (L. 173) et « Reading »(L.221).

    Comme vous le constaterez, ces codes sont relativement basiques et je les veux le plus court possible pour des raisons de performances.

    Quelques idées de ce qui pourrait poser problème ?

    Merci d’avance 

    -----


  2. Publicité
  3. #2
    zepeu

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Voici le programme allant avec le sujet (petit oubli )

    Modifiez l'extension ".txt" en ".py" pour que ce soit lisible

    4 Legs - Full Threading V01.1.txt

  4. #3
    polo974

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    bonjour,

    les threads, c'est du parallélisme apparent, mais en fait, c'est chacun son tour...
    surtout en python (où en fait, il y a un truc qui fait que même avec un quad core, il n'y a qu'un seul thread à la fois python qui tourne (en Cpython, mais il existe des variantes de python plus "exotiques")

    tes threads sont des bourrins de consommation de cpu (boucle continue sans sleep ou autre attente).

    si tu fais un top (commande unix) dans une console, tu verras un gros 100% cpu pour ton programme.

    ton code comporte trop de "global", ça manque de class... (avec et sans jeu de mot)
    tu ne devrais pas dupliquer les données dans DataLoadIn et DataLoadOut, en tout cas, pas partout, en plus tu fais des choses vraiment strange avec DataLoadIn...

    bref, je te conseille un seul thread dans lequel tu entrelaces le in et le out. et un sleep(.001) juste après la ligne "while true:" pour calmer le jeu (valeur d'1 ms à ajuster selon tes besoins et ton acceptation de consommation cpu)...
    Le mieux est l'ennemi du bien, et c'est bien mieux comme ça...

  5. #4
    zepeu

    Red face Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Salut Polo974,

    Merci pour ta réponse,

    J'ai justement changé ce soir les déclarations de variables globales pour des listes globales et un accès direct aux données des listes plutôt que les variable seules.
    C'est beaucoup plus clair, mais je ne peu pas donner de joli petit noms a chaque variable :-/

    J'ai aussi ajouté un "time.sleep(0.05) dans le thread d'envoi des données, pour donner du temps au système pour respirer, ca semble grandement améliorer les choses, je vais continuer de travailler dans cette direction

    En revanche, je ne peut pas ajouter de "time.sleep" dans le thread de lecture des données car même un "time.sleep(0)" dure une bonne demie milliseconde (je dis ça de tête, mon oscilloscope a complétement grillé alors que j 'écrivais ce message :'( ) et non 0 second comme demandé.
    Au même titre, les print() créent des interruptions de 5ms :-/ (j'ai donc créé un thread d'affichage pour gérer ça ;p )

    Les données que je veux capter changent quand même assez fréquemment due aux capteurs de vitesse (système d'optocoupleurs avec roue codeuse), j'ai donc besoin d'un maximum de performances au niveau de la lecture.

    Voici la dernière version quasiment fonctionnelle du programme :
    Quasiment car la lecture de vitesse des moteurs n'est pas stable pour l'instant...

    4 Legs - Full Threading V01.3.txt

    Les 2 threads sont maintenant a la ligne 124 et 156

    Work in progress

    zepeu

  6. #5
    polo974

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    juste une question (je ne connais pas le rpi), ne peux-tu pas utiliser les spi pour envoyer/lire tes datas ?
    tu aurai "juste" à gérer 2 octets au lieu de 16 bits.

    attention, les fonctions genre sleep garantissent un dodo minimum, mais jamais le temps maxi qui dépend de ce qu'il y a à faire ailleurs sur la machine.

    si tu veux faire du vrai temps réel, il faut changer de système, de langage de programmation et de méthode de programmation (gestion des interruptions). c'est un autre monde (chercher xenomai rpi par exemple sur internet (en gros, xenomai est une sous-couche temps réel à un système "normal")).

    sinon, à mi-chemin, tu peux reboucler un pwm sur une entrée et utiliser les callback sur l'input pour avoir une horloge sur laquelle tu séquences tes lectures/écritures sur tes i/o.

    enfin, pour tes thread:
    au lieu de :
    CallPSThread = threading.Thread(target=Readin g.Read)
    j'aurai mis
    CallPSThread = Reading.Thread(target=Reading. Read)
    et ainsi de suite, comme ça, tes classes, tu peux les étendre et les exploiter.
    car pour le moment, tu n'utilises pas tes classes, mais juste les fonctions que tu y as créé...
    Le mieux est l'ennemi du bien, et c'est bien mieux comme ça...

  7. #6
    zepeu

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    juste une question (je ne connais pas le rpi), ne peux-tu pas utiliser les spi pour envoyer/lire tes datas ?
    tu aurai "juste" à gérer 2 octets au lieu de 16 bits.
    il y a bel et bien un port SPI, mais cela nécessite de pouvoir définir une adresse sur chaque périphérique, ce que je n'ai pas étant donné les composants que j'utilise (74hc165 et 74hc595).
    Il existe des cartes toutes pretes, mais acheter 3 blocs, les mettre ensemble et dire "je fais de la robotique" n'est pas vraiment dans mon plan de vol ;-p
    Je pense que mettre ce genre de chose en place est actuellement hors de ma portée (n'hésite pas a me contredire la dessus ), mais j'y viendrai surement lorsque je partirai sur des projets plus complexes que ça

    si tu veux faire du vrai temps réel, il faut changer de système, de langage de programmation et de méthode de programmation (gestion des interruptions). c'est un autre monde (chercher xenomai rpi par exemple sur internet (en gros, xenomai est une sous-couche temps réel à un système "normal")).
    Donc, il n'y a pas d'interruptions en Python? :'(
    Qu'est ce que cette sous couche permettrait ? L'utilisation d'autres langages de programmation dont tu parlais ?
    Je vais me renseigner un peu plus sur le sujet dans les jours a venir. Si tu as des tutoriels/explications ou autres sources de literature intéressantes, n'hésite pas !

    sinon, à mi-chemin, tu peux reboucler un pwm sur une entrée et utiliser les callback sur l'input pour avoir une horloge sur laquelle tu séquences tes lectures/écritures sur tes i/o.
    J'y pensais, soit :
    - générer une PWM pour le signal d'horloge directement sur la carte, puis,
    - à chaque passage des données dans le registre (front descendant sur la broche PL),
    - charger les données des 16bit (coups d'horloge) suivants,
    ou alors
    - Générer les PWM avec le Raspberry PI et les utiliser de la même manière...

    Seulement, étrangement, la fréquence maximum d'une PWM sur le Rpi est de 7092Hz (141µs pour un cycle) alors qu'en control directe via python, la fréquence atteint les 70Khz (14µ sec) au max...
    Je pense que le Python suffira, la vitesse de rotation max de mes moteurs est de 405 rpm, avec des roues codeuses (récupérées sur des souris mécaniques ) qui on environ 34 segments, ça fait des impulsions à hauteur de 200 hz à capter...

    J’hésite à vrai dire, mais pour l’instant, l’électronique aveugle (Mon oscilloscope a fait un belle fumée blanche hier soir :’( ) ça sera pas possible…

    Je changerai de langage de programmation quand je serai un peu plus avancé sur le sujet et que je gérerai Python correctement. Chaque chose en son temps

    Il faudrait juste que la fréquence de lecture soit stable, ca irais mieux

    enfin, pour tes thread:
    au lieu de :
    CallPSThread = threading.Thread(target=Readin g.Read)
    j'aurai mis
    CallPSThread = Reading.Thread(target=Reading. Read)
    et ainsi de suite, comme ça, tes classes, tu peux les étendre et les exploiter.
    car pour le moment, tu n'utilises pas tes classes, mais juste les fonctions que tu y as créé...
    Heuuu… ok…
    Il y a 1 mois et demie, je ne connaissais rien de Python 
    OpenClassroom (Rip « Le site du Zero ») m’a bien aidé, mais il y a encore des subtilités qui m’échappent… Notamment le fonctionnement des classes… Je vais suivre tes conseils

    Je vais me renseigner !

    zepeu
    Dernière modification par zepeu ; 22/05/2018 à 20h19.

  8. #7
    Jack

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    il y a bel et bien un port SPI, mais cela nécessite de pouvoir définir une adresse sur chaque périphérique, ce que je n'ai pas étant donné les composants que j'utilise (74hc165 et 74hc595).
    Il n'y a pas de notion d'adresse avec une liaison SPI: soit tu cascades tous tes registres , soit tu y accède individuellement en commandant leur Chip Select.

  9. #8
    polo974

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    d'après la doc, le pwm peut avoir une horloge jusqu'à plusieurs MHz (19.2 sauf erreur et peut-être selon version du rpi).

    encore faut-il y avoir accès...

    coté spi, il semble que tu puisses envoyer des mots de 16 bits....

    encore faut-il y avoir accès...

    doc du BCM2835
    Le mieux est l'ennemi du bien, et c'est bien mieux comme ça...

  10. #9
    Jack

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Citation Envoyé par polo974 Voir le message
    coté spi, il semble que tu puisses envoyer des mots de 16 bits....

    encore faut-il y avoir accès...
    WiringPi peut faciliter les choses

  11. #10
    zepeu

    Thumbs up Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Merci pour vos réponses, je vais me renseigner sur ce dont vous parlez, ca reste relativement obscure pour l'instant

    Il n'y a pas de notion d'adresse avec une liaison SPI: soit tu cascades tous tes registres , soit tu y accède individuellement en commandant leur Chip Select.
    Il n'y a pas de "chip select" sur ces composants :/
    En revanche, il y a une broche "inhib clock" qui peut peut être être utilisée d'une manière similaire avec un petit étage logique avant pour permettre sa sélection (je découvre le SPI, laissez moi le temps d'apprendre )

    d'après la doc, le pwm peut avoir une horloge jusqu'à plusieurs MHz (19.2 sauf erreur et peut-être selon version du rpi)
    Y'a un truc qui cloche... Peut être l'oscilloscope? Mais c'est l'étage de régulation du courant qui a sauté, pas la mesure... JE vais fouiller dans cette direction car c'est important

    Si vous avez connaissance de tutoriels et/ou explications bien faits concernant le SIP, xenomai ou autres choses utiles, n'hésitez pas à me donner les liens

    zepeu

  12. #11
    Jack

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Il faudrait un schéma de câblage pour parler concrètement. On ne sait par exemple pas comment sont câblés tes registres à décalages: sont-ils en cascade ou indépendants?

  13. #12
    zepeu

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Salut Jack

    Les registres sont en cascade, 2x8bit en entrée et sortie (pour avoir les 16 bit nécessaires dans chaque cas)

    Pour le schéma, j'en suis actuellement aux tests préliminaires pour juste faire bouger la bête, j'ai câblé les registres et alimentations 5v (logic) et 9V (puissance) chacun sur des cartes différentes.

    C'est pas pratique du tout a mettre en place physiquement, du coup, je suis entrain de voir pour tout regrouper et/ou connecter de façon plus pratique et en améliorant les choses (avec des résistances de pull down sur les sorties du registre de commande, pour éviter que les moteurs se mettent en marche lors du lancement du Rpi et un câblage plus "intelligent" des alimentations etc.)

    Mais, oui, tu as raison, j'aurais dû donner les plans de câblage dès le début, mais ils sont un peu fait à la façon zepeu (ca semble être le bazar, mais je m'y retrouver très bien )
    Je met tout ca a jour et j'envoie un joli plan

    zepeu

  14. #13
    Jack

    Re : Python – Raspberry pi - Multithreading – Robotique – Thread interrompant un autre thread

    Citation Envoyé par zepeu Voir le message
    Les registres sont en cascade, 2x8bit en entrée et sortie (pour avoir les 16 bit nécessaires dans chaque cas)
    Du coup, pas besoin de gérer des chip select: tu balances les 16 bits et tu verrouilles la sortie des bascules une fois les registres chargés.

Discussions similaires

  1. Multi thread c#
    Par Tektocraft dans le forum Programmation et langages, Algorithmique
    Réponses: 2
    Dernier message: 07/12/2015, 10h19
  2. thread sur PYGTK
    Par mcherkao88 dans le forum Logiciel - Software - Open Source
    Réponses: 0
    Dernier message: 22/02/2010, 17h42