Mover motores haciendo curva de seno

Hola amigos,

tengo una duda respecto a cómo mover unos motores para que su movimiento genere una curva sinuosa.

Estoy utilizando la librería AccelStepper.h para moverlos, hasta ahí OK, soy capaz de darle ordenes y moverlos, según su posición relativa hacer que cambien de dirección.

El problema es que necesito algo concreto, que es hacer la curva de seno.

¿Como podría darle la orden a un motor para que desde posición 0 se mueva a gire hasta 2048, desde 2048 gire hasta -2048 y vuelva a cero.

También querría saber como hacer una orden para cada motor que genere un desfase int offset = 5; entre ellos. No se si se puede hacer mediante una formula, que genere el movimiento.

Este es el código que tengo, lo que hago es de la posición inicial que es todos en 0 "dibujo" con posiciones escritas a mano la curva y despues trato de moverlos, pero hacen lo que no quiero que hagan. (no pongo parte del codigo porque es un proyecto mas amplio y esto es solo una parte concreta, espero lo entendais)

int sube = 2048;
int inicio = 0;
int baja = -2048;
int arriba = 2048;
int abajo = -2048;

void loop(){
if (motores == true){
    formaseno();
}

  if (m1.currentPosition() == 0){
      m1.moveTo(sube);
  }
  if (m3.currentPosition() == 683){
      m3.moveTo(sube);
  }
  if (m5.currentPosition() == 1366){
      m5.moveTo(sube);
  }
  if (m7.currentPosition() == 2048){
      m7.moveTo(baja*2);
  }
  if (m9.currentPosition() == 1366){
      m9.moveTo(baja*2);
  }
  if (m11.currentPosition() == 683){
      m11.moveTo(baja*2);
  }
  
  if (m1.currentPosition() == arriba){
      m1.moveTo(baja*2);
  }
  if (m3.currentPosition() == arriba){
      m3.moveTo(baja*2);
  }
  if (m5.currentPosition() == arriba){
      m5.moveTo(baja*2);
  }
  if (m7.currentPosition() == abajo){
      m7.moveTo(sube*2);
  }
  if (m9.currentPosition() == abajo){
      m9.moveTo(sube*2);
  }
  if (m11.currentPosition() == abajo){
      m11.moveTo(sube*2);
  }
}

void formaseno(){
    m1.moveTo(0);
    m3.moveTo(683);
    m5.moveTo(1366);
    m7.moveTo(2048);
    m9.moveTo(1366);
    m11.moveTo(683);
    motores = false;  
}

Gracias!

He realizado una simulación en un software de 3D donde muevo unos elementos con la siguiente formula.

sin((F+Offset)*Long)*Amp

En la que F se refiere al numero de frames de mi animación ¿se podría hacer algo similar con Millis()?

Offset es el valor que hace que cada punto tenga una diferencia respecto a la otra.
Longitud y Amplitud de onda. Para la Longitud he utilizado 2Pi = 6.2832... y para Amplitud = 30.

Yo lo haría de otro modo.
Usaría millis() y cada X tiempo en una maquina de estados, haria que el servo copie la posicion seno que debe tener entre 2048 y -2048

La tasa de actualización la fijas con tu código y cada X tiempo o intervalo cambia el motor

algo asi...

#define INTERVALO     20     // 20 milisegundos
#define PASOS         20    // supongo que el servo dará N pasos
#define MAXIMO      2048

unsigned long start;
int step = 0;

void setup(){

}

void loop() {

  if (millis() - start > INTERVALO) {
      double angulo =  HALF_PI*step/PASOS;
      if (++step >= PASOS)
          step = 0;
      m1.moveTo(MAXIMO*sin(angulo));
      start = millis();
  }
}

es una idea.. a ver si te sirve

Perdona, pero yo no consigo entender que quieres hacer.

Una senoide es la proyección de un movimiento circular desarrollado en un eje que representa el tiempo.

Es decir que si haces que un motor-1 haga una vuelta completa y al mismo tiempo este motor-1 se esta desplazando en un eje gracias a un motor-2 que gire a la misma velocidad, el eje del motor 1 habrá dibujado una senoide completa.

Porque no explicas un poco más como se han de mover los motores ?

Gracias por las respuestas.

Estoy probando lo que me comenta surbyte, pero no encuentro una lógica en el movimiento del motor.

Intento aclararlo.

Un motor, con una polea y un cable en esta. En el extremo del cable una esfera. Tengo 18 motores. y trato de generar la idea de curva senoide en movimiento con esas 18 esferas moviendose arriba y abajo.

El motor tarda en dar una vuelta completa aproximadamente 8", que serían 2048 pasos, en la realidad son 30cm. Cada motor está separado del otro 19cm.

