Only released in EOL distros:  

websocket_gui

Package Summary

A declarative widget library based on ros.js

websocket_gui

Package Summary

A declarative widget library based on ros.js

Overview

Using a robot, you want to display information provided by the platform and its sensors. Some tools like webui and QGroundControl already exist but own dependencies with ROS and/or others plug-in. This implicates to install additional software on your visualization computer which could be constraining.

In order to avoid that problem, the remote lab project developed a robot web application wviz based on the websocket protocol. However, this API is still low level and requires some ROS and JavaScript backgrounds.To make things easier for the user, we developed the websocket_gui stack.

websocket_gui furnishes several widgets to display information from ROS messages in a web browser based on rosjs. Its low level communication protocol is managed by the rosbridge node.

List of widgets :

Architecture

Overall

To run this stack, you need the following infrastructure which is quite simple. On the left side, you have your robot with ROS. Rosbridge is a necessary node to create the interface between ROS and your web browser from the client side. When the connection is opened and data are received by the client side, they are displayed calling widgets based on web libraries like JavaScript, HTML5 and CSS3.

infrastructure_150x150_p3.png

Widget concepts

With these widgets, you can display several information, and/or data in the same screen with different types of display. To make things easier for the user, the HTML structure of your page also called Document Object Model is built automatically. If you are familiar with this concept, you can jump to the Widgets section. Otherwise take a look to the next section.

HTML5 and CSS3

HTML and CSS are programming languages used to create web pages. They have been invented by Tim Berners-Lee and Håkon Wium Lie in the 90's. Few years later, the first one created the World Web Consortium W3C which is the reference website (I hope your future Holly Bible) in this field.

  • HTML handles and organizes the content of your web page. It describes what must be display on the page using tags (title, header, footer, paragraph ....).

    A web page is divided in several elements. The document's body is defined by the <body> tag. It contains all the content of an HTML document such as text, picture, video ..... In the body, you can insert <div> tag. They define a division or a section in a HTML document. In our case, each widget defines its own <div> element. You just have to add the widget to the <body> of you HTML page. That's an example of four widgets display in a web browser.

4_widgets_custom_s1.png

  • CSS manages appearance of elements composing your web page. For example, their position, colour-background, font-size, height, width ...

JavaScript

JavaScript is the programming language of scripts for the Web. It has been developed in Netscape, by Brendan Eich. It has to be directly included in the web page and make a HTML page more dynamic for the user. Using this stack, you will be confronted to two JavaScript functions:

  • innertHTML
    This property returns the inner HTML of an HTMLObject element. You need to call this function on the body element to add a widget in your HTML page. The following example displays "/topic_name" on your entire page each 300 milliseconds:

       1    document.body.innerHTML+=DisplayTopic("width:100%;height:100%;","/topic_name",300)
    

    For further details, take a look to innerHTML.

  • dictionary
    This object contains keys on which values are associated. It looks like to a dictionary in Python or C++. This object will be given as argument to some widgets. Following examples show two differents syntax to create the same dictionary:
    Example 1:

       1    var altimeter_dict=
       2    {
       3       "value_start":-6,
       4       "value_end":6,
       5       "value_inc":1,
       6       "id":"altimeter"
       7    };
    

    This notation will be taken as "convention" in this wiki.
    Example 2:

       1    var altimeter_dict= new Object();
       2    altimeter_dict.value_start=-6;
       3    altimeter_dict.value_end=6;
       4    altimeter_dict.value_inc=1;
       5    altimeter_dict.id="altimeter";
    

    For further details, take a look to dictionary.

Widgets

This section lists available widgets.

Altimeter

This widget looks like to a plane altimeter. A needle rotates according the value of the parameter specified in option. At the bottom of this widget, a counter displays the current value.

   1  document.body.innerHTML+=Altimeter(style,topic_name,delay_ms,dictionary)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : ROS topic name (example "/imu_node") string

  • delay_ms : Period in milliseconds to update altimeter. -1 can be chosen and will update the widget each time a new message is received by ROS. integer

  • dictionary : Some parameters are required for this widget because it can not have in advance the range of value to display. To create a dictionary in JavaScript take a look to .5 keys are required, 1 is optional. Two angles are necessary. They delineate the area on which the needle will move. By convention, clockwise rotation is defined positive. The origin is defined as theta equal zero on a unit circle.

    • "theta_start" : angle in radian starting the scale_division. integer or float

    • "theta_end" : angle in radian ending the scale_division. integer or float

    • "value_start" : first value of the scale division. integer or float

    • "value_end" : last value of the scale division. integer or float

    • "value_inc" : increment for the scale division. integer or float

    • "key"(OPTIONAL) : By default, the widget will search for "height" or "altitude" in messages published on the topic given in topic_name. If you need an other key, give it here. string

