[SOLUCIONADO] Sensor Ultrasónico PING))) y dos Servos de 360 Grados.

Buenas.

Soy nuevo con Arduino y tengo unos problemas con el código que me están inquietando un poco. Les comento.
Tengo una placa Arduino Mega 2560, un sensor PING))) de Parallax y dos servomotores de 360 grados GWS S35/STD.

Lo que estoy intentando es hacer girar los servos de forma continuada mientras el "robot" esté a más de 20 cm de un obstaculo
y luego hacerlo retroceder, eso por ahora, ya cuando solvente los numerosos problemas pues hacerlo girar por el camino más
corto y que continúe, para que ande y ande sin tropezar.

Al hacer las pruebas con un servo todo marcha perfecto, responde superápido y ira en sentido contrario de inmediato. El problema
llega al conectar el segundo servo. El sensor deja de funcionar y como tengo puesto un If que dice que si los centímetros son menos
de 20 gire hacia atrás pues giran los dos, pero nunca dejan de girar en ese sentido porque el sensor no funciona y en el terminal
observo que siempre imprime 0 cm. Pongo en comentarios las lineas referentes al segundo servo y el sensor vuelve a funcionar.

#include <Servo.h>

//Declaramos dos objetos Servo, uno por cada motor
Servo servo1;
Servo servo2;

//pingPin = 7 para el PING)))
const int pingPin = 7;
long duration, cm;

void setup() 
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  servo1.attach(3);
  servo2.attach(5);
}

void loop()
{ 
  //Este código del ejemplo PING del soft de Arduino 0022
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);

  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
 
  cm = microsecondsToCentimeters(duration);
  
  //Esta función para hacer girar el servo en una dirección o en otra.
  //servo(servo1);
  //servo(servo2);
  
  //Esta función hace girar los dos servos, en un intento por ver si así 
  //no se fastidiaba el programa.
  //servoDos(servo1, servo2);
  
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(150);
}

void servo(Servo ser)
{
  if(cm >= 20)
  {
    ser.write(0);
  }
  if(cm <= 20)
  {
     ser.write(180);
  }
}

void servoDos(Servo ser1, Servo ser2)
{
  if(cm >= 20)
  {
    ser1.write(0);
    ser2.write(180);
  }
  if(cm <= 20)
  {
     ser1.write(180);
     ser2.write(0);
  }
}

long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

Aquí el código que empleo.

Otra cosa que he observado es que cuando están girando los dos servos uno va más rápido que el otro,
no mucho más, pero si lo suficiente como para que cuando monte el bot gire en círculos, ya que por
ahora solo pruebo las piezas poco a poco para que al montar vaya todo bien.

Si alguien pudiera ayudarme le estaría muy agradecido.

Gracias por adelantado.

Hola royyyyy.

Yo también tuve problemas cuando practicaba con el sensor PING de Parallax y un servo. La conclusión a la que llegué es que la librería Servo debe de utilizar interrupciones que le sientan muy mal al código del PING. Según cuando la librería produce la interrupción dará un resultado u otro al sensor.

Yo lo solucioné no utilizando la librería Servo. Implementé el código para controlar los servos con digitalWrite() y con millis() ejecuto el código cada 20 ms.

Muchas gracias Cheyenne!!! Lo voy a probar y lo comento!!!

Yo lo solucioné no utilizando la librería Servo. Implementé el código para controlar los servos con digitalWrite() y con millis() ejecuto el código cada 20 ms.

Me podrías mostrar como es el código que dices? Es que lo estoy probando y no doy con la tecla!

Gracias.

El código no está muy bien estructurado ya que sólo lo empleé para hacer unas pruebas. He ido borrando líneas de código de más pruebas que iba haciendo y me queda esto. De todos modos como es corto creo que no tendrás problema en entenderlo. Si no ves algo me lo dices.

Por cierto, lo que yo hacía era posicionar el servo en función de un rango de medidas que tomaba con el sensor PING.

#define BAUD 115200   

