Fernsteuerung für den omnidirektionalen Roboter - AZ-Delivery

En la entrada del blog de hoy, queremos construir un robot de exploración teledirigido a partir del kit Acebott Smart Car. El robot se controlará mediante un joystick y mostrará su entorno en un radar óptico en una pequeña pantalla.
El sensor ultrasónico, ya montado en un servomotor con soporte acrílico, se utiliza para escanear el entorno.

El sistema de control utiliza el escudo joystick PS2 y la configuración ESP32 D1 R32 que ya puede estar familiarizado con las entradas del blog sobre el control del robot Bionic Spider.

Hardware

Si ya has realizado las modificaciones al Joystick Shield de PS2 en un blog anterior de la serie Bionic Spider Controller, puedes saltarte el siguiente párrafo.

 

Para la realización del proyecto necesitas:

ESP32 NodoMCU D1 R32

PS2 Joystick Shield

(opcional) pila bloque de 9V + Clip para pilas

y por supuesto una montada Coche robot Acebott

 

Como las entradas analógicas de los dos ejes del joystick están conectadas a GPIO 2 y 4, que a su vez están conectadas al ADC2, no pueden utilizarse simultáneamente con el módulo WLAN. Sin embargo, las conexiones IO 34 e IO35 (ADC1) se encuentran directamente junto a éstas, por lo que su funcionalidad puede establecerse fácilmente mediante un puente de cables.

Figura 1: Puente de cables entre las conexiones

Conecte las conexiones como se muestra arriba.
Como el LED de la placa está conectado a GPIO 2, el pin debe eliminarse aquí, ya que de lo contrario la conexión afectaría al valor de la tensión del eje X.

Lo mismo ocurre con el pin D12, ya que interfiere en el proceso de arranque y, por lo tanto, no sería posible arrancar con la shield conectada. El botón central del joystick está conectado a esta conexión, lo que significa que ya no se puede utilizar. Si aún se quiere implementar, se debe volver a conectar a una conexión libre.

 

Figura 2: Escudo con modificaciones

 

La pantalla puede conectarse simplemente al D1 R32. Asegúrate de que el pequeño interruptor de la esquina izquierda está en la posición 3v3 de lo contrario el ESP32 podría dañarse por el voltaje excesivo de 5V.

 

 

Extensión de la pantalla

Como el controlador debe mostrar el entorno del vehículo en forma de pantalla de radar, como se ve en muchas películas, se necesita una pantalla.

Aquí recomendamos el Pantalla gráfica OLED de 1,3" (SH1106). No sólo se trata de una pantalla gráfica grande y fácil de leer, sino que además tiene la disposición adecuada para la regleta de conectores del módulo HC05 en la pantalla (arriba a la derecha). De este modo, la pantalla puede conectarse sin necesidad de realizar grandes modificaciones.

Sólo es necesario retirar la resistencia SMD superior situada junto al botón A, ya que se diseñó originalmente para reducir a la mitad la tensión en el pin RXD del HC05 para que el módulo no se dañe al funcionar con el microcontrolador UNO.

 

Modificación de imágenes

 

Como el microcontrolador utilizado (ESP32) tiene un nivel lógico de 3,3V, la comunicación con la pantalla dejaría de funcionar con el voltaje reducido a la mitad.

 

Software

Para la comunicación, los paquetes de datos se intercambian mediante UDP. Con este protocolo de comunicación, los paquetes simplemente se envían sin comprobar si se ha recibido el paquete.

Aunque esto hace que el intercambio de datos sea muy rápido, también es inseguro, ya que no es posible determinar si el destinatario está recibiendo realmente los datos. La comunicación a través de una red también permite un mayor alcance en comparación con ESPnow o Bluetooth, ya que la red puede cubrir una amplia zona con repetidores y puntos de acceso.
En principio, los dos dispositivos pueden comunicarse entre sí en cualquier lugar donde haya cobertura con tu smartphone.

 