Sinceramente es para matarte!!! Lo digo en el buen sentido.
por eso siempre pedimos cuenten toda la idea!!

Pasamos de un movimiento para un motor al movimiento de 18 motores
Y si tu código tiene 11 motores o m11 lo que no es lo mismo pero.. costaba mucho decirlo?

Bueno yo te describí el movimiento de un motor.
Obviamente los demas motores estarán defasados X grados respecto del primero.
Asi que usando el mismo criterio, calcula el angulo para los 10 restantes con su defasaje.

Sigue siendo matemáticamente fácil.
Es un seno de 18 bolas movidos solidarias a los motores que las controlan.

Disculpa mi ignorancia matemática. :sob: Quizás estoy bloqueado mentalmente.

Digamos que el motor1 comienza en el paso num 0 y quiero que gire hasta el paso 2048 y cuando llegue ahí hasta -2048 (sentido antihorario)

#define INTERVALO    20     // 20 milisegundos
#define PASOS          20    // supongo que el servo dará N pasos
#define MAXIMO      2048

Teniendo en cuenta esto que me comentabas @surbyte, de que manera debo proceder?
La librería que controla los motores tiene su propia velocidad definida en pasos x sec.

m1.setSpeed(350);
m1.setAcceleration(700);
m1.setMaxSpeed(1000);

Ya te digo, disculpame que estoy bastante obtuso. :slight_smile:

Primero recuerda que aunque sea una línea de código va con etiquetas SIEMPRE!

Si la aceleración es contante => la velocidad es contante

Te pido que tomes un motor, y con el código que te puse verifiques cuanto tarda en completar un ciclo completo.
y busca la velocidad que permita un movimiento oscilatorio de acuerdo a tu criterio. Luego se podría modificar.

Se entiende?
Olvida las 18 bolas y controla una
Que haga su movimiento simple sin seno sin nada, solo moviendose de 2048 a -2048

Ahora estoy de acuerdo que no tiene nada que ver

mX.moveTo(posicion);

asi que lo mejor seria que el motor o servo tuviera un movimiento libre, y no pausado.

Ahora me fijo y te sugiero pero si miras la librería debe haber ejemplos.

Era obvio que es muy simple

#include <AccelStepper.h>

AccelStepper stepper; // Defaults to 4 pins on 2, 3, 4, 5

void setup()
{  
   stepper.setSpeed(50); 
}

void loop()
{  
   stepper.runSpeed();
}

Esto mueve un stepper a velocidad constante.
Ahora tenemos que ver como los arrancamos en una secuencia senoidal y de ese modo tendras tu senoide.

Si arrancas cada uno de forma defasada, tienes el control de los 18 motores de este simple modo.

Espero vayas leyendo cada edición que hago porque a medida que leo se me ocurren ideas.

Puedes usar la posición actual del primer motor para ir accionando los demas. Cuando concluya el ultimo pasas al movimiento continuo.
Digo que todo esto puede hacerse en el setup o bien si debe interrumpir todo crear una rutina que se use en el setup o bajo el control de algún botón o pulsador.

Ejemplo de como hacerlo
Ya que tienes 18 motores, 360 dividido 18 = 20 entonces cada 20 debes accionar un motor
En 0 arranca m1 o simplemente no lo consideras
En la posicion de m1.currentPosition() = 20 debe arrancar m2 y asi en 40 de m1, arranca m3...

if (m1.currentPosition() != 20)
m2.tun

Vale, con el siguiente código muevo la esfera de 0 a 2048 y de 2048 a -2048 y de -2048 a 0. Una sola vez.

#include <AccelStepper.h>
#define FULLSTEP 4

AccelStepper m1(FULLSTEP, 2, 4, 3, 5); // IN1 IN3 IN2 IN4 (INVERTIR CENTRALES)

int velocidad = 350;
int accel = 700;
int maxvel = 1000;

int sube = 2048;
int baja = -2048;
int arriba = 2048;
int abajo = -2048;

boolean motores = true;

void setup(){
  Serial.begin(19200);
  m1.setSpeed(velocidad); 
  m1.setMaxSpeed(maxvel);
  m1.setAcceleration(accel);
}

void loop() { 
m1.run(); 

if (motores == true){
    m1.moveTo(sube);
    }

if (m1.currentPosition() == arriba){
    motores = false;
    m1.moveTo(baja); 
    }
    
if (m1.currentPosition() == abajo){
    m1.moveTo(0); 
    }
    
Serial.println(m1.currentPosition());
}

surbyte:
Primero recuerda que aunque sea una línea de código va con etiquetas SIEMPRE!

Si la aceleración es contante => la velocidad es contante

...codigo...