int TiempoMiServo;
int interval = 20;
int servoPin = 8;

const int pingPin = 7;
long oldduration;
long duration;
long mm;
unsigned long previousMillis = 0;


void setup() {
  Serial.begin(BAUD);
 
  pinMode(servoPin, OUTPUT); 

}

void loop () {

   unsigned long currentMillis = millis();

// Voy tomando lecturas del sensor cada 20 ms

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;

  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  
  pinMode(pingPin, INPUT);

  duration = pulseIn(pingPin, HIGH);

   
 long diferencia = oldduration - duration;
 diferencia = abs(diferencia);
 

   long mm = 10 * duration / 29 / 2;
   
     Input = mm;

        Serial.print(mm);
        Serial.print(" - ");
        Serial.print(duration);
        Serial.print(" - ");
        Serial.print(diferencia);
        Serial.print(" - ");
        Serial.println(Output);
        
oldduration = duration;
TiempoMiServo = map(duration, 100, 2100, 800, 2300);
TiempoMiServo = constrain (TiempoMiServo, 800, 2300);

digitalWrite(servoPin, HIGH);
delayMicroseconds(TiempoMiServo);
digitalWrite(servoPin, LOW);

  }
  
}

Pues no me ha funcionado... ya que haciendo esto

int TiempoMiServo;
int interval = 20;
int servoPin = 3;

const int pingPin = 7;
long duration;
long cm;
unsigned long previousMillis = 0;


void setup() 
{
  Serial.begin(9600);
  pinMode(servoPin, OUTPUT); 
}

void loop () 
{

   unsigned long currentMillis = millis();

  // Voy tomando lecturas del sensor cada 20 ms

  if(currentMillis - previousMillis > interval) 
  {
    previousMillis = currentMillis;

    pinMode(pingPin, OUTPUT);
    digitalWrite(pingPin, LOW);
    delayMicroseconds(2);
    digitalWrite(pingPin, HIGH);
    delayMicroseconds(5);

    digitalWrite(pingPin, LOW);
    pinMode(pingPin, INPUT);
    duration = pulseIn(pingPin, HIGH);

    cm = microsecondsToCentimeters(duration);

    Serial.print(cm);
    Serial.print("cm");
    Serial.println();
    
    TiempoMiServo = map(duration, 100, 2100, 800, 2300);
    TiempoMiServo = constrain (TiempoMiServo, 800, 2300);

    digitalWrite(servoPin, HIGH);
    delayMicroseconds(TiempoMiServo);
    digitalWrite(servoPin, LOW);
  }
}


long microsecondsToCentimeters(long microseconds)
{
   // La velocidad del sonido es 340 m / s ó 29 microsegundos por centímetro.
   // El ping viaja de ida y vuelta, así que para encontrar la distancia del
   // Objeto que tomar la mitad de la distancia recorrida.

  return microseconds / 29 / 2;
}

no resulta... El PING))) vuelve a marcar 0cm hasta el infinito y más allá. Así que con digitalWrite() y con millis() no me va el sensor
igual que antes, solo que por lo menos antes con un servo funcionaba, y ahora ni con un servo funciona. O lo estoy haciendo mal o
no funciona... que probablemente sea lo 1º, lo estoy haciendo mal...

Cheyenne muchas gracias por responder, ya que has sido el último.. :stuck_out_tongue: Que estoy haciendo mal?

Entonces... sigo necesitando ayuda!!! He vuelto al código anterior:

#include <Servo.h>

//Declaramos dos objetos Servo, uno por cada motor
Servo servo1;
Servo servo2;

//pingPin = 7 para el PING)))
const int pingPin = 7;
long duration, cm;

void setup() 
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  servo1.attach(3);
  servo2.attach(5);
}