La biblioteca para la comunicación UDP ya está incluida en el amplio paquete de la placa para el ESP32.

 

Además de las librerías UDP y WiFi, se requieren otras librerías externas, que deberás instalar.

U8g2

ArduinoJson

Pueden descargarse de GitHub como archivo .zip a través de los siguientes enlaces y descargarse en el IDE de Arduino en

Sketch > incluir Biblioteca > Añadir Biblioteca .zip ...

pueden seleccionarse e instalarse.

 

En ESP32Servo, ultrasónico y vehículo Las bibliotecas ya están incluidas en el archivo .zip de las instrucciones. También se han instalado ya en las instrucciones para usuarios avanzados.

 

Código smart car

#include <vehicle.h>
#
include <ultrasonic.h>
#
include <ESP32Servo.h>
#
include <ArduinoJson.h>
#
include <WiFi.h>
#
include <WiFiUdp.h>

ultrasonic myUltrasonic;
vehicle myCar;
Servo myServo;
JsonDocument doc;
WiFiUDP udp;

const char* ssid = "xxx";
const char* password = "xxx";
String cmd;
String msg;
int v = 255//speed

void setup() {
  
Serial.begin(115200);
  
WiFi.begin(ssid, password);
  
while (WiFi.status() != WL_CONNECTED) {
    
delay(500);
    
Serial.print(".");
    
if(millis() > 30000) ESP.restart();
  }

  udp.
begin(4210);

  
Serial.println("ESP8266 ready (Reciever)");
  
Serial.print("IP:");
  
Serial.println(WiFi.localIP());
  myCar.Init();
//Initialize all motors
  myUltrasonic.Init(
13,14);
  myServo.
attach(25);//initialize
}

void loop() {
  
//Serial.print("IP:");
  
//Serial.println(WiFi.localIP());

  
int pSize = udp.parsePacket();
  
if (pSize) {
    
char buf[20];
    
int len = udp.read(buf, 20);
    buf[len] = 
0;

    cmd = 
String(buf);
    
Serial.println(cmd);
  }

  
if(cmd.length() > 0) {
    
if(cmd.indexOf("CMD_FWD") >= 0) {
      
Serial.println("FWD");
      myCar.Move(Forward, v);
    }
    
else if(cmd.indexOf("CMD_BWD") >= 0) {
      
Serial.println("BWD");
      myCar.Move(Backward, v);
    }
    
else if(cmd.indexOf("CMD_RGT") >= 0) {
      
Serial.println("RGT");
      myCar.Move(Move_Right, v);
    }
    
else if(cmd.indexOf("CMD_LFT") >= 0) {
      
Serial.println("LFT");
      myCar.Move(Move_Left, v);
    }
    
else if(cmd.indexOf("CMD_STOP") >= 0) {
      
Serial.println("Stop");
      myCar.Move(Stop,
0);
    }
    
else if(cmd.indexOf("BTN_A") >= 0) {
      
Serial.println("A");
      
int j = 0;
      
for(int i=30; i<=150; i+=5) {
        myServo.
write(i);
        
Serial.println(myUltrasonic.Ranging());
        doc[
"vals"][j]["angle"] = i + 180;
        doc[
"vals"][j]["dist"] = myUltrasonic.Ranging();
        
delay(150);
        j++;
      }
      doc[
"count"] = j;
      serializeJson(doc, msg);
      
Serial.println(msg);
      udp.
beginPacket(udp.remoteIP(), udp.remotePort());
      udp.
print(msg);
      udp.
endPacket();
      myServo.
write(90);
    }
    
else if(cmd.indexOf("BTN_B") >= 0) {
      
Serial.println("B");
    }
    
else if(cmd.indexOf("BTN_C") >= 0) {
      
Serial.println("C");
    }
    
else if(cmd.indexOf("BTN_D") >= 0) {
      
Serial.println("D");
    }
    
else if(cmd.indexOf("BTN_E") >= 0) {
      
Serial.println("E");
      myCar.Move(Clockwise,v);
    }
    
else if(cmd.indexOf("BTN_F") >= 0) {
      
Serial.println("F");
      myCar.Move(Contrarotate,v);
    }

    cmd = 
"";
    
// send ready to controller to recieve new commands
    udp.
beginPacket(udp.remoteIP(), udp.remotePort());
    udp.
print("RDY");
    udp.
endPacket();
    
Serial.println("ACK gesendet");
  }
}

