Grabar y Reproducir posiciones Brazo Robótico

Hola, estoy en un proyecto para controlar un brazo robótico con potenciometros, de tal manera que, con una llave, pase al modo grabar posiciones, y luego al terminar de mover el potenciometro ir apretando un pulsador para grabar cada movimiento.

Definición del Porblema 1: Cuando grabo las posiciones de los servos estos no se mueven al mover el potenciometro, asique no logro ver lo que "estoy haciendo", más allá de que las posiciones se graban perfectamente.

Busco ésta solución: que los servos se muevan efectivamente mientres grabo al mismo tiempo.

Definición del Problema 2: Cuando esta en modo ejecución, es decir, cuando repite los movimientos guardados, lo realiza una vez, hasta llegar a "ultimo paso". Lo que quiero es tener la opcion de que repita los pasos guardados indefinidamente.

Busco ésta solución: que al reproducir los pasos guardados, y al llegar al ultimo paso, repita la secuencia, y así sucesivamente.

Este es el código:

#include <Servo.h>
#define NUMSERVOS 6       // Cantidad de servos
#define PINGRABA 8        // Pin de SWITCH que determina el modo
#define PINMODO 9         // Pin de PULSADOR que determina cuando graba la posicion
#define MAXPASOS 100      // Maxima cantidad de pasos
int paso = 0;             // contador de pasos;
int ultimoPaso = 0;       // Contiene el ultimo paso guardado
byte position[MAXPASOS][NUMSERVOS];  // Array de 100 pasos, de los N servos
Servo servos[NUMSERVOS];  // Array de los servos
byte potpin[NUMSERVOS] = {A0, A1, A2, A3, A4, A5}; // Array de pines de potenciometros
byte pinservo[NUMSERVOS] = {2, 3, 4, 5, 6, 7}; //   // Array de pines de servos
bool modoGraba = true;    // Modo: true:Graba, false:Ejecuta
byte limites[NUMSERVOS][2] = { {0,180}, {0,180}, {0,180}, {0,180}, {0,180}, {0,180} }; //valores limites de cada servo de mi brazo {inferior, superior}

// graba la posicion de los potenciometros en el paso
void savePos() {
  if (paso >= MAXPASOS)   // Si excede la cantidad de pasos comienza a sobreescribir del inicio
    paso = 0;
  Serial.print("Grabando paso (");
  Serial.print(paso);
  Serial.print(")");
  for (byte i = 0; i < NUMSERVOS; i++) {
    int lectura = analogRead(potpin[i]);
    int valor = map(lectura, 0, 1023, limites[i][0], limites[i][1]);
    Serial.print(" | ");
    //Serial.print(lectura);
    //Serial.print(",");
    Serial.print(valor);
    position[paso][i] = valor;
  }
  Serial.println("");
  paso++;
  ultimoPaso = paso;
}

// ejecuta la posicion en el paso
void execPos() {
  if (paso == ultimoPaso){
    Serial.println("Ultimo paso");
    paso++; // Para que el mensaje solo salga una vez
  }
  if (paso >= ultimoPaso) return; // Si llego al ultimo paso no avanza
  Serial.print("Ejecutando paso:");
  Serial.print(paso);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].write(position[paso][i]);
    delay(15);
    Serial.print(" | ");
    Serial.print(position[paso][i]);
  }
  Serial.println("");
  paso++;
  delay(1000);
}

// validaciones del modo de grabacion o ejecucion
void validarModo() {
  bool modo = digitalRead(PINMODO);
  // si cambio de modo resetear paso a 0
  bool cambio = false;
  if (modoGraba != modo) {
    cambio = true;
    Serial.print("Cambio de modo, Modo graba:");
    Serial.println(modo);
  } else {
    cambio = false;
  }
  modoGraba = modo;
  if (cambio) { 
    paso = 0;
  }

}

// Arduino setup
void setup() {
  Serial.begin(9600);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].attach(pinservo[i]);
  }
  pinMode(PINGRABA, INPUT);
  pinMode(PINMODO, INPUT);
  //Valores iniciales de mi brazo/servos
  servos[0].write(90);
  servos[1].write(90);
  servos[2].write(90);
  servos[3].write(90);
  servos[4].write(90);
  servos[5].write(90);
}

// Arduino loop
void loop() {

  validarModo();
  if (modoGraba) {  // modo Graba
    int graba = digitalRead(PINGRABA);
    if (graba == HIGH) {
      savePos();
      delay(1000);
    }
  } else { // modo Ejecucion
    execPos();
  }
}

Alguna sugerencia?

Su publicacion se MUEVE a su ubicacion actual ya que es mas adecuada.

¿Puede aclarar cómo planea manejar el tiempo de muestreo y la velocidad de reproducción?

El principal problema tal como yo lo veo, es como estas encarando el código:

delay(1000);

