Getting Started

This tutorial will take you through the steps up setting up a virtualized operating system for programming with ROS and writing a basic controller which moves a wheeled robot about its environment.

Table of Contents

1 Set up Virtualbox

Install Virtualbox and the Virtualbox extension pack. You can download it here.

Next, download the Ubuntu disk image (~7GB):

    http://www.cs.bham.ac.uk/research/projects/poplog/robot-club/Ubuntu-10.10.vdi
This is also available here, but download is likely to be much slower.

Once that is finished, start Virtualbox and click on the new machine icon. The image size is ~7gb, so be patient.

./vb_new_machine.png

Now, you can click through the installer. I used the name "Ubuntu 10.10", but you may use whatever you want. You may have to select the operating system manually if you use an alternative name.

When you get to the section titled "Virtual Hard Disk", select the "Use existing hard disk" radio button and select the previously downloaded disk image.

./vb_choose_vdi.png

You're almost done now. Before you start the machine, click on the "Settings" button next to the new machine icon. There are some settings worth changing. If you have the memory, you probably want to increase the amount of memory. For running in simulation, you need to enable the 3D acceleration of video. I usually bump the memory up to 20mb as well.

./vb_settings.png

Okay, now you're ready! Start up the virtual machine and you will be greeted with a login screen. The password is hacker.

./vb_startup.png

Onward!

2 Download the BARC repository

We're going to download the repository into our ROS workspace. You can find this at ~/ros. To verify that ROS will be able to find it, let's check our ROS_PACKAGE_PATH. This is list of places where ROS will look for the programs we write. Open a terminal (CTRL-ALT-T shortcut) and verify this:

hacker@barc:~$ echo $ROS_PACKAGE_PATH
/home/hacker/ros:/opt/ros/electric/stacks

And so there it is.

We can now move to this directory and checkout the repository. This will ask you to verify the certificate. If you select permanently, be careful about sharing your disk image with others, because your password may be stored on the system.

hacker@barc:~$ cd ros
hacker@barc:~/ros$ svn checkout https://codex.cs.bham.ac.uk/svn/nah/robotclub/trunk barc
Error validating server certificate for 'https://codex.cs.bham.ac.uk:443':
 - The certificate is not issued by a trusted authority. Use the
   fingerprint to validate the certificate manually!
Certificate information:
 - Hostname: codex.cs.bham.ac.uk
 - Valid: from Wed, 29 Apr 2009 13:15:48 GMT until Sun, 29 Apr 2012 13:15:48 GMT
 - Issuer: Educational CA, Cybertrust, BE
 - Fingerprint: 8d:67:17:91:c7:02:9a:65:04:76:52:df:c7:2c:3b:58:c3:fc:f7:3c
(R)eject, accept (t)emporarily or accept (p)ermanently? p
Authentication realm: <https://codex.cs.bham.ac.uk:443> Subversion Projects Repository
Password for 'hacker':
Authentication realm: <https://codex.cs.bham.ac.uk:443> Subversion Projects Repository
Username: YOUR_USERNAME
Password for 'YOUR_USERNAME':
A    barc/guide_bot
.
.
.
A    barc/nao
Checked out revision 92.

Now, let's test the system. Open another terminal and start the ROS master server:

hacker@barc:~$ roscore
... logging to /home/hacker/.ros/log/b26bc95c-f764-11e0-9ff1-080027808671/roslaunch-barc-4678.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://barc:59763/
ros_comm version 1.6.2

SUMMARY
========

PARAMETERS
 * /rosversion
 * /rosdistro

NODES

auto-starting new master
process[master]: started with pid [4693]
ROS_MASTER_URI=http://barc:11311/

setting /run_id to b26bc95c-f764-11e0-9ff1-080027808671
process[rosout-1]: started with pid [4706]
started core service [/rosout]

Back in your original terminal, you can make sure that the simulated environment works by running:

hacker@barc:~/ros/barc$ roslaunch barc_stage pioneer_stage.launch

You should see the ground floor of the computer science building with a robot in the center. Play around with the views, for example turning on the data and going into perspective mode.

./stage.png

Almost there!

3 Set up rosjava

The version of rosjava in the Ubuntu repository is out of date and broken, so it have to install it from their repository. First, you have to remove the old version and then you can install the current version.

