- 首先两台电脑win下,都连接在同一局域网(WiFi)下,且在虚拟机Ubuntu 22.04 中安装ROS2。在ROS2中,理想情况是,多个机器上的不同节点只要在同一个ROS_DOMAIN_ID、同一个局域网下,既可以进行多机通讯,即在设备1上运行节点,在设备2上的命令行输入如下指令即可查看运行于设备1中的节点名称。但是我经过多次测试,设置虚拟机网络桥接等办法,最终结果是虚拟机1,电脑1,电脑2,虚拟机2,四个环境下可以相互ping通。(具体操作流程我参考了这位博主的内容,最终失败了ROS2多机通讯,基于两台VM虚拟机的多机通讯,通讯失败解决办法(适用于多台虚拟机或物理机间的互PING)_ros2 node 局域网-CSDN博客)
- 接下来我将把我设置的具体流程和大家一起交流分享一下,有些专业术语可能用词不当,还请大家一起交流。
一. 配置环境
1. 配置两台虚拟机的网络环境。点击虚拟机--->设置,进行下面设置。
2. 点击编辑--->虚拟网络编辑器,进行下面设置。
上面操作完成后,若没有弹出下面这个界面,请看一下任务栏,点击出来即可,接着进行下面操作。
安装上面操作后,两台虚拟机即可完成桥接模式,则相当于直接连接主机的物理端口,虚拟机使用独立的IP,虚拟机和主机在局域网中属于平级关系。
3. 查看两台虚拟机的ip,并检测是否可以相互ping通。
首先两台虚拟机进行网络重启
sudo systemctl restart NetworkManager.service
查看两台虚拟机的ip,其中 ens33 为虚拟机网口名称,192.168.1.59是为虚拟机设置的静态IP。
ifconfig
此时检测两台虚拟机是否可以相互ping通。此过程不需要关闭win防火墙,也不需要在 “网络和internet->状态->高级网络设置->网络和共享中心->更改高级共享设置”中启用网络发现和打印机共享(反正我没有设置也可以正常通信)。
ping 192.168.1.72
二.两台虚拟机通过ROS2进行通信
注意:下面指令中运行包含运行ROS2节点时,都需要加载文件,我没有加是因为把下面指令加入了 .bashrc 中!!!
source /home/df/ROS2/ws00_hell0world/install/setup.bash
source /home/df/ROS2/ws01_plumbing/install/setup.bash
电脑1 VMware下(.bashrc文件中包含 export ROS_DOMAIN_ID=6)
新终端1输入:
fastdds discovery --server-id 0 --ip-address 192.168.1.59 --port 14520
新建终端2:
export ROS_DISCOVERY_SERVER="192.168.1.59:14520"
export ROS_DOMAIN_ID=23
ros2 run cpp01_topic demo01_talker_str --ros-args --remap __node:=demo01_talker_remap_14520
电脑2 VMware下(.bashrc文件中包含 export ROS_DOMAIN_ID=5)
新终端1输入:
export ROS_DISCOVERY_SERVER="192.168.1.59:14520"
export ROS_DOMAIN_ID=23
ros2 run cpp01_topic demo02_listener_str --ros-args --remap __node:=demo02_listener_remap_14520
新建终端2:
export ROS_DISCOVERY_SERVER="192.168.1.59:14520"
rqt_graph
最终结果显示:
实验结果一(1):
验证 export ROS_DOMAIN_ID=23 对结果是否有影响?
将上述方法中的指令 export ROS_DOMAIN_ID=23 全部删除后,电脑1 VMware中默认(.bashrc文件中包含 export ROS_DOMAIN_ID=6),而电脑2 VMware中默认((.bashrc文件中包含 export ROS_DOMAIN_ID=5),但实验最终结果和实验一相同,都可以进行通信。实验结果表明ROS_DOMAIN_ID 是否相同对此方法无影响。这个我也不太了解希望知道的朋友可以一起讨论一下。
实验结果一(2):
通过实验结果rqt_graph可以看出,两个节点demo01_talker_remap_14520 ,demo02_listener_remap_14520 中间并没有显示话题 chatter。一开始猜测两节点通信是否和话题无关。但是最终测试发现两节点正常通信,仍然需要具有相同的话题!
- 单机下默认环境(终端开始未执行此指令 export ROS_DISCOVERY_SERVER="192.168.1.59:14520")下结果:
- 单机下更改talker_node_cpp中话题名称为 chatter_1后
- 更改talker_node_cpp中话题名称为 chatter_1后,双机联调后运行结果:电脑2 VMware下并未收到电脑1VMware 的数据,但是执行 rqt_graph 后发现和两虚拟机正常通信的图一样。
下图为电脑2VMware
电脑1VMware有数据发送
三.总结
电脑通过wifi在局域网里连接,虽然udp可以联通,但组播的方式仍收到限制,最后通过集中式的发现协议成功联通。
通过Wi-Fi连接的局域网环境可能会存在一些网络配置限制,特别是在使用UDP多播时。局域网的路由器和防火墙设置可能会影响多播通信的有效性,因此在某些情况下,您可能需要使用其他方法来确保通信的可靠性。
使用集中式的发现协议,例如通过配置发现服务器,是一种有效的方法,可以帮助确保节点之间的连接和通信。这种方式通过中央服务器来管理节点的注册和发现,从而克服了多播可能面临的限制。这种方法特别适用于复杂的网络环境,其中多播可能受到限制或不可靠。
在ROS 2中,Fast DDS的发现服务可以配置为使用中央发现服务器,以帮助节点在具有网络限制的环境中进行通信。这种方法提供了更高的可靠性,使节点能够轻松地找到和连接到其他节点,无论它们在网络中的位置如何。
总之,根据特定的网络环境和需求,可以选择适当的通信方式,包括UDP多播、中央发现服务器或其他方法,以确保ROS节点之间的可靠通信。
关于实验的节点代码是赵虚左老师视频教程的程序,感谢感谢!!!
demo01_talker_str.cpp
/*
需求: 以某个固定频率发送文本“helloworld!”,文本后缀编号,每发布一条,编号+1。
流程:
1.包含头文件;
2.初始化ROS2客户端;
3.自定义节点类;
3-1.创建发布方;
3-2.创建定时器;
3-3.组织并发布信息。
4.调用spin函数,传入自定义类对象指针;
5.释放资源;
*/
// 1.包含头文件;
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
// 3.自定义节点类;
class Talker: public rclcpp::Node{
public:
Talker():Node("talker_node_cpp"),count(0)
{
RCLCPP_INFO(this->get_logger(),"发布节点创建!");
// 3-1.创建发布方;
/*
模板:被发布的消息类型;
参数:
1.话题名称;
2.QOS(消息队列长度)。
返回值:发布对象指针。
*/
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter",10);
// 3-2.创建定时器;
/*
参数:
1.时间间隔;
2.回调函数;
返回值:定时器对象指针。
*/
timer_ = this->create_wall_timer(1s, std::bind(&Talker::on_timer, this));
}
private:
void on_timer()
{
// 3-3.组织并发布信息。
auto message = std_msgs::msg::String();
message.data = "hello world!" + std::to_string(count++);
RCLCPP_INFO(this->get_logger(), "发布方发布的消息:%s", message.data.c_str());
publisher_->publish(message);
}
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
size_t count;
};
int main(int argc, char ** argv)
{
// 2.初始化ROS2客户端;
rclcpp::init(argc, argv);
// 4.调用spin函数,传入自定义类对象指针;
rclcpp::spin(std::make_shared<Talker>());
// 5.释放资源;
rclcpp::shutdown();
return 0;
}
demo02_listener_str.cpp
/*
需求:订阅发布方发布的消息,并在终端输出。
流程:
1.包含头文件;
2.初始化ROS2 客户端;
3.自定义节点类;
3-1.创建订阅方;
3-2.解析并输出数据。
4.调用spin函数,并传入节点对象指针;
5.资源释放;
*/
// 1.包含头文件;
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
// 3.自定义节点类;
class Listener: public rclcpp::Node{
public:
Listener():Node("listener_node_cpp")
{
RCLCPP_INFO(this->get_logger(), "订阅方创建!");
// 3-1.创建订阅方;
/*
模板:消息类型;(发布使用什么消息类型,接收就用什么消息类型)
参数:
1.话题名称;(和发布方保持一致)
2.QQS,队列长度;
3.绑定一个回调函数。
返回值:订阅对象指针。
*/
Subscription_ = this->create_subscription<std_msgs::msg::String>("chatter", 10, std::bind(&Listener::do_cb, this, std::placeholders::_1));
}
private:
void do_cb(const std_msgs::msg::String &msg)
{
// 3-2.解析并输出数据。
RCLCPP_INFO(this->get_logger(), "订阅到的消息是:%s", msg.data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr Subscription_;
};
int main(int argc, char const *argv[])
{
// 2.初始化ROS2 客户端;
rclcpp::init(argc, argv);
// 4.调用spin函数,并传入节点对象指针;
rclcpp::spin(std::make_shared<Listener>());
// 5.资源释放;
rclcpp::shutdown();
return 0;
}