O objetivo deste tutorial é apresentar como realizar a conexão do ESP32 com o ROS (no ubuntu 20.04), seja por meio da conexão usb seja via wifi.
Arduino IDE para ESP32
Ref.: https://www.usinainfo.com.br/blog/programar-esp32-com-a-ide-arduino-tutorial-completo/
Em Preferências, cole a URL abaixo no campo de URLs adicionais:
ou
Com isso, permitimos que a IDE acesse uma pequena “base de dados” no formato .json que contém a configuração de inúmeras placas. Após isso, devemos acessar o menu Ferramentas → Placa → Gerenciador de Placas.
Instale a versão mais recente do driver que irá aparecer: “esp32 by Espressif Systems’
Selecione a placa ESP32 corretamente:
Instale os drivers de conexão no Ubuntu
sudo usermod -a -G dialout $USER && sudo apt-get install git && wget https://bootstrap.pypa.io/get-pip.py && sudo python get-pip.py && sudo pip install pyserial && mkdir -p ~/Arduino/hardware/espressif && cd ~/Arduino/hardware/espressif && git clone https://github.com/espressif/arduino-esp32.git esp32 && cd esp32 && git submodule update –init –recursive && cd tools && python3 get.py
Permissão de acesso/escrita para a porta usb no Ubuntu
sudo chmod a+rw /dev/ttyUSB0
Teste básico ESP32
Compile e faça o upload do seguinte código de teste, que fará o LED built-in piscar:
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
ROSserial ESP32
Fonte: https://github.com/sachin0x18/rosserial_esp32 e https://robotisim.com/2022/02/01/ros-communication-with-esp32-using-rosserial-package/
Na versão 2.0 do Arduino IDE, instale a biblioteca rosserial (Ferramentas ⇒ Gerenciador de bibliotecas)
Instale o ROS (Robot Operating System) seguindo todos os passos de: http://wiki.ros.org/noetic/Installation/Ubuntu
(opcional) Na sua máquina após instalar o ROS, crie um novo workspace que será usado para configurar e instalar o ROSserial
sudo apt install ros-noetic-rosserial
mkdir rosserial/src -p
cd rosserial/src
catkin_init_workspace
git clone https://github.com/sachin0x18/rosserial_esp32.git
cd ..
catkin_make
source devel/setup.bash
Vá até a pasta libraries do sketchbook do Arduino e faça o seguinte comando (será criada uma pasta ros_lib para os projetos feitos na IDE do Arduino):
rosrun rosserial_arduino make_libraries.py .
Altere as seguintes linhas do arquivo ros_lib/ros.h
De:
#if defined(ROSSERIAL_ARDUINO_TCP)
#include "ArduinoTcpHardware.h"
#else
#include "ArduinoHardware.h"
#endif
Para:
#if defined(ESP8266) or defined(ESP32) or defined(ROSSERIAL_ARDUINO_TCP)
#include "ArduinoTcpHardware.h"
#else
#include "ArduinoHardware.h"
#endif
Teste subscriber
Compile e faça o upload do código abaixo para o ESP32
/*
* rosserial Subscriber Example
* Blinks an LED on callback
*/
#include <ros.h>
#include <std_msgs/Empty.h>
ros::NodeHandle nh;
void messageCb( const std_msgs::Empty& toggle_msg){
digitalWrite(LED_BUILTIN, HIGH-digitalRead(LED_BUILTIN)); // blink the led
}
ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
nh.initNode();
nh.subscribe(sub);
}
void loop()
{
nh.spinOnce();
delay(1);
}
Iremos criar agora um novo ambiente (pasta de trabalho) para os projetos com ROS e iremos criar um nó em Python que publica uma informação vazia:
mkdir esp32_drivers/src -p
cd esp32_drivers/src/
catkin_init_workspace
catkin_create_pkg blinkLed rospy roscpp
mkdir blinkLed/scripts
Dentro da pasta scripts adicione um arquivo blinkLed.py com o código em Python do Publisher:
#!/usr/bin/env python
import rospy
from std_msgs.msg import Float32
if __name__ == '__main__' :
try :
pub = rospy.Publisher( 'esp32_data' , Float32, queue_size = 10 )
rospy.init_node( 'talker' , anonymous = True )
rate = rospy.Rate( 2 ) # 2hz
data = 0.0
while not rospy.is_shutdown():
rospy.loginfo(data)
pub.publish(data)
data += 0.1
if data > 1000 :
data = 0.0
rate.sleep()
except rospy.ROSInterruptException:
pass
Após salvar o arquivo, seguindo os comandos no prompt aberto:
chmod +x blinkLed/scripts/blinkLed.py
cd ..
catkin_make
source devel/setup.bash
Abra dois novos prompts de comando. No primeiro rode:
roscore
No segundo:
rosrun rosserial_python serial_node.py _baud:= 57600
Pronto! Você estará enviando um dado Float para Arduino via ROS no tópico /esp32_data. Ao receber o dado, o LED irá alternar entre ligado e desligado.
Enviando um comando de velocidade
Utilizando a mesma estrutura iremos criar um novo arquivo no arquino que recebe a informação no tópico /cmd_vel e que republica a mensagem de verificação no /vel_esp32received.
Então no computador iremos rodar um nó em python que lê a informação do teclado (turtlesim turtle_teleop_key) e republica no tópico /cmd_vel.
Código cmd_vel_controller.py:
#!/usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
vel_msg = Twist()
def callback (msg):
global vel_msg
vel_msg = msg #update info from turtle1_teleop
if __name__ == '__main__' :
try :
rospy.Subscriber( "turtle1/cmd_vel" , Twist, callback)
pub = rospy.Publisher( 'cmd_vel' , Twist, queue_size = 1 )
rospy.init_node( 'controller' , anonymous = True )
rate = rospy.Rate( 10 ) # 2hz
data = 0.0
while not rospy.is_shutdown():
#rospy.get_time()
rospy.loginfo(vel_msg)
pub.publish(vel_msg)
rate.sleep()
except rospy.ROSInterruptException:
pass
Lembre de dar permissão de execução. Por exemplo, considerando que o arquivo foi criado na pasta src/blinkLed/scripts:
chmod +x src/blinkLed/scripts/cmd_vel_controller.py
catkin_make
Código no ESP32
#include <ros.h>
#include <std_msgs/Float32.h>
#include <geometry_msgs/Vector3.h>
#include <geometry_msgs/Twist.h>
ros::NodeHandle nh;
geometry_msgs::Vector3 vel_msg;
ros::Publisher pub_vel_received("vel_esp32received", &vel_msg);
float vel_linear = 0.0;
float vel_angular = 0.0;
void callback_cmd_vel( const geometry_msgs::Twist& msg){
vel_linear = msg.linear.x;
vel_angular = msg.angular.z;
vel_msg.x = vel_linear;
vel_msg.z = vel_angular;
digitalWrite(LED_BUILTIN, HIGH-digitalRead(LED_BUILTIN)); // blink the led
}
ros::Subscriber<geometry_msgs::Twist> sub_vel("/cmd_vel", &callback_cmd_vel );
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
nh.initNode();
nh.advertise(pub_vel_received);
nh.subscribe(sub_vel);
}
void loop()
{
pub_vel_received.publish(&vel_msg);
nh.spinOnce();
delay(1/10); // 10Hz
}
Abra 4 terminais
roscore # terminal 1
rosrun rosserial_python serial_node.py _baud:= 57600 #terminal 2
rosrun turtlesim turtle_teleop_key # terminal 3 onde deve ser enviado os comandos pelo teclado
rosrun blinkLed cmd_vel_controller.py # terminal 4 (lembre-se de rodá-lo após habilitar o pacote com source devel/setup.bash)
Conexão via wifi
Compilar e fazer o upload do seguinte código .ino
#define ROSSERIAL_ARDUINO_TCP
#define ESP32
#include <WiFi.h>
#include <ros.h>
#include <std_msgs/Float32.h>
#include <geometry_msgs/Vector3.h>
#include <geometry_msgs/Twist.h>
const char* ssid = "colocar o nome da rede";
const char* password = "colocar a senha da rede";
// Defina o endereço IP do servidor do soquete (rosserial)
IPAddress server(192,168,0,116);
// Defina a porta do servidor do soquete (rosserial)
const uint16_t serverPort = 11411;
ros::NodeHandle nh;
geometry_msgs::Vector3 vel_msg;
ros::Publisher pub_vel_received("vel_esp32received", &vel_msg);
float vel_linear = 0.0;
float vel_angular = 0.0;
void callback_cmd_vel( const geometry_msgs::Twist& msg){
vel_linear = msg.linear.x;
vel_angular = msg.angular.z;
vel_msg.x = vel_linear;
vel_msg.z = vel_angular;
digitalWrite(LED_BUILTIN, HIGH-digitalRead(LED_BUILTIN)); // blink the led
}
ros::Subscriber<geometry_msgs::Twist> sub_vel("/cmd_vel", &callback_cmd_vel );
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
// Usando o monitor serial do ESP
Serial.begin(115200);
Serial.println();
Serial.print("Conectando em ");
Serial.println(ssid);
// conecta o ESP ao wifi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP Adress: ");
Serial.println(WiFi.localIP());
Serial.println("");
// Definindo a conexão com o servidor do soquete (rosserial)
nh.getHardware() -> setConnection(server, serverPort);
nh.initNode();
while (nh.getHardware() ->connected()!=1) {
nh.getHardware() -> setConnection(server, serverPort);
nh.initNode();
delay(50);
Serial.print(".");
}
Serial.print("\nOK TCP-IP = ");
Serial.println(nh.getHardware() -> getLocalIP());
//Serial.println(nh.getHardware() ->connected());
nh.advertise(pub_vel_received);
nh.subscribe(sub_vel);
}
void loop()
{
if (nh.getHardware() ->connected()) {
pub_vel_received.publish(&vel_msg);
nh.spinOnce();
delay(50); // 20Hz
}
else{
Serial.println("disconnected\n");
while (nh.getHardware() ->connected()!=1) {
nh.getHardware() -> setConnection(server, serverPort);
nh.initNode();
delay(50);
Serial.print(".");
}
}
}
Rodar os comandos anteriores, porém utilizando o parâmetro tcp para o rosserial_python:
rosrun rosserial_python serial_node.py tcp 11411