Explicación:

En el arranque, el ESP32 se conecta a la WLAN, abre un puerto UDP e inicializa los motores, el sensor ultrasónico y el servo. En el bucle principal, espera los comandos UDP entrantes y los evalúa como comandos de texto. Dependiendo del comando, se controlan los motores o se ejecutan funciones especiales.

Con el comando BTN_A, el servo gira el sensor ultrasónico por pasos, mide la distancia en cada posición y guarda el ángulo y la distancia en una matriz JSON. Una vez finalizada la exploración, el JSON se serializa y se envía de vuelta al remitente a través de UDP, tras lo cual el servo vuelve a la posición central.

Después de cada acción ejecutada, el ESP32 envía la retroalimentación "RDY" para señalar que está listo para el siguiente comando.

 

Puede utilizar el código aquí descargar.

Una vez que haya cargado el programa en el microcontrolador con los datos de acceso WLAN correctos, se mostrará la dirección IP del dispositivo. Anótala, ya que es necesaria para el controlador.

Controlador de código

#include <math.h>
#
include <Arduino.h>
#
include <U8g2lib.h>
#
include <Wire.h>
#
include <WiFi.h>
#
include <esp_wifi.h>
#
include <WiFiUdp.h>
#
include <ArduinoJson.h>

#
define X 34
#
define Y 35

#
define A 26
#
define B 25
#
define C 17
#
define D 16
#
define E 27
#
define F 14

const char* ssid = "xxx";
const char* password = "xxx";
const char* rcvIP = "192.168.178.xxx";


int calibY, calibX;
bool status = true;
long sentTime;
String msg;

WiFiUDP udp;
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, U8X8_PIN_NONE, 
31);

JsonDocument doc;

void drawLineAngle(float angleDeg, float length) {
  
float angleRad = angleDeg * DEG_TO_RAD;

  
int x1 = 64 - cos(angleRad) * length;
  
int y1 = 63 + sin(angleRad) * length;

  u8g2.drawLine(
6463, x1, y1);
}

void setup() {
  
Serial.begin(115200);
  
delay(2000);

  
WiFi.begin(ssid, password);
  
while (WiFi.status() != WL_CONNECTED) {
    
delay(500);
    
Serial.print(".");
    
if(millis() > 7000) ESP.restart();
  }

  u8g2.
begin();
  u8g2.clearBuffer();

  
Serial.println("ESP32 ready (Controller)");

  calibX = 
analogRead(X); //calibrating current Position to zero
  calibY = 
analogRead(Y);

  
pinMode(A, INPUT_PULLUP);
  
pinMode(B, INPUT_PULLUP);
  
pinMode(C, INPUT_PULLUP);
  
pinMode(D, INPUT_PULLUP);
  
pinMode(E, INPUT_PULLUP);
  
pinMode(F, INPUT_PULLUP);
}

