Mostrando entradas con la etiqueta leer PWM. Mostrar todas las entradas
Mostrando entradas con la etiqueta leer PWM. Mostrar todas las entradas

martes, 24 de julio de 2012

Leer emisora RC Parte 2

    Como continuación a Leer emisora RC Parte 1 escribo esta entrada en la que explico las mejoras necesarias que he introducido en el código. Son varios los cambios introducidos que producen una estabilidad muy grande en la medición de los canales y por eso paso a la versión 2.0.

    Recordemos que el código original tenía unas variaciones en las mediciones que oscilaban en unos 12 µs. Aunque pueda parecer poco con las pruebas que he ido haciendo es relativamente importante. Si en 20 ms o 40 ms (uno o dos ciclos) se produce esa variación brusca le estamos indicando al programa del cuadricóptero que queríamos esa variación brusca, los PID se pondrán en marcha y las señales finales a los motores también. Uno o dos ciclos más tarde estaremos indicando un cambio brusco en dirección contrario. El resultado será que no conseguiremos un ajuste fino del cuadricóptero ni una buena estabilidad. El suavizado de las lecturas mediante el promedio de las últimas lecturas (6 por defecto) aminora estos efectos pero tras estudiar el problema a fondo podemos mejorarlo mucho más.

    En primer lugar tenemos que estudiar por qué se produce esa variación en las medidas y he encontrado dos causas. Lo primero es pensar en la precisión de la propia emisora pero elimino esta posibilidad ya que cualquier motor con su ESC o servo funciona con una precisión y estabilidad exquisita cuando se maneja directamente con la emisora.
    Causas encontradas:
    La primera es la precisión en la medición del tiempo. La función micros() tiene una medición mínima de 4 µs, esto es, no nos da lecturas de por ejemplo 9 µs, 10 µs o 11 µs. Nos da lecturas de 8 µs o 12 µs. Sobre esta función no podemos hacer directamente nada.
    La segunda es la velocidad en la ejecución del código. Para muchas cosas Arduino es suficientemente rápido pero en casos como el que nos lleva vemos que se vuelve lento. Vamos con la pequeña parte del código en la que esto se produce:

  while (digitalRead(2) == 1) {
    if ((micros() - TiempoParcial1) > 2000) {
       ErrorRadio = true;
       break;
    }
    else {
      ErrorRadio = false;
    }
  }

    Aunque son muy pocas instrucciones cada una de ellas lleva algún microsegundo ejecutarla. El problema es que al estar todo dentro de un while el tiempo de ejecución no es fijo. Pongamos que el micro ejecuta la primera instrucción, el while. Lee la entrada y ve que es 1. Entonces sigue ejecutando todo el resto de instrucciones de modo que si nada más ejecutar la primera la entrada pasa a 0 no se dará cuenta hasta que vuelva otra vez al while lo que supone añadir el tiempo de ejecutar todas las instrucciones. Y eso para Arduino son unos cuantos microsegundos. En el siguiente ciclo sin embargo puede pasar que justo cuando la entrada cambia a 0 es cuando se ejecuta el while por lo que saldrá ya del mismo sin apenas pérdida de tiempo.

    Aleatoriamente las dos causas se van sumando o restando. Entre la anulación máxima o la suma máxima tenemos esa variación de 12 µs (que en alguna lectura esporádica llega incluso a 16 µs).

    Entonces, ¿qué podemos hacer? Sobre la precisión de micros() directamente nada. Y en la serie de instrucciones del while todas son necesarias y no conozco ninguna forma de optimizarlo salvo la instrucción (digitalRead(2) == 1). Esta instrucción del IDE está compuesta por una docena de instrucciones del micro. Las mediciones que he realizado me da que el tiempo de ejecución para la instrucción a = digitalRead(2) es de 4,8 µs. Hay otra forma de leer una entrada que es con los registros del micro. Para un Atmega 168/328 la instrucción es a = bitRead(PIND, 2) y he calculado un tiempo de ejecución de tan solo 0,06 µs. Utilizando esto el ahorro de tiempo es muy importante. Una prueba preliminar simplemente cambiando esta instrucción hace que el rizado en la medición baje a 8 µs con muchas zonas de 4 µs. Es más esporádica una variación de 12 µs. Pero además el rizado es menos caótico haciendo que el promedio de lecturas sea más estable.
    Sólo hay un problema y es que el mapeo de pines puede ser distinto y de hecho lo es de un micro a otro en el IDE. En concreto para los micros Atmega 8/168/328 el mapeo es el mismo pero en el Atmega 2560 cambia y en el Atmega 32U4 de la nueva placa Leonardo también. La instrucción sólo está probada para Atmega 8/168/328 pero anoto cómo debería ser para los otros micros aunque repito que no lo he probado:

                            Cualquier micro       Atmega 8/168/328          Atmega 2560            Atmega 32U4