cada vez que estas grabando se pausa x 1 segunundo sin hacer nada.
En cada sección de código que uses delay() tendras pausas que detienen servos y todo lo demás.
Esta situación se repite en varios tramos del sketch.
Asi que tu mismo introduces el problema.

Debes reemplazar su uso con millis(). Ve a documentación y lee sobre el. Hay muchos y muy buenos tutoriales.
Su uso hará que el código, no se detenga pero, debes hacerlo de tal modo que tampoco te grabe cada 1 mseg, así que reduce las grabaciones a algo que tenga sentido para ti.

Más allá de los problemas que pueda presentar el código y que te ha hecho notar @Surbyte, puntualmente

Agrega

servos[i].write(valor); 
delay(15);

Luego de la lectura del potenciómetro y "mapaedo" de valor

El problema es que no reinicias la variable pasos al llegar al último paso.
Agrega

if (paso >= ultimoPaso) paso = 0;

antes de la llamada a execPos().
O

paso = 0;

antes del return en execPos.

Hola gatul, gracias por la respuesta, me sirvió bastante. He modificado el código, y ahora gracias a modificar la varialble ultimo paso, puse paso =0; y efectivamente entra en loop la reproducción de lo grabado. Me refiero a esta parte:


// ejecuta la posicion en el paso
void execPos() {
  if (paso == ultimoPaso) {
    Serial.println("Ultimo paso");
    paso++; // Para que el mensaje solo salga una vez
    **paso = 0;**
  }
  if (paso >= ultimoPaso) return; // Si llego al ultimo paso no avanza
  Serial.print("Ejecutando paso:");
  Serial.print(paso);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].write(position[paso][i]);
    delay(15);
    Serial.print(" | ");
    Serial.print(position[paso][i]);

Reespecto a ver lo que hago he modificado el codigo con tus sugerencias, pero solamente he logrado que al apretar el boton de guardado, reproduzca la posicion moviendose el servo, pero no mientras muevo el potenciometro. Me refiero a esta parte del codigo:


void savePos() {
  if (paso >= MAXPASOS)   // Si excede la cantidad de pasos comienza a sobreescribir del inicio
    paso = 0;
  Serial.print("Grabando paso (");
  Serial.print(paso);
  Serial.print(")");
  for (byte i = 0; i < NUMSERVOS; i++) {
    int lectura = analogRead(potpin[i]);
    int valor = map(lectura, 0, 1023, limites[i][0], limites[i][1]);
    **servos[i].write(valor);**
**    delay(15);**
    Serial.print(" | ");
    //Serial.print(lectura);
    //Serial.print(",");
    Serial.print(valor);
    position[paso][i] = valor;
  }

Es una mejora, cada vez que puiso guardar posicion, los servos se mueven, pero no puedo ver antes hasta donde moví el servo. Alguna sugerencia?

El codigo con las modificaciones quedó asi:

#include <Servo.h>
#define NUMSERVOS 6       // Cantidad de servos
#define PINGRABA 8        // Pin de SWITCH que determina el modo
#define PINMODO 9         // Pin de PULSADOR que determina cuando graba la posicion
#define MAXPASOS 100      // Maxima cantidad de pasos
int paso = 0;             // contador de pasos;
int ultimoPaso = 0;       // Contiene el ultimo paso guardado
byte position[MAXPASOS][NUMSERVOS];  // Array de 100 pasos, de los N servos
Servo servos[NUMSERVOS];  // Array de los servos
byte potpin[NUMSERVOS] = {A0, A1, A2, A3, A4, A5}; // Array de pines de potenciometros
byte pinservo[NUMSERVOS] = {2, 3, 4, 5, 6, 7}; //   // Array de pines de servos
bool modoGraba = true;    // Modo: true:Graba, false:Ejecuta
byte limites[NUMSERVOS][2] = { {0, 180}, {0, 180}, {0, 180}, {0, 180}, {0, 180}, {0, 180} }; //valores limites de cada servo de mi brazo {inferior, superior}

// graba la posicion de los potenciometros en el paso
void savePos() {
  if (paso >= MAXPASOS)   // Si excede la cantidad de pasos comienza a sobreescribir del inicio
    paso = 0;
  Serial.print("Grabando paso (");
  Serial.print(paso);
  Serial.print(")");
  for (byte i = 0; i < NUMSERVOS; i++) {
    int lectura = analogRead(potpin[i]);
    int valor = map(lectura, 0, 1023, limites[i][0], limites[i][1]);
    servos[i].write(valor);
    delay(15);
    Serial.print(" | ");
    //Serial.print(lectura);
    //Serial.print(",");
    Serial.print(valor);
    position[paso][i] = valor;
  }
  Serial.println("");
  paso++;
  ultimoPaso = paso;
}

// ejecuta la posicion en el paso
void execPos() {
  if (paso == ultimoPaso) {
    Serial.println("Ultimo paso");
    paso++; // Para que el mensaje solo salga una vez
    paso = 0;
  }
  if (paso >= ultimoPaso) return; // Si llego al ultimo paso no avanza
  Serial.print("Ejecutando paso:");
  Serial.print(paso);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].write(position[paso][i]);
    delay(15);
    Serial.print(" | ");
    Serial.print(position[paso][i]);
  }
  Serial.println("");
  paso++;
  delay(1000);
}

