ROS是一个强大的机器人操作系统,它提供了许多方便的功能来让机器人更容易开发和使用。但每当我需要查看某个话题内容的时候,我就需要在一堆终端中找到显示话题内容的那个终端,或者新开一个。这实在是不够优雅,本着遇见彩虹(bushi吃定困难的精神!能不能使用一个第三方设备,比如说ipad或者手机来实时显示话题内容或者显示更多东西?当然这个第三方设备一定不会是和主机有线连接的,也最好不需要在设备上安装什么软件。那什么方案能解决以上问题捏?当然是web服务!

一、系统架构

系统架构主要包括以下几个部分:

    1. ROS(Robot Operating System):ROS 是你的机器人应用程序的核心,负责处理如导航、感知、控制等任务,并通过 ROS 主题发布消息。在我们的案例中,我们关注的是 ROS 主题 your_topic

      ,它发布 std_msgs/String

       类型的消息。

    2. Flask 应用:Flask 应用是你的 Web 服务器,它提供了一个 Web 界面可以让用户通过浏览器进行交互。它还运行了一个 ROS 节点,订阅了 your_topic

       主题,然后将收到的消息通过 WebSocket 连接发送给客户端。

    3. Flask-SocketIO:Flask-SocketIO 是 Flask 应用的一个扩展,它提供了 WebSocket 功能。在 Flask 应用中,当接收到来自 ROS 主题的消息时,Flask-SocketIO 会将消息转发给所有连接的 WebSocket 客户端。

    4. Web 客户端:Web 客户端是用户的浏览器,它加载了一个包含 JavaScript 代码的 HTML 页面。这个 JavaScript 代码创建了一个 WebSocket 连接到 Flask 应用,并监听来自服务器的消息。当接收到消息时,它将消息内容打印到浏览器的控制台。

这个系统的工作流程如下:

    1. Flask 应用启动,创建一个 ROS 节点并订阅 your_topic  主题。
    2. 用户在浏览器中打开 Flask 应用的 URL,浏览器加载 HTML 页面并运行 JavaScript 代码。
    3. JavaScript 代码创建一个 WebSocket 连接到 Flask 应用。
    4. 当 ROS 通过 your_topic  主题发布消息时,Flask 应用的 ROS 节点会接收到消息。
    5. Flask 应用通过 WebSocket 连接将消息发送给浏览器。
    6. 浏览器接收到 WebSocket 消息,并将消息内容打印到网页。

二、环境安装

  1. 安装rosbridge-server

    ROS Bridge是一个ROS包,可以让非ROS应用通过网络接口访问ROS服务。安装如下:

    sudo apt-get install ros-<rosdistro>-rosbridge-server
    其中`<rosdistro>`应被替换为你的ROS版本,例如`melodic`,`noetic`或`kinetic`。

  1. 安装并设置Flask

    Flask是一个轻量级的Web服务器框架,可以用来构建你的web服务。首先,你需要安装Flask和Flask-SocketIO,可以通过pip来安装:

pip install flask flask_socketio

小tips:安装失败的大部分原因都是pip没有换源捏

  1. 使用roslibjs或者roslibpy连接到rosbridge的示例(本博客不使用这个方法)

    roslibjs是一个JavaScript库,可以在网页中使用,用于连接到rosbridge并订阅ROS话题。以下是一个基本的示例:

var ROSLIB = require('roslib');

var ros = new ROSLIB.Ros({
  url : 'ws://localhost:9090'
});

var topic = new ROSLIB.Topic({
  ros : ros,
  name : '/your_topic',
  messageType : 'std_msgs/String'
});

topic.subscribe(function(message) {
  console.log('Received message on ' + topic.name + ': ' + message.data);
  topic.unsubscribe();
});
```

在这个例子中,你需要替换`/your_topic`为你想要订阅的话题的名称,`std_msgs/String`为你的话题的消息类型。

你可以在Flask应用中集成这段JavaScript代码,然后当你访问你的IP地址时,你的浏览器将会连接到rosbridge,订阅你指定的话题,并在控制台打印出接收到的消息。

  1. 三、功能实现
    1. 创建一个简单的Flask服务器,如下所示:
      from flask import Flask, render_template
      from flask_socketio import SocketIO
      
      app = Flask(__name__)
      socketio = SocketIO(app)
      
      @app.route('/')
      def sessions():
          return render_template('session.html')
      
      if __name__ == '__main__':
          socketio.run(app, host='0.0.0.0', port=5000)

这将创建一个在0.0.0.0:5000上运行的服务器,并在访问时返回一个名为session.html

的页面。我们需要创建这个HTML文件,以便Flask能找到它。

    1. 创建一个WebSocket连接

      WebSocket连接将用于实时传输ROS主题的信息。你需要在Flask服务器中添加一些代码来处理WebSocket连接。

      在Flask服务器代码中添加以下代码:

      @socketio.on('connect')
      def connect():
          print('Client connected')
       
    2. 将ROS节点和WebSocket连接
    3. 新建一个ROS节点订阅指定的话题,并将ROS节点和WebSocket连接。你可以通过在callback 函数中添加一行代码来实现这一点,该函数将消息发送到WebSocket。
      #!/usr/bin/env python
      import rospy
      from std_msgs.msg import String
      from flask import Flask, render_template
      from flask_socketio import SocketIO, emit
      from threading import Thread
      
      # 创建 Flask 应用
      app = Flask(__name__)
      app.debug = True
      socketio = SocketIO(app)
      
      @app.route('/')
      def sessions():
          return render_template('session.html')
      
      @socketio.on('connect')
      def connect():
          print('Client connected')
      
      # 全局变量,用于存储 ROS 节点是否已经初始化
      ros_initialized = False
      
      # 定义 ROS 回调函数
      def callback(data):
          rospy.loginfo(rospy.get_caller_id() + " I heard %s", data.data)
          # 当收到 ROS 消息时,通过 WebSocket 广播消息
          socketio.emit('message', {'data': data.data})
      
      # 定义 ROS 监听器
      def listener():
          global ros_initialized
          if not ros_initialized:
              rospy.init_node('listener', anonymous=True)
              ros_initialized = True
          rospy.Subscriber("your_topic", String, callback)
          rospy.spin()
      
      # 定义 Flask 应用线程
      def flask_thread():
          socketio.run(app, host='0.0.0.0', port=5000, use_reloader=False)
      
      if __name__ == '__main__':
          rospy.init_node('listener', anonymous=True)
          Thread(target=listener).start()
          Thread(target=flask_thread).start()
    4. 在HTML页面中显示消息

      最后,你需要在HTML页面中显示这些消息。你可以使用JavaScript和Socket.IO客户端库来实现这一点。

      在你的session.html

      文件中,添加以下代码:

      <!DOCTYPE html>
      <html>
          <head>
              <title>ROS Topic Viewer</title>
              <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
              <script type="text/javascript">
                  var socket = io.connect('http://' + document.domain + ':' + location.port);
                  socket.on('message', function(msg) {
                      $('#log').append('<p>' + msg.data + '</p>');
                  });
              </script>
          </head>
          <body>
              <div id="log"></div>
          </body>
      </html>

四、功能测试

  1. 运行ROS节点
  2. 手动发布测试话题
  3. 通过第三方设备访问端口大功告成!我们已经成功实现了从web页面查看指定的话题内容的基础功能。但也许可以开发一个功能包使我们能够在web页面优雅的查看整个ros中的话题、话题内容、消息格式、以及节点与节点直接的话题订阅关系?