Proyecto Cuadricoptero UAV: Problema con receptor de emisora RC

Hola a todos,
estoy construyendo un cuadricoptero UAV para ello quiero poner el Arduino UNO entre el receptor de la emisora RC y los ESC con los motores. El problema que tengo es que los pines analogicos de entrada del Arduino no me detectan las señales del receptor de RC. Se que no las detecta ya que hice un sketch que mostraba en por el serial los valores del pin analogico y no variaban a medida que movia el interruptor de la emisora sino que marca siempre 0 y de vez encuando un valor aleatorio. La transmisión entre emisora y receptor RC se que es correcta por que con un tester media el señal del receptor y los valores si que varian a la vez que muevo el interruptor, el voltaje del señal varia entre 0.17V y 0.33V

PD: mi emisora de RC es la HK6DF.

Saludops y gracias de antemano.

Si que varia la señal pero con el arduino por el puerto serie no lo puedes ver? pon el código del arduino así igual es mas fácil ayudarte!

Un saludo.

La salida de la emisora es analógica o es PWM??

Los receptores suelen sacar PPM

Las emisoras suelen transmitir en PPM (creo que es analógico) y el receptor transforma la señal PPM en las salidas PWM de cada uno de los canales.

Para leer la emisora están las dos posibilidades, leer la señal PPM o las PWM de cada canal. Para leer las PPM hay que acceder al interior del receptor y buscar en el circuito dónde se intercepta esta señal. Por la red hay explicaciones de cómo hacerlo en los modelos más comunes de emisoras.

Si no se quiere tener que acceder al interior del receptor se puede leer la señal PWM de cada canal.

Para leer PPM creo que hay que utilizar una entrada analógica y para PWM una entrada digital para cada canal que quiere ser leído.

adrib, no he mirado detenidamente tu emisora pero yo diría que es PPM. ¿Qué señal es la que estás interceptando, la PPM o las PWM? ¿Dónde las estás tomando?

Ambas se leen en digital. en PPM se utiliza solo un pin , en PWM un pin por cada canal.

buscate el proyecto de multiwii copter, es el que hice yo. el código te permite leer los datos del receptor en ambos casos. echale un vistazo al codigo y puedes hacerte una idea de como hacerlo .

Un saludo ¡

Por lo que he leido en vuestras respuestas diria que estoy leyendo PWM ya que tengo un cable de señal por cada canal y ya que segun decis se leen en pin digital he probado con el codigo:

void setup() {
  Serial.begin(9600);
  pinMode(9,INPUT);
}
void loop() {
  int sensorValue =analogRead(9); // He probado tanto analogRead como digitalRead;
  Serial.println(sensorValue);
  delay(1000);
}