void loop()
{ 
  //Este código del ejemplo PING del soft de Arduino 0022
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);

  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
 
  cm = microsecondsToCentimeters(duration);
  
  //Esta función para hacer girar el servo en una dirección o en otra.
  //servo(servo1);  //Con uno solo funciona, el PING))) funciona perfectamente.
  //servo(servo2);  //Con los dos el PING))) marca 0cm.
  
  //Esta función hace girar los dos servos, en un intento por ver si así 
  //no se fastidiaba el programa. Pero solo giran en un sentido, ya que
  //como el ping marca 0cm giran en el If de menos de 20cm.
  //servoDos(servo1, servo2);
  
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(150);
}

void servo(Servo ser)
{
  if(cm >= 20)
  {
    ser.write(0);
  }
  if(cm <= 20)
  {
     ser.write(180);
  }
}

void servoDos(Servo ser1, Servo ser2)
{
  if(cm >= 20)
  {
    ser1.write(0);
    ser2.write(180);
  }
  if(cm <= 20)
  {
     ser1.write(180);
     ser2.write(0);
  }
}

long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

AYUDA POR FAVOR!!!

En tu caso piensa que con el sistema que te propongo el código sólo es válido para que el sensor PING tome lecturas a distancias de unos 3 metros como máximo. Si la distancia es mayor la función pulseIn sobre el pingPin durará más de los 20 ms para los que hemos establecido el ciclo (que es el tiempo necesario para los servos). Si esta limitación de distancia no es problema yo te recomiendo altamente que sigas trabajando con base al código que te he puesto.

La prueba es bien sencilla, yo hice lo siguiente. Sobre el código que te he puesto fui imprimiendo todos los valores del sensor, a mí me funcionaba perfecto. Añadí un pequeño código que hacía que al cabo de 10 segundos de empezar, activaba la librería Servo. La resultante era instantánea, las lecturas empezaban a fallar dando de vez en cuando lecturas nulas. Y la cantidad de lecturas nulas se va incrementando cuanto más distancia trata de medir el sensor PING, claro, interpreto que es porque hay más probabilidad de que choquen las interrupciones que crea la librería Servo con la medición del tiempo en el sensor PING.

Yo lo que te recomiendo es que sobre el código que has probado, el que te has basado en lo que te he pasado, pruebes a dejar sólo las líneas de la medición del sensor PING con su impresión en pantalla. Cuando compruebes que eso está bien, ves añadiendo el resto del código para el control del servo.

Hola Cheyenne.

Lo primero que hice fue copiar y pegar tu código. Y si me funcionaba el sensor, pero el servo se mueve a trompicones muy lentos..
Luego lo que hice fue ir acoplando tu código al que yo usaba, pero el sensor no funcionaba, así que fui comentando lineas para ver
donde fallaba.
Si comento este código

    TiempoMiServo = map(duration, 100, 2100, 800, 2300);
    TiempoMiServo = constrain (TiempoMiServo, 800, 2300);

ni se mueve el servo, obviamente, y el sensor no funciona. Siempre marca nulo.

Luego descomento las dos lineas de código y comento estas lienas:

    /*
         digitalWrite(servoPin, HIGH);
         delayMicroseconds(TiempoMiServo);
         digitalWrite(servoPin, LOW);
    */

Y el sensor funciona, pero claro, el servo no funciona. Así que luego de todo eso vine al foro y escribí el
anterior post. Con lo que me quedo es... con la librería servo no puedo poner dos servos por las interrupciones
que intenté paliar con cli() y sei()... no conseguí nada. Con digitalWrite() y millis() no puedo poner ni un servo
y en ocasiones ni el sensor... Pero claro, he visto miles de video sque la gente usa sus 3 o 4 servos y su sensor
y les va bien, así que se que se puede... pero yo no lo consigo!!!

Una cosa tonta, ¿cómo alimentas los servos? Ten en cuenta que no se pueden alimentar directamente desde el Arduino, tienes que ponerles una fuente externa.

Los alimento directamente con Arduino, pero claro, antes de comprar el Arduino Mega 2560 pregunté a los de la tienda, BricoGeek, que tiene bastantes videos de como hacer cosas con Arduino, y me dijeron que no tenía que comprar ninguna otra placa de alimentación, ni de control.. que con Arduino Mega podría conectar bastantes motores más y que no necesitaba ningún suplemento!