void loop() {
  
int valX = analogRead(X) - calibX;
  
int valY = analogRead(Y) - calibY;
  
//Serial.println(valX);
  
//Serial.println(valY);

//no acknowledgement recieved after 10 sec. -> set marker to true; reenable sending
  
if((millis() - sentTime) > 10000) {
    status = true;
  }
  
if(status) {
    
//threshold values
    
if(valX < -50 || valY < -50 || valX > 50 || valY > 50) {
      
Serial.println("trig");
      
//choose dominant coordinate
      
if(abs(valX) > abs(valY)) {
        
if(valX < 0) {
          
Serial.println("LEFT");
          msg = 
"CMD_LFT";
        }
        
else {
          
Serial.println("Right");
          msg = 
"CMD_RGT";
        }
      }
      
else {
        
if(valY < 0) {
          
Serial.println("BWD");
          msg = 
"CMD_BWD";
        }
        
else {
          
Serial.println("FWD");
          msg = 
"CMD_FWD";
        }
      }
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else {
      msg = 
"CMD_STOP";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }

    
if(!digitalRead(A)) {
      
Serial.println("A");
      msg = 
"BTN_A";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation +3000
    }
    
else if(!digitalRead(B)) {
      
Serial.println("B");
      msg = 
"BTN_B";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(C)) {
      
Serial.println("C");
      msg = 
"BTN_C";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(D)) {
      
Serial.println("D");
      msg = 
"BTN_D";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(E)) {
      
Serial.println("E");
      msg = 
"BTN_E";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(F)) {
      
Serial.println("F");
      msg = 
"BTN_F";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
   
    udp.
beginPacket(rcvIP, 4210);
    udp.
print(msg);
    udp.
endPacket();
    msg = 
"";
  }
 
  
int pSize = udp.parsePacket();
  
if (pSize) {
    
char buf[pSize];
    
int len = udp.read(buf, pSize);
    buf[len] = 
0//termination

    
if (String(buf) == "RDY") {
      status = true;
      
Serial.println("ACK");
     
    }
    
else {
      
Serial.println(buf);
      deserializeJson(doc, buf);
      
float l;
      u8g2.clearBuffer();
      
for(int i = 0; i<doc["count"]; i++) {
        l = doc[
"vals"][i]["dist"];
        
if(l>60) l = 60;
        drawLineAngle(doc[
"vals"][i]["angle"] , l);
      }
      u8g2.sendBuffer();
    }
  }
  
delay(100);
}

Explicación:

Tras el arranque, el ESP32 se conecta a la WLAN, inicializa la pantalla OLED, calibra el joystick en su posición central y configura los GPIO necesarios para los botones. Los ejes del joystick se analizan continuamente en el bucle principal. Si superan un valor umbral, se determina una dirección de desplazamiento y se envía a través de UDP como comando (por ejemplo, adelante, atrás, izquierda, derecha) o se detiene si el joystick está en la posición central. Además, se pueden activar comandos especiales mediante los botones A-F. Para evitar transmisiones múltiples, cada orden sólo se vuelve a enviar después de la confirmación o de que se haya agotado el tiempo de espera.

 

También se analizan los paquetes UDP entrantes. Si se recibe la respuesta "RDY", esto indica que el receptor está listo para el siguiente comando. Si, por el contrario, el paquete contiene un objeto JSON con datos de medición, éste se analiza y se muestra como una línea en la OLED. El ángulo y la distancia de los valores medidos representan líneas que parten del centro de la pantalla. Esto crea una representación gráfica sencilla del entorno detectado por el otro ESP32.

 

Las líneas se crean utilizando la función drawLineAngle(). Aquí, las coordenadas finales de la línea se calculan utilizando el seno y el coseno. Puedes encontrar una explicación detallada en el blog Reloj retro con GC9A01A.

 

Puede utilizar el código aquí descargar.

Cargue el programa con los datos de acceso correctos y la dirección IP del receptor en el microcontrolador.


Conclusión

Si ahora pulsa el botón A del mando, la pantalla le mostrará gráficamente el entorno en los próximos 60 cm como una línea para cada medición. Un píxel corresponde aproximadamente a un centímetro. Si desea un radio de visualización más amplio, puede ajustar el programa del controlador en consecuencia, pero debe tener en cuenta la longitud máxima de 60 píxeles.

Por supuesto, este proyecto sólo sienta las bases para tus propias ampliaciones y modificaciones. Además de escanear el entorno, también se pueden añadir otras funciones; los botones no asignados del escudo controlador están disponibles aquí.

Diviértete reconstruyendo :)

Esp32Esp8266Projekte für anfänger

Deja un comentario

Todos los comentarios son moderados antes de ser publicados