赞
踩
以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/Igm51siCI-4FUtumMWwW-w
ROS 作为一个非常优秀的机器人开发框架,内部各个功能逻辑被划分成各个节点(进程),而各个节点之间数据指令访问非常频繁,形式比如发布订阅主题、调用服务等。
那么在机器人外部如果想要访问其内部数据,比如做一个数据面板,实时监控机器人的各项传感器数据和地图轨迹,甚至遥控运动,又该如何从 ROS 内部获取或者发送数据呢?
有个 ROS 包就专门为解决上面的需求而生,Rosbridge 提供了基于 JSON 格式的数据访问 WebSocket 接口,方便非 ROS 软件和 ROS 系统通信。WebSocket 接口特别适用于服务器和客户端之间的高效通信,那么在启动了 Rosbridge 后,我们可以把 ROS 系统当作服务器后端使用,然后通过客户端对其访问。
话不多说,马上开始体验之旅~
Rosbridge 依赖于 ROS 的安装,目前 ROS 主要运行于 Linux 平台,并且已经迭代到了 ROS2 多个版本,目前主流 ROS 应用版本都是 ROS2 了。但很多教程都是介绍 ROS1,殊不知 ROS1 已经脱离实际使用市场了,皆因 ROS2 通信性能的巨大优化,后边八戒会有个专题介绍,敬请关注!
如果你还没安装好 ROS2,可以参看一下八戒之前写的《Linux 流畅安装 ROS2》。但是之前用的版本是 crystal,在 apt 里搜了下找不到对应的 Rosbridge 包,而且 Ubuntu 18 环境下只能找到 dashing 版本的 Rosbridge apt 安装包,所以决定升级系统到 Ubuntu 20 并且重新安装 ROS2 foxy。
为何选择 ROS2 的 foxy 版本呢?这个和底层的 DDS 通信协议实现版本有关,这里不展开了,后续介绍 ROS2 通信性能巨大优化时再聊。
参考《Linux: 流畅安装 ROS2》时,由于系统 Ubuntu 版本不一样,所以 apt 安装源需要注意变更为
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
# deb-src http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
安装 ROS 完成后记得在 ~/.bashrc 中追加启动环境,要不然每次启动终端都要手动载入一次环境
$ echo "source /opt/ros/foxy/setup.bash" >> ~/.bashrc
确认 ROS2 指令有效
$ ros2
只要 ros2 指令返回没有提示该指令不存在就表示环境 OK。
接着安装 foxy rosbridge 包
$ sudo apt install ros-foxy-rosbridge-server
在 ROS 内部收发消息的方式有很多,其中比较简单的方式就是创建一个主题(Topic),并在此基础上发布订阅消息。对于主题来说,发布就是以主题的名义发送消息,订阅就是只接收和主题相关的消息。
为了简单演示发布订阅的大概过程,下面用指令的形式执行。
创建一个节点,该节点订阅主题为 /browser_topic
而且类型为 std_msgs/String
的消息,同时打印接收到的消息
$ ros2 topic echo /browser_topic std_msgs/String
指令 topic 用于主题相关,子命令 echo 用于将主题的消息输出。
由于系统中还没有任何的其它节点被启动,也没有相应的主题消息被发布,所以订阅后暂时看不到任何的消息打印。
接着创建一个节点,该节点发布主题为 /browser_topic
和类型为 std_msgs/String
的消息
$ ros2 topic pub /browser_topic std_msgs/String "data: 'hello fellow, i am a robot'"
publisher: beginning loop
publishing #1: std_msgs.msg.String(data='hello fellow, i am a robot')
publishing #2: std_msgs.msg.String(data='hello fellow, i am a robot')
publishing #3: std_msgs.msg.String(data='hello fellow, i am a robot')
publishing #4: std_msgs.msg.String(data='hello fellow, i am a robot')
publishing #5: std_msgs.msg.String(data='hello fellow, i am a robot')
...
子命令 pub 用于发布主题消息,data: 'hello fellow, i am a robot'
是 JSON 格式数据作为主题 /browser_topic
的消息发送出去。发布消息的节点会循环发送消息,直到被终止。
如果需要发布消息的节点在发布一次消息后自动退出,可以在命令结尾添加 -1
。
发布消息的节点建立后,可以看到订阅主题 /browser_topic
的消息节点开始输出接收到的消息
data: hello fellow, i am a robot
---
data: hello fellow, i am a robot
---
data: hello fellow, i am a robot
---
data: hello fellow, i am a robot
---
data: hello fellow, i am a robot
---
...
众多的客户端中,数浏览器比较统一,而且浏览器的 API 接口极为丰富和易于使用,用于机器人的数据展示再适合不过了,那么如何实现机器人和浏览器之间的数据流动呢?如果浏览器和机器人在局域网内的不同终端里呢?
在 Rosbridge 出现之前,ROS 外界程序和 ROS 内部节点沟通只能通过比较底层的传输层协议通信,比如 TCP、UDP等。为了解决通信的麻烦,Rosbridge 提供了 websocket 接口开放给 ROS 外部程序访问,同时针对浏览器还提供了基于 Javascript 的库(roslibjs)方便网页调用 Rosbridge 的 websocket 接口。
roslibjs 库的官方下载链接
https://github.com/RobotWebTools/roslibjs/releases
Rosbridge 是一系列包的集合,通常以 rosbridge_suite 的开发包形式提供,如需要让 ROS 外界程序和 ROS 内部节点沟通,需要启动 ROS 包 rosbridge_server 以提供 websocket 接口支持。
$ ros2 launch rosbridge_server rosbridge_websocket_launch.xml
rosbridge_websocket_launch.xml
是启动 websocket 接口的配置文件。原来 rosbridge_server 默认的服务端口是 9090,如果需要修改成其它端口,可以修改这个配置文件的 port
字段对应的值,比如 9090
改为 9080
$ cat /opt/ros/foxy/share/rosbridge_server/launch/rosbridge_websocket_launch.xml
<launch>
<arg name="port" default="9080" />
<arg name="address" default="" />
<arg name="ssl" default="false" />
<arg name="certfile" default=""/>
<arg name="keyfile" default="" />
ROS 后端已经准备好,那么接下来就设计一个略微简陋的网页程序,提供访问 ROS 的 demo 面板。
这个页面主要功能设计要点:
连接本地主机的 rosbridge 服务,地址为 ws://localhost:9090
。
打开页面或者刷新页面后发布一次主题为 /browser_topic
的消息 “hi, robot”。
订阅主题为 /browser_topic
的消息,接收到消息后打印到页面。
按照标准 html 页面格式编写网页文件内容如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Browser communicate with robot via Rosbridge of ROS2</h1> <p>What will this page do:</p> <ul> <li>Connect rosbridge at <code>ws://localhost:9090</code></li> <li>Publish a "hi, robot" ROS messages to ros topic <code>/browser_topic</code> once after page opened or pressing F5</li> <li>Start subscribe ROS messages from ros topic <code>/browser_topic</code> & print it on this page</li> </ul> <p>View the full tutorial at <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzUxMTgxMzExNQ==&scene=124#wechat_redirect" target="_blank">公众号: ENG八戒</a></p> <hr/> <p>Connection: <span id="status" style="font-weight: bold;">N/A</span></p> <p><code>/browser_topic</code> messages sended: <ul id="messages_send" style="font-weight: bold;"></ul></p> <p><code>/browser_topic</code> messages received: <ul id="messages_recv" style="font-weight: bold;"></ul></p> <script type="text/javascript" src="../js/roslib.js"></script> <script type="text/javascript"> // TODO: Add custom JS code here </script> </body> </html>
启动当前网页看看
$ firefox html/index.html
这里调用火狐浏览器 firefox 来打开网页 index.html,换成其它主流浏览器也可以,比如 chrome 等。
index.html 页面虽然已经加载了 Javascript 库 roslibjs,但这时的网页还是静态页面,缺乏自定义的脚本代码实现功能逻辑,那么接下来尝试添加 Javascript 代码给页面添加上前面设计好的功能逻辑。
前面在设计页面的时候预留了添加功能逻辑代码的位置
<script type="text/javascript">
// TODO: Add custom JS code here
</script>
首先,使用 roslibjs 库提供的全局对象 ROSLIB 创建 ros 对象,并指定后端服务接口地址 ws://localhost:9090
const ros = new ROSLIB.Ros({ url: "ws://localhost:9090" });
localhost 表示服务器位于本地主机,端口是 9090。Javascript 返回的对象一般存储在 const 类型的变量中。
开始的时候提了个需求,如果浏览器和机器人在局域网内的不同终端里,浏览器又如何访问机器人呢?这个时候只需要把创建 ros 对象时 localhost
替换为 rosbridge_server 服务运行所在的主机的网络 IP 地址即可,比如
const ros = new ROSLIB.Ros({ url: "ws://192.168.234.151:9090" });
接着添加连接状态回调,一旦连接上显示 yes,连接关闭则显示 no,连接发生故障则显示 errored out 末尾追加故障信息。状态显示在 id 为 status 的页面 span 元素中
ros.on("connection", () => {
document.getElementById("status").innerHTML = "yes";
});
ros.on("error", (error) => {
document.getElementById("status").innerHTML = `errored out (${error})`;
});
ros.on("close", () => {
document.getElementById("status").innerHTML = "no";
});
使用全局对象 ROSLIB 创建主题为 /browser_topic
的对象,用于监听接收消息和主动发布消息,并且指定主题的消息类型为 std_msgs/String
const my_topic_object = new ROSLIB.Topic({
ros: ros,
name: "/browser_topic",
messageType: "std_msgs/String",
});
设置接收到主题消息后的执行回调,把接收到的消息作为字符串添加到 id 为 messages_recv
的页面 ul 元素中
my_topic_object.subscribe((message) => {
const ul = document.getElementById("messages_recv");
const newMessage = document.createElement("li");
newMessage.appendChild(document.createTextNode(message.data));
ul.appendChild(newMessage);
});
使用全局对象 ROSLIB 创建一个消息对象,数据用 JSON 格式封装。在打开页面或者刷新页面后,主动发送一次该消息,并且把消息的 data 字段值作为字符串添加到 id 为 messages_send
的页面 ul 元素中
var greet_msg = new ROSLIB.Message({
data : "hi, robot"
});
my_topic_object.publish(greet_msg);
{
const ul = document.getElementById("messages_send");
const newMessage = document.createElement("li");
newMessage.appendChild(document.createTextNode(greet_msg.data));
ul.appendChild(newMessage);
}
最后,重新打开页面 index.html 或者刷新该页面,看看执行效果
该页面调用 roslibjs 库在发布消息后,同时也会接收到自己发布出去的消息,因为订阅的主题和发布主题是一样的。
当然,接收到的消息也包含了从 ros 系统发布的同样主题的消息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。