Calico ROS

From IPRE Wiki
Revision as of 11:04, 15 October 2012 by Doug Blank (Talk | contribs) (RosCS)

Jump to: navigation, search

This page describes creating a ROS/Calico connection.

ROS

ROS is the Robot Operating System. Here we assume a standard ROS install. Also, we assume that Mono has been installed. The example below uses Ubuntu.

First, we need a local copy of ROS. We are using the current version of ROS (called "fuerte"):

  1. source /opt/ros/fuerte/setup.bash
  2. mkdir ~/ros
  3. rosws init ~/ros /opt/ros/fuerte
  4. source ~/ros/setup.bash
  5. roscd

RosCS

RosCS is a ROS package which creates a connection between Mono and ROS. It takes a C# file and dynamically builds all of the needed links into a set of dynamic link libraries (DLL). It wraps ROS C++ calls (from roscpp) in the C# code through DllImports.

Next, we need to install the package RosCS:

  1. rosws set cn-roscs-ros-pkg --git https://code.google.com/p/cn-roscs-ros-pkg/
  2. source ~/ros/setup.sh
  3. rosws update cn-roscs-ros-pkg
  4. [remove <rosdep name="mono"> from ~/ros/cn-roscs-ros-pkg/roscs/manifest.xml]
  5. rosdep install roscs

Finally, we create a C# file and build the DLLs:

  1. mkdir sandbox
  2. rosws set ~/ros/sandbox
  3. source ~/ros/setup.sh
  4. cd sandbox
  5. roscreate-pkg ROS std_msgs rospy roscpp roscs
  6. Create a C# file in ROS/src/hello.cs (see below)
  7. Edit ROS/CMakeLists.txt to add text (see below)
  8. roscd ROS
  9. rosmake

If it compiles without errors, then you can copy the .dll and .so files from lib to Calico/modules/

  1. cd lib
  2. cp *.dll *.so ~/Calico/trunk/modules/

The next section explores using these libraries.

hello.cs:

using System;
using System.Threading;
using RosCS;

public class RosCsExample
{
    Node node;
    Publisher pub;

    public RosCsExample()
    {
	this.node = Node.MainNode;
	this.pub = new Publisher(this.node,"DummyTopic",RosCS.std_msgs.String.TypeId,1);
	this.node.Subscribe("DummyTopic",OnMessage,6);
        
    }

    public void OnMessage(RosCS.std_msgs.String msg) {
	Console.WriteLine("Got message: "+msg.Data);
    }

    public void Run() {
	int i = 0;
	while(RosSharp.Ok()) {
	    RosCS.std_msgs.String msg = new RosCS.std_msgs.String();
	    msg.Data ="Hello "+i;
	    this.pub.Send(msg);
	    i++;
	    Thread.Sleep(1000);
	}                       
    }               

    public static void Main(string[] args) {                        
	RosSharp.Init("NodeName",args);                 
	RosCsExample rce = new RosCsExample();
	rce.Run();
    }
}

Add to bottom of CMakeLists.txt:

rosbuild_include(roscs cncs)
rosbuild_gen_cs_msg()
rosbuild_gensrv()
CSHARP_ADD_TARGET_DEPENDENCY(hello Mono.C5) 
CSHARP_ADD_EXE(hello src/hello.cs)

Explanation

By doing the above, you built the ~/ros/sandbox/ROS/bin/hello.exe Mono executable, and all of the required shared libraries (.dll and .so files) to run it. However, we are more interested in the shared libraries found in ~/ros/sandbox/ROS/lib/. We could also make the code in hello.exe be a DLL, or we could add code to the RosCS or RosSharp C# code to make it accessible to Calico.

Calico

  1. In a terminal, run roscore:
    1. source ~/ros/setup.bash
    2. roscore
  2. In another terminal, start Calico:
    1. StartCalico
    2. Run sample Python code (below)
  3. In another terminal, run rostopic
    1. rostopic echo DummyTopic
import System
import RosCS

# First we initialize the root (NodeName) and get the node:
RosCS.RosSharp.Init("NodeName", System.Array[str]([]))
node = RosCS.Node.MainNode

# Next, we create a publisher to post on a topic, here DummyTopic:
publisher = RosCS.Publisher(node, "DummyTopic", RosCS.std_msgs.String.TypeId, 1)
# It is setup to receive string messages

# Next, we setup a String Message receiver callback:
def OnMessage(msg):
    ## msg is RosCS.std_msgs.String
    print("Got message: " + msg.Data);

# Directly calling the Subscribe doesn't work because signature is based on type:
# node.Subscribe("DummyTopic", OnMessage, 6)
# So, we get the overload by type:
Subscribe = node.Subscribe.Overloads[str, RosCS.OnString, int]
# and call it:
Subscribe("DummyTopic", OnMessage, 6)

# And now we start sending and receiving messages:
i = 0
while RosCS.RosSharp.Ok() and i < 10:
    # Create a string message:
    msg = RosCS.std_msgs.String()
    msg.Data = "Hello " + str(i)
    # Send it:
    publisher.Send(msg)
    # Wait for a second, and continue:
    i += 1
    System.Threading.Thread.Sleep(1000)

# and unsubscribe:
UnSubscribe = node.UnSubscribe.Overloads[str, RosCS.OnString]
UnSubscribe("DummyTopic", OnMessage)

Issues

  1. Does it shutdown correctly? I think so, as long as you call UnSubscribe. How to do that automatically?
  2. If you run the code twice, how to unsubscribe from the first, if there is an error?
    1. finally?
    2. with...?
  3. Can we package this up so that it callable from other languages?
    1. Add functions to C# code.