Pues no sé si serán todos los problemas pero por descontado que tienes que alimentar los servos con otra fuente externa. De hecho yo también alimentaba un servo de lo más enano con el Arduino y no iba bien. Y eso que el servo lo manejaba en vacío. Fue poner otra alimentación y funcionó perfectamente. Lo hice porque al preguntarlo en este mismo foro no recuerdo quién me lo aseguró que era así.

Los de BricoGeek probablemente se referían a que no necesitabas ningún shield o similar. Pero los servos, y más si ya los estás utilizando con carga, tienes que alimentarlos con otra fuente.

Una vez hecho lo de la fuente nos cuentas qué código es el que te funciona.

Ok lo probaré.

Cuando dices otra fuente de alimentación es por ejemplo la caja de 4 pilas de 1'5V a los servos? 6V? A cada servo?... porque esto me pilla en pelotas!

Muchas gracias Cheyenne!

Alimentas los dos servos con las 4 pilas a 6 V. Y el control de los servos con cada una de las dos salidas de Arduino. Y no te olvides que tienes que unir el GND del Arduino con el negativo de la alimentación de los servos.

royyyyy:
Cuando dices otra fuente de alimentación es por ejemplo la caja de 4 pilas de 1'5V a los servos? 6V? A cada servo?... porque esto me pilla en pelotas!

He tenido un dejavu http://arduino.cc/forum/index.php/topic,74723.0.html en este post no habla de servos pero la filosofía es muy parecida y las dudas creo que eran casi iguales. Salu2

Pues gracias a los dos! Voy a probar lo que me habéis dicho, iré a por pilas y a montarlo y comento que tal me ha ido!

Gracias.

Pues listo. Está solucionado.

Gracias a esto:

Una cosa tonta, ¿cómo alimentas los servos? Ten en cuenta que no se pueden alimentar directamente desde el Arduino, tienes que ponerles una fuente externa.

ya he resuelto el problema. He alimentado el segundo servo con 4 pilas de 1'5V, he conectado masa a Arduino y he probado el código del que partía desde un principio, con la función que tomaba como parámetros dos objetos Servos.

El código final es el siguiente:

#include <Servo.h>

//Declaramos dos objetos Servo, uno por cada motor
Servo servo1;
Servo servo2;

//pingPin = 7 para el PING)))
const int pingPin = 7;
long duration, cm;

void setup() 
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  servo1.attach(3);
  servo2.attach(5);
}

void loop()
{ 
  //Este código del ejemplo PING del soft de Arduino 0022
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);

  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
 
  cm = microsecondsToCentimeters(duration);
  
  //Esta función hace girar los dos servos
  servoDos(servo1, servo2);
  
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(150);
}

void servoDos(Servo ser1, Servo ser2)
{
  if(cm >= 20)
  {
    ser1.write(0);
    ser2.write(180);
  }
  if(cm <= 20)
  {
     ser1.write(180);
     ser2.write(0);
  }
}

long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

Así que nada, muchas gracias por haberme ayudado, Cheyenne, "ere un maquina pixa"!!!

Me alegro que lo hayas solucionado. Indicas que alimentas el segundo servo con las pilas, te recomiendo altamente que alimentes con las pilas los dos servos, no tienes ninguna necesidad de apurar con consumos al mega.

Por último decir que probaré tu código ya que con la librería servo a mí nunca me funcionó de manera suave.

Ok, lo haré así, en la próxima revisión y montaje del bot alimentaré los dos servos con las pilas y la placa con una pila de petaca de 9V, por la entrada de corriente que está al lado de la entrada USB...

Tal y como lo tengo ahora, con el código que puse esto es lo que tengo: - YouTube

Está mu cutre aún pero espero a solventar todos los problemas para terminar el montaje.

Un saludo.