This widget resizes itself according the height and the width of the body element. It finds the optimal scale to keep its ratio. Moreover the altimeter is centred in its div.

DisplayImage

This widget display images from a ROS image topic. Two modes are available. Firstly, you can stream video as another message type from ROS using rosjs (included in rosbridge). In order to increase performance benefits mjeg_server has been developed. It's a streaming server that subscribes to a ROS image topic, publishes data as MJPEG streams via HTTP and displays it in your browser.

   1 document.body.innerHTML+=DisplayImage(style,topic_name,period_ms,port_opt)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : topic name in ros (example "/cam"). string

  • period_ms : Period in milliseconds to update image. -1 can be chosen and will update the widget each time a new message is received by ROS. integer

  • port_opt (OPTIONAL) : If you give a port, the mjpeg_server mode will be used. The period_ms is not used because the streaming server adapts its period following the publishing period of the ros topic. integer It requires to launch the mjpeg_server node manually.

This widget resizes itself according the height and the width of the body element. It finds the optimal scale to keep the image ratio.

DisplayTopic

This widget displays a topic like the rostopic echo command in ROS.

   1 document.body.innerHTML+=DisplayTopic(style,topic_name,period_ms)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : topic name in ros (example "/cam"). string

  • period_ms : Period in milliseconds to update image. -1 can be chosen and will update the widget each time a new message is received by ROS. integer

The size of the font is defined by default.

Plot

It plots one or several data from a topic according the timestamps contained in the message header. If the message does not own timestamps, a pseudo time is automatically created.This widget uses a JavaScript library flot.

   1 document.body.innerHTML+=Graph(style,topic_name,period_ms,dictionary)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : topic name in ROS (example "/imu_node") string

  • period_ms : Period in milliseconds to update altimeter. -1 can be chosen and will update the widget each time a new message is received by ROS. integer

  • dictionary : Some parameters are required for this widget in order to allowed the user to custom its graph. To create a dictionary in JavaScript take a look here.3 keys are required. Let's take a ros message in example.

       1  header: 
       2    seq: 31640
       3    stamp: 
       4      secs: 1322223101
       5      nsecs: 967478037
       6  height: 5.127
       7  angular_velocity: 
       8    x: 0.279339946782
       9    y: 0.186924762889
      10    z: -0.0646644487864
      11  angular_velocity_covariance: [0.006,0.0,0.0,0.0,0.006,0.0,0.0,0.0,0.006]
    
    • "key" : array containing names of value to plot. array of string Example 1 : Plot the height

         1   var plot_dict=
         2   {
         3     "key":["height"]
         4   };
      

      Example 2 : Plot the secs

         1   var plot_dict=
         2   {
         3     "key":["header.stamp.secs"]
         4   };
      

      Example 3 : Plot the first parameter of the angular_velocity_covariance

         1   var plot_dict=
         2   {
         3     "key":["angular_velocity_covariance.0"]
         4   }
      

      Example 4 : Plot angular_velocity vector

         1   var plot_dict=
         2   {
         3     "key":["angular_velocity.x","angular_velocity.y","angular_velocity.z"]
         4   };
      
    • "color" : array containing color for each value. array of string You must have as much colors as key elements. You can define a color using three different ways (name, hexadecimal or RGB notations). For all color references, visit w3schools.com.
      The following code colors vector components from the last example respectively in red, green and blue.

         1   var plot_dict=
         2   {
         3     "color":["rgb(255,0,0)","rgb(0,255,0)","rgb(0,0,255)"]
         4   };
      
    • "nb_pt" : maximal number of points to plot simultaneously . integer or float

This widget adapts its scale division for a proper display and resizes itself automatically.

Log

This widget displays a console showing main steps of the process. A color code is established to display information, warning and error. Two sources can provide messages, ROS (as rxconsole) and JavaScript.

   1 document.body.innerHTML+=Log(style,topic_name,bool_rxconsole,bool_js)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : topic name in ros (example "/imu_node") string

  • bool_rxconsole : Bool for rx_console information. bool

  • bool_js : Bool for JavaScript information. bool

RosTurtle

This widget displays a turtle drawing a square and its trajectory like turtlesim

   1 document.body.innerHTML+=RosTurtle(style,teleop_turtle)
  • style : CSS style (width and height in % or px are wished). string

  • teleop_turtle : display arrows panel to control the turtle. (bool, default:false)

