No logro sincronizar el movimiento de dos servos

Hola a todos, es la primera vez que participo, espero no hacerlo mal.

Estoy tratando de mover dos servos, uno para vertical y otro para horizontal, de forma sincronizada.

Les paso las variables de los valores "HORIZONTAL", "VERTICAL" y "VELOCIDAD" respectivamente y para la sincronización he anidado un ciclo FOR dentro de otro, para poder dar valores diferentes a cada uno de los dos servos.

La intención era que antes de terminar uno de los ciclos lo fuera combinando con el otro.

Lo cierto es que no funciona como esperaba, primero mueve todo el ciclo de un servo y luego el otro ciclo del otro servo.

¿Me podéis ayudar con esto?

Gracias.

void servos(float h, float v, float s){ // La función recibe valores para posición horizontal, vertical y velocidad
  int h_calc = h*((h_max-h_min)/100)+h_min; //Hallamos un valor de h porcentual convertido a posición del servo horizontal
  int v_calc = v*((v_max-v_min)/100)+v_min; //Hallamos un valor de v porcentual convertido a posición del servo vertical

if(h_actual <= h_calc && v_actual <= v_calc) // Verificamos si la posición objetivo es mayor que la actual para horizontal y vertical
   { 
    for(int nh = h_actual; nh <= h_calc; nh +=1){
      for(int nv = v_actual; nv <= v_calc; nv +=1){
              servo_h.write(nh);
              servo_v.write(nv);
              h_actual = nh;
              v_actual = nv;
              delay(s);
          }
         }
    }else if(h_actual <= h_calc && v_actual >= v_calc)  // Verificamos si la posición objetivo es mayor que la actual para horizontal y menor para vertical
    {
     for(int nh = h_actual; nh <= h_calc; nh +=1){
      for(int nv = v_actual; nv >= v_calc; nv -=1){
              servo_h.write(nh);
              servo_v.write(nv);
              h_actual = nh;
              v_actual = nv;
              delay(s);
          }
         }
    }else if(h_actual >= h_calc && v_actual >= v_calc)  // Verificamos si la posición objetivo es menor que la actual para horizontal y vertical
    {
     for(int nh = h_actual; nh >= h_calc; nh -=1){
      for(int nv = v_actual; nv >= v_calc; nv -=1){
              servo_h.write(nh);
              servo_v.write(nv);
              h_actual = nh;
              v_actual = nv;
              delay(s);
          }
         }
    }else if(h_actual >= h_calc && v_actual <= v_calc)  // Verificamos si la posición objetivo es menor que la actual para horizontal y mayor para vertical
    {
     for(int nh = h_actual; nh >= h_calc; nh -=1){
      for(int nv = v_actual; nv <= v_calc; nv +=1){
              servo_h.write(nh);
              servo_v.write(nv);
              h_actual = nh;
              v_actual = nv;
              delay(s);
          }
         }
    }
  delay(100);
}

Si te interpreté bien puede que te sirva

Muchas gracias @MaximoEsfuerzo funciona perfectamente.

No entiendo porque en el código que he subido no funcionan al mismo tiempo, he incorporado el "delay(2);" pero no es el motivo.

Ya entiendo que en mi código coge un valor del primer FOR y ejecuta el FOR anidado completo, por eso lo percibo.

Entiendo que hallas un multiplicador que se puede aplicar a cada uno de ellos y así meterlo todo en un único FOR.

Muchas gracias, me parece muy interesante, me lo estudio, lo aplico y te digo.

:+1:

Entiendo que:

  int hdir = hdif < 0 ? -1 : 1;

Es un operador condicional, parecido a un IF oero más compacto y elegante, para decalarar la variable como "1" O "-1" en función de si es negativa o positiva.

Genial.

Muchas gracias.

1 Like

Tu código no funciona por los for() anidados.
Lo que hice fue calcular cual servo tiene el mayor desplazamiento (pasos).
Como puede ser que un servo deba retroceder mientras el otro avanza, guardo el signo de la diferencia entre la posición inicial y la final.
Luego el lazo for() se encarga de mover los servos la cantidad de pasos calculados.

servo_x.write(oldxpos + xdif / (float)pasos * xdir * i );

Mueve el servo a la diferencia entre la posición anterior y la inicial (o sea xdif) dividida por la cantidad de pasos y multiplicada por el signo de la diferencia (o sea xdir) y por i todo sumado a la posición inicial (o sea oldxpos).
Pongo x porque simboliza a ambos servos.

Fijate que cambié el código del ejemplo, creo que ahora te es más útil.

Exacto es como if()/else compacto.
Y como no hay función que devuelva el signo (como en otros lenguajes) lo calculo de esa manera.

Esta operación me está volviendo un poco loco.

Por un lado restas, de la posición anterior, la diferencia entre la posición anterior y la posición objetivo.

Y todo esto lo divides por: el número de pasos mayor (potencialmente la misma diferencia) multiplicado por 1 ó -1 (dependiendo) y multiplicado por "i" que va en aumento hasta alcanzar el valor de pasos mayor (potencialmente la misma diferencia)

¿Es esa la idea? me llama la atención, especialmente multiplicar por "i"

Creo que lo he entendido, lo que haces es calcular un multiplicador que ajusta el valor que se añade en función del objetivo.

Dicho de otra manera si el valor es el de mayor diferencial de los dos, el resultado de:

xdif / (float)pasos * xdir * i 

Será 1 o menos 1.

Mientras que si es el menor será una fracción de 1 que al multiplicarla por el valor mayor de los dos de como resultado el menor.

