ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

ROSBRIDGE and robotwebtools.org will have everything you need. Both have tutorials to make it easy. I have remote interface that runs fine in safari on iPhone, chrome on android and windows, and firefox on ubuntu. In my case tested from one state to another(1500 Km) using controls and streaming video with minimal lag.

ROSBRIDGE and robotwebtools.org will have everything you need. Both have tutorials to make it easy. I have remote interface that runs fine in safari on iPhone, chrome on android and windows, and firefox on ubuntu. In my case tested from one state to another(1500 Km) using controls and streaming video with minimal lag.

UPDATE after additional question:

My setup is personal hobby, so I just use an old laptop for the server running apache2 on ubuntu. It works fine for my limited case.

Nodes running on the laptop:

  • Full navigation stack and robot interface. AMCL, movebase, map server etc all standard. Robot interface is custom.

  • A few custom house keeping nodes that use voice and keyboard inputs to control robot state(sleeping, idle, navigating, etc) and issue goals to navigation stack, decode cmd_vel, etc.

  • Raspicam is running on a raspberry pi with camera to get video stream. I strongly suggest you find a different source than raspberry pi for video. Major pain to get working.

Nodes used to support the web interface:

  1. web_video_server

  2. robot_pose_publisher

  3. image_transport republish compressed in:=/raspicam_node/image/ out:=/convertedimage

  4. rosbridge_server rosbridge_websocket.launch

The HTML code below is one of the pages that displays the video stream, shows the location of the robot in the map, and has buttons for sending robot to predefined locations. The output from these buttons get published to a topic that is used for voice control local to the robot so the buttons get handled as though they are voice commands being issued to the robot from someone standing right there. The robot will not obey any voice command not preceeded by "robot" so the function for the buttons puts a "robot" on the topic prior to sending the intended location request. These buttons will not help you with your application but serve as one example on how to put info to topic from web interface.

One note: the file "Ros2D.js" from the robotwebtools.org site needs to be in the folder with your index.html file. I'm sure I am doing this is a non-standard way but have no need to fix it.

I am doing this using ROS Jade on Ubuntu 14.04.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://static.robotwebtools.org/EaselJS/current/easeljs.js"></script>
<script type="text/javascript" src="http://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js"></script>
<script type="text/javascript" src="http://static.robotwebtools.org/roslibjs/current/roslib.min.js"></script>
<script src="https://static.robotwebtools.org/roslibjs/current/roslib.js"></script>
<script src="Ros2D.js"></script>

<script type="text/javascript">
  var pi_img = new Image(); 
  var ip = location.host;
</script>

<script type="text/javascript" type="text/javascript">
  var ip = String(location.host);
  var x = 0;
  var z = 0;
  var scale = 0;
  var scale_max = 0.2;
  var out_message = "";

  // Connecting to ROS
  // -----------------
  var newURL = "ws://" + ip + ":9090";
  var ros = new ROSLIB.Ros({
    url : newURL
  });

  ros.on('connection', function() {
    console.log('Connected to websocket server.');

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // ----------------------------------------------------------------------
        // Subscribing to the robot's Pose
        // ----------------------------------------------------------------------

        // The ROSLIB.Topic handles subscribing and publishing a ROS topic. This
        // topic interacts with the /robot_pose topic, published by the robot.
            var poseTopic = new ROSLIB.Topic({
                ros         : ros,
                name        : '/robot_pose',
                messageType : 'geometry_msgs/Pose'
            });

        // Subscribes to the robot's pose. When rosbridge receives the pose
        // message from ROS, it forwards the message to roslibjs, which calls this
        // callback.
            console.log('Before callback');
            poseTopic.subscribe(function(message) {
            console.log('In pose subscribe callback');

        // Formats the pose for outputting.
            var now = new Date();
            var position = 'x: ' + message.position.x
                + ', y: ' + message.position.y
                + ', z: 0.0';
            var orientation = 'x: ' + message.orientation.x
                + ', y: ' + message.orientation.y
                + ', z: ' + message.orientation.z
                + ', w: ' + message.orientation.w;
            });

        // Create the main viewer.
            var viewer = new ROS2D.Viewer({
                divID : 'map',
                width : 400,
                height : 300
            });

        // Setup the map client.
            var gridClient = new ROS2D.OccupancyGridClient({
                ros : ros,
                rootObject : viewer.scene
            });

        // Scale the canvas to fit to the map
            gridClient.on('change', function() {
                viewer.scaleToDimensions(gridClient.currentGrid.width, gridClient.currentGrid.height);
                viewer.shift(gridClient.currentGrid.pose.position.x, gridClient.currentGrid.pose.position.y);
                displayPoseMarker();
            });

        // ----------------------------------------------------------------------
        // Showing the pose on the map
        // ----------------------------------------------------------------------

            function displayPoseMarker() {
        // Create a marker representing the robot.
                var robotMarker = new ROS2D.NavigationArrow({
                  size : 12,
                  strokeSize : 1,
                  fillColor : createjs.Graphics.getRGB(255, 128, 0, 0.66),
                  pulse : true
                });
                robotMarker.visible = false;
                console.log('creating robotMarkr: ');

        // Add the marker to the 2D scene.
                gridClient.rootObject.addChild(robotMarker);
                var initScaleSet = false;

        // Subscribe to the robot's pose updates.
                var poseListener = new ROSLIB.Topic({
                  ros : ros,
                  name : '/robot_pose',
                  messageType : 'geometry_msgs/Pose',
                  throttle_rate : 100
                });

                poseListener.subscribe(function(pose) {

        // Orientate the marker based on the robot's pose.
                  console.log('Got Pose data:', pose.position.x, pose.position.y );  
                  robotMarker.x = pose.position.x;
                  robotMarker.y = -pose.position.y;
                  console.log('Pose updated: ', robotMarker.x);
                  if (!initScaleSet) {
                    robotMarker.scaleX = 1.0 / viewer.scene.scaleX;
                    robotMarker.scaleY = 1.0 / viewer.scene.scaleY;
                    initScaleSet = true;
                  }
                  robotMarker.rotation = viewer.scene.rosQuaternionToGlobalTheta(pose.orientation);
                  robotMarker.visible = true;
                });
            } // end display pose marker
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }); // end ROS.On connection

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

  ros.on('close', function() {
    console.log('Connection to websocket server closed.');
  });


  function sendLocation(out_message){

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

    var out_string = new ROSLIB.Message({});

    out_string.data = 'robot';

    outputTopic.publish(out_string);

    out_string.data = out_message;

    outputTopic.publish(out_string);
    console.log('output publisher: ' + out_string.data);

  } // end sendLocation


</script>

</head>

<body onload="init()">

  <h1>Robot Web Interface</h1>
  <h2>Allows remote access to the family robot.</h2>
  <p>Choose a location to send the robot.</p>
    <input type="button" id="kitchen" value="kitchen" onclick="sendLocation(value);" />

    <input type="button" id="front door" value="front door"  onclick="sendLocation(value);" />

    <input type="button" id="bar"   value="bar"  onclick="sendLocation(value);" />

    <input type="button" id="coffee table"  value="coffee table"  onclick="sendLocation(value);" />

    <input type="button" id="dining table"  value="dining table"  onclick="sendLocation(value);" />

  <p></p>

 <div id="map"></div>

  <p></p> 

    <iframe src= '' id= 'target_frame' name='target_frame' width="410" Height="310"></iframe>
  <p></p>

  <script>
    // put the video stream into the target frame
    document.getElementById('target_frame').src = "http://" + ip + ":8080/stream?topic=/convertedimage";
  </script>

</body>

</html>