Red de sensores bastante complicada

9

Estaba trabajando en un proyecto recientemente y fue el primero que participó lo suficiente como para complicar las redes 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 las personas más experimentadas habrían resuelto este problema. Esta es una lectura larga, pero creo que es bastante interesante, así que quédese con ella. El problema era diseñar un dirigible autónomo capaz de navegar una carrera de obstáculos y tirar pelotas de ping pong en objetivos de caja marrón. Aquí va:

Sensores

  • Módulo de cámara 4D Systems uCAM-TTL - interfaz UART
  • Brújula digital HMC6352 - interfaz I2C
  • Maxbotix Sonar ez4 - interfaz analógica de 1 pin

Actuadores

  • 2x controladores de motor L293D (conectados a simples motores de pasatiempo): se utilizaron para conducir 6 motores bidireccionalmente. Requerían entradas PWM para variar la velocidad. Ahora 3 de nuestros motores siempre estaban haciendo lo mismo (los que controlaban el movimiento hacia arriba / abajo), por lo que solo requerían 2 salidas PWM de nuestros controladores para controlar los 3 motores. Los otros 3 motores que controlaban el movimiento lateral necesitaban un control individual (para el movimiento omnidireccional), por lo que se necesitaron otras 6 salidas PWM de nuestros controladores.
  • Servomotor - interfaz PWM

Controladores

Por razones que quedarán claras más adelante, terminamos usando 2x ATmega328Ps. Usamos un Arduino Uno para programarlos (no teníamos acceso a un ISP) pero fabricamos una PCB personalizada, por lo que no tuvimos 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 sea mucho más rápido y fácil.

Comunicación y procesamiento

  • 2x Xbee Basic
  • 2x ATmega328P
  • Computadora de escritorio con C ++ con openCV

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 cargar tanto peso y no nos sentimos cómodos implementando la visión por computadora en un microcontrolador. Entonces, lo que terminamos haciendo fue usar XBee para retransmitir 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 las 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 inconveniente fue que no pudimos controlar la cámara con un microcontrolador por un par de razones. El problema principal era que la memoria interna en el uP no podía manejar el almacenamiento de un marco completo. Es posible que haya habido formas de evitar esto mediante una codificación inteligente, pero a los fines de esta pregunta, supongamos que era imposible. Entonces, para resolver este problema, hicimos que el lado del servidor enviara comandos de la 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 inconveniente 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 (maldición ...). Es por eso que decidimos usar una segunda. De todos modos, el código se prestó perfectamente al procesamiento paralelo porque el control de altura era completamente independiente del control de movimiento lateral (por lo que 2 micros probablemente eran mejores que uno conectado a un controlador PWM). Por lo tanto, U1 fue responsable de 2 salidas PWM (arriba / abajo) y de leer la sonda. U2 fue responsable de leer la brújula, controlar 6 salidas PWM (los motores laterales) y también leer la sonda. U2 también fue responsable de recibir comandos del servidor a través de XBee.