Display the turtle and its trajectory sensed from /turtle1/pose . If teleop_turtle set to true you can control the turtle clicking on arrow or using arrows on your keyboard. When the webbrowser is resized the trajectory is clear.

Battery

This widget displays a battery and the battery_voltage.

   1 document.body.innerHTML+=Battery(style,topic_name,period_ms,dict)
  • style : CSS style (width and height in % or px are wished). string

  • topic_name : topic name in ros (example "/imu_node") string

  • period_ms : Period in milliseconds to update altimeter. -1 can be chosen and will update the widget each time a new message is received by ROS. integer

  • dictionary : Some parameters are required for this widget in order to allowed the user to custom its battery display. To create a dictionary in JavaScript take a look to . 3 keys are required, 1 is optionnal

    • "value_min" : Lower value reachable by the battery. (integer or flot, default:0)

    • "value_max" : Higher value reachable by the battery. (integer or flot , default:12)

    • "safety_threshold" : Threshold value delimiting the green range and the orange/red range. (integer or flot , default:("value_max"-"value_min")/2)
      The green color is selected for value contained in [threshold;value_max[
      The orange color is selected for value contained in [value_min+50%*(threshold-value_min),threshold[
      The red color is selected for value contained between [value_min;value_min+50%*(threshold-value_min)[

    • "display_value" : Display battery voltage under the battery (bool, default:false)

    • "key" (OPTIONAL) : Key associated to the battery value in your ros message. (String,default keys:"battery","battery_voltage", "battery_lvl")

If no key "key" is present in the dictionary, the widget searches for default key iteratively. If no key is found, an error message is written in the Log widget.

Installation

To use the websocket_gui, you will need the trunk of the websocket_gui stack: and one additional package such as rosbridge.

In order to download and build the websocket_gui stack, open a terminal and follow these steps:

  Show EOL distros: 

   1 #Go to your working ros directory
   2 cd somewhere/in/my/ros/folder
   3 
   4 #Clone the brown_remotelab stack containing rosbridge package in this directory
   5 git clone --recursive https://brown-ros-pkg.googlecode.com/svn/trunk/distribution/brown_remotelab
   6 #Build rosbridge
   7 cd brown_remotelab/rosbridge
   8 make
   9 
  10 #Clone the websocket_gui stack in this directory
  11 git clone --recursive https://github.com/ethz-asl/websocket_gui
  12 #Build it
  13 cd websocket_gui
  14 make
  15 
  16 #Install dependencies for the Map widget
  17 #gdal library (http://www.gdal.org/)
  18 sudo apt-get install gdal-bin python-gdal libgdal-doc
  19 #Mapserver library (http://mapserver.org/)
  20 sudo apt-get install cgi-mapserver mapserver-doc mapserver-bin python-mapscript php5-mapscript
  21 #Test mapserv for cgi by typing in your favorite browser:
  22 http://localhost/cgi-bin/mapserv
  23 #You should obtain the following answer:
  24 "No query information to decode. QUERY_STRING is set, but empty." 
  25 
  26 #Now your installation is successful, let's play with some widgets 

   1 #This installation supposed you have set your ros environment and created a ros workspace.
   2 
   3 #Add the brown_remotelab stack containing rosbridge package to the overlay
   4 roslocate info brown_remotelab | rosws merge
   5 
   6 #Add the websocket_gui package to the overlay
   7 roslocate info websocket_gui | rosws merge
   8 
   9 #Update repositories in the overlay
  10 rosws update
  11 
  12 #Build packages
  13 rosmake websocket_gui
  14 rosmake rosbridge
  15 
  16 #Install dependencies for the Map widget
  17 #gdal library (http://www.gdal.org/)
  18 sudo apt-get install gdal-bin python-gdal libgdal-doc
  19 #Mapserver library (http://mapserver.org/)
  20 sudo apt-get install cgi-mapserver mapserver-doc mapserver-bin python-mapscript php5-mapscript apache2
  21 #Test mapserv for cgi by typing in your favorite browser:
  22 http://localhost/cgi-bin/mapserv
  23 #You should obtain the following answer:
  24 "No query information to decode. QUERY_STRING is set, but empty." 
  25 
  26 #Now your installation is successful, let's play with some widgets 

TOtest.

Quick start

First example with a well known turtle ...

This example works with the [[http://www.ros.org/wiki/turtlesim|turtlesim] node used in the ros_tutorial. A turtle pops and draws a rectangle. The altimeter displays the turtle angle.

Open you favourite terminal and write the following commands:

 roslaunch websocket_gui turtle_draw_rectangle.launch

This launch file starts the turtlesim and the draw_square nodes.

Then open in firefox or google chrome somewhere/in/my/ros/folder/websocket_gui/examples/html/turtle_drawing_rectangle.html.

Well this is a really basic example, it would be more funny if we could teleop the turtle... Okay, so let's do it

Second example to teleop the turtle

Let's take the first example and improve it in order to control the turtle's movements.

If you ran the first example, you can stop the last launch file.

Open you favourite terminal and write the following commands:

 roslaunch websocket_gui turtle_teleop.launch

It's the same launch file compared to the previous except that the draw_square node has been removed.

A turtle pops and does not move but don't be too sad,.

Open in firefox or google chrome somewhere/in/my/ros/folder/websocket_gui/examples/html/turtle_teleop.html.

You can teleop the turtle pressing your keyboard arrays or clicking on the arrays drawn into the page.

Turtle examples were fun but quite simplistic. Now let's talk about a more practical example.

Third example with a micro-helicopter

For this example we will not use the turtlesim package. Our inputs will be provided by a rosbag which is a sample of a dataset. It was acquired on a micro-helicopter platform by a team from the ETHZ. To run this example properly, you need to install the following packages for the rosbag: - ethzasl_sensor_fusion - asctec_mav_framework Open you favourite terminal and write the following commands:

   1  #Go to your working ros directory
   2  cd somewhere/in/my/ros/folder
   3  #Download ethzasl_sensor_fusion
   4  git clone git://github.com/ethz-asl/ethzasl_sensor_fusion
   5  #Download asctec_mav_framework
   6  git clone https://github.com/ethz-asl/asctec_mav_framework
   7  #Build packages
   8  rosmake ethzasl_sensor_fusion
   9  rosmake asctec_mav_framework
  10  #It's done! Now you just have to start the launch file
  11  roslaunch websocket_gui helicopter.launch
  12  #It launches the rosbridge node and plays the rosbag in loop.

Open in firefox or google chrome somewhere/in/my/ros/folder/websocket_gui/examples/html/helicopter.html.

In this example the map uses maps.google API and is online which could present difficulties if you don't have access on your experiment place.

A tool has been developed to download tiles from the internet and create your own local map. It will be present in the next example.

Forth example with a micro-helicopter and an offline map

In order to process the tiles downloaded and create the "map" server, some packages must be installed.

Open you favourite terminal and write the following commands:

   1  #Install mapserver packages http://mapserver.org/
   2  sudo apt-get install mapserver-bin mapserver-doc cgi-mapserver python-mapscript php5-mapscript
   3  #Install gdal libraries http://www.gdal.org/
   4  sudo apt-get install gdal-bin
   5  #go to the script directory
   6  cd somewhere/in/my/ros/folder/websocket_gui/tools/Map/script
   7  #Execute script
   8  python GetMap.py 47.418912 8.566297 1123 674 -z 0.25 -s 256 -a
   9  # Open in firefox or google chrome ''somewhere/in/my/ros/folder/websocket_gui/examples/html/helicopter2.html
  10  

Files and Organization

This part parses the websocket_gui directory and details all files purpose. If you want to create your own widget, it will help you to find out how this API works.

In your terminal, write the following command

 cd websocket_gui
 ls

You obtain:

websocket_gui.png

Let's make a review of the main files and directories.

build

Contains websocket_gui.js generated from the Makefile and grouping all the JavaScript API.

examples

examples_ls.png

  • css
    Contains style.css defining some css properties for the body and the widget class elements of the html pages.

  • html
    Contains a list of html pages for each widgets.

  • launch
    Contains the launch file necessary to run the turtle_example.html

  • pictures
    Contains several pictures necessary for some widgets like RosTurtle

js

js_ls.png

Contains all the JavaScript API.

  • flot
    Contains the flot API. It's a pure JavaScript plotting library for jQuery.

  • process.js
    This file is the spine of the API.

    • - Firstly, it instantiates the WebSocketGui class function.

         1   function WebSocketGui()
         2   {
         3    //ROS
         4    this.topics=[]; //array of ros topic
         5    this.types=[];  //types associated to topics
         6    // Socket server 
         7    if (window.location.protocol == "file:")
         8    {
         9     this.address="localhost";
        10    }
        11    else
        12    {
        13     this.address=window.location.hostname;
        14    }
        15    this.port="9090";
        16    this.connection=""; //connection object from rosjs (rosbridge)
        17    //list of handlers
        18    this.handlers=[]; //array containing Handlers objects
        19    this.widgets=[]; //array containing WidgetWithTask objects
        20    this.resize_tasks=[]; //array containing all resize tasks
        21    //process_state
        22    this.ros_topics_available=false;
        23    this.RosJsHdl_created=[];
        24    this.widget_handler_created=false;
        25    //current widget_id
        26    this.id_widget=0;
        27    //Verbose for log
        28    this.verbose=false;
        29   }
      
      - Then, it creates the websocket connection and manages it. When the connection is open, 4 tasks are run.
         1   initialize_ros //Stores list of ros topics and types in websocket_gui_obj
         2   create_widget_handlers //Creates or updates '''Handler''' obj for each element in websocket_gui.widgets 
         3   create_rosbridge_handlers //Creates "callback" for all topics contained in  websocket_gui.handlers
         4   call_rosbridge_services // Subscribes to all topics contained in websocket_gui.handlers
         5 
      
  • process_factory.js
    Contains functions used in process.js like:

    • Handler()

         1   function Handler(topic_name,period_ms)
         2   {
         3    this.topic=topic_name;  //one topic per handler object
         4    this.period_ms=period_ms; //one period to get messages from "this.topic"
         5    this.tasks=[]; //array of tasks for each widgets related to "this.topic"
         6    this.rsp=""; //current ros message published on "this.topic"
         7    this.add_task=function (task) // push a new task in "this.tasks"
         8    {
         9     this.tasks.push(task);
        10    };
        11    this.run_tasks=function() //execute list of tasks contained in "this.tasks"
        12    {
        13     for (var id_task in this.tasks)
        14     {
        15      this.tasks[id_task]();
        16     }
        17    };
        18   }
      
      Each instance of this class is associated to only one ROS topic. It stores tasks of all widgets and the current ros_msg.
    • resize_widgets()
      Resizes all widgets contained in the html page owning a resize task.

  • rosbridge_wsview_v2.js
    Contains rosbridge_wsview functions.They have been upgraded to handle more image format for the DisplayImage widget.

  • websocket_gui.js
    Contain the load script function to add some additional JavaScript source to your html5 page. It's an alternative version to the MakeFile.

  • widgets
    widgets.png All this JavaScript widgets files are build on the same scheme. There are 4 functions to underline:

    • WidgetClass()
      This function contains all parameters required to ensure the good functioning of the widget.

    • Widget()
      This function must be called and added dynamically to a part a your HTML5 document. It's general running is detailed in the next part.

    • widget_ref
      This variable contains an anonymous function defining task(s) to execute when new messages from ROS related to this widget are received. These tasks can be compared to actions contained in a ROS callback function.

    • resize_widget_task()
      This function will re-size the widget when the document's body of your HTML page is re-sized.

  • widgets_factory.js
    Contains functions called by the widgets like :

    • WidgetWithTask()

         1   function WidgetWithTask(topic,period_ms,div_id,task,task_name,resize_task_function)
         2   {
         3    this.topic=topic; //topic related to this widget
         4    this.period_ms=period_ms; //period related to this widget
         5    this.task=task; //create or update handler for this topic
         6    this.task_name=task_name; // Name of the widget
         7    this.div_id=div_id; //"widget_i" with i the number-1 of widget  present in your page
         8    this.run_task=function() //Execute this.task with the proper parameters
         9    {
        10     his.task(this.topic,this.period_ms,this.div_id);
        11    }
        12    if (resize_task_function) //define a resize_task
        13    {
        14     this.resizetask=function()
        15     {
        16      resize_task_function(this.div_id);
        17     }
        18    }
        19   }
      
      Each widget is associated to a ros topic,
    • create_task_for_widget(topic_name)
      It return a task (function) which will be stored in the WidgetWithTask object. This function checks if the topic_name exists. If it does, this function creates or updates the Handler object associated to this topic.

    • create_resize_task_for_widget()
      It returns a task(function) which will be stored in the WidgetWithTask object. This function will resize the widget.

API functioning

Opening a html page, two process will be executed in parallel. The first one is link to your widget: Firstly the widget function creates a WidgetWithTask Object. A widget_task and a resize_widget_task functions are store in this object. Then the WidgetWithTask object is added the websocket_gui_obj which is a instance of WebSocketGui. Finaly the <div> element associated to the widget is returned.

During this time,

Report a Bug

You can use our issue tracker

Wiki: websocket_gui (last edited 2013-01-11 12:48:16 by BenoitLescot)