// validaciones del modo de grabacion o ejecucion
void validarModo() {
  bool modo = digitalRead(PINMODO);
  // si cambio de modo resetear paso a 0
  bool cambio = false;
  if (modoGraba != modo) {
    cambio = true;
    Serial.print("Cambio de modo, Modo graba:");
    Serial.println(modo);
  } else {
    cambio = false;
  }
  modoGraba = modo;
  if (cambio) {
    paso = 0;
  }

}

// Arduino setup
void setup() {
  Serial.begin(9600);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].attach(pinservo[i]);
  }
  pinMode(PINGRABA, INPUT);
  pinMode(PINMODO, INPUT);
  //Valores iniciales de mi brazo/servos
  servos[0].write(90);
  servos[1].write(90);
  servos[2].write(90);
  servos[3].write(90);
  servos[4].write(90);
  servos[5].write(90);
}

// Arduino loop
void loop() {

  validarModo();
  if (modoGraba) {  // modo Graba
    int graba = digitalRead(PINGRABA);
    if (graba == HIGH) {
      savePos();
      delay(1000);
    }
  } else { // modo Ejecucion
    execPos();
  }
}

No sé a qué te refieres con tiempo de muestreo, pero tu mensaje me dejó pensando. Efectivamente la reproduccón se produce a máxima velocidad. Supongo que debo incluir la libreria

<VarSpeedServo.h>

Pero nunca la usé, asique tendré que estudiarla y ver como insertarla en el codigo. Se aceptan sugerencias.

Hola, gracias por tu respuesta, efectivamente el único deleay es aquí

void loop() {

  validarModo();
  if (modoGraba) {  // modo Graba
    int graba = digitalRead(PINGRABA);
    if (graba == HIGH) {
      savePos();
      delay(1000);
    }
  } else { // modo Ejecucion
    execPos();
  }
}

Y tiene sentido para mi, el otro delay es para el monitor serie y se puede sacar:

// ejecuta la posicion en el paso
void execPos() {
  if (paso == ultimoPaso) {
    Serial.println("Ultimo paso");
    paso++; // Para que el mensaje solo salga una vez
    paso = 0;
  }
  if (paso >= ultimoPaso) return; // Si llego al ultimo paso no avanza
  Serial.print("Ejecutando paso:");
  Serial.print(paso);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].write(position[paso][i]);
    delay(15);
    Serial.print(" | ");
    Serial.print(position[paso][i]);
  }
  Serial.println("");
  paso++;
  delay(1000);
}

Creo que el mayor problema es es el delay de 1 seg. que tienes en loop().

Si quito el delay, o lo reduzco, se graban demasiadas posiciones ,simulataneamente al movimiento del potenciometro, como si dejara apretado el boton de grabado. No es un problema del delay en sí, sino que Arduino no es bueno ejecutando operaciones simultaneas, creo yo. Será así nomas.

Parece que no entiendes que delay() detiene el programa durante X milisegundos, ¿qué operación pretendes que ejecute si tu mismo lo detienes?

No creo que digas que un automóvil no sirve porque no avanza cuando le pones el freno. ¿O si?

Tal vez, si inicias el modo grabación con una pulsación, mueves potenciómetro y motor a donde quieres y luego grabas al volver a pulsar el botón la cosa podría funcionar mejor.

Te vuelvo a repetir, te lo dije en el post#4 no uses delay().
Ve a documentación y usa millis(). Aprende a hacerlo o morirás en el intento de hacer funcionar tu brazo robótico.

Tambien te dije esto sobre millis()

Su uso hará que el código, no se detenga pero, debes hacerlo de tal modo que tampoco te grabe cada 1 mseg, así que reduce las grabaciones a algo que tenga > sentido para ti.

Por eso tienes que tomar muestras. Un millis() de 1 segundo y otro cada 100 mseg para tomar muestras de las posiciones del brazo por ejemplo.

Gatul: Quiero que el servo se mueva antes de guardar, no mientras guarda.

"Tal vez, si inicias el modo grabación con una pulsación, mueves potenciómetro y motor a donde quieres y luego grabas al volver a pulsar el botón la cosa podría funcionar mejor."

Tu sugerencia podria funcionar. Lo voy a tratar.

slds

Por eso mi sugerencia.

