NOTE: rosoct requires Octave 3.0. If you use older Linux distributions (e.g. Feisty), you may have to manually download and install from ftp://ftp.octave.org/pub/octave/.

Introduction

Allows Octave users to communicate with the ROS network. Internally, rosoct wraps roscpp in a mex file called rosoct.mex. All API functions are in $(rosoct)/octave.

  • rosoct(cmd) - initializes the run-time, must be called before anything else. Possible commands are:

  • shutdown - disconnect from the ROS network and terminate

  • clear - clear all resources and topics advertised

  • nohook - launch rosoct without registering a hook to automatically call rosoct_worker

  • rosoct_X - public API calls that wrap the low-level calls and make using Octave simpler

  • success = rosoct_advertise(servname,@msg,queuesize)

  • success = rosoct_advertise_service(servname,@msg,@callback)

  • [sessionid, response] = rosoct_create_session(sessionname,request)

  • success = rosoct_publish(topicname, message)

  • response = rosoct_service_call(servicename, request)

  • response = rosoct_session_call(sessionid, servicename, req)

  • success = rosoct_subscribe(topicname,@msg,@callback,queuesize)

  • rosoct_X_ - low-level API calls. Some functions are wrapped directly by the public API so are now specified here.

  • paramvalue = rosoct_get_param(paramname)

  • topics = rosoct_get_topics(type) - type can e one of 'advertised' or 'published'

  • success = rosoct_msg_unsubscribe(topicname)

  • rosoct_set_param(paramname,paramvalue)

  • success = rosoct_terminate_session(sessionid)

  • success = rosoct_unadvertise(topicname)

  • success = rosoct_unadvertise_service(servicename)

  • success = rosoct_wait_for_service(servicename)

  • numprocessed = rosoct_worker() - worker function that handles all the pending service requests and message callbacks.

For every function in octave, just type help [functionname] to get a brief description of its parameters and how it is used.

Because all octave functions need to be added to the octave path at startup, each user of rosoct either has to add addpath(fullfile(getenv('ROS_ROOT'),'core','experimental','rosoct','octave'))

to their ~/.octaverc file, or has to make a system call to rospack to find the correct path. Here's an example of the latter:

[status,rosoctpath] = system('rospack find rosoct');
rosoctpath = strtrim(rosoctpath);
addpath(fullfile(rosoctpath, 'octave'));

Messages/Services

The auto-generated octave file for every message can be found in $(pkg)/msg/oct/$(pkg), service files are the same. There are a couple of API calls that make adding these paths simple: * rosoct_findpackage(pkgname) * rosoct_add_msgs(pkgname) * rosoct_add_srvs(pkgname)

Every message filename is prefixed with its package name. For example a message type of MechanismState.msg in the robot_msgs package will be called robot_msgs_MechanismState.m. To instantiate a message just use:

msg = robot_msgs_MechanismState();

For services, you can instantiate both the request and response via:

[request,response] = openraveros_env_getbodies();

The response can be left out if only interested in the request. The request has a _create_response function that allows you to create a response directly.

In messages, primitive array types are handled as 1-dim octave matrices [], arrays of other messages or arrays of strings are handled as octave cell arrays. For example, accessing the position of the 2nd actuator in a MechanismState message will be

msg.actuators{2}.position

All the fields of arrays of messages with known size have been initialized in the message constructor, so they can be used directly. However if a new element is added for unknown array sizes, the constructor of that type must be called explicitly. For example:

 msg.actuators = {};
 for i = 1:numjoints
   msg.actuators{i} = robot_msgs_ActuatorState();
 end 

Simple Examples

There are many examples inside $(rosoct)/test that test the various features of rosoct. Before running any rosoct code you need to start the ROS master with roscore and in octave call

rosoct();

to have octave connect to the ROS network.

Advertising and publishing a topic

rosoct_add_msgs('rosoct');
suc = rosoct_advertise('chatter',@rosoct_String, 1);
for i = 1:1000
    msg = rosoct_String();
    msg.data = sprintf('hellow world %d', i);
    suc = rosoct_publish('chatter',msg);
    if( ~suc )
        error('failed to publish chatter');
    end 
end

Subscribing to a topic

Notice how the callback function is inlined, you can also use a predefined function with @fn.

rosoct_subscribe('chatter', @rosoct_String, @(msg) display(sprintf('stringcb: %s', msg.data)), 1);

Advertising a service

rosoct_add_srvs('rosoct');
suc = rosoct_advertise_service('mytempserv',@rosoct_StringString,@stringservcb);

To specify a failure in the service request function, just return an empty response.

function res = stringservcb(req)
res = req.create_response_();
if( strcmp(req.str,'dofail') ) % some arbitrary failure condition
  req = [];
else
  res.str = [req.str '_a'];
end

Calling a session

rosoct internally supports sessions introduced by the roscpp_sessions package. Here is an example of using sessions for the openraveros server. First start the server, then create a session via:

rosoct_add_srvs('openraveros');
req = openraveros_openrave_session();
req.viewer = 'qtcoin'; % attach a viewer to the session
[localsessionid,res] = rosoct_create_session('openrave_session',req);

To load a simple scene do:

rosoct_add_msgs('openraveros');
req = openraveros_env_loadscene();
req.filename = 'data/lab1.env.xml';
res = rosoct_session_call(localsessionid,'env_loadscene',req);

The openraveros API simplifies the usage of all its session calls with an extra API. For example, the above load scene call can be made with one function orEnvLoadScene that transparently manages a session. The API is in $(openraveros)/octave

A vision example

Octave is really great for prototyping vision algorithms and sometimes it is easier to use the octave function directly in ROS than to rewrite everything in C++ or python. So here's a simple example on how to create a service that returns a canny edge image from the image_msgs/Image.msg message. ImageImageService is a service that takes an Image.msg and produces an Image.msg.

rosoct_add_msgs('image_msgs');
% rosoct_add_srvs('..') % add the package where ImageImageService resides
suc = rosoct_advertise_service('mycannyserv',@ImageImageService,@cannyservcb);

function res = cannyservcb(req)
res = req.create_response_();

% assume a monocular image
width = req.byte_data.layout.dim{1}.size;
height = req.byte_data.layout.dim{2}.size;
I = reshape(req.byte_data.data,[width height])';
Iedge = edge(I,'canny');

res.layout = req.layout;
res.data = Iedge(:);

With persistent services and shared memory, most of time taken to transfer the data could be eliminated allowing for Octave services to be called at 1000+Hz (persistent services themselves can be called at ~15000Hz).

Data recording

Here's a script to record the joint values of the robot along with all the link transformations using openraveros.

global data robotid
data = {};
rosoct_add_msgs('robot_msgs'); % add robot messages
addpath(fullfile(rosoct_findpackage('openraveros'),'octave')); % add the openraveros API

robotid = orEnvCreateRobot('testrobot,'robots/pr2full.robot.xml'); % open the robot
rosoct_subscribe('mechanism_state', @robot_msgs_MechanismState, @mechanismcb, 4);

function mechanismcb(msg)
global robotid data
jointvalues = cellfun(@(act) act.position, msg.actuator_states);

orBodySetJointValues(robotid,jointvalues);
links = orBodyGetLinks(robotid);

data{end+1} = struct('jointvalues',jointvalues,'links',links);

TODO

  • persistent services that are not sessions - return handle
  • handle multiple subscribe calls to the same topic from the same octave instance - unsubscribe from topic using id

Wiki: rosoct (last edited 2010-10-18 21:26:50 by KenConley)