hacker@barc:~$ sudo apt-get remove ros-electric-rosjava-core
hacker@barc:~$ cd ros/sources
hacker@barc:~/ros/sources$ hg clone https://rosjava.googlecode.com/hg/ rosjava_core
hacker@barc:~/ros/sources$ rospack profile && rosstack profile
hacker@barc:~/ros/sources$ rosmake rosjava

Okay, that's done now. Towards greatness!

4 Writing your first piece of robot code

4.1 Create the project

Now comes time for the real fun! You're going to write a simple Braitenberg machine that avoids walls. It does this rather simply, by splitting its laser scan in half and using each half to calculate the excitation to the wheels.

hacker@barc:~/ros/barc/barc_stage$ cd ~/ros
hacker@barc:~/ros$ roscreate-pkg braitenberg rosjava std_msgs sensor_msgs geometry_msgs nav_msgs
Created package directory /home/hacker/ros/braitenberg
Created package file /home/hacker/ros/braitenberg/Makefile
Created package file /home/hacker/ros/braitenberg/manifest.xml
Created package file /home/hacker/ros/braitenberg/CMakeLists.txt
Created package file /home/hacker/ros/braitenberg/mainpage.dox

Please edit braitenberg/manifest.xml and mainpage.dox to finish creating your package

Now you have to edit some files for rosjava to work correctly.

First, change into the new Braitenberg directory and then edit the Makefile:

hacker@barc:~/ros$ cd braitenberg/
hacker@barc:~/ros/braitenberg$ gedit Makefile &

You have to replace the line in the Makefile with this snippet:

:include $(shell rospack find rosjavabootstrap)/rosjava.mk

Now, you have to add the build.xml file. You can create and open the file by doing:

hacker@barc:~/ros/braitenberg$ touch build.xml
hacker@barc:~/ros/braitenberg$ gedit build.xml &

Now paste this into the file:

<?xml version="1.0" encoding="UTF-8"?>
<project name="." default="default">

  <property file="ros.properties" />
  <property name="build" location="build" />

  <property name="dist" location="dist" />
  <property name="build" location="build" />
  <property name="src" location="src" />

  <path id="classpath">
    <pathelement path="${ros.compile.classpath}" />
  </path>

  <echo message="${toString:classpath}" />

  <target name="default" depends="init, compile" />

  <target name="init">
    <fail unless="ros.compile.classpath" message="ros.properties is missing.  Please type 'rosmake' first "/>
    <mkdir dir="${build}" />
    <mkdir dir="${dist}" />
  </target>

  <target name="compile" depends="init">

    <javac destdir="${build}">
      <classpath refid="classpath" />
      <src path="${src}" />
    </javac>
  </target>

  <target name="clean">
    <delete dir="${build}" />
    <delete dir="${dist}" />
  </target>

  <!-- required entry point -->
  <target name="test" />

</project>

Okay, now you can make the package. The reason you build the package for a Java project is because rosmake needs to build the jars for each message type you depend on, as listed in your manifest.xml file. After the initial call to rosmake, you can just use ant to build your project. This saves a lot of time.

hacker@barc:~/ros/braitenberg$ rosmake

As part of the build process, rosmake generates Eclipse project files and some other system specific files. When you start working with the repository, you'll want to make sure you set the svn:ignore properties to exclude these files from being submitted.

4.2 Coding the Braitenberg machine

To get started, we first have to install Netbeans (or whatever editor you prefer).

hacker@barc:~/ros/braitenberg$ sudo apt-get install netbeans

Once that is installed, start Netbeans. Once Netbeans is started, navigate to File > Import project > Eclipse project. Choose to import the project ignoring the project dependencies. Make the input and output folders the braitenberg package.

./netbeans_import.png

Create a package called barc. Inside the package, make a Java file named BraitnebergMover. Now make the class implement NodeMain. This is the interface that specifies node behavior and create the empty implementations of the required methods. Your class should look like this:

package barc;

import org.ros.node.NodeConfiguration;
import org.ros.node.NodeMain;

/**
 *
 * @author hacker
 */
public class BraitenbergMover implements NodeMain {

