Estuve trabajando en un proyecto recientemente y fue el primero que estuvo lo suficientemente involucrado como para complicar la red de sensores. Al final, creo que la comunicación fue el cuello de botella en términos de rendimiento general y me pregunto cómo personas más experimentadas habrían resuelto este problema. Esta es una lectura larga, pero creo que es bastante interesante, así que por favor sigan con ella. El problema era diseñar un dirigible autónomo capaz de navegar por una carrera de obstáculos y dejar caer pelotas de ping pong en objetivos de cajas marrones. Aquí va:
Sensores
Actuadores
Controladores
Por razones que se aclararán más adelante, terminamos usando 2x ATmega328P. Usamos un Arduino Uno para programarlos (no teníamos acceso a un ISP), pero fabricamos una PCB personalizada para no tener que usar placas arduino, ya que eso solo agregaría un peso innecesario a nuestro dirigible. En cuanto a por qué elegimos el ATmega328P, estaba muy familiarizado con el entorno arduino y creo que eso hizo que el desarrollo del código fuera mucho más rápido y fácil.
Comunicación y Procesamiento
Entonces, como puede ver en el módulo de la cámara, la mayor parte de nuestro proyecto se basó en la visión por computadora. Los dirigibles solo podían llevar un peso limitado y no nos sentíamos cómodos implementando la visión por computadora en un microcontrolador. Entonces, lo que terminamos haciendo fue usar XBee para transmitir los datos de la imagen a una computadora de escritorio. Entonces, en el lado del servidor, recibimos datos de imagen y usamos openCV para procesar la imagen y resolver cosas a partir de ella. Ahora, el lado del servidor también necesitaba conocer la información de altura (del sonar) y la información de la brújula.
El primer problema fue que no pudimos tener la cámara controlada por un microcontrolador por un par de razones. El problema principal era que la memoria interna de la uP no podía manejar el almacenamiento de un cuadro completo. Podría haber formas de evitar esto a través de una codificación inteligente, pero para los fines de esta pregunta, supongamos que era imposible. Entonces, para resolver este problema, hicimos que el lado del servidor enviara comandos de cámara a través del transceptor XBee y el receptor XBee (a bordo del dirigible) tenía su salida conectada a la entrada de la cámara.
El siguiente problema fue que no hay suficientes PWM en un solo ATmega328P para controlar todos los motores PORQUE la interfaz I2C usa uno de los pines PWM (malditos sean...). Es por eso que decidimos usar una segunda. De todos modos, el código se prestaba perfectamente al procesamiento en paralelo porque el control de altura era completamente independiente del control de movimiento lateral (por lo que 2 micros probablemente era mejor que uno conectado a un controlador PWM). Por lo tanto, U1 era responsable de 2 salidas PWM (arriba/abajo) y lectura del Sonar. U2 era responsable de leer la brújula, controlar 6 salidas PWM (los motores laterales) y también leer el Sonar. U2 también era responsable de recibir comandos del servidor a través del XBee.
Eso llevó a nuestro primer problema de comunicación. La línea XBee DOUT se conectó tanto al microcontrolador como a la cámara. Ahora, por supuesto, diseñamos un protocolo para que nuestros microcomandos ignoraran los comandos de la cámara y los comandos de la cámara ignoraran los microcomandos, así que estuvo bien. Sin embargo, la cámara, al ignorar nuestros microcomandos, enviaría datos NAK en su línea de salida. Dado que el comando estaba destinado al micro, necesitábamos de alguna manera apagar la salida de la cámara al XBee. Para resolver esto, hicimos el micro control 2 FET que estaban entre la cámara y XBee (ese es el primer FET) y también entre U2 y el XBee (ese es el segundo FET). Por lo tanto, cuando la cámara intentaba enviar información al servidor, el primer FET estaba "encendido" y el segundo FET estaba "apagado".
Entonces, para darle una idea de cómo funcionó esto, aquí hay algunos ejemplos:
Definitivamente omití una buena cantidad de información, pero creo que es suficiente para comprender algunas de las complicaciones. Al final, nuestros problemas fueron simplemente sincronizar todo. A veces quedaban datos en los búferes, pero solo 3 bytes (todos nuestros comandos eran secuencias de 6 bytes). A veces perdíamos la conexión con nuestra cámara y teníamos que resincronizarla.
Entonces mi pregunta es: ¿Qué técnicas sugerirían ustedes para hacer que la comunicación entre todos esos componentes sea más confiable/robusta/simple/mejor?
Por ejemplo, sé que uno hubiera sido agregar un circuito de retardo entre el XBee integrado y la cámara para que el micro tuviera la oportunidad de apagar la línea de conversación de la cámara antes de que respondiera a los microcomandos con NAK. ¿Alguna otra idea como esa?
Gracias y estoy seguro de que esto requerirá muchas ediciones, así que estad atentos.
Edit1:Empalmar los datos UART de la cámara a través de uno de los micros no nos parecía posible. Había dos opciones para datos de cámara, mapa de bits sin formato o JPEG. Para un mapa de bits sin procesar, la cámara simplemente le envía datos tan rápido como puede. El ATmega328P solo tiene 128 bytes para un búfer en serie (técnicamente, esto es configurable, pero no estoy seguro de cómo) y no pensamos que podríamos sacarlo del búfer y llegar al XBee lo suficientemente rápido. Eso dejó el método JPEG donde envía cada paquete y espera a que el controlador lo ACK (pequeño protocolo de negociación). Lo más rápido que podía llegar era 115200 baudios. Ahora, por alguna razón, lo más rápido que pudimos transmitir de manera confiable grandes cantidades de datos a través del XBee fue 57600 baudios (esto es incluso después de que hicimos el emparejamiento de nodo/red para permitir la capacidad de reenvío automático). Agregar la parada adicional en nuestra red (cámara a micro a XBee en lugar de solo cámara a XBee) para el micro simplemente redujo demasiado el tiempo que tomó transferir una imagen. Necesitábamos una cierta frecuencia de actualización en las imágenes para que nuestro algoritmo de control de motores funcionara.
Entiendo que querías elegir un entorno de desarrollo con el que estuvieras familiarizado, de modo que pudieras comenzar a ejecutar, pero creo que la compensación de hardware/software puede haberte encerrado al quedarte con Arduino y no elegir una parte que tuviera todo. los periféricos de hardware que necesitaba y escribir todo en C controlado por interrupciones en su lugar.
Estoy de acuerdo con la sugerencia de @Matt Jenkins y me gustaría ampliarla.
Hubiera elegido un uC con 2 UART. Uno conectado al Xbee y otro conectado a la cámara. El uC acepta un comando del servidor para iniciar una lectura de cámara y se puede escribir una rutina para transferir datos desde el canal UART de la cámara al canal UART XBee byte por byte, por lo que no hay búfer (o como mucho solo un pequeño uno) necesario. Habría tratado de eliminar el otro uC por completo eligiendo una parte que también se adaptara a todas sus necesidades de PWM (¿8 canales PWM?) Y si quisiera quedarse con 2 uC diferentes cuidando su eje respectivo, entonces quizás un Hubiera sido mejor una interfaz de comunicaciones diferente, ya que se tomarían todos sus otros UART.
Alguien más también sugirió mudarse a una plataforma Linux integrada para ejecutar todo (incluido openCV) y creo que eso también habría sido algo para explorar. Sin embargo, he estado allí antes, un proyecto escolar de 4 meses y solo necesitas terminarlo lo antes posible, no se puede detener por parálisis por análisis. ¡Espero que te haya resultado bien!
EDIT #1 En respuesta a los comentarios @JGord:
Hice un proyecto que implementó el reenvío de UART con un ATmega164p. Tiene 2 UART. Aquí hay una imagen de una captura del analizador lógico (analizador lógico USB Saleae) de ese proyecto que muestra el reenvío de UART:
La línea superior son los datos de origen (en este caso, sería su cámara) y la línea inferior es el canal UART al que se reenvía (XBee en su caso). La rutina escrita para hacer esto manejó la interrupción de recepción de UART. Ahora, ¿creería usted que mientras se lleva a cabo este reenvío de UART, podría configurar felizmente sus canales PWM y manejar sus rutinas I2C también? Déjame explicarte cómo.
Cada periférico UART (para mi AVR de todos modos) se compone de un par de registros de desplazamiento, un registro de datos y un registro de control/estado. Este hardware hará las cosas por sí solo (suponiendo que ya haya inicializado la velocidad en baudios y demás) sin su intervención si:
De importancia aquí es el registro de desplazamiento y el registro de datos. Supongamos que entra un byte en UART0 y queremos reenviar ese tráfico a la salida de UART1. Cuando se ha desplazado un nuevo byte al registro de desplazamiento de entrada de UART0, se transfiere al registro de datos de UART0 y se activa una interrupción de recepción de UART0. Si ha escrito un ISR para él, puede tomar el byte en el registro de datos UART0 y moverlo al registro de datos UART1 y luego configurar el registro de control para que UART1 comience a transferir. Lo que hace es decirle al periférico UART1 que tome lo que acaba de poner en su registro de datos, lo ponga en su registro de desplazamiento de salida y comience a cambiarlo. Desde aquí, puede salir de su ISR y volver a la tarea que estaba haciendo su uC antes de que se interrumpiera. Ahora UART0, después de haber borrado su registro de desplazamiento, y haber borrado su registro de datos puede comenzar a cambiar nuevos datos si aún no lo ha hecho durante la ISR, y UART1 está desplazando el byte que acaba de ingresar; todo eso sucede en solo sin su intervención mientras su uC está haciendo alguna otra tarea. Todo el ISR tarda microsegundos en ejecutarse, ya que solo estamos moviendo 1 byte alrededor de la memoria, y esto deja mucho tiempo para salir y hacer otras cosas hasta que llegue el siguiente byte en UART0 (que tarda cientos de microsegundos).
Esta es la belleza de tener periféricos de hardware: simplemente escribe en algunos registros asignados a la memoria y se encargará del resto desde allí y señalará su atención a través de interrupciones como la que acabo de explicar anteriormente. Este proceso ocurrirá cada vez que ingrese un nuevo byte en UART0.
Observe cómo solo hay un retraso de 1 byte en la captura lógica, ya que solo estamos "almacenando en búfer" 1 byte si quiere pensarlo de esa manera. No estoy seguro de cómo ha llegado a su O(2N)
estimación. Voy a suponer que ha alojado las funciones de la biblioteca serial de Arduino en un bucle de bloqueo en espera de datos. Si tenemos en cuenta la sobrecarga de tener que procesar un comando de "leer cámara" en el uC, el método impulsado por interrupción es más como O(N+c)
donde c
abarca el retraso de un solo byte y la instrucción "leer cámara". Esto sería extremadamente pequeño dado que está enviando una gran cantidad de datos (datos de imagen, ¿verdad?).
Todos estos detalles sobre el periférico UART (y todos los periféricos en la uC) se explican detalladamente en la hoja de datos y todo es accesible en C. No sé si el entorno Arduino le brinda ese bajo acceso para que pueda comenzar a acceder se registra, y esa es la cuestión, si no lo hace, está limitado por su implementación. Tienes el control de todo si lo has escrito en C (incluso más si lo has hecho en ensamblador) y realmente puedes llevar el microcontrolador a su verdadero potencial.
¿Por qué no pudiste canalizar los datos de la cámara a través del µC? No me refiero a almacenar en búfer las imágenes, sino a transmitir los datos UART a través del µC para que pueda decidir qué debe devolverse y qué no.
Es más fácil si tiene un µC con dos UART, pero podría emularse en el software.
Se me ha ocurrido otra opción, pero esto podría ser algo voluminoso y demasiado pesado para su proyecto: -
Sí, voluminosos, pero si los quitas y tal vez usas USB en lugar de RS232 cuando sea posible, podrías salirte con la tuya...
darrón
NickHalden