Eso llevó a nuestro primer problema de comunicación. La línea XBee DOUT estaba conectada tanto al microcontrolador como a la cámara. Ahora, por supuesto, diseñamos un protocolo para que nuestros microcomandos ignoren los comandos de la cámara y los comandos de la cámara ignoren los microcomandos, por lo 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 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:

  1. El servidor solicita una imagen: PIC_REQUEST pasa por XBee y llega a U2 y la cámara. U2 lo ignora y la cámara envía los datos de la imagen.
  2. El servidor acaba de procesar una imagen y está enviando datos del motor para indicarle a blimp que gire a la derecha: MOTOR_ANGLE (70) pasa por XBee y llega a U2 y la cámara. U2 reconoce como un micro comando y por lo tanto apaga el FET de la cámara (pero tal vez la cámara ya respondió con un NAK ?? quién sabe ...). U2 responde al comando cambiando las salidas PWM del motor. Luego vuelve a encender el FET de la cámara (esta era la configuración predeterminada ya que los datos de la imagen eran los más importantes).
  3. El servidor se da cuenta de que hemos llegado a un punto en la carrera de obstáculos donde nuestra altura de desplazamiento predeterminada ahora debe ser de 90 pulgadas en lugar de 50 pulgadas. SET_HEIGHT pasa por XBee y sucede lo mismo que en el ejemplo 2. U2 reconoce el comando SET_HEIGHT y desencadena una interrupción en U1. U1 ahora sale de su circuito de control de altura y espera recibir datos seriales de U2. Así es, más datos en serie. En este punto, el FET de U2 está encendido (y el FET de la cámara está apagado), por lo que el servidor recibe la altura que U2 también está enviando a U1. Eso fue para fines de verificación. Ahora U1 restablece su variable interna para height2HoverAt. U2 ahora apaga su FET y vuelve a encender la cámara FET.

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 quedarían datos en buffers, pero solo 3 bytes (todos nuestros comandos eran secuencias de 6 bytes). A veces perderíamos la conexión con nuestra cámara y tendríamos que volver a sincronizarla.

Entonces mi pregunta es: ¿Qué técnicas sugerirían para que la comunicación entre todos esos componentes sea más confiable / robusta / más simple / mejor?

Por ejemplo, sé que uno hubiera sido agregar un circuito de retardo entre el XBee de a bordo 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 micro comandos con NAK. ¿Alguna otra idea como esa?

Gracias y estoy seguro de que esto requerirá muchas ediciones, así que estad atentos.


Editar1: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 la cámara, mapa de bits sin formato o JPEG. Para un mapa de bits sin procesar, la cámara solo 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 hacerlo) y no pensamos que seríamos capaces de sacarlo del búfer y pasarlo 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 protocolo de enlace). Lo más rápido que pudo alcanzar fue 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 disminuyó el tiempo que tomó transferir una imagen demasiado. Necesitábamos una cierta frecuencia de actualización en las imágenes para que nuestro algoritmo de control motor funcionara.

NickHalden
fuente
3
Estás poniendo mucho esfuerzo en no expandir tus habilidades de microcontrolador. No tengo nada en contra de Arduino, pero no es muy apropiado para eso. ¿Eventualmente puedes hacer que funcione? Probablemente. Sin embargo, sería mucho más útil aprender una plataforma más capaz. Si está preguntando cómo lo harían las personas con más experiencia, diría algo como un SBC ARM para openCV y control, y un FPGA que sirve como puente para todas las interfaces. Sin embargo, eso sería un pequeño salto, así que sugeriría probar una nueva cosa importante ... ¿tal vez un micro de 32 bits con suficientes periféricos para interactuar con todo?
darron
Jaja sí, estaba yendo muy lejos. Ver que este proyecto era una tarea escolar y queríamos enfocarnos en hacer que funcione, no hacer que uno de los dos EE del equipo aprenda una nueva plataforma de microcontrolador. Estoy pensando en entrar en ARM y micros más avanzados este verano en realidad. El otro EE en nuestro equipo había tomado una clase de FPGA en realidad ... Iré a gritarle por no sugerir que = P
NickHalden

Respuestas:

4

Entiendo que quería elegir un entorno de desarrollo con el que estaba familiarizado de manera que pueda comenzar a ejecutar, pero creo que el intercambio de hardware / software puede haberlo encajonado al quedarse con Arduino y no elegir una parte que tuviera todo los periféricos de hardware que necesitabas y, en su lugar, escribía todo en C controlado por interrupciones.

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 XBee UART byte por byte, por lo que no hay búfer (o como máximo solo un muy pequeño uno) necesario. Hubiera intentado eliminar el otro uC todos juntos eligiendo una parte que también se adaptara a todas sus necesidades PWM (¿8 canales PWM?) Y si quisiera quedarse con 2 uC diferentes cuidando sus respectivos ejes, entonces tal vez un una interfaz de comunicación diferente hubiera sido mejor 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 hacerlo lo antes posible, no se puede detener por parálisis por análisis, ¡espero que te haya salido bien!