    public void main(NodeConfiguration nc) throws Exception {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void shutdown() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}

Now that you have the skeleton, it's time to flesh it out a bit. First, we need some fields for the class. We need a field of type Node, which will be the way this class communicates to the ROS XML-RPC server. You also need to create a Publisher of type Twist will which publish our movement messages to subscribing nodes. The publisher will publish on the cmd_vel topic, which is listened to by other noes which control the robot.

After the fields are initialized, you need to subscribe to messages published to base_scan of type LaseScan. This is the laser data that the robot will use to guide its movement. Inside the subscriber method, make an anonymous class to call the callback method move.

Finally, you need to implement the shutdown method. This is simply involves calling node.shutdown(). When you're done, the changed code should like this:

private Node node;
private Publisher pub;

public void main(NodeConfiguration nc) throws Exception {
    node = new DefaultNodeFactory().newNode("braitenberg", nc);
    pub = node.newPublisher("cmd_vel", "geometry_msgs/Twist");

    node.newSubscriber("base_scan", "sensor_msgs/LaserScan", new MessageListener<LaserScan>() {
        @Override
        public void onNewMessage(LaserScan scan) {
            move(scan);
        }
    });
}

public void shutdown() {
    node.shutdown();
}

The interesting part of the code is creating the move method. This is where you get to tell the robot how to move.

This is a very basic way to move the robot. Essentially, each half of the lase scan is summed into its own variable. Then the difference between these two halves is used to calculate the rotation in the z-axis. Also, a slight forward motion is always applied just to keep the robot going along. The method looks like this:

private void move(LaserScan scan) {
        double left = 0.0, right = 0.0;

        // get space on each side
        for (int i = 0; i < scan.ranges.length; i++) {
            double range = (scan.ranges[i] == 0.0) ? scan.range_max : scan.ranges[i];
            if (i < scan.ranges.length / 2) {
                left += range;
            } else {
                right += range;
            }
        }

        // publish a twist command to move towards the area of greatest space
        Twist move = new Twist();
        move.linear.x = 0.1;
        move.angular.z = (right - left) / (scan.ranges.length) * 0.5;
        pub.publish(move);
}

So, in the end, your code should look like this:

package barc;

import org.ros.internal.node.DefaultNode;
import org.ros.message.MessageListener;
import org.ros.message.geometry_msgs.Twist;
import org.ros.message.sensor_msgs.LaserScan;
import org.ros.node.DefaultNodeFactory;
import org.ros.node.Node;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeMain;
import org.ros.node.topic.Publisher;

/**
 *
 * @author hacker
 */
public class BraitenbergMover implements NodeMain {

    private Node node;
    private Publisher<Twist> pub;

    public void main(NodeConfiguration nc) throws Exception {
        node = new DefaultNodeFactory().newNode("braitenberg", nc);
        pub = node.newPublisher("cmd_vel", "geometry_msgs/Twist");

        node.newSubscriber("base_scan", "sensor_msgs/LaserScan", new MessageListener<LaserScan>() {

                @Override
                    public void onNewMessage(LaserScan scan) {
                    move(scan);
                }
            });
    }

    private void move(LaserScan scan) {
        double left = 0.0, right = 0.0;

        // get space on each side
        for (int i = 0; i < scan.ranges.length; i++) {
            double range = (scan.ranges[i] == 0.0) ? scan.range_max : scan.ranges[i];
            if (i < scan.ranges.length / 2) {
                left += range;
            } else {
                right += range;
            }
        }

        // publish a twist command to move towards the area of greatest space
        Twist move = new Twist();
        move.linear.x = 0.1;
        move.angular.z = (right - left) / (scan.ranges.length) * 0.5;
        pub.publish(move);
        System.out.println("Published: " + move);
    }

    public void shutdown() {
        node.shutdown();
    }
}

4.3 Running it

To get your code running, you will need to start roscore if it's not already running and then start the simulator, both in separate terminals. Then, once that is done, you can start your program.

hacker@barc:~$ roscore
hacker@barc:~$ roslaunch barc_stage pioneer_stage.launch
hacker@barc:~/ros/barc/barc_stage$ roscd braitenberg/
hacker@barc:~/ros/braitenberg$ ant
hacker@barc:~/ros/braitenberg$ rosrun rosjava_bootstrap run.py braitenberg barc.BraitenbergMover

And with any luck, you should not see your robot moving about its environment and avoiding obstacles. You can experiment to find better ways to move!

If you have any trouble that you cannot resolve yourself (i.e., Google), please post to the mailing list! There are a number of us there who can help.