Je travaillais sur un projet récemment et c'était le premier qui était suffisamment impliqué pour compliquer la mise en réseau des capteurs. Au final, je pense que la communication a été le goulot d'étranglement en termes de performances globales et je me demande comment des personnes plus expérimentées auraient résolu ce problème. Ceci est une longue lecture, mais je pense que c'est assez intéressant, alors respectez-la. Le problème était de concevoir un dirigeable autonome capable de naviguer sur un parcours du combattant et de larguer des balles de ping-pong dans des cibles en boîte brune. Voici:
Capteurs
- Module de caméra uCAM-TTL 4D Systems - Interface UART
- HMC6352 Digital Compass - Interface I2C
- Maxbotix Sonar ez4 - Interface analogique 1 broche
Actionneurs
- 2 pilotes de moteur L293D (connectés à de simples moteurs de loisir) - Ils ont été utilisés pour entraîner 6 moteurs dans les deux sens. Ils avaient besoin d'entrées PWM pour faire varier la vitesse. Maintenant, 3 de nos moteurs faisaient toujours la même chose (ceux qui contrôlaient le mouvement haut / bas), ils n'avaient donc besoin que de 2 sorties PWM de nos contrôleurs pour contrôler les 3 moteurs. Les 3 autres moteurs qui contrôlaient le mouvement latéral avaient tous besoin d'un contrôle individuel (pour le mouvement omnidirectionnel), de sorte que 6 autres sorties PWM étaient requises de nos contrôleurs.
- Servomoteur - Interface PWM
Contrôleurs
Pour des raisons qui deviendront claires plus tard, nous avons fini par utiliser 2x ATmega328Ps. Nous avons utilisé un Arduino Uno pour les programmer (nous n'avions pas accès à un FAI), mais nous avons fabriqué un PCB personnalisé, nous n'avons donc pas eu à utiliser de cartes Arduino car cela ne ferait qu'ajouter du poids inutile à notre dirigeable. Quant à la raison pour laquelle nous avons choisi l'ATmega328P, je connaissais très bien l'environnement Arduino et je pense que cela a rendu le développement du code beaucoup plus rapide et plus facile.
Communication et traitement
- 2x Xbee Basic
- 2x ATmega328P
- Ordinateur de bureau exécutant C ++ avec openCV
Comme vous pouvez le constater à partir du module caméra, la plupart de notre projet reposait sur la vision par ordinateur. Les dirigeables ne pouvaient supporter autant de poids et nous ne nous sentions pas à l'aise d'implémenter la vision par ordinateur sur un microcontrôleur. Nous avons donc fini par utiliser XBee pour relayer les données d'image vers un ordinateur de bureau. Donc, côté serveur, nous avons reçu des données d'image et utilisé openCV pour traiter l'image et en extraire des éléments. Maintenant, le côté serveur devait également connaître les informations de hauteur (du sondeur) et les informations de la boussole.
La première ride était que nous ne pouvions pas faire contrôler la caméra par un microcontrôleur pour deux raisons. Le problème principal était que la mémoire interne de l'UP ne pouvait pas gérer le stockage d'une trame entière. Il pourrait y avoir des moyens de contourner cela grâce à un codage intelligent, mais pour les besoins de cette question, supposons que cela était impossible. Donc, pour résoudre ce problème, nous avons demandé au serveur d'envoyer des commandes de caméra via l'émetteur-récepteur XBee et le récepteur XBee (à bord du dirigeable) avait sa sortie câblée à l'entrée de la caméra.
Le problème suivant était qu'il n'y a pas assez de PWM sur un seul ATmega328P pour contrôler tous les moteurs PARCE QUE l'interface I2C utilise l'une des broches PWM (bon sang ...). C'est pourquoi nous avons décidé d'en utiliser un 2ème. Le code se prêtait en fait parfaitement au traitement parallèle de toute façon parce que le contrôle de la hauteur était complètement indépendant du contrôle du mouvement latéral (donc 2 micros étaient probablement meilleurs que celui attaché à un contrôleur PWM). Par conséquent, U1 était responsable de 2 sorties PWM (haut / bas) et de la lecture du sonar. U2 était responsable de la lecture de la boussole, du contrôle de 6 sorties PWM (les moteurs latéraux) et de la lecture du sonar. U2 était également responsable de la réception des commandes du serveur via le XBee.
Cela a conduit à notre premier problème de communication. La ligne XBee DOUT était connectée à la fois au microcontrôleur et à la caméra. Maintenant, bien sûr, nous avons conçu un protocole pour que nos micro-commandes ignorent les commandes de la caméra et les commandes de la caméra ignorent les micro-commandes, ce qui était bien. Cependant, la caméra, en ignorant nos micro-commandes, renverrait des données NAK sur sa ligne de sortie. Étant donné que la commande était destinée au micro, nous avions besoin d'une manière ou d'une autre de désactiver la sortie de la caméra vers le XBee. Pour résoudre ce problème, nous avons fait le micro contrôle 2 FET qui étaient entre la caméra et XBee (c'est le premier FET) et également entre U2 et le XBee (c'est le deuxième FET). Par conséquent, lorsque la caméra essayait de renvoyer des informations au serveur, le premier FET était «activé» et le second FET était «désactivé».
Donc, pour vous donner une idée de la façon dont cela a fonctionné, voici quelques exemples:
- Le serveur demande une image - PIC_REQUEST passe par XBee et arrive à U2 et à la caméra. U2 l'ignore et la caméra renvoie des données d'image.
- Le serveur vient de terminer le traitement d'une image et envoie des données de moteur pour dire au dirigeable de tourner à droite - MOTOR_ANGLE (70) passe par XBee et arrive à U2 et à la caméra. U2 reconnaît comme une micro-commande et désactive donc le FET de la caméra (mais peut-être que la caméra a déjà répondu avec un NAK ?? qui sait ...). U2 répond ensuite à la commande en modifiant les sorties PWM du moteur. Il rétablit ensuite le FET de la caméra (c'était le paramètre par défaut car les données d'image étaient les plus importantes).
- Le serveur se rend compte que nous sommes arrivés à un point dans la course à obstacles où notre hauteur de vol stationnaire par défaut doit maintenant être de 90 pouces au lieu de 50 pouces. SET_HEIGHT passe par XBee et la même chose se produit que dans l'exemple 2. U2 reconnaît la commande SET_HEIGHT et déclenche une interruption sur U1. U1 sort maintenant de sa boucle de contrôle de hauteur et attend de recevoir des données série de U2. C'est vrai, plus de données série. À ce stade, le FET de l'U2 est activé (et le FET de la caméra est désactivé), de sorte que le serveur reçoit la hauteur que U2 envoie également à U1. C'était à des fins de vérification. Maintenant, U1 réinitialise sa variable interne pour height2HoverAt. U2 désactive maintenant son FET et rallume le FET de la caméra.
J'ai définitivement omis une bonne quantité d'informations, mais je pense que cela suffit pour comprendre certaines des complications. En fin de compte, nos problèmes ne faisaient que tout synchroniser. Parfois, il restait des données dans les tampons, mais seulement 3 octets (toutes nos commandes étaient des séquences de 6 octets). Parfois, nous perdions la connexion avec notre caméra et devions la resynchroniser.
Donc ma question est: Quelles techniques suggéreriez-vous pour avoir rendu la communication entre tous ces composants plus fiable / robuste / plus simple / meilleure?
Par exemple, je sais que l'on aurait dû ajouter un circuit de retard entre la sortie XBee intégrée et la caméra afin que le micro ait la possibilité de désactiver la ligne de discussion de la caméra avant de répondre aux micro-commandes avec NAK. D'autres idées comme ça?
Merci et je suis sûr que cela nécessitera de nombreuses modifications, alors restez à l'écoute.
Edit1:L'épissage des données UART de la caméra à travers l'un des micros ne nous a pas paru possible. Il y avait deux options pour les données de caméra, le bitmap brut ou JPEG. Pour un bitmap brut, la caméra vous envoie simplement des données aussi vite que possible. L'ATmega328P n'a que 128 octets pour un tampon série (techniquement, cela est configurable mais je ne sais pas comment) et nous ne pensions pas que nous serions en mesure de le sortir du tampon et de le faire passer assez rapidement vers le XBee. Cela a laissé la méthode JPEG où il envoie chaque paquet et attend que le contrôleur l'acquitte (petit protocole de prise de contact). La vitesse la plus rapide a été de 115200 bauds. Maintenant, pour une raison quelconque, le plus rapide que nous pouvions transmettre de manière fiable de grandes quantités de données sur le XBee était 57600 bauds (c'est même après que nous ayons fait le couplage nœud / réseau pour permettre la capacité de renvoi automatique). L'ajout de l'arrêt supplémentaire dans notre réseau (caméra vers micro vers XBee par opposition à seulement caméra vers XBee) pour le micro a simplement ralenti le temps nécessaire pour transférer trop une image. Nous avions besoin d'un certain taux de rafraîchissement sur les images pour que notre algorithme de contrôle moteur fonctionne.
la source
Réponses:
Je comprends que vous vouliez choisir un environnement de développement que vous connaissiez de manière à pouvoir démarrer rapidement, mais je pense que le compromis matériel / logiciel vous a peut-être empêché de rester avec Arduino et de ne pas choisir une partie qui avait tout les périphériques matériels dont vous aviez besoin et écrivez tout en C piloté par interruption.
Je suis d'accord avec la suggestion de @Matt Jenkins et j'aimerais la développer.
J'aurais choisi un uC avec 2 UART. Un connecté au Xbee et un connecté à la caméra. L'uC accepte une commande du serveur pour lancer une lecture de la caméra et une routine peut être écrite pour transférer les données du canal UART de la caméra vers le canal UART XBee octet par octet - donc pas de tampon (ou tout au plus seulement un très petit un) nécessaire. J'aurais essayé d'éliminer les autres uC tous ensemble en choisissant une partie qui répondait également à tous vos besoins PWM (8 canaux PWM?) Et si vous vouliez vous en tenir à 2 uC différentes en prenant soin de leur axe respectif, alors peut-être un une interface de communication différente aurait été meilleure car tous vos autres UART auraient été pris.
Quelqu'un d'autre a également suggéré de passer à une plate-forme Linux embarquée pour tout exécuter (y compris openCV) et je pense que cela aurait également été quelque chose à explorer. J'y suis déjà allé cependant, un projet d'école de 4 mois et vous avez juste besoin de le faire dès que possible, ne peut pas être bloqué par la paralysie par l'analyse - j'espère que cela s'est bien passé pour vous!
EDIT # 1 En réponse aux commentaires @JGord:
J'ai fait un projet qui implémentait le transfert UART avec un ATmega164p. Il a 2 UART. Voici une image d'une capture d'analyseur logique (analyseur logique Saleae USB) de ce projet montrant le transfert UART:
La ligne du haut correspond aux données sources (dans ce cas, ce serait votre caméra) et la ligne du bas est le canal UART à transférer (XBee dans votre cas). La routine écrite à cet effet a géré l'interruption de réception UART. Maintenant, croiriez-vous que pendant que ce transfert UART se déroule, vous pouvez avec plaisir configurer vos canaux PWM et gérer également vos routines I2C? Laissez-moi vous expliquer comment.
Chaque périphérique UART (pour mon AVR de toute façon) est composé d'un couple de registres à décalage, d'un registre de données et d'un registre de contrôle / état. Ce matériel fera les choses de lui-même (en supposant que vous avez déjà initialisé le débit en bauds et autres) sans aucune intervention de votre part si:
Le registre à décalage et le registre de données sont importants ici. Supposons qu'un octet arrive sur UART0 et que nous voulons transférer ce trafic vers la sortie de UART1. Lorsqu'un nouvel octet a été décalé dans le registre à décalage d'entrée de UART0, il est transféré dans le registre de données UART0 et une interruption de réception UART0 est déclenchée. Si vous avez écrit un ISR pour celui-ci, vous pouvez prendre l'octet dans le registre de données UART0 et le déplacer vers le registre de données UART1, puis définir le registre de contrôle pour UART1 pour commencer le transfert. Ce qu'il fait, c'est qu'il dit au périphérique UART1 de prendre tout ce que vous venez de mettre dans son registre de données, de le mettre dans son registre à décalage de sortie et de commencer à le décaler. À partir de là, vous pouvez revenir de votre ISR et revenir à la tâche que votre uC effectuait avant son interruption. Maintenant UART0, après avoir simplement effacé son registre à décalage et effacé son registre de données peut commencer à changer de nouvelles données s'il ne l'a pas déjà fait pendant l'ISR, et UART1 décale l'octet que vous venez d'y mettre - tout cela se produit sur son propre sans votre intervention pendant que votre uC est en train de faire une autre tâche. L'ensemble de l'ISR prend des microsecondes pour s'exécuter car nous ne déplaçons qu'un octet autour de la mémoire, ce qui laisse beaucoup de temps pour s'éteindre et faire d'autres choses jusqu'à ce que le prochain octet sur UART0 arrive (ce qui prend des centaines de microsecondes). et UART1 décale l'octet que vous venez d'y mettre - tout cela se fait tout seul sans votre intervention pendant que votre uC est en train de faire une autre tâche. L'ensemble de l'ISR prend des microsecondes pour s'exécuter car nous ne déplaçons qu'un octet autour de la mémoire, ce qui laisse beaucoup de temps pour s'éteindre et faire d'autres choses jusqu'à ce que le prochain octet sur UART0 arrive (ce qui prend des centaines de microsecondes). et UART1 décale l'octet que vous venez d'y mettre - tout cela se fait tout seul sans votre intervention pendant que votre uC est en train de faire une autre tâche. L'ensemble de l'ISR prend des microsecondes pour s'exécuter car nous ne déplaçons qu'un octet autour de la mémoire, ce qui laisse beaucoup de temps pour s'éteindre et faire d'autres choses jusqu'à ce que le prochain octet sur UART0 arrive (ce qui prend des centaines de microsecondes).
C'est la beauté d'avoir des périphériques matériels - il vous suffit d'écrire dans certains registres mappés en mémoire et il s'occupera du reste à partir de là et signalera votre attention à travers des interruptions comme celle que je viens d'expliquer ci-dessus. Ce processus se produit chaque fois qu'un nouvel octet arrive sur UART0.
Remarquez qu'il n'y a qu'un retard de 1 octet dans la capture logique car nous ne "tamponnons" que 1 octet si vous voulez y penser de cette façon. Je ne sais pas comment vous avez trouvé votre
O(2N)
estimation - je vais supposer que vous avez hébergé les fonctions de la bibliothèque série Arduino dans une boucle de blocage en attente de données. Si nous prenons en compte le surcoût de devoir traiter une commande "lecture de caméra" sur le uC, la méthode pilotée par interruption ressemble davantage à celleO(N+c)
quic
englobe le délai d'un octet et l'instruction "lecture de caméra". Ce serait extrêmement petit étant donné que vous envoyez une grande quantité de données (données d'image n'est-ce pas?).Tous ces détails sur le périphérique UART (et tous les périphériques sur l'UC) sont expliqués en détail dans la fiche technique et ils sont tous accessibles en C. Je ne sais pas si l'environnement Arduino vous donne un accès aussi bas que vous pouvez commencer à y accéder registres - et c'est la chose - si ce n'est pas le cas, vous êtes limité par leur mise en œuvre. Vous contrôlez tout si vous l'avez écrit en C (encore plus si cela se fait en assemblage) et vous pouvez vraiment pousser le microcontrôleur à son vrai potentiel.
la source
Pourquoi ne pouviez-vous pas diriger les données de la caméra à travers le µC? Je ne veux pas mettre en mémoire tampon les images, mais relayer les données UART à travers le µC afin qu'il puisse décider ce qui doit être renvoyé et ce qui ne doit pas?
Plus facile si vous avez un µC avec deux UART, mais que vous pouvez l'émuler dans le logiciel.
la source
Une autre option m'est venue à l'esprit, mais cela pourrait être un peu volumineux et trop lourd pour votre projet: -
Oui, encombrant, mais si vous les retirez et utilisez peut-être USB au lieu de RS232 dans la mesure du possible, vous pourriez éventuellement vous en tirer ...
la source