EDITAR # 1 En respuesta a los comentarios @JGord:

Hice un proyecto que implementó el reenvío UART con un ATmega164p. Cuenta con 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 UART: captura analizador

La línea superior es la fuente de datos (en este caso sería su cámara) y la línea inferior es el canal UART que se reenvía (XBee en su caso). La rutina escrita para hacer esto manejó la interrupción de recepción UART. Ahora, ¿creería que mientras se realiza este reenvío UART, podría configurar sus canales PWM y manejar sus rutinas I2C? Déjame explicarte cómo.

Cada periférico UART (para mi AVR de todos modos) está compuesto por un par de registros de desplazamiento, un registro de datos y un registro de control / estado. Este hardware hará las cosas por sí mismo (suponiendo que ya haya inicializado la velocidad de transmisión y tal) sin ninguna de su intervención si:

  1. Un byte entra o
  2. Se coloca un byte en su registro de datos y se marca para salida

Aquí es importante el registro de desplazamiento y el registro de datos. Supongamos que llega un byte en UART0 y queremos reenviar ese tráfico a la salida de UART1. Cuando un nuevo byte se ha desplazado al registro de desplazamiento de entrada de UART0, se transfiere al registro de datos UART0 y se activa una interrupción de recepción 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 regresar de su ISR y volver a cualquier tarea que su uC estaba haciendo 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 los datos nuevos si aún no lo ha hecho durante el ISR, y UART1 está cambiando el byte que acaba de poner en él; todo eso sucede en solo sin su intervención mientras su uC está apagado haciendo otra tarea. El ISR completo tarda microsegundos en ejecutarse, ya que solo estamos moviendo 1 byte alrededor de alguna memoria, y esto deja mucho tiempo para salir y hacer otras cosas hasta que llegue el siguiente byte en UART0 (que toma cientos de microsegundos). y UART1 está cambiando el byte que acaba de poner en él; todo eso sucede por sí solo sin su intervención mientras su uC está haciendo otra tarea. El ISR completo tarda microsegundos en ejecutarse, ya que solo estamos moviendo 1 byte alrededor de alguna memoria, y esto deja mucho tiempo para salir y hacer otras cosas hasta que llegue el siguiente byte en UART0 (que toma cientos de microsegundos). y UART1 está cambiando el byte que acaba de poner en él; todo eso sucede por sí solo sin su intervención mientras su uC está haciendo otra tarea. El ISR completo tarda microsegundos en ejecutarse, ya que solo estamos moviendo 1 byte alrededor de alguna memoria, y esto deja mucho tiempo para salir y hacer otras cosas hasta que llegue el siguiente byte en UART0 (que toma cientos de microsegundos).

Esta es la belleza de tener periféricos de hardware: solo tiene que escribir en algunos registros mapeados de memoria y se encargará del resto desde allí e indicará su atención a través de interrupciones como la que acabo de explicar anteriormente. Este proceso ocurrirá cada vez que aparezca 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 memoria intermedia" 1 byte si desea pensar 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 en serie Arduino en un bucle de bloqueo en espera de datos. Si consideramos la sobrecarga de tener que procesar un comando "leer cámara" en el uC, el método de interrupción es más parecido a O(N+c)donde cabarca el retardo 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 el uC) se explican a fondo en la hoja de datos y todo está accesible en C. No sé si el entorno Arduino le brinda un acceso tan bajo para que pueda comenzar a acceder registros, y esa es la cuestión, si no es así, está limitado por su implementación. Usted tiene el control de todo si lo ha escrito en C (aún más si lo hace en conjunto) y realmente puede impulsar el microcontrolador a su potencial real.