Pero lo único que me muestra por el serial son valores aleatorio =(

no te has mirado lo que te he dicho ....

mírate el código para ver de que manera obtiene los valores de cada uno de los canales.

Os lo tenemos que dar todo mascao...

el codigo viene comentado, vienen las dos formas de obtener los datos

un saludo

pd: hay una version 7 de este codigo pero actualiza otras cosas, buscala de todas formas y le echas un vistazo
tambien tiene un software con el cual puedes ver el valor de los sensores , acelerometros, posicion de los sticks etc .

un saludo

MultiWii_1_6.pde (72.4 KB)

una pista ...

// ******************
// rc functions
// ******************

Si, he estado mirando el codigo SrDonGato pero nose que tengo que sacar de ahi ya que si el arduino me diera valores proporcionales al potenciometro de la emisora ya sabria que codigo ponerle, el problema es que el arduino ni se inmuta a los cambios de la emisora simplemente me devuelve 0.

La forma que tienes de ver el estado del pin es si está a 0 o 1 en un determinado momento.

Te has fijado como son las señales PWM y PPM ?, debes fijarte primero que es lo que queires leer por esa patilla.

aqui tienes un codigo que saque de no me acuerdo donde para leer una señal PPM, normalmente tienes que abrir el receptor para obtener la señal del pin de un chip que tiene interno, que es el que decodifica de PPM a PWM a cada canal. la ventaja del PPM es que con 1 entrada digital obtienes los valores de todos los canales. con PWM obtienes el valor de 1 solo canal por lo que necesitas tantas entradas digitales como canales tenga tu radio .

#define channumber 6 //Cuantos canales tiene tu radio???????/How many channels have your radio???
#define filter 10 // Filtro anti salto/ Glitch Filter
int channel[channumber]; //Valores de canales leidos/ readed Channel values
int lastReadChannel[channumber]; //Ultima lectura obtenida/ Last  values readed
int conta=0; //Contador/couter


void setup()
{
  Serial.begin(9600); //Iniciamos com serial/ Serial Begin
  pinMode(4, INPUT); //Patita 4 como entrada / Pin 4 as input
  pinMode(13, OUTPUT); // Led pin 13
}

void loop()
{

  if(pulseIn(4, HIGH) > 3000) //Si el pulso del pin 4 es > 3000 usegundos continua /If pulse > 3000 useconds, continues
  {
    for(int i = 0; i <= channumber-1; i++) //lee los pulsos de los canales / Read the pulses of the channels
    {
	channel[i]=pulseIn(4, HIGH);
    }
    for(int i = 0; i <= channumber-1; i++) //Promedia los pulsos/Average the pulses
    {
	if((channel[i] > 2000) || (channel[i] <100))//Si se pasa del rango envia ultimo pulso/ If channel > max range, chage the value to the last pulse
	{
	 channel[i]= lastReadChannel[i];
	}
	else
	{
	channel[i]=(lastReadChannel[i]+channel[i])/2; //Promedio el pulso pasado con el nuevo pulso/Average the last pulse eith the current pulse
	conta++; //Incrementa el contador/ increment counter
	}
    }

    }
    if(conta > filter)//Si el contador es mayor al filtro imprime valores/ If counter is > than filter, then prints values
    {
	for(int i = 0; i <= channumber-1; i++) //Ciclo para imprimir valores/Cycle to print values
	{
	  Serial.print("CH"); //Canal/Channel
	  Serial.print(i+1); // Numero del canal / Channel number
	  Serial.print(": "); // que te importa
	  Serial.println(channel[i]);
	  lastReadChannel[i]=channel[i];
	}
	if(channel[4] > 1000) //si el canal 5 tiene un rango mayor a 500 enciende el LED/ If channel 5 is > than 500 turn on the led
	{
	  digitalWrite(13, HIGH);
	}
	else
	{
	  digitalWrite(13, LOW);//Si no lo apaga/If not turn it off
	}
	delay(400); //Delay
	conta=0;//Reinicia el contador/ Restart couter.
    }
  }

a ver si te encuentro lo otro ...

// ******************
// rc functions
// ******************
#define MINCHECK 1100
#define MAXCHECK 1900

static uint8_t pinRcChannel[8] = {ROLLPIN, PITCHPIN, YAWPIN, THROTTLEPIN, AUX1PIN,AUX2PIN,CAM1PIN,CAM2PIN};
volatile uint16_t rcPinValue[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; // interval [1000;2000]
static int16_t rcData[8] ; // interval [1000;2000]
static int16_t rcCommand[4] ; // interval [1000;2000] for THROTTLE and [-500;+500] for ROLL/PITCH/YAW 
static int16_t rcHysteresis[8] ;
static int16_t rcData4Values[8][4];

static uint8_t rcRate8;
static uint8_t rcExpo8;
static float rcFactor1; 
static float rcFactor2;

// ***PPM SUM SIGNAL***
#ifdef SERIAL_SUM_PPM
static uint8_t rcChannel[8] = {SERIAL_SUM_PPM};
#endif
volatile uint16_t rcValue[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; // interval [1000;2000]

// Configure each rc pin for PCINT
void configureReceiver() {
  #ifndef SERIAL_SUM_PPM
    for (uint8_t chan = 0; chan < 8; chan++)
      for (uint8_t a = 0; a < 4; a++)
        rcData4Values[chan][a] = 1500; //we initiate the default value of each channel. If there is no RC receiver connected, we will see those values
    #if defined(PROMINI)
      // PCINT activated only for specific pin inside [D0-D7]  , [D2 D4 D5 D6 D7] for this multicopter
      PORTD   = (1<<2) | (1<<4) | (1<<5) | (1<<6) | (1<<7); //enable internal pull ups on the PINs of PORTD (no high impedence PINs)
      PCMSK2 |= (1<<2) | (1<<4) | (1<<5) | (1<<6) | (1<<7); 
      PCICR   = 1<<2; // PCINT activated only for the port dealing with [D0-D7] PINs
    #endif
    #if defined(MEGA)
      // PCINT activated only for specific pin inside [A8-A15]
      DDRK = 0;  // defined PORTK as a digital port ([A8-A15] are consired as digital PINs and not analogical)
      PORTK   = (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) | (1<<6) | (1<<7); //enable internal pull ups on the PINs of PORTK
      PCMSK2 |= (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) | (1<<6) | (1<<7);
      PCICR   = 1<<2; // PCINT activated only for PORTK dealing with [A8-A15] PINs
    #endif
  #else
    PPM_PIN_INTERRUPT
  #endif
}

#ifndef SERIAL_SUM_PPM
ISR(PCINT2_vect) { //this ISR is common to every receiver channel, it is call everytime a change state occurs on a digital pin [D2-D7]
  uint8_t mask;
  uint8_t pin;
  uint16_t cTime,dTime;
  static uint16_t edgeTime[8];
  static uint8_t PCintLast;

  #if defined(PROMINI)
    pin = PIND;             // PIND indicates the state of each PIN for the arduino port dealing with [D0-D7] digital pins (8 bits variable)
  #endif
  #if defined(MEGA)
    pin = PINK;             // PINK indicates the state of each PIN for the arduino port dealing with [A8-A15] digital pins (8 bits variable)
  #endif
  mask = pin ^ PCintLast;   // doing a ^ between the current interruption and the last one indicates wich pin changed
  sei();                    // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
  PCintLast = pin;          // we memorize the current state of all PINs [D0-D7]

  cTime = micros();         // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
  
  // mask is pins [D0-D7] that have changed // the principle is the same on the MEGA for PORTK and [A8-A15] PINs
  // chan = pin sequence of the port. chan begins at D2 and ends at D7
  if (mask & 1<<2)           //indicates the bit 2 of the arduino port [D0-D7], that is to say digital pin 2, if 1 => this pin has just changed
    if (!(pin & 1<<2)) {     //indicates if the bit 2 of the arduino port [D0-D7] is not at a high state (so that we match here only descending PPM pulse)
      dTime = cTime-edgeTime[2]; if (900<dTime && dTime<2200) rcPinValue[2] = dTime; // just a verification: the value must be in the range [1000;2000] + some margin
    } else edgeTime[2] = cTime;    // if the bit 2 of the arduino port [D0-D7] is at a high state (ascending PPM pulse), we memorize the time
  if (mask & 1<<4)   //same principle for other channels   // avoiding a for() is more than twice faster, and it's important to minimize execution time in ISR
    if (!(pin & 1<<4)) {
      dTime = cTime-edgeTime[4]; if (900<dTime && dTime<2200) rcPinValue[4] = dTime;
    } else edgeTime[4] = cTime;
  if (mask & 1<<5)
    if (!(pin & 1<<5)) {
      dTime = cTime-edgeTime[5]; if (900<dTime && dTime<2200) rcPinValue[5] = dTime;
    } else edgeTime[5] = cTime;
  if (mask & 1<<6)
    if (!(pin & 1<<6)) {
      dTime = cTime-edgeTime[6]; if (900<dTime && dTime<2200) rcPinValue[6] = dTime;
    } else edgeTime[6] = cTime;
  if (mask & 1<<7)
    if (!(pin & 1<<7)) {
      dTime = cTime-edgeTime[7]; if (900<dTime && dTime<2200) rcPinValue[7] = dTime;
    } else edgeTime[7] = cTime;
  #if defined(MEGA)
    if (mask & 1<<0)    
      if (!(pin & 1<<0)) {
        dTime = cTime-edgeTime[0]; if (900<dTime && dTime<2200) rcPinValue[0] = dTime; 
      } else edgeTime[0] = cTime; 
    if (mask & 1<<1)      
      if (!(pin & 1<<1)) {
        dTime = cTime-edgeTime[1]; if (900<dTime && dTime<2200) rcPinValue[1] = dTime; 
      } else edgeTime[1] = cTime;
    if (mask & 1<<3)
      if (!(pin & 1<<3)) {
        dTime = cTime-edgeTime[3]; if (900<dTime && dTime<2200) rcPinValue[3] = dTime;
      } else edgeTime[3] = cTime;
  #endif
}

#else 
void rxInt() {
  uint16_t now,diff;
  static uint16_t last = 0;
  static uint8_t chan = 0;

  now = micros();
  diff = now - last;
  last = now;
  if(diff>5000) chan = 0;
  else {
    if(900<diff && diff<2200 && chan<8 ) rcValue[chan] = diff;
    chan++;
  }
}
#endif

uint16_t readRawRC(uint8_t chan) {
  uint16_t data;
  uint8_t oldSREG;
  oldSREG = SREG;
  cli(); // Let's disable interrupts
  #ifndef SERIAL_SUM_PPM
    data = rcPinValue[pinRcChannel[chan]]; // Let's copy the data Atomically
  #else
    data = rcValue[rcChannel[chan]]; 
  #endif
  SREG = oldSREG;
  sei();// Let's enable the interrupts
  return data; // We return the value correctly copied when the IRQ's where disabled
}
  
void computeRC() {
  static uint8_t rc4ValuesIndex = 0;
  uint8_t chan,a;

  rc4ValuesIndex++;
  for (chan = 0; chan < 8; chan++) {
    rcData4Values[chan][rc4ValuesIndex%4] = readRawRC(chan);
    rcData[chan] = 0;
    for (a = 0; a < 4; a++)
      rcData[chan] += rcData4Values[chan][a];
    rcData[chan]= (rcData[chan]+2)/4;
    if ( rcData[chan] < rcHysteresis[chan] -4)  rcHysteresis[chan] = rcData[chan]+2;
    if ( rcData[chan] > rcHysteresis[chan] +4)  rcHysteresis[chan] = rcData[chan]-2;
  }
  #if defined(FORCE_LEVEL)
    rcData[AUX1] = 2000;
  #endif
}

los valores de los canales los guarda en rcData[chan] siendo chan el número de canal .

Si quieres hacer el quadcopter y no tienes mucha idea de programacion lo vas a tener complicado. mirate el wiicopter , funcina muy bien , los sensores son baratos ( los de la wii) y ya tienes el codigo hecho, ademas un software para ver el estado de los sensores y motores, posicion del aparato etc.

un saludo

Ya he conseguido leer un dato proporcional a la acción sobre la emisora, gracias por todo y disculpa las molestias SrDonGato la inforamación que me has dado me ha sido muy útil para solucionar el problema.

PD: Gracias también a los demás por vuestras aportaciones.

SrDonGato, te veo puesto en el tema, cuando menos lo has usado y practicado. Aprovecho para hacerte unas consultas. Decirte que ahora estoy escribiendo desde un móvil y no veo las ventanas de código enteras (no me aparecen las barras de desplazamiento).

Entiendo que el primer código que has puesto lee PPM y el segundo no tengo claro si es el del proyecto wiicopter y por tanto está preparado para leer tanto PPM como PWM tal como decías más arriba.

Además de la diferencia de necesitar un sólo pin para PPM (ventaja para este método) pero no tener que acceder al interior del receptor y localizar la señal PPM en el caso de las PWM (ventaja para este otro método), ¿sabes o has notado si uno u otro método tiene más precisión, fiabilidad, o menor exigencia del arduino?

Sobre el proyecto del wiicopter comentas que lo hiciste. Una duda. ¿Todos los cálculos se realizan sobre una sóla tarjeta arduino? Entiendo que en este proyecto hacen todo, el IMU con los acelerómetros y gyros aplicando no sé si filtro Kalman o sistema de cosenos directores, lectura de canales RC y cálculos PID. No sé si me dejo algún otro cálculo que exija en gran medida a arduino.

adrib:
disculpa las molestias SrDonGato

No son mmolestias atrib ¡¡ mas que ponemos soluciones y no las miráis, estamos para ayudar :slight_smile: para cuando querais

Cheyenne, la placa de arduino hace todo, lee la radio, lee los sensores a traves de I2C, los acelerometros, giroscopos, sensores barométricos etc etc.

el codigo tiene la particularidad de que es configurable para cualquier modelo, activas y desactivas opciones al principio del codigo segun lo tengas construido, tiene en cuenta las tensiones de las celdas de las lipos, un pequeño zumbador etc.

solo la placa de aruino hace todo, todos los calculos y demas. hay muchos videos en youtube y puedes ver los resultados de como vuelan. yo aun no he conseguido poner bien los parametros pid bien, ademas lo tengo parado desde que se me rompieron las helices, estoy esperando un nuevo pedido porque solas me salen por un ojo la cara.

Sin saber mucho de programacion ( yo tampoco soy un gurú) es fácil el montaje y la configuracion del mismo a traves el programa que hn hecho a tal efecto, y puedes ver los valores de las configuraciones, los valores de los sticks y los valores comandados a los motores.

se han currado mucho el codigo .

un saludo ¡

Veo mucha confusión con el tema del PWM y el radio control, a ver si arrojo un poco de luz sobre esto hasta llegar al capítulo de mi curso en el que lo explicaré con todo lujo de detalles.

En un sistema de RC (Radio Control) las PWM se utilizan SOLO entre el variador y el motor, para NADA más.

Tampoco se utiliza PPM. Esto es algo parecido, pero no es igual.

Toda la comunicación física entre elementos de RC (receptor, servos, variadores, giróscopos, mezcladores, inversores de servos, etc) se hace por un particular tren de pulsos: un pulso de entre 1 y 2 milisegundos, una pausa de 20 milisegundos. Ya está, no tiene ninguna otra complicación.

Para leer las señales que emite un receptor hacia sus periféricos basta con programar un control de tiempos con las entradas digitales o utilizar el comando pulseIn(), aunque este último puede dar problemas a la hora de leer varios canales a la vez.

Por favor, no me volvais a mezclar PWMs con servos o receptores, que hace que me duela la vista.

Un saludo.

josemanu:
Veo mucha confusión con el tema del PWM y el radio control, a ver si arrojo un poco de luz sobre esto hasta llegar al capítulo de mi curso en el que lo explicaré con todo lujo de detalles.

En un sistema de RC (Radio Control) las PWM se utilizan SOLO entre el variador y el motor, para NADA más.

Tampoco se utiliza PPM. Esto es algo parecido, pero no es igual.

Toda la comunicación física entre elementos de RC (receptor, servos, variadores, giróscopos, mezcladores, inversores de servos, etc) se hace por un particular tren de pulsos: un pulso de entre 1 y 2 milisegundos, una pausa de 20 milisegundos. Ya está, no tiene ninguna otra complicación.

Si parece que hay un error de conceptos ....

al variador como a los servos hay que entregarle una señal PWM par hacerlos funcionar, el variador entregará al motor una fuerza-velocidad proporcional al ancho del pulso que se le entrega, en el caso de los servos la posicion del servo es proporcional al ancho del pulso que se le entrega.
La señal PWM en RC tiene una frecuencia de 50 hz, aunque esta frecuencia puede ser variable, es decir tiene un periodo de 20 milisegundos., y no una pausa como dice josemanu, en esos 20 milisegundos en PPM tiene que haber un pulso por cada canal que tengamos en la emisora.
Las emisoras emiten en PPM, es el receptor el que se ocupa de convertir las señales PPM en PWM, una por cada canal.

En un sistema de RC (Radio Control) las PWM se utilizan SOLO entre el variador y el motor, para NADA más.

No es cierto del todo, la señal que se entrega al motor varia en frecuencia y tensión, en algunos casos se utiliza una señal PWM interna para generar una onda senoidal, pero son técnicas de paso de D/A.
puedes ver un articulo de tecnicas para el control de los mismos en Domain Registered at Safenames y Domain Registered at Safenames

Os dejo un ejemplo de como obtener la señal PPM de un receptor RC. En algunos casos en necesaria amplificarla para utilizarla.

http://forum.mikrokopter.de/topic-2747.html

y una gráfica para entender como pasa el chip del receptor de señal PPM a PWM y como se generan, como se puede ver las señales PWM de todos los canales no empiezan a la misma vez, sino que se concatenan, cuando hay un flanco de bajada del canal anterior empieza el canal siguiente, aun así mantienen todas la misma frecuencia.

Espero que todo se haya aclarado. Un saludo

Coincido con lo explicado por SrDonGato, funcionamiento de emisión PPM, conversión a PWM a cada canal a frecuencia 50 Hz.

Muy interesantes los PDFs sobre generación de señales PWM para motores brushless... pero esas señales las genera el variador y se las manda al motor.

al variador como a los servos hay que entregarle una señal PWM par hacerlos funcionar

No, no y no. Es lo que intentaba aclarar: una señal modulada por ancho de pulso (PWM) sirve para "simular" una señal analógica mediante una digital... y eso no funciona ni con servos ni con receptores.

Una señal de Modulación por Posición de Pulso (PPM) es un tipo de modulación en la cual una palabra de R bits es codificada por la transmisión de un único pulso que puede encontrarse en alguna de las 2M posiciones posibles.... se parece mucho, pero no es lo mismo. Por mucho que en el foro de mikrocopter lo llamen PPM no es PPM.

Cometí un error en mi explicación: no es pulso, pausa, pulso, pausa, etc.....
Es cada 20 milisegundos un flanco de subida, un pulso que dura entre 1 y 2 milisegundos, y se repite. No es PWM por que no se simula una salida analógica y no es PPM por que el pulso emitido está siempre en la misma posición, varía su anchura no su posición. Para ser PPM el pulso siempre tiene la misma anchura pero varía su posición.

Gráfico:

Puede ser confundido con una señal PWM por que modula en ancho del pulso dentro de sus intervalos de 20ms, pero no es lo mismo ni se le parece a la señal PWM que generan los pines 3, 5, 6, 9, 10 y 11. De hecho para controlar un servo o un variador no se necesita utilizar esos pines, se puede utilizar cualquiera.

Buscando en la Wikipedia resulta que también llaman PWM a la señal de control de RC, aunque especifican que es una señal PWM de 50hz. Vale, quizá sea correcto, no quiero entrar en tanto detalle.

La cuestión es que la señal de control de RC si la llamamos PWM se confunde directamente con la señal que generan los pines PWM de Arduino al dar una orden analogWrite().

Por eso (no por nada más) insisto e insistiré siempre en no llamar PWM a la señal de control en RC. Ateniendonos a definiciones en mucho más correcto decir que la señal de RC es un "Tren de Pulsos".

Uf..... a ver si ahora queda claro.

http://www.endurance-rc.com/ppmtut.php

:wink: