我的AI之路(26)--使用ROSBridge WebSocket和roslibjs構建一個簡單的控制機器人的web demo

Arnold-FY-Chen發表於2019-02-09

     在一個複雜的機器人後端控制平臺系統開發完成以前,往往需要對你的機器人產品進行簡單的軟體架構設計驗證或進行控制測試,這時,如果能花比較少的時間快速做一個web頁面或者一個Android app來作此用途的話能節省不少人力,本人花比較少的時間做了一個web demo和幾個安卓app用於不同機器人的通訊和控制的驗證和測試,先只說怎麼做web demo,實現安卓app的思路跟基於Java EE開發機器人控制平臺的思路比較相似,關鍵技術是使用Java WebSocket建立與機器人上的Rosbridge之間的雙向長連線和實現自定義的應用協議與rosbridge v2.0 Protocol 之間的轉換來實現應用和ROS通訊的解耦合,就先不說了,後面有空再說,先只說說怎麼做一個簡單的web demo。

    前面說過,ROSBridge提供了使用tcp/udp/websocket連線呼叫機器人上的ROS來實現對機器人的控制的途徑,ROSBridge提供了WebSocket Server可以連線,那麼寫一個web頁面使用websocket連線到Rosbridge遵循rosbridge v2.0 Protocol 與ROS進行互動就可以控制機器人了,roslibjs就是個可以用於此目的工具庫,roslibjs是ROS JavaScript庫,看它的原始碼就知道,實際上它的下層就是使用websocket與Rosbridge進行通訊,寫過JavaScript的對ROS有一定了解後可以用它很快實現一個頁面來控制機器人,另外,如果頁面中如果還需要有2D、3D視覺化效果的話,還需要用到ros2djs和ros3djs,此外還需要用到一些相關javascript支援庫。

    具體步驟:

    1.首先當然得安裝rosbridge:

      sudo apt-get install ros-kinetic-rosbridge-suite

     2.獲取相關JavaScript庫(我把收集到的這些js庫和引用到的相關css檔案打包上傳到這裡了,設定了5積分下載為防惡意訪問):

      獲取roslib.js,ros3d.min.js,ros2d.min.js(也可以從https://static.robotwebtools.org下載)

      git clone https://github.com/RobotWebTools/roslibjs.git

      git clone https://github.com/RobotWebTools/ros2djs.git

      git clone https://github.com/RobotWebTools/ros3djs.git

       把下面這些js檔案下載到本地並且與roslib.js、ros3d.min.js,ros2d.min.js放到同一個目錄scripts下供稍後web頁面使用:

       https://static.robotwebtools.org/threejs/current/three.js
       https://static.robotwebtools.org/threejs/current/ColladaLoader.js
       https://static.robotwebtools.org/threejs/current/STLLoader.js
       https://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js
       https://static.robotwebtools.org/roslibjs/current/roslib.js

       https://static.robotwebtools.org/ros2djs/current/ros2d.min.js
       https://static.robotwebtools.org/ros3djs/current/ros3d.min.js
       https://static.robotwebtools.org/keyboardteleopjs/current/keyboardteleop.js

       https://github.com/rctoris/mjpegcanvasjs/blob/develop/src/MjpegCanvas.js

       另外:https://github.com/mrdoob/three.js 這裡可以找到JavaScript 3D的全部js檔案。

       roslib.js,ros2d.js,ros3d.js的API可以這裡查:

        roslibjs APIs: http://robotwebtools.org/jsdoc/roslibjs/current/
        ros2djs APIs: http://robotwebtools.org/jsdoc/ros2djs/current/
        ros3djs APIs: http://robotwebtools.org/jsdoc/ros3djs/current/

    3.如果需要用到模擬視訊監控,還需下載和編譯安裝一個支援HTTP streaming of ROS
images的web_video_server包:

      cd ~/catkin_ws/src

      git clone https://github.com/RobotWebTools/web_video_server.git

      cd ..

      catkin_make

     4. 主要程式碼 (可以參考<<ROS Robotics Projects>>一書的第12章對應的html原始碼,尤其可以在speechcommands.html的基礎上修改,不過要注意的是,speech_commands示例程式碼已被作者早就從https://github.com/qboticslabs/ros_robotics_projects上刪除,需要從歷史版本中下載,事實上這個上面的不少程式碼都已經不能直接使用,因為有些檔案或者引用的檔案都找不著了,需要自己想辦法琢磨解決錯誤,所以僅供參考,下載後恐怕多半跑不起來 :) ):

        (1)包含用到的js和css:

           <link rel="stylesheet" type="text/css" href="scripts/jquery-ui.css"/>
          <link rel="stylesheet" type="text/css" href="scripts/bootstrap.min.css"/>
          <link rel="stylesheet" type="text/css" href="scripts/font-awesome.min.css"/>

          ...

          <script type="text/javascript" src="scripts/jquery.min.js"></script>
         <script type="text/javascript" src="scripts/bootstrap.min.js"></script>
         <script type="text/javascript" src="scripts/jquery-ui.min.js"></script>
         <script type="text/javascript" src="scripts/eventemitter2.min.js"></script>
         <script type="text/javascript" src="scripts/roslib.min.js"></script> 
         <script type="text/javascript" src="scripts/bootbox.min.js"></script>
         <script type="text/javascript" src="scripts/three.js"></script>
         <script type="text/javascript" src="scripts/ColladaLoader.js"></script>
        <script type="text/javascript" src="scripts/STLLoader.js"></script>
        <script type="text/javascript" src="scripts/ColladaLoader2.js"></script>
        <script type="text/javascript" src="scripts/ros3d.min.js"></script>
        <script type="text/javascript" src="scripts/mjpegcanvas.js"></script>
        <script type="text/javascript" src="scripts/keyboardteleop.js"></script>

      (2) 對speech_commands.html做的主要修改(改變連線控制和增加了3D視覺化控制元件):

        function connect() {
            var connectButton;

            if (connected) {            // disconnect
                ros.close();
            } else {
                if(hasEverConected){
                   bootbox.alert ("請重新整理頁面後再連線!");  //因為roslibjs和ros3djs沒有資源管理和釋放功能,所以不得不這麼限制!否則每次連線頁面會重複建立Viewer object!
                   return;
                }
                robotUrl = document.getElementById("robotUrlEntry").value.trim();
                if (robotUrl == '') {
                    bootbox.alert ("請輸入URL和port");
                    return;
                }
                robotUrl = robotUrl.replace("https:", "wss:");
                robotUrl = robotUrl.replace("http:", "ws:");
                if ((robotUrl.slice (0,5) != "wss://") && (robotUrl.slice (0,4) != "ws://") &&
                        (robotUrl.charAt(robotUrl.length - 5) != ":")) {
                
                    var r = bootbox.alert 
                        ("URL應該以http, https, ws或wss開頭, 並以埠結尾, 例如 ':9090'.");
                        return;
                }

                ros = new ROSLIB.Ros({    // Connecting to ROS.
                    url: robotUrl                             
                }); 

                //////////////////////////////建立視覺化控制元件////////////////////////////////////////////////
                ip = location.hostname;
                if(ip=="")ip="localhost";
                res_path= "http://"+ip+"/share";

                if(!hasEverConected)
                {            

                    viewer = new ROS3D.Viewer({
                      background : 000,
                      divID : 'urdf',
                      //width : 1280,
                      //height : 600,
                      width : 800,
                      height : 400,
                      antialias : true

                    });
                 
                    // Add a grid.
                    viewer.addObject(new ROS3D.Grid());
                    
                   // Create the Stream viewer.
                    streamViewer = new MJPEGCANVAS.MultiStreamViewer({
                      divID : 'mjpeg',
                      host : 'localhost',
                      width : 800,
                      height : 400,
                      host : ip,
                      port : 8080,
                      interval : 200,
                      topics : [ '/camera/rgb/image_raw', '/camera/rgb/image_raw',
                      '/camera/rgb/image_raw' ],
                      labels : [ 'Robot View', 'Left Arm View', 'Right Arm View' ]

                    });

                 hasEverConected=true;
               }
             
            // Setup a client to listen to TFs.
            tfClient = new ROSLIB.TFClient({
                      ros : ros,
                      fixedFrame : base_frame,
                      angularThres : 0.01,
                      transThres : 0.01,
                      rate : 10.0
                    });

                // Setup the URDF client.                  
             urdfClient = new ROS3D.UrdfClient({
                      ros : ros,
                      tfClient : tfClient,
                      //path : 'http://resources.robotwebtools.org/',
                      path : res_path,
                      rootObject : viewer.scene,
                      loader : ROS3D.COLLADA_LOADER
                    });
                             
            // Initialize the teleop.
             teleop = new KEYBOARDTELEOP.Teleop({
                      ros : ros,
                      topic : teleop_topic
                    });

                  
        } //else

            ros.on('connection', function() {
                    localStorage.robotUrl = robotUrl;
                    connectButton = document.getElementById("connectButton");
                    connectButton.innerHTML = "斷開";
                    connectButton.style.background="#00cc00";            // green
                    say ('connected');
                    connected = true;
                    console.log ('Connected to websocket server.');
                });

            ros.on('error', function(error) {
                 console.log (error);
                 bootbox.alert ('Error connecting to websocket server. ');
            });

            ros.on('close', function() {
                if (connected) {            // throw away a second call
                    connected = false;
                    connectButton = document.getElementById("connectButton");
                    connectButton.style.background = "#006dcc";    
                    connectButton.innerHTML = "連線"
                    say ('connection closed');   
                    console.log('Connection to websocket server closed.');
                }
            });

        } //function connect()    

    4.需要用到3D視覺化效果,那麼還需要安裝tf2_web_republisher,tf2_web_republisher的作用是計算TF資料並通過rosbridge_server發給ros3djs client(ROSLIB.TFClient 例項),ROSLIB.TFClient物件訂閱來自tf2_web_republisher的TF資料並更新ROS3D.Viewer物件,ROS3D.Viewer物件將URDF model在瀏覽器裡視覺化:

       cd ~/catkin_ws/src
       git clone https://github.com/RobotWebTools/tf2_web_republisher
       sudo apt-get install ros-kinetic-tf2-ros
       cd ~/catkin_ws
       catkin_make

    5.頁面裡用到3D效果,如果urdf檔案也放在機器人上(或者模擬機器人所在的Linux伺服器上):

         var urdfClient = new ROS3D.UrdfClient({
            ros : ros,
            tfClient : tfClient,
            path : 'http://localhost/share',
            rootObject : viewer.scene,
            loader : ROS3D.COLLADA_LOADER
          });

      那麼還需安裝個http sever,比如安裝apache,下面是安裝apache2並修改配置和啟動:

       sudo apt-get install apache2

       #把urdf所在的資料夾share通過連結放到apache的頂級目錄下

       ln -s /opt/ros/kinetic/share /var/www/html

       cd /etc/apache2
       vi apache2.conf 

       #做如下修改:

       <Directory /var/www/>
         Options Indexes FollowSymLinks
         AllowOverride None
         Require all granted
         Header set Access-Control-Allow-Origin http://localhost/share
      </Directory>

       #Enabling module headers. otherwise, "Invalide command Header" will be seen when you starting apache2.service

       sudo a2enmod headers

       #start apache   

       systemctl restart apache2.service

       

     如果apache裡不做上面的Allow-Control-Allow-Origin設定,會報跨域訪問錯誤:

    Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/share/turtlebot_description/meshes/stacks/hexagons/pole_bottom.dae. (Reason: CORS request did not succeed).

    做了上面的改動後,Firefox可以了,但Chrome瀏覽器還不行, 需下載最新谷歌跨域擴充套件外掛,在谷歌瀏覽器輸入:chrome://extensions,然後把下載好的檔案拖入chrome://extensions頁面,點選安裝即可完成安裝;不需要特殊設定,就可以完成跨域請求了。

   6. 啟動模擬機器人及相關節點和Server的命令:

      roslaunch turtlebot_gazebo turtlebot_world.launch
      rosrun web_video_server web_video_server
      rosrun tf2_web_republisher tf2_web_republisher
      roslaunch rosbridge_server rosbridge_websocket.launch

   7.拷貝demo目錄(在speech_commands包上做的修改:補齊了原來缺失的js檔案和根據需要修改或增加了speech_commands.html的程式碼)到apache下並訪問它:

      cp -rf demo /var/www/html

      #假設模擬機器人所在的伺服器的IP是192.168.1.133,在瀏覽器裡輸入下面的地址即可開啟demo並操控機器人

      http://192.168.1.133/demo/demo.html

      

      

相關文章