Esto mueve un stepper a velocidad constante.
Ahora tenemos que ver como los arrancamos en una secuencia senoidal y de ese modo tendras tu senoide.

Si arrancas cada uno de forma defasada, tienes el control de los 18 motores de este simple modo.

Si no establezco una aceleración, el motor va girando muuuuuy lento como paso a paso. La velocidad en vez de 50 la he establecido en 500 y sigue igual.

Como puedo hacerlo, con ese código que te paso arriba, para hacer el desfase entre motores. porque lo que el movimiento de un motor que sube y baja lo tendría.

corrige la velocidad a tu gusto pero prueba con un motor y encuentra la configuracion adecuada, luego lo demas podrias ir asi

#include <AccelStepper.h>
#define VELOCIDAD   50
AccelStepper m1; // Defaults to 4 pins on 2, 3, 4, 5
AccelStepper m2; // Defaults to 4 pins on 2, 3, 4, 5
// todos los demas motores
AccelStepper m18; // Defaults to 4 pins on 2, 3, 4, 5
int Activomotor = 0;
bool sigo = true;

void setup()
{  
  m1.setSpeed(VELOCIDAD);
  m2.setSpeed(VELOCIDAD);
  // todos los demas motores
  m18.setSpeed(VELOCIDAD);


  while (sigo)
        if (m1.currenPosition() == Posicion) {
            ActivoMotor += 20;
        }

        switch (ActivoMotor) {
            case   0: m1.runSpeed();
                      ActivoMotor = 1;
            case  20: m1.runSpeed();
                      m2.runSpeed();
                      break;
            case  40: m1.runSpeed();
                      m2.runSpeed();
                      m3.runSpeed();
                      break;            
            // todos los casos intermedios


            case 160: m1.runSpeed();
                      m2.runSpeed();
                      m3.runSpeed();
                      m4.runSpeed();
                      m5.runSpeed();
                      m6.runSpeed();
                      m7.runSpeed();
                      m8.runSpeed();
                      m9.runSpeed();
                      m10.runSpeed();
                      m11.runSpeed();
                      m12.runSpeed();
                      m13.runSpeed();
                      m14.runSpeed();
                      m15.runSpeed();
                      m16.runSpeed();
                      m17.runSpeed();
                      m18.runSpeed();
                      sigo = false;   // termina el accionamiento defasado                  
                      break;            
        }
}

void loop() {  
    m1.runSpeed();
    m2.runSpeed();
    m3.runSpeed();
    m4.runSpeed();
    m5.runSpeed();
    m6.runSpeed();
    m7.runSpeed();
    m8.runSpeed();
    m9.runSpeed();
    m10.runSpeed();
    m11.runSpeed();
    m12.runSpeed();
    m13.runSpeed();
    m14.runSpeed();
    m15.runSpeed();
    m16.runSpeed();
    m17.runSpeed();
    m18.runSpeed();
}

Si, voy leyendo las ediciones. Con lo último nada, el motor se queda congelado. No inicia.

Con la velocidad constante digamos que el motor se mueve 1 paso por segundo, aproximadamente.

Con el codigo este, si consigo arriba y abajo, practicamente constante y a una velocidad optima.

¿Se podría utilizar para crear la rutina?

#include <AccelStepper.h>
#define FULLSTEP 4

AccelStepper m1(FULLSTEP, 2, 4, 3, 5); // IN1 IN3 IN2 IN4 (INVERTIR CENTRALES)

int velocidad = 350;
int accel = 700;
int maxvel = 1000;

int sube = 2048;
int baja = -2048;
int arriba = 2048;
int abajo = -2048;

boolean motores = true;

void setup(){
  Serial.begin(19200);
  m1.setSpeed(velocidad);
  m1.setMaxSpeed(maxvel);
  m1.setAcceleration(accel);
}

void loop() {
m1.run();

if (motores == true){
    m1.moveTo(sube);
    }

if (m1.currentPosition() == arriba){
    motores = false;
    m1.moveTo(baja);
    }
   
if (m1.currentPosition() == abajo){
    m1.moveTo(0);
    }
   
Serial.println(m1.currentPosition());
}

¿Si te digo que si quito las lineas

Serial.begin(19200);
Serial.println(m1.currentPosition());

No funcina bien el motor?

Disculpa que condicione mi ayuda, pero es un proyecto escolar?

No, es para un proyecto artistico.

A ver. el m1.moveTo(0); lo mueve en pasos.

Lo que yo te estoy sugiriendo es que lo muevas como motor a velocidad constante.

Crea un sketch que lo mueva a velocidad constante y luego solo mantienes eso para cada motor y ya

Ya tengo el motor, subiendo y bajando en busca de 2048 y -2048 a velocidad constante.