Pin digital 2        digitalRead(2)        bitRead(PIND, 2)         bitRead(PINE, 4)       bitRead(PIND, 1)
Pin digital 3        digitalRead(3)        bitRead(PIND, 3)         bitRead(PINE, 5)       bitRead(PIND, 0)
Pin digital 4        digitalRead(4)        bitRead(PIND, 4)         bitRead(PING, 5)       bitRead(PIND, 4)

    Implementada esta mejora y con el promedio de los últimos 6 valores tomados se consigue tener un rizado máximo de unos 2 µs. Aunque tengo en mente otra posible mejora a nivel de filtrado por el momento considero el código totalmente operativo aunque más adelante tal vez me ponga a mejorarlo.

    Por último, como este código es el que usaremos como base de tiempos en el código completo del cuadricóptero hay que tener previsto un sustituto ante fallo de la emisora. Así, si se produce algún fallo la variable ErrorRadio se vuelve true pero tenemos que, además de seguir intentando volver a leer la emisora, establecer una rutina que haga que el resto del código siga ejecutándose cada 20 ms. Con tan sólo tres líneas de código ha quedado implementado

Archivo para Arduino 1.0.1: Leer radio v2.0

domingo, 29 de enero de 2012

Leer emisora RC Parte 1

    Existen dos posibilidades para leer la información que envía una emisora RC (de radio control).

    Una primera opción es leer la señal PPM en el receptor de la emisora. La ventaja es que esta señal trae codificados todos los canales de la emisora por lo que sólo necesitamos una entrada digital en Arduino. La desventaja es que hay que acceder al interior del receptor e interceptar esta señal. Cada receptor será distinto por lo que puede ser una tarea complicada.

    Otra opción es leer los canales de salida que ya ha separado el receptor. Se corresponden a señales PWM, tantas como canales tiene la emisora. Son las señales que van hacia los servos o los ESC's de los motores. La ventaja es la no invasión del receptor. Desventaja que tenemos que usar en Arduino tantas entradas digitales como canales tiene la emisora.

    Yo he optado por la segunda solución pero con una pequeña implementación para no necesitar tantas entradas de Arduino. En concreto para leer los 8 canales de mi emisora, la Turnigy TGY 9X, sólo necesitaré 3 entradas. ¿Cómo hacer esto? El primer canal lo llevamos a una entrada de Arduino. Además de calcular su lectura lo usaremos para sincronizar el inicio de todas las lecturas. El resto de canales lo que hacemos es sumarlos, los pares por una lado y los impares por otro. De este modo puedo detectar el final del pulso de un canal ya que el siguiente, que va seguido, está en la otra entrada de Arduino.

    Así tendremos el Canal 1 en la entrada PIN 2 de Arduino. Los Canales 2, 4, 6 y 8 sumados y a la entrada PIN 3 de Arduino. Y los Canales 3, 5 y 7 sumados y a la entrada PIN 4 de Arduino. ¿Y cómo sumamos los canales? Muy sencillo. Conectamos un diodo de señal (como el 1N4148) en cada uno de los Canales 2, 4, 6 y 8, la otra patilla de los diodos las unimos a una resistencia de 47K y la otra patilla de la resistencia a GND. Lo mismo con los Canales 3, 5 y 7.

    Una cuestión importante. Esto es así cuando la secuencia de las señales PWM de los canales llevan el orden lógico de 1, 2, 3, 4, 5, 6, 7 y 8. Sin embargo comprobé que mi emisora Turnigy TGY 9X no lleva esa secuencia. Tiene cambiados en orden los canales 2 y 3. Cosa inexplicable que no llego a entender y que por otro lado no sé si es así en otras emisoras. Así en mi caso la secuencia de canales es 1, 3, 2, 4, 5, 6, 7 y 8. Y por tanto mi configuración definitiva es:

- Canal 1 directo a pin 2.
- Canales 3, 4, 6 y 8 a traves de diodos a pin 3.
- Canales 2, 5 y 7 a traves de diodos a pin 4.



    Una vez tenemos las lecturas podemos comprobar el rizado. Con una palanca centrada obtengo una lectura de unos 1450 µs que varía en un rango de hasta 12 µs. Tenemos que alisar esa señal. En la primera versión desarrollada esto lo hago haciendo la media de las últimas X medidas tomadas. Por defecto lo he dejado en 6 por estar en lo que yo creo es el punto de equilibrio. Pero, ¿qué equilibrio? Pues cuantas más medidas utilicemos para hacer la media mejor estamos filtrando la señal. Sin embargo aumentar el número de medidas a promediar produce un retardo de la señal.

    Consultando sobre el tema del filtrado de señales Igor R me informó en los foros de Arduino de que el promedio de los últimos X valores es más bien un método malo. Podéis verlo en filtro paso-bajo con arduino. Así que al tiempo que la versión 1.0 de Leer Radio es totalmente operativa, como el filtrado lo realizo con el promedio de los últimos 6 valores, sigo haciendo pruebas con distintos tipos de filtros digitales.

    Archivo para Arduino 1.0: Leer radio v1.0

    Nota: tras hacer diferentes pruebas con distintos tipos de filtros (Butterworth, Bessel y Chebyshev) de distinto orden y frecuencia de corte concluyo que no percibo una mejoría en el filtrado por lo que finalmente me quedo con el filtrado mediante promedio.

    Al ir trabajando con este código he ido pensando en algunas ideas para mejorar el rizado que tengo que plasmar en forma de código. Cuando lo desarrolle pondré una nueva entrada.

    La nueva entrada ha llegado, podéis verla en Leer emisora RC Parte 2.