Yo veo 2 opciones, una es tomar muestras cada X mseg. (creo que ya te lo sugirió @Surbyte) , o sea, entras en grabación, ajustas el potenciómetro, mueves el brazo, y pasado el intervalo grabas su posición actual, repites hasta terminar el movimiento y salir del modo grabación.
Y esa otra que te sugerí, entrar en grabación, ajustas el potenciómetro, mueves el brazo, cuando terminas el movimiento pulsas para salir de grabación y antes grabas el dato. Incluso podrías tomar el tiempo del movimiento y también grabarlo para luego reproducirlo a la misma velocidad.

Saludos

Bueno lo arregle así, pero debo hacer pruebas...

#include <Servo.h>
#define NUMSERVOS 6       // Cantidad de servos
#define PINGRABA 8        // Pin de PULSADOR que determina cuando graba la posicion
#define PINMODO 9         // Pin de SWITCH que determina el modo
#define PINREPRODUCE 10 // Pulsador que determina la Reproducción    
#define MAXPASOS 100      // Maxima cantidad de pasos
int paso = 0;             // contador de pasos;
int ultimoPaso = 0;       // Contiene el ultimo paso guardado
byte position[MAXPASOS][NUMSERVOS];  // Array de 100 pasos, de los N servos
Servo servos[NUMSERVOS];  // Array de los servos
byte potpin[NUMSERVOS] = {A0, A1, A2, A3, A4, A5}; // Array de pines de potenciometros
byte pinservo[NUMSERVOS] = {2, 3, 4, 5, 6, 7}; //   // Array de pines de servos
bool modoGraba = true;    // Modo: true:Graba, false:Ejecuta
byte limites[NUMSERVOS][2] = { {0, 180}, {0, 180}, {0, 180}, {0, 180}, {0, 180}, {0, 180} }; //valores limites de cada servo de mi brazo {inferior, superior}

// graba la posicion de los potenciometros en el paso
void savePos() {
  if (paso >= MAXPASOS)   // Si excede la cantidad de pasos comienza a sobreescribir del inicio
    paso = 0;
  Serial.print("Grabando paso (");
  Serial.print(paso);
  Serial.print(")");
  for (byte i = 0; i < NUMSERVOS; i++) {
    int lectura = analogRead(potpin[i]);
    int valor = map(lectura, 0, 1023, limites[i][0], limites[i][1]);
    servos[i].write(valor);
    delay(15);
    Serial.print(" | ");
    //Serial.print(lectura);
    //Serial.print(",");
    Serial.print(valor);
    position[paso][i] = valor;
  }
  Serial.println("");
  paso++;
  ultimoPaso = paso;
}

// ejecuta la posicion en el paso
void execPos() {
  if (paso == ultimoPaso) {
    Serial.println("Ultimo paso");
    paso++; // Para que el mensaje solo salga una vez
  }
  if (paso >= ultimoPaso) return; // Si llego al ultimo paso no avanza
  Serial.print("Ejecutando paso:");
  Serial.print(paso);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].write(position[paso][i]);
    delay(15);
    Serial.print(" | ");
    Serial.print(position[paso][i]);
  }
  Serial.println("");
  paso++;
  delay(1000);
}

// validaciones del modo de grabacion o ejecucion
void validarModo() {
  bool modo = digitalRead(PINMODO);
  // si cambio de modo resetear paso a 0
  bool cambio = false;
  if (modoGraba != modo) {
    cambio = true;
    Serial.print("Cambio de modo, Modo graba:");
    Serial.println(modo);
  } else {
    cambio = false;
  }
  modoGraba = modo;
  if (cambio) {
    paso = 0;
  }

}

// Arduino setup
void setup() {
  Serial.begin(9600);
  for (byte i = 0; i < NUMSERVOS; i++) {
    servos[i].attach(pinservo[i]);
  }
  pinMode(PINGRABA, INPUT);
  pinMode(PINMODO, INPUT);
  pinMode(PINREPRODUCE,INPUT);
  //Valores iniciales de mi brazo/servos
  servos[0].write(90);
  servos[1].write(90);
  servos[2].write(90);
  servos[3].write(90);
  servos[4].write(90);
  servos[5].write(90);
}

// Arduino loop
void loop() {
   if(digitalRead(PINREPRODUCE) == HIGH)   
   //Si tenemos apretado el de reproducir, copiara los movimientos de los potenciometros
   for (byte i = 0; i < NUMSERVOS; i++) {
    int lectura = analogRead(potpin[i]);
    int valor = map(lectura, 0, 1023, limites[i][0], limites[i][1]);
    servos[i].write(valor);
    delay(15);
  }
  validarModo();
  if (modoGraba) {  // modo Graba
    int graba = digitalRead(PINGRABA);
    if (graba == HIGH) {
      savePos();
      delay(1000);
    }
  } else { // modo Ejecucion
    execPos();
  }
}

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