Jon L
fuente
Supongo que no expliqué esto bien. El problema al colocar el micro entre la cámara y el XBee fue que el micro no puede hacer nada más mientras obtiene los datos de la imagen a menos que todo lo demás esté en interrupciones de temporizadores. Además, si supone que obtener una imagen con N píxeles tardó 5 segundos, cuando coloca el micro en él ahora tarda ~ 10 segundos. Claro que sigue siendo O (N) pero es realmente 2N tiempo de ejecución que en este caso fue suficiente para arruinar nuestro objetivo de frecuencia de actualización. En última instancia, el espacio de memoria no era realmente el factor limitante, sino la velocidad. Suspiro, parece ser la única respuesta real ...
NickHalden
era usar hardware más avanzado. Esperaba que alguien sugiriera algo realmente inteligente que sirviera como un truco útil para todos mis años como EE. Oh, bueno, supongo que al menos eso significa que no fui demasiado tonto para pensar en el truco = P Oh, y funcionó bastante bien.
NickHalden
@JGord, por coincidencia, hice un proyecto usando el reenvío UART de la manera que describo entre una tarjeta con chip y una máquina de lavandería usando un ATmega164 jonathan-lee.ca/DC18-Smart-Card.html del que estoy hablando usando UART ISR para mover un solo byte de un registro de datos de UART al otro registro de datos de UART y luego regresar. El hardware UART actúa de forma independiente y desplaza los datos desde allí. El ISR toma microsegundos y una vez que regresa, el uC es libre de hacer lo que quiera hasta que cambie el siguiente byte. Una mejor explicación viene en una edición, cada vez que llego a casa.
Jon L
Interesante. Entonces, ¿su sugerencia sería que el servidor hable solo con el micro? ¿Entonces ese micro retransmite comandos del servidor a la cámara y las respuestas de la cámara al servidor? Entonces, en el UART0 ISR (terminar de hablar con el servidor), pude verificar si es o no un comando de cámara. En caso afirmativo, refleje en UART1 a la cámara. De lo contrario, no refleje y, en su lugar, cambie algún valor (ángulo lateral, altura, etc., alguna variable con la que mi bucle de control verifique). Entonces, el ISR de UART1 sería simplemente reflejar los datos de la imagen en UART0 siempre. Sí, supongo que eso funcionaría. Así que realmente solo
obtengo
y suficientes canales PWM habrían sido el camino a seguir. Así que ahora digamos que el servidor envió una solicitud get_data que se supone que el micro debe responder con una secuencia de 6 bytes en la que envía una altura, rumbo de la brújula y algunos ECC. Por alguna razón, el servidor lee 6 bytes pero no tienen el protocolo correcto, es decir, los bytes de inicio y fin del mensaje no se alinean. ¿Quién sabe por qué? ¿Simplemente tiraría el mensaje y solicitaría nuevamente o qué? Porque si hubiera como un byte ficticio en el búfer, el mensaje de 6 bytes nunca volvería a alinearse. ¿Recomendaría enjuagarse después de un mensaje fallido?
NickHalden
1

¿Por qué no podrías 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 de UART a través de µC para que pueda decidir qué se debe enviar y qué no.

Es más fácil si tiene un µC con dos UART, pero podría emularse en un software.

Majenko
fuente
Ja, sí, esa fue una de las cosas que dejé fuera ... pregunta de edición ahora. Buena idea, sin embargo, nos emocionamos mucho cuando pensamos en eso también.
NickHalden
1

Se me ocurrió otra opción, pero podría ser algo voluminosa y demasiado pesada para su proyecto:

  • ¿Por qué no utilizar un servidor USB inalámbrico, conectado a un pequeño concentrador USB, con adaptadores USB-> RS232 para dar múltiples canales de control a las diferentes partes del sistema?

Sí, voluminoso, pero si los quita y tal vez use USB en lugar de RS232 cuando sea posible, podría salirse con la suya ...

Majenko
fuente