#include <AccelStepper.h>
AccelStepper m1(4, 2, 4, 3, 5); // IN1 IN3 IN2 IN4 (INVERTIR CENTRALES)
int sube = 2048;
int baja = -2048;
int arriba = 2048;
int abajo = -2048;
boolean motores = true;
void setup()
{  
   m1.setMaxSpeed(1000);
   m1.setSpeed(500);        
}
void loop()
{  
    m1.runSpeed();
   
   
   if (m1.currentPosition() == arriba){
     m1.setSpeed(-500); 
     m1.runSpeed();
    }

    if (m1.currentPosition() == abajo){
      m1.setSpeed(500);  
      m1.runSpeed();
     }

}

Primero hay que desarrollar un algoritmo que describa el movimiento de los motores en función del tiempo
Y=A sen(alfa) donde A es la amplitud y alfa es el Angulo
Ahora bien, como tienes 18 motores y no quieres que todos se muevan igual, hay que darle un desfasaje a cada motor.
Suponiendo que a lo largo de los motores quieres graficar una longitud de onda, entonces cada motor debe estar desfasado 1/18 de una longitud de onda
Y=A sen( alfa + n /18* 2pi) donde n es un motor (en radianes 2 pi es una longitud de onda)
Para el cálculo de A hay que hacer otra suposición
Supongamos que la amplitud máxima es una revolución de la polea, pero sabemos que una revolución son 2048 pulsos, es decir igual a A
p=2048 sen( alfa + n /18
2* pi)
como último paso hay que relacionar el ángulo con el tiempo, pero no podemos hacerla depender directamente del tiempo, porque la función seno puede desbordar.
Por lo tanto hay que definir el tiempo de una revolución ( Tr) y hacer que alfa varíe entre 0 y pi en ese tiempo, esto se logra con un contador que se resetee a 0 cuando se cumpla el Tr

Alfatiempo =Abs( (conta-milis()) / Tr) 2 pi

If abs(conta-milis())>= Tr then conta=milis()

Al final la función de pulsos en función del tiempo queda
p=2048 sen(( Abs( (conta-milis()) / Tr) * pi ) + n /18* 2*pi) donde n es el motor n y Tr es el tiempo de una revolución en milisegundos.

Espero que te sirva.
Saludos

Hola de nuevo, gracias por la respuesta.

he estado haciendo alguna prueba, tratando de desarollar ese algoritmo, pero sinceramente no soy nada experto en esto, y no he podido llegar a nada claro.

La duda es ¿Cómo puedo hacer el codigo para el contador de tiempo?

la funcion que varia entre 0 y pi

float previoconta = 0;        
long Tr = 1000;         
float Alfatiempo;
long pi ;
void setup() {
    Serial.begin(9600);
}
void loop()
{
  float pi=3.1416 ;
  float conta = millis();
  if(conta - previoconta > Tr) {
      previoconta = conta; 
    }
Alfatiempo = (conta-previoconta) / Tr *pi;
 delay(10);
  Serial.println(Alfatiempo);
 }

Okey, he probado esto y he rematado con

int p=2048*sin((Alfatiempo) + 1 /18* pi);
 delay(10);
  Serial.println(p);

Con lo que tengo un valor que va de 0 a 2048, cuando llega a 2048 se pone en 0 y vuelve a contar.

Creo que esto no me sirve para lo que quiero.

Estoy trabajando en el siguiente codigo, algo rudimentario para pobar, en el que al pulsar un boton activo el motor 1 (m1) y quiero que cuando éste alcance una posición, en este caso 133. Se mueva el motor 3 (m3)

void loop(){  


valorPulsador = digitalRead(pulsadorPin);
if (valorPulsador && valorPulsadorAnt) {
    estado = true;
}
valorPulsadorAnt = valorPulsador;


if (estado) {
  m1.runSpeed();
  
if (m1.currentPosition() == 133){
    m3.runSpeed();
  } 
   if (m1.currentPosition() == 2048){
    m1.setSpeed(-500); 
    m1.runSpeed();
   }
  if (m1.currentPosition() == -2048){
    m1.setSpeed(500);  
    m1.runSpeed();
   }
     
   if (m3.currentPosition() == 2048){
    m3.setSpeed(-500); 
    m3.runSpeed();
   }
  if (m3.currentPosition() == -2048){
    m3.setSpeed(500);  
    m3.runSpeed();
   }
}
}

Aquí me encuentro con un problema. Cuando m1 alcanza la posición 133, m3 gira pero solamente 1 step y se queda parado. Lo que quiero es que dispare el motor a andar igual que lo hace
if (estado) { m1.runSpeed(); }

¿Alguna sugerencia?