Es así.
Primero se calcula la distancia que debe moverse cada servo (genéricamente xdif)
Se guarda para cada uno el signo (+ o -) de esa diferencia que indica hacia que lado se debe mover cada servo, si avanza o si retrocede.
Ya guardado el signo, se toma el valor absoluto de cada diferencia y se determina cual es mayor, ese valor es el número de pasos que insume el movimiento de ambos servos. ¿Para qué? Para evitar tener que hacer comparaciones entre cada valor, o sea, evitar algo como esto

if(h_actual <= h_calc && v_actual <= v_calc) {
// ...
} else if(h_actual <= h_calc && v_actual >= v_calc) {
// ...
} else if(h_actual >= h_calc && v_actual >= v_calc) {
// ... etc.

Ahora el lazo for() cuenta los pasos y controla el movimiento, el valor que se escribe a cada servo es (entendemos que x representa h ó v según de que servo se trate):

xdif / (float)pasos

es la distancia que se desplaza un servo por cada paso.
Tipifico pasos como float porque necesito los decimales de la división en el cálculo intermedio aunque el resultado terminará siendo entero.

xdif / (float)pasos * xdir

Multiplica por el "signo" para determinar si el movimiento es de avance o de retroceso, o sea, si la posición destino es mayor o menor que la posición en que está el motor antes de comenzar el movimiento.
Nota: Este cálculo podría hacerse antes de entrar al for() guardando el resultado en una variable porque no cambia el resultado y usar esa variable en lugar de hacer el cálculo en cada paso.

Ahora, si este valor no cambia necesito algo que haga que el motor se mueva, sino se queda detenido.
Sabemos que la posición de partida es

oldxpos

le sumamos el desplazamiento de cada paso

oldxpos + xdif / (float)pasos * xdir

pero falta algo, sumar el desplazamiento acumulado.

Podría haber guardado el resultado en la misma variable oldxpos

oldxpos = oldxpos + xdif / (float)pasos * xdir;

// o en la forma abreviada
oldxpos += xdif / (float)pasos * xdir;

pero me pareció más sencillo directamente multiplicar por el valor de i ya que a matemáticamente es lo mismo ( por ej, desplazarse a la posición 10 + 10 es lo mismo que ir a la 10 * 2 así como desplazarse a la posición 10 + 10 + 10 + 10 + 10 es lo mismo que ir a la posición 10 * 5)
Por lo que juntando todo llegamos a

oldxpos + xdif / (float)pasos * xdir * i 

Y ahora me doy cuenta que el lazo debería ser

for (int i = 1 ; i <= pasos; i++) {

// en lugar de 
// for (int i = 0 ... 

porque para el valor 0 no hay desplazamiento y es una pérdida de tiempo innecesaria.

Espero haber clarificado el asunto aunque estabas muy pero muy bien encaminado. :clap:

Muchas gracias, los novatos lo sudamos :sweat_smile: gracias por la ayuda.

No se qué pasa que el valor negativo, es decir la cuenta descendente no me está funcionando.

El monitor serie me dice que i es -1 pero luego suma positivamente y en está lectura:

Serial.println("v actual: "+String(v_actual)+"movimiento a: "+String(v_actual + v_dif / (float)mayor * v_dir * i)+" hacia v objetivo "+String(v_obj));

(Los nombres están un poco cambiados) me da siempre positivo, quizás debo añadir (float), lo he intentado en v_dir y h_dir pero nada, o me he saltado algo.

También me pasa que llamo 3 veces a la función desde un botón pero solo la activa las dos primeras, aunque entra en la función las tres porque he puesto un Serial.print que si carga pero se queda antes del bucle FOR.

Todo el código igual es un poco demasiado pero puedo subirlo.

Estos son dos extractos de los Print que parecen aclarar por dónde está el error, h es positivo y v negativo, sin embargo crece en ambos:

Cita
19:49:35.935 -> La mayor de las diferencias es: 70 el valor de h es: 1 El valor de v es: -1

19:49:36.033 ->

19:49:36.033 -> h actual: 80.00movimiento a: 81.00 hacia h objetivo 150

19:49:36.098 ->

19:49:36.098 -> v actual: 80.00movimiento a: 81.00 hacia v objetivo 10

19:49:36.163 ->

19:49:36.163 -> h actual: 80.00movimiento a: 82.00 hacia h objetivo 150

19:49:36.229 ->

19:49:36.229 -> v actual: 80.00movimiento a: 82.00 hacia v objetivo 10

Cita

Parece que era un abs:

servo_h.write(h_actual + h_dif / (float)mayor * abs(h_dir) * i ); 

Pues parecía que entraba en el bucle de la última llamada pero por algo no entra, si añado más llamadas, termina todas menos la última.

Me hubieras avisado que ibas a seguir usando el código que no te funciona así no perdía tiempo escribiendo el código ni explicándolo lo más detallado que pude (porque no soy docente).

Hasta me tomé el trabajo de rehacer el código para que fuese cuestión de copiar y pegar la función a la que le puse hasta el mismo nombre que la que tu tienes en el código.

¿Tu has visto que haya h_actual en el cálculo dentro del lazo? ¿Para qué perdí tiempo explicando lo que hace cada parámetro?
Con abs(h_dir) mandas el signo al mismísimo diablo (por ser educado).

Ya te regalé casi un día de mi tiempo, es demasiado.

Saludos

Ante todo siento que te hayas molestado, era la última de mis intenciones con lo que me has ayudado.

El código era más largo y no quería liar a nadie de más, además si no lo incorporo no lo entiendo y no avanzaré ni podré ayudar a nadie.

Más allá del error cometido con abs, lo siento, te agradezco mucho todo el tiempo dedicado y gracias a ti he aprendido unas cuantas cosas.

Siento el mal entendido, espero que entiendas mis motivos y puedas disculparme.

Por mi parte tomo nota y no volvere a cometer el mismo error.

Gracias.

Todo bien.
Saludos

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.