目录
一、前言
最近的工作中需要用到ROS和多个ESP32单片机无线通讯(wifi)的需求,因此这里简单记录一下这个过程中遇到的坑,以供参考。
说来惭愧,由于本人不是计算机领域的学者,对于网络知识基础确实比较有限,之前一直以为ESP32只要和roscore在同一个局域网中,能ping通就能直接进行esp32上的ros节点和主机ros节点的通讯,今天实际上手之后吗,发现并没有那么简单,还是需要一些其他的设定才能实现这种通讯。不过好在最后发现其实也确实不算复杂,只是中间增加了一个socket工具和端口来进行设备间的通讯,稍加了解socket的工作原理后还是成功实现了这一工作。
二、ROS与单个ESP32的连接
ROS与ESP32连接可以参考ROS的官方例程,也就是rosserial_arduino,基本不需要太多修改,官方例程拿过来直接用即可。这里还是简述一下我开发环境及对应的流程:
2.1 ESP32端
我最常使用的ESP32开发环境是基于VS Code的Platform IO插件,首先新建一个ESP32的项目,并通过导入Platform IO平台导入库的功能导入Rosserial Arduino Library这个库
之后将库内包含的Esp8266例程复制粘贴到自己的main.cpp内即可,当然需要修改一些信息,下面贴出我的代码并对修改的内容进行说明:
- WIFI的头文件,例程是用的esp8266,我的硬件是esp32,根据自身硬件修改即可
- WIFI的SSID和密码,这个无需多说,连入主机(运行roscore的设备)所在的局域网即可,此处注意:需要和主机在同一网段
- 主机的IP地址,这里根据自身硬件情况设置即可,可在主机上通过指令ifconfig查看
- 其他的例如发布者的名称,topic的设定自己随便设定,我这里只是尝试一下功能,就不做复杂的修改了,就用的例程的代码。
#include <WiFi.h>
#include <ros.h>
#include <std_msgs/String.h>
const char* ssid = "LAB121";
const char* password = "12345678";
// Set the rosserial socket server IP address
IPAddress server(10,1,1,12);
// Set the rosserial socket server port
const uint16_t serverPort = 11411;
ros::NodeHandle nh;
// Make a chatter publisher
std_msgs::String str_msg;
ros::Publisher chatter("esp32dev", &str_msg);
// Be polite and say hello
char hello[13] = "hello world!";
void setup()
{
// Use ESP8266 serial to monitor the process
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
// Connect the ESP8266 the the wifi AP
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Set the connection to rosserial socket server
nh.getHardware()->setConnection(server, serverPort);
nh.initNode();
// Another way to get IP
Serial.print("IP = ");
Serial.println(nh.getHardware()->getLocalIP());
// Start to be polite
nh.advertise(chatter);
}
void loop()
{
if (nh.connected()) {
Serial.println("Connected");
// Say hello
str_msg.data = hello;
chatter.publish( &str_msg );
} else {
Serial.println("Not Connected");
}
nh.spinOnce();
// Loop exproximativly at 1Hz
delay(1000);
}
2.2 主机端
本人主机是实验室的jetson nano国产套件,运行ubuntu 18.04,估计其他的随便什么ubuntu都适用这一方法。
主机端就比较容易了,先运行:
roscore
然后运行
rosrun rosserial_python serial_node.py tcp
即可实现通讯,之后通过rostopic list即可查看到esp32发布的话题,也可以通过rostopic echo 查看话题内容,或通过其他节点同时也订阅这个话题。这里注意命令的最后有一个tcp的后缀,ros官方的例程大部分是通过有线的串口,这里写的是串口的地址。这里也是参考了其他大佬的方法:
(60条消息) 在Arduino平台上使用ESP8266:订阅ROS消息,通过tcp无线控制IO_庆钊你好呀的博客-CSDN博客
现在我们已经实现了单个ESP32与ros主机的通讯,下面就是多个esp32与主机的通讯。
三、ROS与多个ESP32的连接
这里用到了两个esp32设备,但是方法适用于更多的设备,但是由于ros官方的rosserial包似乎只支持最多25个Publishers和Subscribers,当然这个数字一般情况下是够用了,如果需要似乎还可以更大,这就需要更深入的学习,暂不考虑。
3.1 ESP32_1端
ESP32_1的代码与上述第二节一致,无需进行其他任何修改。
3.2 ESP32_2端
ESP32_2的代码如下,有两个地方与 ESP32_1的不同需要进行修改“
- 修改一个与 ESP32_1不同的主机端口号,即serverPort。(在前言提到过,esp32与ros主机的基于wifi的通讯实际上还是一种通过端口的通讯,只不过相较于常规的串口端口,这里用到了虚拟端口的概念。本人也不是网络方面的专业从业者,只是百度上简单搜索了一些资料过了这个坎就没再深入研究,因此这里就不在展开说这一问题了,可以搜索一些相关的资料自己学习。)rosserial默认的端口是11411,这里只要选择一个不同于11411的端口即可,但是别用一些其他进程正在使用的端口,可以在主机上通过netstat -anp指令查看端口占用情况,经过测试我这里11412是可以的;
- 修改一下topic的名称,ros不支持同样名字的topic。
-
#include <WiFi.h> #include <ros.h> #include <std_msgs/String.h> const char* ssid = "LAB121"; const char* password = "12345678"; // Set the rosserial socket server IP address IPAddress server(10,1,1,12); // Set the rosserial socket server port const uint16_t serverPort = 11412; ros::NodeHandle nh; // Make a chatter publisher std_msgs::String str_msg; ros::Publisher chatter("esp32pico", &str_msg); // Be polite and say hello char hello[13] = "hello world!"; void setup() { // Use ESP8266 serial to monitor the process Serial.begin(115200); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); // Connect the ESP8266 the the wifi AP WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // Set the connection to rosserial socket server nh.getHardware()->setConnection(server, serverPort); nh.initNode(); // Another way to get IP Serial.print("IP = "); Serial.println(nh.getHardware()->getLocalIP()); // Start to be polite nh.advertise(chatter); } void loop() { if (nh.connected()) { Serial.println("Connected"); // Say hello str_msg.data = hello; chatter.publish( &str_msg ); } else { Serial.println("Not Connected"); } nh.spinOnce(); // Loop exproximativly at 1Hz delay(1000); }
3.3 主机端
主机端这里就不能再直接通过命令行调用ros官方的rosserial节点来运行了,需要通过一个launch文件来运行:
<launch>
<node pkg="rosserial_python" type="serial_node.py" name="ESP32DEV" args="tcp 11411"/>
<node pkg="rosserial_python" type="serial_node.py" name="ESP32PCIO" args="tcp 11412"/>
</launch>
launch文件随便放在那个功能包都行,反正调用的也是官方rosseria节点,实际使用的时候可以卸载bringup.launch中。
这里需要注意的是,需要给两个节点起不同的名字,才能运行,此外最后的args也是需要与上述ESP32端的端口号一一对应。
最后展示一下运行结果,可以看到两个话题都能被rostopic list出来,且rostopic echo也能正常打印通讯内容,大功告成。
参考文献
Ubuntu查看端口占用情况_51CTO博客_查看端口占用情况linux
【Arduino】【ROS】rosserial连接两片Arduino 英伟达NVIDIA jetson nano ROS_哔哩哔哩_bilibili