# Main Page

This is the Main Orocos.org Wiki page.

From here you can find links to all Orocos related Wiki's.

Orocos Wiki pages are organised in 'books'. In each book you can create child pages, edit them and move them around. The Wiki itself creates an overview of the child pages of each book.

To create a new page, click 'Add Child page' below. To edit a page, click on the Edit tab of that page. You can also write down a link to a to be written page using the Example Page syntax. Click below to read the rest of this post.This is the Main Orocos.org Wiki page.

From here you can find links to all Orocos related Wiki's.

Orocos Wiki pages are organised in 'books'. In each book you can create child pages, edit them and move them around. The Wiki itself creates an overview of the child pages of each book.

To create a new page, click 'Add Child page' below. To edit a page, click on the Edit tab of that page. You can also write down a link to a to be written page using the Example Page syntax. When that link is clicked and the page does not exist, one is offered to create it and write it.

Currently, the Orocos wiki pages are written in MediaWiki style. You should create your pages in this style as well.

Feel free to click on the 'Edit' tab above to see how this page was written (and to improve it ! ).

# Development

This section covers all development related pages.

# Contributing

How you can get involved and contribute & participate in the Orocos project

## Development strategy

The Orocos toolchain uses git, with the official repositories hosted at gitorious.

### Branches

The various branches are
• master : main development line, latest features. Replaces rtt-2.0-mainline
• toolchain-2.0 : stable release branch for the 2.0 release series.
• rtt-1.0-svn-patches: remains for svn bridge of 1.x release series
• ocl-1.0-svn : remains for svn bridge of 1.x release series

The master branch gets updated when new branches are merged into it by its maintainer. This can be a merge from the bugfix branches (ie merge from toolchain-2.x) or a merge from a development branch.

The stable branch should always point to the latest toolchain-2.x tip. This isn't automated, and so it lags (probably something for a hudson job or a git commit hook).

All branches in the rtt-2.0-... are no longer updated. The rtt-2.0-mainline has been merged with master, which means that if you have a rtt-2.0-mainline branch, you can just do git pull origin master, and it will fast-forward your tree to the master branch, or you checkout the local master.

## Contributing packages

You may contribute a software package to the community. It must respect the rules set out in the Component Packages section. Packages general enough can be adopted by the Orocos Toolchain Gitorious project. Make sure that your package name only contains word and number characters and underscores. A 'dash' ('-') is not acceptable in a package name.

## Contributing patches

Small contributions should go on the mailing lists as patches. Larger features are best communicated using topic branches in a git repository clone from the official repositories. Send pull requests to the mailing lists. These topic branches should be hosted on a publicly available git server (e.g. github, gitorious).

NB for the Orocos v1, no git branches will be merged (due to SVN), use individual patches instead. v2 git branches can be merged without problems.

## Making suggestions

The easiest way to make suggestions is to use the mailing list (register here). This allows discussion about what you are suggesting (which after all, someone else may already be working), as well as informing others of what you are interested in (or are willing to do).

## Reporting bugs

Before reporting a bug, please check the Bug Tracker and in the Mailing list and Forum to see whether this is a known issue. If this is a new issue then TBD email the mailing lists, OR enter an issue in the bug tracker

# Orocos developers meetup at ERF

Goals of the meeting: discuss the future or the Orocos toolchain w.r.t. Rock and ROS.

Identified major goals

• make Orocos components usable across all use cases (ROS, Orocos and Rock). There should be no "rock-only" or "ros-only" components
• make as much as the Rock toolchain usable with "plain Orocos components" (see below)
• make the parallel usage of the three cases (Orocos toolchain, Orocos in ROS and Rock) as painless as possible

## Sharing installations of the orocos toolchain

The main issue is the ability to compile the toolchain and components only once, e.g. in a Rock installation, and use them in ROS or vice-versa.

• already existing ignore_packages mechanism. Need to create a wiki page on how to set it up for sharing an orocos installation. Manual, but should be working.
• more automatic mechanisms:
• allow dependencies between autoproj installations. Fully automatic for orocos toolchain/Rock interoperability
• point to a prefix and have autoproj find out things (for ROS installs)

## Sharing Orocos components across use-cases

Using rock components on plain Orocos should just work [needs testing and documentation].

### Using rock tools on plain Orocos

The use of orogen or typegen would be required

• as far as we know, there is no missing "core" functionality in orogen to make typegen usable for "core" orocos libraries like KDL. Need to make some functionality such as opaques available to typegen though (only available to orogen currently). This can be done through the ability to make typegen load an oroGen SL file (trivial)
• allow passing -I options directly to both oroGen and typeGen
• mechanism to define "side-loading" typekits that define constructors and operators separately for scripting
• the core of the Rock tooling is orocos.rb. Need to test and update orocos.rb so that it can work without a model. Method: update the test suite by mocking TaskContext#model to return nil and/or getModelName to not exist. From there, test tools like oroconf and vizkit

### Dataflow between ROS and Rock / plain Orocos

• need data conversions: one must be able to publish a C++ type over a ROS topic and vice-versa
• typegen generation for ROS messages
• type specification when creating ROS streams (since the ROS topic and the orocos port might have different types)
• type conversions on the data flow: use already existing constructor infrastructure to do the conversion, need to create the channel element and change the connection code
• add type conversion support in oroGen (equivalent system than opaques)

## Other discussed topics

• make TypeInfo very thin so that we can register it once per type and never change it. Only transports / constructors / ... could then be overriden
• make the deployer a library

Where things are going, and how we plan to get there.

TODO Autoproj, RTT, OCL, etc.

## Real-time logging

The goal is to provide a real-time safe, low overhead, flexbible logging system useable throughout an entire system (ie within both components and any user applications, like GUIs).

We chose to base this on log4cpp, one of the C++-based derivates of log4j, the respected Java logging system. With only minor customimzations log4cpp is now useable in user component code (but not RTT code itself, see below). It provides real-time safe, hierarchical logging with multiple levels of logging (e.g. INFO vs DEBUG).

### Near future

• Provide a complete system example demonstrating use of the real-time logging framework in both user components, and a GUI-based application. Based on the v2 toolchain.
• Provide a component-based appender demonstrating transport of logging events. Most likely demonstrate centralized logging of a distributed system.
• Add logging system stress tests. (l already have this for v1, but need to port to v2 and submit)
• Able have multiple appenders per category. This is simply a technical limitation of the initial approach, and should be readily changeable.

### Long term plans

• Replace the existing RTT::Logger functionality with the real-time logging framework. This really can't involve rewriting all the logging statements in RTT, etc.
• Provide levels of DEBUG logging. Some logging system use FINE, FINER, FINEST levels, whilst others use DEBUG plus an integer level within debug (e.g. debug-1 thru debug-9, from verbose to most-verbose). Chose one approach, and modify log4cpp to support it.
• Support use by scripting and state machines (possibly also Lua?). This means both being able to log, as well as being able to configure categories, appenders, etc.

While the project is still in the (heavy?) turmoil of the 1.x-to-2.x transition, it might be useful to start thinking about the next version, 3.x. Below are a number of developments and policies that could eventually become 3.x; please, use the project's (user and developer) mailinglists to give your opinions, using a 3.x Roadmap message tag.

Disclaimer: there is nothing official yet about any of the below-mentioned suggestions; on the contrary, they are currently just the reflections of one single person, Herman Bruyninckx. (Please, update this disclaimer if you add your own suggestions.)

General policies to be followed in this Roadmap:

• the anti-Not Invented Here policy: whenever there exists a FOSS project that has already a solution for (part of) this roadmap, we should try to cooperate with that project, instead of putting efforts in our own version.
• the big critical mass projects first policy: when being confronted with the situation above, it is much preferred to cooperate with (contribute to) projects that have a high critical mass (Cmake, Linux, Eclipse, Qt, etc.) instead of with single-person or single-team projects, even when the latter currently have better functionalities and ideas. At the same time, promising single-person projects will be stimulated to make their efforts useful in a larger critical mass project.

## Orocos distribution

Much can be improved to bring Orocos closer to users, and the concept of a simple-to-install distribution is a proven best practice. However, Orocos should not try to develop its own distribution, but should rather hook on to existing, successful efforts. ROS is the obvious first choice, and the orocos_toolchain_ros is the concrete initiative that has already started in this direction. However, this "only" makes "some" relevant low-level Orocos functionality available in a form that is easier to install for many robotics users; in order to allow users to profit from all Orocos functionality, the following extra steps have to be set:
• a "Hello Robot!" application, installable as a ROS stack. It could contain a simulated robot, visualised in Morse or Gazebo, and componentized in an RTT component, together with an RTT/KDL/BFL-based set of motion controllers and estimators. (Morse is currently the most promising candidate, from a component-based development point of view.)
• a (Wiki) book that explains the whole setup, not just from a software point of view, but also a motivation why the presented example could be considered as a "best practice" as a robotics system. This Wiki book should not be an Orocos-only effort, but be useful for the whole community.
• a similar "Hello Machine!" application, targeting not the robotics community, but the mechatronics, or machine tools community.

Contributors to this part of the Roadmap need not be RTT developers, but motivated users!

## RTT

The road towards better decoupling, as started in 2.x, is designed and implemented further:
• RTT will get a complete and explicit component model, existing of component, composite component, connection, port, interface, discrete behaviour, and communication:
• The OROMACS development at the University of Twente have already produced a core of the composite component. That concept is required for a full support of the Model-Driven Engineering approach.
• the connection is the data-less, event-less and command-less representation of the architecture of a system, consisting of only the identification of which components will interact with each other.
• the difference between a port and an interface is that a port belongs to a component, and implements an interface; the interface in itself must become a first-class citizen of the component model.
• discrete behaviour is the current state machine. Further developments in this context are probably only to be expected at the implementation and tooling front.
• communication: Orocos has had, from day one, the ambition to not provide communication middleware, since there are so many other projects that do that. RTT should, however, improve its decoupling of (i) using data structures inside a component, (ii) providing them for communication in a port, and (iii) transporting them from one component's port to another component's port. Maybe this is as easy as cleanly separating the configuration files for all three aspects; maybe it's more involved than that.
• the mapping on real hardware resources (computational thread, communication field bus) is separated from the definition of a component.
• the process of defining data flow data structures is supported by an IDL language. This IDL has to be chosen together with other projects, and should not be an Orocos-only effort. A real IDL includes the definition of the meaning of the fields in the data, and not just their computer language representation.
• the codel idea of GenoM3 is supported for the construction of continuous behaviour inside a component. The important role of the codel idea in the context of realtime systems is that one should give the component designer full control over when which computations are to be executed (instead of relying on the OS scheduler); this requires a design in which computations can be subdivided in pre-emptible pieces (codels), and in which they can be scheduled in efficient Directed Acyclic Graphs.

Contributors to this part of the Roadmap need be RTT developers!

## BFL, KDL, SCL

SCL does not yet exist, but there is a high and natural need for a Systems and Control Library, next to BFL and KDL.

All three libraries share a common fundamental design property, and that is that they can all be considered as special cases of executable graphs, so a common support will be developed for the flexible, configurable scheduling off all computations (codels) in complex networks (Bayesian networks, kinematic/dynamic networks, control diagrams).

Contributors to this part of the Roadmap need not be RTT developers, but domain experts that have become power users of the RTT infrastructure!

## iTaSC and beyond

A usable robotics control systems consists, of course, not only of RTT, BFL, KDL and/or SCL components, but there is an obvious need for a task primitive: the brain that contains all the knowledge about when to use which component, with what configuration, and until what conditions are being satisfied.

As a first step, the instantaneous version of a constrained-based optimization approach to task-level control will be provided. Following steps will extend the instantaneous idea towards non-instantaneous tasks. This extension must be focused on tasks that require realtime performance, since non-realtime solutions are provided by other projects, such as ROS.

Contributors to this part of the Roadmap need not be RTT developers, but domain experts that also happen to be average users of the RTT infrastructure! They will open up the functionalities of Orocos to the normal end-user.

## Tooling

More and improved tools have been a major feature of the 2.x evolution. The major tooling effort for 3.x, will be to bring the above-mentioned component model into the Eclipse eco-system.

The first efforts in this direction have started, in the context of the European project BRICS.

Contributors to this part of the Roadmap need not be RTT developers, but programmers familiar with the advanced Eclipse features, such as ecore models, EMG, etc.

# European Robotics Forum 2011 Workshop on the Orocos Toolchain

At the European Robotics Forum 2011 Intermodalics, Locomotec and K.U.Leuven are organizing a two-part seminar, appealing to both industry and research institutes, titled:

1. Real-Time Robotics with state-of-the-art open source software: case studies (45min presentation, open to all)
2. Exploring the Orocos Toolchain (2 hours hands-on, registration required)

The session will be on April 7, 9h00-10h30 + 11h00-12h30

 Remaining seats : 0 out of 20 (last update: 06/04/2011)

## Real-Time Robotics with state-of-the-art open source software: case studies

In this presentation, Peter Soetens and Ruben Smits introduce the audience to todays Open Source robotics eco-system. Which are the strong and weak points of existing software ? Which work seamlessly together, and on which operating systems (Windows, Linux, VxWorks,... ) ? We will prove our statements with practical examples from both academic and industrial use cases. This presentation is the result of the long standing experience of the presenters with a open source technologies in robotics applications and will offer the audience leads and insights to further explore this realm.

## Exploring the Orocos Toolchain

In this hands-on session, the participants are invited to bring their own laptop with Orocos and ROS (optionally) installed. We will support Linux, Mac OS-X and Windows users and will provide instructions on how they can prepare to participate.YouBot: A real and simulated YouBot will be used

We will let the participants experience that the Orocos toolchain:

• nicely integrates with other popular robotics software
• provides the hooks and extensions to allow application supervision
• gives you the possibility to write expressive, compact, yet real-time applications
• makes 'separation of concerns' in software a natural habit for the programmer
• allows to easily script an application together and inspect/modify with it easily while running

If you'll be using the bootable USB-sticks, prepared by the organisers, you can skip all installation instructions and directly start the assignment at https://github.com/bellenss/euRobotics_orocos_ws/wiki

If you are attending the hands-on session you can bring your own computer. Depending on you operating system you should install the necessary software using the following installation instructions:

YouBot Demo Setup The workshop will start with making you familiar with the Orocos Toolchain, which does not require the YouBot. The hands-on will continue then on a robot in simulation and on the real hardware. We will use the ROS communication protocol to send instructions to the simulator (Gazebo) or the YouBot. Installing Gazebo is not required, since this simulation will run on a dedicated machine. Documentation on the workshop application and the assignment can be found at https://github.com/bellenss/euRobotics_orocos_ws/wiki.

## Registration

You first need to register for attending the euRobotics Forum. Registration for the workshop is mandatory, but free of charge. For the hands-on session, we will limit the number of participants to 20. The workshop is guided by 6 experienced Orocos users. Please register your participation by sending an email to info at intermodalics dot eu. We will confirm your participation with a short notice. Later-on, you will receive a second email with more details about how to prepare. You should receive this second, detailed email in the week of March, 20, 2011.

# euRobotics Forum Linux Setup

## Toolchain Installation

The installation instructions depend on if you have ROS installed or not.

NOTE: ROS is required to participate in the YouBot demo.

### With ROS on Ubuntu Lucid/Maverick

Install Diamondback ROS using Debian packages for Ubuntu Lucid (10.04) and Maverick (10.10) or the ROS install scripts, in case you don't run Ubuntu.

• Install the ros-diamondback-orocos-toolchain-ros debian package version 0.3.1 or later:  apt-get install ros-diamondback-orocos-toolchain-ros After this step, proceed to Section 2: Workshop Sources below.

### With ROS on Debian, Fedora or other systems

• We did not succeed in releasing the Diamondback 0.3.0 binary packages for your target of the Orocos Toolchain. This means that you need to build this 'stack' yourself with 'rosmake', after you installed ROS (See http://www.ros.org/wiki/diamondback/Installation). This 'rosmake' step may take about 30 minutes to an hour, depending on your laptop.

Instructions after ROS is installed:

source /opt/ros/diamondback/setup.bash
mkdir ~/ros
cd ~/ros
export ROS_PACKAGE_PATH=$HOME/ros:$ROS_PACKAGE_PATH
git clone http://git.mech.kuleuven.be/robotics/orocos_toolchain_ros.git
cd orocos_toolchain_ros
git checkout -b diamondback origin/diamondback
git submodule init
git submodule update --recursive
rosmake --rosdep-install orocos_toolchain_ros

NOTE: setting the ROS_PACKAGE_PATH is mandatory for each shell that will be used. It's a good idea to add the export ROS_PACKAGE_PATH line above to your .bashrc file (or equivalent)..

### Without ROS

• Run the bootstrap.sh (version 2.3.1) script in an empty directory.

## Workshop Sources

An additional package is being prepared that will contain the workshop files. See euRobotics Workshop Sources.

# euRobotics Forum Mac OS-X Setup

## Toolchain Installation

Due to a dynamic library issue in the current 2.3 release series, Mac OS-X can not be supported during the Workshop. We will make available a bootable USB stick which contains a pre-installed Ubuntu environment containing all necessary packages.

# euRobotics Forum Windows Setup

## Toolchain Installation

Windows users can participate in the first part of the hands-on where Orocos components are created and used. Pay attention that installing the Orocos Toolchain on Win32 platforms may take a full day in case you are not familiar with CMake, compiling Boost or any other dependency of the RTT and OCL.

Requirements:

• Visual Studio 2005 or 2008
• CMake 2.6.3 or newer (2.8.x works too)
• Boost C++ 1.40.0
• Cygwin installation (default setup is fine)

See the Compiling on Windows with Visual Studio wiki page for instructions. The TAO/Corba part is not required to participate in the workshop.

You need to follow the instructions for RTT/OCL v2.3.1 or newer, which you can download from the Orocos Toolchain page. We recommend to build for Release.

In case you have no time nor the experience to set this up, we provide bootable USB sticks that contain Ubuntu Linux with all workshop files.

## Workshop Sources

Windows users might also install the Kst program which is a KDE plot program that also runs on Linux. We provided a .kst file for plotting the workshop data. See the Kst download page.

In case you completed building and installing RTT and OCL, you can launch a cygwin or cmd.exe prompt and run the orocreate-pkg script to create a new package, which is in your c:\orocos\bin directory. Make sure that your PATH variable is propertly extended with
set PATH=%PATH%;c:\orocos\bin;c:\orocos\lib;c:\orocos\lib\orocos\win32;c:\orocos\lib\orocos\win32\plugins
(replace c:\orocos with the actual installation path, which might also be c:\Program Files\orocos)

You repeat the classical CMake steps with this package, generate the Solution file and build and install it. Then start up the deployer with the deployer-win32.exe program and type 'ls'. It should start and show meaningful information. If you see strange characters in the output, you need to turn of the colors with the '.nocolors' command at the Deployer's prompt.

# euRobotics Workshop Material

The euRobotics Forum workshop on Orocos has been a great success. About 30 people attended and participated in the hands-on workshop. The Real-Time & Open Source in Robotics track drew more than 60 people. Both tracks were overbooked.

You can find all presentation material in PDF form below

# euRobotics Workshop Sources

There are two ways you can get the sources for the workshop:

• Using Git
• Using a zip file

Since the sources are still evolving, it might be necessary to update your version before the workshop.

## Hello World application

The first, ROS-independent part, uses the classical hello-world examples from the rtt-exercises package.

You can either check it out with

mkdir ~/ros
cd ~/ros
git clone git://gitorious.org/orocos-toolchain/rtt_examples
cd rtt_examples/rtt-exercises

Or you can download the examples from here. You need at least version 2.3.1 of the exercises.

If you're not using ROS, you can download/unzip it in another directory than ~/ros.

## Youbot demo application

The hands-on session involves working on a demo application with a YouBot robot. The application allows you to

• Drive the YouBot around based on pose estimates using laser scan measurements
• Simulate the YouBot on your own computer

The Youbot demo application is available on https://github.com/bellenss/euRobotics_orocos_ws (this is still work in progress and will be updated regularly)

You can either check it out with

mkdir ~/ros
export ROS_PACKAGE_PATH=\$ROS_PACKAGE_PATH:$HOME/ros
cd ~/ros
git clone http://robotics.ccny.cuny.edu/git/ccny-ros-pkg/scan_tools.git
git clone http://git.mech.kuleuven.be/robotics/orocos_bayesian_filtering.git
git clone http://git.mech.kuleuven.be/robotics/orocos_kinematics_dynamics.git
git clone git://github.com/bellenss/euRobotics_orocos_ws.git
roscd youbot_supervisor
rosmake --rosdep-install

Check that ~/ros is in your ROS_PACKAGE_PATH environment variable at all times, by also adding the export line above to your .bashrc file.

Here are some instructions to see if you're running a system usable for the workshop.

### Non-ROS users

You have built RTT with the 'autoproj' tool. This tool generated an 'env.sh' file. You need to source that file in each terminal where you want to build or run an Orocos application:

cd orocos-toolchain
source env.sh

Next, cd to the rtt-exercises directory that you unpacked, enter hello-1-task-execution, and type make:

cd rtt-exercises-2.3.0/hello-1-task-execution
make all
cd build
./HelloWorld-gnulinux

### ROS users

You have built RTT from the orocos_toolchain_ros package. Make sure that you source the /opt/ros/diamondback/setup.bash script and that the unpacked exercises are under a directory of the ROS_PACKAGE_PATH:

cd rtt-exercises-2.3.0
source /opt/ros/diamondback/setup.bash
export ROS_PACKAGE_PATH=\$ROS_PACKAGE_PATH:\$(pwd)

Next, you proceed with going into an example directory and type make:

cd hello-1-task-execution
make all
./HelloWorld-gnulinux

### Testing the YouBot Demo

After you have built the youbot_supervisor, you can test the demo by opening two consoles and do in them:

First console:

roscd youbot_supervisor
./simulation.sh

Second console:

roscd youbot_supervisor
./changePathKst (you only have to do this once after installation)
kst plotSimulation.kst

If you do not have 'kst', install it with  sudo apt-get install kst kst-plugins

# European Robotics Forum 2012: workshops

At the European Robotics Forum 2012 KU Leuven and Intermodalics are organizing a three-part seminar, appealing to both industry and research institutes, titled:

1. Introduction to state charts and reusable, modular task specification through the Orocos eco-system
2. Hands-on1: getting started with state charts in the Orocos eco-system
3. Hands-on2: getting started with instantaneous motion specification using constraints (iTaSC): reusable and modular task specification

The sessions will be on March 6, 8h30-10h30 + 11h00-12h30 + 13h30-15h00 (Track four). For more detail consult the European Robotics Forum program

 Remaining seats: (last update: March 2, 2012) Hands-on 1: 0 out of 20 Hands-on 2: 0 out of 20

We're fully booked, but don't be shy to come and peek or sit along, although we can't guarantee you a table or a chair !

(Information on last year's workshop can be found here.)

## Registration

You first need to register for attending the euRobotics Forum. Registration for the workshop is mandatory, but free of charge. For the hands-on sessions (hands-on 1 and hands-on 2), we will limit the number of participants to 20. The workshops are guided by different experienced Orocos users. Please register your participation by sending an email to info at intermodalics dot eu indicating which workshops you want to attend. We will confirm your participation with a short notice. Later-on, you will receive a second email with more details about how to prepare. You should receive this second, detailed email in the week of February, 27, 2012.

## Motivation and objective

The workshop consists of three rather independent parts. It is advised but not required to follow the preceding session(s) when attending session two or three.
1. The first session is a presentation session, it introduces the basic concepts of Orocos application programming, followed by rFSM state charts and the iTaSC framework.
2. The second session is a hands-on session, that aims at making the participants familiar with rFSM state charts, which is a powerful though easy to use tool for robotic coordination and supervision tasks,
3. The third sessions is also a hands-on session, that aims at introducing the concepts of constraint-based motion specification using the iTaSC framework. This framework and its software implementation was developed at the KU Leuven during the past years. It's key advantages are the composability of (partial) constraints and reusability of the constraint specification. The software is an open-source project, which has recently reached its 2.0 version.

## Approach

1. Presentation session, giving a high-level overview of rFSM and iTaSC by introducing the key concepts.
2. Hands-on session: guided exercise where the participants will have to create an application with interacting state machines, that can be used for example to coordinate the behavior of the iTaSC application of the following session.
3. Hands-on session: guided exercise where the participants will have to create an application consisting of multiple tasks on a robot in simulation. Eg. Drawing a figure on a table and avoiding a moving obstacle with a Kuka Youbot.

## Feedback form

Participant feedback is gratefully appreciated. Please fill in the feedback form. Some browsers/pdf viewers do not support in-browser usage of the form. To avoid problems, please download the form first.

## Presentations

• Session 3
• Session 1:

AttachmentSize
RTT-Overview.pdf1.67 MB
erf_itasc_theory_opt.pdf526.82 KB

# Installation instructions

## Ubuntu Installation with ROS

### Installation

• Install Electric ROS using Debian packages for Ubuntu Lucid (10.04) or later. In case you don't run Ubuntu you can use the ROS install scripts. See the ros installation instructions.
• Make sure the following debian packages are installed: ros-electric-rtt-ros-integration ros-electric-rtt-ros-comm ros-electric-rtt-geometry ros-electric-rtt-common-msgs ros-electric-pr2-controllers ros-electric-pr2-simulator ruby
• Create a directory in which you want to install all the workshops source (for instance erf)

mkdir ~/erf

• Add this directory to your $ROS_PACKAGE_PATH export ROS_PACKAGE_PATH=~/erf:$ROS_PACKAGE_PATH

• Get rosinstall

sudo apt-get install python-setuptools
sudo easy_install -U rosinstall

• Get the workshop's rosinstall file . Save it as erf.rosinstall in the erf folder.
• Run rosinstall

rosinstall ~/erf erf.rosinstall /opt/ros/electric

• As the rosinstall tells you source the setup script

source ~/erf/setup.bash

• Install all dependencies (ignore warnings)

rosdep install itasc_examples
rosdep install rFSM

• Compile the workshop sources

rosmake itasc_examples

### Setup

• Add the following functions in your $HOME/.bashrc file: useERF(){ source$HOME/erf/setup.bash;
source $HOME/erf/setup.sh; source /opt/ros/electric/stacks/orocos_toolchain/env.sh; setLUA; } setLUA(){ if [ "x$LUA_PATH" == "x" ]; then LUA_PATH=";;"; fi
if [ "x$LUA_CPATH" == "x" ]; then LUA_CPATH=";;"; fi export LUA_PATH="$LUA_PATH;rospack find rFSM/?.lua"
export LUA_PATH="$LUA_PATH;rospack find ocl/lua/modules/?.lua" export LUA_PATH="$LUA_PATH;rospack find kdl/?.lua"
export LUA_PATH="$LUA_PATH;rospack find rttlua_completion/?.lua" export LUA_PATH="$LUA_PATH;rospack find youbot_master_rtt/lua/?.lua"
export LUA_PATH="$LUA_PATH;rospack find kdl_lua/lua/?.lua" export LUA_CPATH="$LUA_CPATH;rospack find rttlua_completion/?.so"

# Your first application using semantic checking on geometric relations (without coordinate checking)

This tutorial assumes you have prepared a ROS package with name myApplication and that you have set your ROS_PACKAGE_PATH environment variable accordingly, as explained in this tutorial.

In this tutorial we first explain how you can create basic semantic objects (without coordinates and coordinate checking) and perform semantic operations on them. We will show how you can create any of the supported geometric relations: position, orientation, pose, transational velocity, rotational velocity, twist, force, torque, and wrench.

Remark that the file resulting from following this tutorial is attached to this wiki page for completeness.

## Prepare the main file

• Go to the directory of our first application using:

roscd myApplication
• Create a main file (in this tutorial called myFirstApplication.cpp) in which we will put the code of our first application.

touch myFirstApplication.cpp
• Edit the C++ file with your favorite editor. For instance:

vim myFirstApplication.cpp

#include <Position/PositionSemantics.h>
#include <Orientation/OrientationSemantics.h>
#include <Pose/PoseSemantics.h>
#include <LinearVelocity/LinearVelocitySemantics.h>
#include <AngularVelocity/AngularVelocitySemantics.h>
#include <Twist/TwistSemantics.h>
#include <Force/ForceSemantics.h>
#include <Torque/TorqueSemantics.h>
#include <Wrench/WrenchSemantics.h>

• Next we use the geometric_semantics namespace for convenience:

using namespace geometric_semantics;

• Create a main program:

int main (int argc, const char* argv[])
{
// Here comes the code of our first application
}

• To build you application you should edit the CMakeLists.txt file created in your application directory.

vim CMakeLists.txt

• Add the C++ main file to be build as an executable by adding the following line:

rosbuild_add_executable(myFirstApplication myFirstApplication.cpp)

• Now you are ready to build, so type

rosmake myApplication

and the executable will be created in the bin directory.

• To run the executable do:

bin/myFirstApplication

You will get the semantic output on your screen.

## Creating the geometric relations semantics

• We will start with creating the geometric relation semantics objects for the relation between body C with point a and orientation frame [e], and body D with point b and orientation frame [f]:

    // Creating the geometric relations semantics
PositionSemantics position("a","C","b","D");
OrientationSemantics orientation("e","C","f","D");
PoseSemantics pose("a","e","C","b","f","D");

LinearVelocitySemantics linearVelocity("a","C","D");
AngularVelocitySemantics angularVelocity("C","D");
TwistSemantics twist("a","C","D");

TorqueSemantics torque("a","C","D");
ForceSemantics force("C","D");
WrenchSemantics wrench("a","C","D");

## Doing semantic operations

• We can for instance take the inverses of the created semantic geometric relations by:

    //Doing semantic operations with the geometric relations
// inverting
PositionSemantics positionInv = position.inverse();
OrientationSemantics orientationInv = orientation.inverse();
PoseSemantics poseInv = pose.inverse();
LinearVelocitySemantics linearVelocityInv = linearVelocity.inverse();
AngularVelocitySemantics angularVelocityInv = angularVelocity.inverse();
TwistSemantics twistInv = twist.inverse();
TorqueSemantics torqueInv = torque.inverse();
ForceSemantics forceInv = force.inverse();
WrenchSemantics wrenchInv = wrench.inverse();
And if we print the inverses, we will see they are semantically correct:
    std::cout << "-----------------------------------------" << std::endl;
std::cout << "Inverses: " << std::endl;
std::cout << "     " << positionInv << " is the inverse of " << position << std::endl;
std::cout << "     " << orientationInv << " is the inverse of " << orientation << std::endl;
std::cout << "     " << poseInv << " is the inverse of " << pose << std::endl;
std::cout << "     " << linearVelocityInv << " is the inverse of " << linearVelocity << std::endl;
std::cout << "     " << angularVelocityInv << " is the inverse of " << angularVelocity << std::endl;
std::cout << "     " << twistInv << " is the inverse of " << twist << std::endl;
std::cout << "     " << torqueInv << " is the inverse of " << torque << std::endl;
std::cout << "     " << forceInv << " is the inverse of " << force << std::endl;
std::cout << "     " << wrenchInv << " is the inverse of " << wrench << std::endl;

• Now we can for instance compose the result with their inverses. Mind that the order of composition does not matter, since this is automatically derived from the semantic information in the objects.

    //Composing
PositionSemantics positionComp = compose(position,positionInv);
OrientationSemantics orientationComp = compose(orientation,orientationInv);
PoseSemantics poseComp = compose(pose,poseInv);
LinearVelocitySemantics linearVelocityComp = compose(linearVelocity,linearVelocityInv);
AngularVelocitySemantics angularVelocityComp = compose(angularVelocity,angularVelocityInv);
TwistSemantics twistComp = compose(twist,twistInv);
TorqueSemantics torqueComp = compose(torque,torqueInv);
ForceSemantics forceComp = compose(force,forceInv);
WrenchSemantics wrenchComp = compose(wrench,wrenchInv);
If you execute the program you will get screen output on the semantic correctness of the compositions (if not check the build flags of your geometric_semantics library as explained in the user guide. You can print and check the result of the composition using:
    std::cout << "-----------------------------------------" << std::endl;
std::cout << "Composed objects: " << std::endl;
std::cout << "     " << positionComp << " is the composition of " << position << " and " << positionInv << std::endl;
std::cout << "     " << orientationComp << " is the composition of " << orientation << " and " << orientationInv <<  std::endl;
std::cout << "     " << poseComp << " is the composition of " << pose <<  " and " << poseInv << std::endl;
std::cout << "     " << linearVelocityComp << " is the composition of " << linearVelocity  << " and " << linearVelocityInv << std::endl;
std::cout << "     " << angularVelocityComp << " is the composition of " << angularVelocity  << " and " << angularVelocityInv << std::endl;
std::cout << "     " << twistComp << " is the composition of " << twist <<  " and " << twistInv << std::endl;
std::cout << "     " << torqueComp << " is the composition of " << torque <<  " and " << torqueInv << std::endl;
std::cout << "     " << forceComp << " is the composition of " << force <<  " and " << forceInv << std::endl;
std::cout << "     " << wrenchComp << " is the composition of " << wrench <<  " and " << wrenchInv << std::endl;

AttachmentSize
myFirstApplication.cpp4.28 KB

# Your second application using semantic checking on geometric relations including coordinate checking

This tutorial assumes you have prepared a ROS package with name myApplication and that you have set your ROS_PACKAGE_PATH environment variable accordingly, as explained in this tutorial.

In this tutorial we first explain how you can create basic semantic objects (without coordinates but with coordinate checking) and perform semantic operations on them. We will show how you can create any of the supported geometric relations: position, orientation, pose, transational velocity, rotational velocity, twist, force, torque, and wrench.

Remark that the file resulting from following this tutorial is attached to this wiki page for completeness.

## Prepare the main file

• Prepare a mySecondApplication.cpp main file as explained in this tutorial.
• Edit the C++ file with your favorite editor. For instance:

vim mySecondApplication.cpp

#include <Position/PositionCoordinatesSemantics.h>
#include <Orientation/OrientationCoordinatesSemantics.h>
#include <Pose/PoseCoordinatesSemantics.h>
#include <LinearVelocity/LinearVelocityCoordinatesSemantics.h>
#include <AngularVelocity/AngularVelocityCoordinatesSemantics.h>
#include <Twist/TwistCoordinatesSemantics.h>
#include <Force/ForceCoordinatesSemantics.h>
#include <Torque/TorqueCoordinatesSemantics.h>
#include <Wrench/WrenchCoordinatesSemantics.h>

• Next we use the geometric_semantics namespace for convenience:

using namespace geometric_semantics;

• Create a main program:

int main (int argc, const char* argv[])
{
// Here comes the code of our second application
}

• To build you application you should edit the CMakeLists.txt file created in you application directory. Add the your C++ main file to be build as an executable adding the following line:

rosbuild_add_executable(mySecondApplication mySecondApplication.cpp)

• Now you are ready to build, so type

rosmake myApplication

and the executable will be created in the bin directory.

• To run the executable do:

bin/mySecondApplication

You will get the semantic output on your screen.

## Creating the geometric relations coordinates semantics

• We will start with creating the geometric relation coordinates semantics objects for the relation between body C with point a and orientation frame [e], and body D with point b and orientation frame [f], all expressed in coordinate frame [r]:

    // Creating the geometric relations coordinates semantics
PositionCoordinatesSemantics position("a","C","b","D","r");
OrientationCoordinatesSemantics orientation("e","C","f","D","r");
PoseCoordinatesSemantics pose("a","e","C","b","f","D","r");

LinearVelocityCoordinatesSemantics linearVelocity("a","C","D","r");
AngularVelocityCoordinatesSemantics angularVelocity("C","D","r");
TwistCoordinatesSemantics twist("a","C","D","r");

TorqueCoordinatesSemantics torque("a","C","D","r");
ForceCoordinatesSemantics force("C","D","r");
WrenchCoordinatesSemantics wrench("a","C","D","r");

## Doing semantic coordinate operations

• We can for instance take the inverses of the created geometric relation coordinates semantics by:

    //Doing semantic operations with the geometric relations
// inverting
PositionCoordinatesSemantics positionInv = position.inverse();
OrientationCoordinatesSemantics orientationInv = orientation.inverse();
PoseCoordinatesSemantics poseInv = pose.inverse();
LinearVelocityCoordinatesSemantics linearVelocityInv = linearVelocity.inverse();
AngularVelocityCoordinatesSemantics angularVelocityInv = angularVelocity.inverse();
TwistCoordinatesSemantics twistInv = twist.inverse();
TorqueCoordinatesSemantics torqueInv = torque.inverse();
ForceCoordinatesSemantics forceInv = force.inverse();
WrenchCoordinatesSemantics wrenchInv = wrench.inverse();
And if we print the inverses, we will see they are semantically correct:
    std::cout << "-----------------------------------------" << std::endl;
std::cout << "Inverses: " << std::endl;
std::cout << "     " << positionInv << " is the inverse of " << position << std::endl;
std::cout << "     " << orientationInv << " is the inverse of " << orientation << std::endl;
std::cout << "     " << poseInv << " is the inverse of " << pose << std::endl;
std::cout << "     " << linearVelocityInv << " is the inverse of " << linearVelocity << std::endl;
std::cout << "     " << angularVelocityInv << " is the inverse of " << angularVelocity << std::endl;
std::cout << "     " << twistInv << " is the inverse of " << twist << std::endl;
std::cout << "     " << torqueInv << " is the inverse of " << torque << std::endl;
std::cout << "     " << forceInv << " is the inverse of " << force << std::endl;
std::cout << "     " << wrenchInv << " is the inverse of " << wrench << std::endl;

• Now we can for instance compose the result with their inverses. Mind that the order of composition does not matter, since this is automatically derived from the semantic information in the objects.

    //Composing
PositionCoordinatesSemantics positionComp = compose(position,positionInv);
OrientationCoordinatesSemantics orientationComp = compose(orientation,orientationInv);
PoseCoordinatesSemantics poseComp = compose(pose,poseInv);
LinearVelocityCoordinatesSemantics linearVelocityComp = compose(linearVelocity,linearVelocityInv);
AngularVelocityCoordinatesSemantics angularVelocityComp = compose(angularVelocity,angularVelocityInv);
TwistCoordinatesSemantics twistComp = compose(twist,twistInv);
TorqueCoordinatesSemantics torqueComp = compose(torque,torqueInv);
ForceCoordinatesSemantics forceComp = compose(force,forceInv);
WrenchCoordinatesSemantics wrenchComp = compose(wrench,wrenchInv);
If you execute the program you will get screen output on the semantic correctness (and mark: in this case also incorrectness) of the compositions (if not check the build flags of your geometric_semantics library as explained in the user guide. You can print and check the result of the composition using:
    std::cout << "-----------------------------------------" << std::endl;
std::cout << "Composed objects: " << std::endl;
std::cout << "     " << positionComp << " is the composition of " << position << " and " << positionInv << std::endl;
std::cout << "     " << orientationComp << " is the composition of " << orientation << " and " << orientationInv <<  std::endl;
std::cout << "     " << poseComp << " is the composition of " << pose <<  " and " << poseInv << std::endl;
std::cout << "     " << linearVelocityComp << " is the composition of " << linearVelocity  << " and " << linearVelocityInv << std::endl;
std::cout << "     " << angularVelocityComp << " is the composition of " << angularVelocity  << " and " << angularVelocityInv << std::endl;
std::cout << "     " << twistComp << " is the composition of " << twist <<  " and " << twistInv << std::endl;
std::cout << "     " << torqueComp << " is the composition of " << torque <<  " and " << torqueInv << std::endl;
std::cout << "     " << forceComp << " is the composition of " << force <<  " and " << forceInv << std::endl;
std::cout << "     " << wrenchComp << " is the composition of " << wrench <<  " and " << wrenchInv << std::endl;

AttachmentSize
mySecondApplication.cpp4.72 KB

# Your third application doing actual geometric calculations on top of the semantic checking

This tutorial assumes you have prepared a ROS package with name myApplication and that you have set your ROS_PACKAGE_PATH environment variable accordingly, as explained in this tutorial.

In this tutorial we first explain how you can create full geometric relation objects (with semantics and actual coordinate representation) and perform operations on them. We will show how you can create any of the supported geometric relations: position, orientation, pose, transational velocity, rotational velocity, twist, force, torque, and wrench. To this end we will use the coordinate representations of the Orocos Kinematics and Dynamics Library. The semantic support on top of this geometry library is already provided by the geometric_semantics_kdl package.

Remark that the file resulting from following this tutorial is attached to this wiki page for completeness.

## Prepare the main file

• Prepare a myThirdApplication.cpp main file as explained in this tutorial.
• Edit the C++ file with your favorite editor. For instance:

vim myThirdApplication.cpp

#include <Position/Position.h>
#include <Orientation/Orientation.h>
#include <Pose/Pose.h>
#include <LinearVelocity/LinearVelocity.h>
#include <AngularVelocity/AngularVelocity.h>
#include <Twist/Twist.h>
#include <Force/Force.h>
#include <Torque/Torque.h>
#include <Wrench/Wrench.h>

#include <Position/PositionCoordinatesKDL.h>
#include <Orientation/OrientationCoordinatesKDL.h>
#include <Pose/PoseCoordinatesKDL.h>
#include <LinearVelocity/LinearVelocityCoordinatesKDL.h>
#include <AngularVelocity/AngularVelocityCoordinatesKDL.h>
#include <Twist/TwistCoordinatesKDL.h>
#include <Force/ForceCoordinatesKDL.h>
#include <Torque/TorqueCoordinatesKDL.h>
#include <Wrench/WrenchCoordinatesKDL.h>

#include <kdl/frames.hpp>
#include <kdl/frames_io.hpp>

• Next we use the geometric_semantics namespace and the KDL namespace for convenience:

using namespace geometric_semantics;
using namespace KDL;

• Create a main program:

int main (int argc, const char* argv[])
{
// Here goes the code of our third application
}

• To build you application you should edit the CMakeLists.txt file created in you application directory. Add the your C++ main file to be build as an executable adding the following line:

rosbuild_add_executable(myThirdApplication myThirdApplication.cpp)

• Now you are ready to build, so type

rosmake myApplication

and the executable will be created in the bin directory.

• To run the executable do:

bin/myThirdApplication

You will get the semantic output on your screen.

## Creating the geometric relations

• We will start with creating the geometric relation objects for the relation between body C with point a and orientation frame [e], and body D with point b and orientation frame [f], all expressed in coordinate frame [r], together with their coordinate representations using KDL types.

  // Creating the geometric relations

// a Position with a KDL::Vector
Vector coordinatesPosition(1,2,3);
Position<Vector> position("a","C","b","D","r",coordinatesPosition);

// an Orientation with KDL::Rotation
Rotation coordinatesOrientation=Rotation::EulerZYX(M_PI/4,0,0);
Orientation<Rotation> orientation("e","C","f","D","f",coordinatesOrientation);

// a Pose with a KDL::Frame
KDL::Frame coordinatesPose(coordinatesOrientation,coordinatesPosition);
Pose<KDL::Frame> pose1("a","e","C","b","f","D","f",coordinatesPose);

// a Pose as aggregation of a Position and a Orientation
Pose<Vector,Rotation> pose2(position,orientation);

// a LinearVelocity with a KDL::Vector
Vector coordinatesLinearVelocity(1,2,3);
LinearVelocity<Vector> linearVelocity("a","C","D","r",coordinatesLinearVelocity);

// a AngularVelocity with a KDL::Vector
Vector coordinatesAngularVelocity(1,2,3);
AngularVelocity<Vector> angularVelocity("C","D","r",coordinatesAngularVelocity);

// a Twist with a KDL::Twist
KDL::Twist coordinatesTwist(coordinatesLinearVelocity,coordinatesAngularVelocity);
geometric_semantics::Twist<KDL::Twist> twist1("a","C","D","r",coordinatesTwist);

// a Twist of a LinearVelocity and a AngularVelocity
geometric_semantics::Twist<Vector,Vector> twist2(linearVelocity,angularVelocity);

// a Torque with a KDL::Vector
Vector coordinatesTorque(1,2,3);
Torque<Vector> torque("a","C","D","r",coordinatesTorque);

// a Force with a KDL::Vector
Vector coordinatesForce(1,2,3);
Force<Vector> force("C","D","r",coordinatesForce);

// a Wrench with a KDL::Wrench
KDL::Wrench coordinatesWrench(coordinatesForce,coordinatesTorque);
geometric_semantics::Wrench<KDL::Wrench> wrench1("a","C","D","r",coordinatesWrench);

// a Wrench of a Force and a Torque
geometric_semantics::Wrench<KDL::Vector,KDL::Vector> wrench2(torque,force);

## Doing geometric operations

• We can for instance take the inverses of the created geometric relation by:

    //Doing operations with the geometric relations
// inverting
Position<Vector> positionInv = position.inverse();
Orientation<Rotation> orientationInv = orientation.inverse();
Pose<KDL::Frame> pose1Inv = pose1.inverse();
Pose<Vector,Rotation> pose2Inv = pose2.inverse();
LinearVelocity<Vector> linearVelocityInv = linearVelocity.inverse();
AngularVelocity<Vector> angularVelocityInv = angularVelocity.inverse();
geometric_semantics::Twist<KDL::Twist> twist1Inv = twist1.inverse();
geometric_semantics::Twist<Vector,Vector> twist2Inv = twist2.inverse();
Torque<Vector> torqueInv = torque.inverse();
Force<Vector> forceInv = force.inverse();
geometric_semantics::Wrench<KDL::Wrench> wrench1Inv = wrench1.inverse();
geometric_semantics::Wrench<Vector,Vector> wrench2Inv = wrench2.inverse();
And if we print the inverses, we will see they are semantically correct:
    // print the inverses
std::cout << "-----------------------------------------" << std::endl;
std::cout << "Inverses: " << std::endl;
std::cout << "     " << positionInv << " is the inverse of " << position << std::endl;
std::cout << "     " << orientationInv << " is the inverse of " << orientation << std::endl;
std::cout << "     " << pose1Inv << " is the inverse of " << pose1 << std::endl;
std::cout << "     " << pose2Inv << " is the inverse of " << pose2 << std::endl;
std::cout << "     " << linearVelocityInv << " is the inverse of " << linearVelocity << std::endl;
std::cout << "     " << angularVelocityInv << " is the inverse of " << angularVelocity << std::endl;
std::cout << "     " << twist1Inv << " is the inverse of " << twist1 << std::endl;
std::cout << "     " << twist2Inv << " is the inverse of " << twist2 << std::endl;
std::cout << "     " << torqueInv << " is the inverse of " << torque << std::endl;
std::cout << "     " << forceInv << " is the inverse of " << force << std::endl;
std::cout << "     " << wrench1Inv << " is the inverse of " << wrench1 << std::endl;
std::cout << "     " << wrench2Inv << " is the inverse of " << wrench2 << std::endl;

• Now we can for instance compose the result with their inverses. Mind that the order of composition does not matter, since this is automatically derived from the semantic information in the objects.

    //Composing
Position<Vector> positionComp = compose(position,positionInv);
Orientation<Rotation> orientationComp = compose(orientation,orientationInv);
Pose<KDL::Frame> pose1Comp = compose(pose1,pose1Inv);
Pose<Vector,Rotation> pose2Comp = compose(pose2,pose2Inv);
LinearVelocity<Vector> linearVelocityComp = compose(linearVelocity,linearVelocityInv);
AngularVelocity<Vector> angularVelocityComp = compose(angularVelocity,angularVelocityInv);
geometric_semantics::Twist<KDL::Twist> twist1Comp = compose(twist1,twist1Inv);
geometric_semantics::Twist<Vector,Vector> twist2Comp = compose(twist2,twist2Inv);
Torque<Vector> torqueComp = compose(torque,torqueInv);
Force<Vector> forceComp = compose(force,forceInv);
geometric_semantics::Wrench<KDL::Wrench> wrench1Comp = compose(wrench1,wrench1Inv);
geometric_semantics::Wrench<Vector,Vector> wrench2Comp = compose(wrench2,wrench2Inv);;    

If you execute the program you will get screen output on the semantic correctness (and mark: in this case also incorrectness) of the compositions (if not check the build flags of your geometric_semantics library as explained in the user guide. You can print and check the result of the composition using:

    // print the composed objects
std::cout << "-----------------------------------------" << std::endl;
std::cout << "Composed objects: " << std::endl;
std::cout << "     " << positionComp << " is the composition of " << position << " and " << positionInv << std::endl;
std::cout << "     " << orientationComp << " is the composition of " << orientation << " and " << orientationInv <<  std::endl;
std::cout << "     " << pose1Comp << " is the composition of " << pose1 <<  " and " << pose1Inv << std::endl;
std::cout << "     " << pose2Comp << " is the composition of " << pose2 <<  " and " << pose2Inv << std::endl;
std::cout << "     " << linearVelocityComp << " is the composition of " << linearVelocity  << " and " << linearVelocityInv << std::endl;
std::cout << "     " << angularVelocityComp << " is the composition of " << angularVelocity  << " and " << angularVelocityInv << std::endl;
std::cout << "     " << twist1Comp << " is the composition of " << twist1 <<  " and " << twist1Inv << std::endl;
std::cout << "     " << twist2Comp << " is the composition of " << twist2 <<  " and " << twist2Inv << std::endl;
std::cout << "     " << torqueComp << " is the composition of " << torque <<  " and " << torqueInv << std::endl;
std::cout << "     " << forceComp << " is the composition of " << force <<  " and " << forceInv << std::endl;
std::cout << "     " << wrench1Comp << " is the composition of " << wrench1 <<  " and " << wrench2Inv << std::endl;
std::cout << "     " << wrench2Comp << " is the composition of " << wrench1 <<  " and " << wrench2Inv << std::endl;

AttachmentSize
myThirdApplication.cpp7.63 KB

# Some extra examples

In case you are looking for some extra examples you can have a look at the geometric_semantics_examples package. So far it already contains an example showing the advantage of using semantics when integrating twists, and when programming two position controlled robots.

# KDL wiki

Kinematic Chain

Skeleton of a serial robot arm with six revolute joints. This is one example of a kinematic structure, reducing the motion modelling and specification to a geometric problem of relative motion of reference frames. The Kinematics and Dynamics Library (KDL) develops an application independent framework for modelling and computation of kinematic chains, such as robots, biomechanical human models, computer-animated figures, machine tools, etc. It provides class libraries for geometrical objects (point, frame, line,... ), kinematic chains of various families (serial, humanoid, parallel, mobile,... ), and their motion specification and interpolation.

# Installation Manual

This document is not ready yet, but it's a wiki page so feel free to contribute

• Linux
• Windows
• Mac

## Installation

There are different ways for getting the software.

### From debian/ubuntu packages

Orocos KDL is part of the geometry stack in the ROS distributions pre-Electric.

• ros-cturtle/diamondback-geometry

Since ROS Electric it is available stand-alone as the orocos-kinematics-dynamics stack.

• ros-electric/fuerte-orocos-kinematics-dynamics

### Source using git

git clone http://git.mech.kuleuven.be/robotics/orocos_kinematics_dynamics.git

## Building source with plain CMake

• create a build directory inside the orocos_kdl-dir and go inside:

mkdir <kdl-dir>/build ; cd <kdl-dir>/build
• Launch ccmake

ccmake ..
• configure [c], select the bindings you want to create, choose an appropriate installation directory
• configure[c], generate makefiles [g]
• make, wait, install and check

make;make check;make install

# KDL typekit

## The use of KDL types in the TaskBrowser

### Creation of variables of a KDL types

• Make sure you've loaded the KDL typekit, by checking all available types in the TaskBrowser:
.types
• In the list you should find eg. KDL.Frame, so you can create a variable z of this type by: var KDL.Frame z
• z has a standard value now (Identity frame), there are multiple ways to change it:
• value by value:
z.p.X=1 or z.M.X_x=2
• the full position vector
z.p = KDL.Vector(1,2,3)
• the full rotation matrix
z.M=KDL.Rotation(0,1.57,0) (roll, pitch, yaw angles???)
• You can check the current value by just typing it's variable name, for this example, z.
z
• Look in the KDL typekit for more details

# User Manual

## Why to use KDL?

• Extensive support for :
• Geometric primitives: point, frame, twist ...
• Kinematic Trees: chain and tree structures. In literature, multiple definitions exist for a kinematic structure: a chain as the equivalent for all types of kinematic structures (chain, tree, graph) or chain as the serial version of a kinematic structure. KDL uses the last, or using graph-theory terminology:
• A closed-loop mechanism is a graph,
• an open-loop mechanism is a tree, and
• an unbranched tree is a chain.
Next to kinematics, also parameters for dynamics are included (inertia...)
• Realtime-safe operations/functions whenever relevant: they do not lead to dynamic memory allocations and all of them are deterministic in time.
• Python bindings
• Typekits and transport-kits for Orocos/RTT
• Integrated in ROS

# Geometric primitives

## KDL::Vector

Browse KDL API Documentation

A Vector is a 3x1 matrix containing X-Y-Z coordinate values. It is used for representing: 3D position of a point wrt a reference frame, rotational and translational part of a 6D motion or force entity : <equation id="vector"><equation>

### Creating Vectors

  Vector v1; //The default constructor, X-Y-Z are initialized to zero
Vector v2(x,y,z); //X-Y-Z are initialized with the given values
Vector v3(v2); //The copy constructor
Vector v4 = Vector::Zero(); //All values are set to zero

### Get/Set individual elements

The operators [ ] and ( ) use indices from 0..2, index checking is enabled/disabled by the DEBUG/NDEBUG definitions:

  v1[0]=v2[1];//copy y value of v2 to x value of v1
v2(1)=v3(3);//copy z value of v3 to y value of v2
v3.x( v4.y() );//copy y value of v4 to x value of v3

### Multiply/Divide with a scalar

You can multiply or divide a Vector with a double using the operator * and /:

  v2=2*v1;
v3=v1/2;

  v2+=v1;
v3-=v1;
v4=v1+v2;
v5=v2-v3;

### Cross and scalar product

  v3=v1*v2; //Cross product
double a=dot(v1,v2)//Scalar product

### Resetting

You can reset the values of a vector to zero:

  SetToZero(v1);

### Comparing vectors

Element by element comparison with or without user-defined accuracy:

  v1==v2;
v2!=v3;
Equal(v3,v4,eps);//with accuracy eps

## KDL::Rotation

A Rotation is the 3x3 matrix that represents the 3D rotation of an object wrt the reference frame.

<equation id="rotation"><equation>

### Creating Rotations

#### Safe ways to create a Rotation

The following result always in consistent Rotations. This means the rows/columns are always normalized and orthogonal:

  Rotation r1; //The default constructor, initializes to an 3x3 identity matrix
Rotation r1 = Rotation::Identity();//Identity Rotation = zero rotation
Rotation r2 = Rotation::RPY(roll,pitch,yaw); //Rotation built from Roll-Pitch-Yaw angles
Rotation r3 = Rotation::EulerZYZ(alpha,beta,gamma); //Rotation built from Euler Z-Y-Z angles
Rotation r4 = Rotation::EulerZYX(alpha,beta,gamma); //Rotation built from Euler Z-Y-X angles
Rotation r5 = Rotation::Rot(vector,angle); //Rotation built from an equivalent axis(vector) and an angle.

#### Other ways

The following should be used with care, they can result in inconsistent rotation matrices, since there is no checking if columns/rows are normalized or orthogonal

  Rotation r6( Xx,Yx,Zx,Xy,Yy,Zy,Xz,Yz,Zz);//Give each individual element (Column-Major)
Rotation r7(vectorX,vectorY,vectorZ);//Give each individual column

### Getting values

Individual values, the indices go from 0..2:

  double Zx = r1(0,2);

Getting EulerZYZ, Euler ZYX, Roll-Pitch-Yaw angles , equivalent rotation axis with angle:

  r1.GetEulerZYZ(alpha,beta,gamma);
r1.GetEulerZYX(alpha,beta,gamma);
r1.GetRPY(roll,pitch,yaw);
axis = r1.GetRot();//gives only rotation axis
angle = r1.GetRotAngle(axis);//gives both angle and rotation axis

Getting the Unit vectors:

  vecX=r1.UnitX();//or
r1.UnitX(vecX);
vecY=r1.UnitY();//or
r1.UnitY(vecY);
vecZ=r1.UnitZ();//or
r1.UnitZ(vecZ);

### Inverting Rotations

Replacing a rotation by its inverse:

  r1.SetInverse();//r1 is inverted and overwritten

Getting the inverse rotation without overwriting the original:

  r2=r1.Inverse();//r2 is the inverse rotation of r1

### Composing rotations

Compose two rotations to a new rotation, the order of the rotations is important:

  r3=r1*r2;


Compose a rotation with elementary rotations around X-Y-Z:

  r1.DoRotX(angle);
r2.DoRotY(angle);
r3.DoRotZ(angle);

this is the shorthand version of:

  r1 = r1*Rotation::RotX(angle)

### Rotation of a Vector

Rotating a Vector using a Rotation and the operator *:

  v2=r1*v1;

### Comparing Rotations

Element by element comparison with or without user-defined accuracy:

  r1==r2;
r1!=r2;
Equal(r1,r2,eps);

## KDL::Frame

A Frame is the 4x4 matrix that represents the pose of an object/frame wrt a reference frame. It contains:

• a Rotation M for the rotation of the object/frame wrt the reference frame.
• a Vector p for the position of the origin of the object/frame in the reference frame

<equation id="frame"><equation>

### Creating Frames

  Frame f1;//Creates Identity frame
Frame f1=Frame::Identity();//Creates an identity frame: Rotation::Identity() and Vector::Zero()
Frame f2(your_rotation);//Create a frame with your_rotation and a zero vector
Frame f3(your_vector);//Create a frame with your_vector and a identity rotation
Frame f4(your_rotation,your_vector);//Create a frame with your_rotation
Frame f5(your_vector,your_rotation);//and your_vector
Frame f5(f6);//the copy constructor

### Getting values

Individual values from the 4x4 matrix, the indices go from 0..3:

  double x = f1(0,3);
double Yy = f1(1,1);

Another way is to go through the underlying Rotation and Vector:

   Vector p = f1.p;
Rotation M = f1.M;

### Composing frames

You can use the operator * to compose frames. If you have a Frame F_A_B that expresses the pose of frame B wrt frame A, and a Frame F_B_C that expresses the pose of frame C wrt to frame B, the calculation of Frame F_A_C that expresses the pose of frame C wrt to frame A is as follows:

  Frame F_A_C = F_A_B * F_B_C;

F_A_C.p is the location of the origin of frame C expressed in frame A, and F_A_C.M is the rotation of frame C expressed in frame A.

### Inverting Frames

Replacing a frame by its inverse:

  //not yet implemented

Getting the inverse:

  f2=f1.Inverse();//f2 is the inverse of f1

### Comparing frames

Element by element comparison with or without user-defined accuracy:

  f1==f2;
f1!=f2;
Equal(f1,f2,eps);

## KDL::Twist

A Twist is the 6x1 matrix that represents the velocity of a Frame using a 3D translational velocity Vector vel and a 3D angular velocity Vector rot:

<equation id="twist"><equation>

### Creating Twists

  Twist t1; //Default constructor, initializes both vel and rot to Zero
Twist t2(vel,rot);//Vector vel, and Vector rot
Twist t3 = Twist::Zero();//Zero twist

Note: in contrast to the creation of Frames, the order in which vel and rot Vectors are supplied to the constructor is important.

### Getting values

Using the operators [ ] and ( ), the indices from 0..2 return the elements of vel, the indices from 3..5 return the elements of rot:

  double vx = t1(0);
double omega_y = t1[4];
t1(1) = vy;
t1[5] = omega_z;

Because some robotics literature put the rotation part on top it is safer to use the vel, rot members to access the individual elements:

  double vx = t1.vel.x();//or
vx = t1.vel(0);
double omega_y = t1.rot.y();//or
omega_y = t1.rot(1);
t1.vel.y(v_y);//or
t1.vel(1)=v_y;
//etc

### Multiply/Divide with a scalar

The same operators as for Vector are available:

  t2=2*t1;
t2=t1*2;
t2=t1/2;

The same operators as for Vector are available:

  t1+=t2;
t1-=t2;
t3=t1+t2;
t3=t1-t2;

### Comparing Twists

Element by element comparison with or without user-defined accuracy:

  t1==t2;
t1!=t2;
Equal(t1,t2,eps);

## KDL::Wrench

A Wrench is the 6x1 matrix that represents a force on a Frame using a 3D translational force Vector force and a 3D moment Vector torque:

<equation id="wrench"><equation>

### Creating Wrenches

  Wrench w1; //Default constructor, initializes force and torque to Zero
Wrench w2(force,torque);//Vector force, and Vector torque
Wrench w3 = Wrench::Zero();//Zero wrench

### Getting values

Using the operators [ ] and ( ), the indices from 0..2 return the elements of force, the indices from 3..5 return the elements of torque:

  double fx = w1(0);
double ty = w1[4];
w1(1) = fy;
w1[5] = tz;

Because some robotics literature put the torque part on top it is safer to use the torque, force members to access the individual elements:

  double fx = w1.force.x();//or
fx = w1.force(0);
double ty = w1.torque.y();//or
ty = w1.torque(1);
w1.force.y(fy);//or
w1.force(1)=fy;//etc

### Multiply/Divide with a scalar

The same operators as for Vector are available:

  w2=2*w1;
w2=w1*2;
w2=w1/2;

The same operators as for Twist are available:

  w1+=w2;
w1-=w2;
w3=w1+w2;
w3=w1-w2;

### Comparing Wrenchs

Element by element comparison with or without user-defined accuracy:

  w1==w2;
w1!=w2;
Equal(w1,w2,eps);

## Twist and Wrench transformations

Wrenches and Twists are expressed in a certain reference frame; the translational Vector vel of the Twists and the moment Vector torque of the Wrenches represent the velocity of, resp. the moment on, a certain reference point in that frame. Common choices for the reference point are the origin of the reference frame or a task specific point.

The values of a Wrench or Twist change if the reference frame or reference point is changed.

### Changing only the reference point

If you want to change the reference point you need the Vector v_old_new from the old reference point to the new reference point expressed in the reference frame of the Wrench or Twist:

t2 = t1.RefPoint(v_old_new);
w2 = w1.RefPoint(v_old_new);

### Changing only the reference frame

If you want to change the reference frame but want to keep the reference point intact you can use a Rotation matrix R_AB which expresses the rotation of the current reference frame B wrt to the new reference frame A:

ta = R_AB*tb;
wa = R_AB*wb;

Note: This operation seems to multiply a 3x3 matrix R_AB with 6x1 matrices tb or wb, while in reality it uses the 6x6 Screw transformation matrix derived from R_AB.

### Changing both the reference frame and the reference point

If you want to change both the reference frame and the reference point you can use a Frame F_AB which contains (i) Rotation matrix R_AB which expresses the rotation of the current reference frame B wrt to the new reference frame A and (ii) the Vector v_old_new from the old reference point to the new reference point expressed in A:

ta = F_AB*tb;
wa = F_AB*wb;

Note: This operation seems to multiply a 4x4 matrix F_AB with 6x1 matrices tb or wb, while in reality it uses the 6x6 Screw transformation matrix derived from F_AB.

## First order differentiation and integration

t = diff(F_w_A,F_w_B,timestep)//differentiation
F_w_B = F_w_A.addDelta(t,timestep)//integration

t is the twist that moves frame A to frame B in timestep seconds. t is expressed in reference frame w using the origin of A as velocity reference point.

# Kinematic Trees - KDL 1.0.x

## KDL::Joint

A Joint allows translation or rotation in one degree of freedom between two Segments

### Creating Joints

Joint rx = Joint(Joint::RotX);//Rotational Joint about X
Joint ry = Joint(Joint::RotY);//Rotational Joint about Y
Joint rz = Joint(Joint::RotZ);//Rotational Joint about Z
Joint tx = Joint(Joint::TransX);//Translational Joint along X
Joint ty = Joint(Joint::TransY);//Translational Joint along Y
Joint tz = Joint(Joint::TransZ);//Translational Joint along Z
Joint fixed = Joint(Joint::None);//Rigid Connection
Note: See the API document for a full list of construction possibilities

### Pose and twist of a Joint

Joint rx = Joint(Joint::RotX);
double q = M_PI/4;//Joint position
Frame f = rx.pose(q);
double qdot = 0.1;//Joint velocity
Twist t = rx.twist(qdot);
f is the pose resulting from moving the joint from its zero position to a joint value q t is the twist expressed in the frame corresponding to the zero position of the joint, resulting from applying a joint speed qdot

## KDL::Segment

A Segment is an ideal rigid body to which one single Joint is connected and one single tip frame. It contains:

• a Joint located at the root frame of the Segment.
• a Frame describing the pose between the end of the Joint and the tip frame of the Segment.

### Creating Segments

Segment s = Segment(Joint(Joint::RotX),
Frame(Rotation::RPY(0.0,M_PI/4,0.0),
Vector(0.1,0.2,0.3) )
);
Note: The constructor takes copies of the arguments, you cannot change the frame or joint afterwards!!!

### Pose and twist of a Segment

double q=M_PI/2;//joint position
Frame f = s.pose(q);//s constructed as in previous example
double qdot=0.1;//joint velocity
Twist t = s.twist(q,qdot);
fis the pose resulting from moving the joint from its zero position to a joint value q and expresses the new tip frame wrt the root frame of the Segment s. t is the twist of the tip frame expressed in the root frame of the Segment s, resulting from applying a joint speed qdot at the joint value q

## KDL::Chain

A KDL::Chain is

• a kinematic description of a serial chain of bodies connected by joints.
• built out of KDL::Segments.

A Chain has

• a default constructor, creating an empty chain without any segments.
• a copy-constructor, creating a copy of an existing chain.
• a =-operator.

Chain chain1;
Chain chain2(chain3);
Chain chain4 = chain5;

Chains are constructed by adding segments or existing chains to the end of the chain. These functions add copies of the arguments, not the arguments themselves!

chain1.addSegment(segment1);
chain1.addChain(chain2);

You can get the number of joints and number of segments (this is not always the same since a segment can have a Joint::None, which is not included in the number of joints):

unsigned int nj = chain1.getNrOfJoints();
unsigned int js = chain1.getNrOfSegments();

You can iterate over the segments of a chain by getting a reference to each successive segment:

Segment& segment3 = chain1.getSegment(3);

## KDL::Tree

A KDL::Tree is

• a kinematic description of a tree of bodies connected by joints.
• built out of KDL::Segments.

A Tree has

• a constructor that creates an empty tree without any segments and with the given name as its root name. The root name will be "root" if no name is given.
• a copy-constructor, creating a copy of an existing tree.
• a =-operator

Tree tree1;
Tree tree2("RootName");
Tree tree3(tree4);
Tree tree5 = tree6;

Trees are constructed by adding segments, existing chains or existing trees to a given hook name. The methods will return false if the given hook name is not in the tree. These functions add copies of the arguments, not the arguments themselves!

bool exit_value;
exit_value = tree1.addTree(tree2,"root");

You can get the number of joints and number of segments (this is not always the same since a segment can have a fixed joint (Joint::None), which is not included in the number of joints):

unsigned int nj = tree1.getNrOfJoints();
unsigned int js = tree1.getNrOfSegments();

You can retrieve the root segment:

std::map<std::string,TreeElement>::const_iterator root = tree1.getRootSegment();

You can also retrieve a specific segment in a tree by its name:

std::map<std::string,TreeElement>::const_iterator segment3 = tree1.getSegment("Segment 3");

You can retrieve the segments in the tree:

std::map<std::string,TreeElement>& segments = tree1.getSegments();

It is possible to request the chain in a tree between a certain root and a tip:

bool exit_value;
Chain chain;
exit_value = tree1.getChain("Segment 1","Segment 3",chain);
//Segment 1 and segment 3 are included but segment 1 is renamed.
Chain chain2;
exit_value = tree1.getChain("Segment 3","Segment 1",chain2);
//Segment 1 and segment 3 are included but segment 3 is renamed.

# Kinematic Trees - KDL 1.1.x

## KDL::Joint

A Joint allows translation or rotation in one degree of freedom between two Segments

### Creating Joints

Joint rx = Joint(Joint::RotX);//Rotational Joint about X
Joint ry = Joint(Joint::RotY);//Rotational Joint about Y
Joint rz = Joint(Joint::RotZ);//Rotational Joint about Z
Joint tx = Joint(Joint::TransX);//Translational Joint along X
Joint ty = Joint(Joint::TransY);//Translational Joint along Y
Joint tz = Joint(Joint::TransZ);//Translational Joint along Z
Joint fixed = Joint(Joint::None);//Rigid Connection
Note: See the API document for a full list of construction possibilities

### Pose and twist of a Joint

Joint rx = Joint(Joint::RotX);
double q = M_PI/4;//Joint position
Frame f = rx.pose(q);
double qdot = 0.1;//Joint velocity
Twist t = rx.twist(qdot);
f is the pose resulting from moving the joint from its zero position to a joint value q t is the twist expressed in the frame corresponding to the zero position of the joint, resulting from applying a joint speed qdot

## KDL::Segment

A Segment is an ideal rigid body to which one single Joint is connected and one single tip frame. It contains:

• a Joint located at the root frame of the Segment.
• a Frame describing the pose between the end of the Joint and the tip frame of the Segment.

### Creating Segments

Segment s = Segment(Joint(Joint::RotX),
Frame(Rotation::RPY(0.0,M_PI/4,0.0),
Vector(0.1,0.2,0.3) )
);
Note: The constructor takes copies of the arguments, you cannot change the frame or joint afterwards!!!

### Pose and twist of a Segment

double q=M_PI/2;//joint position
Frame f = s.pose(q);//s constructed as in previous example
double qdot=0.1;//joint velocity
Twist t = s.twist(q,qdot);
fis the pose resulting from moving the joint from its zero position to a joint value q and expresses the new tip frame wrt the root frame of the Segment s. t is the twist of the tip frame expressed in the root frame of the Segment s, resulting from applying a joint speed qdot at the joint value q

## KDL::Chain

A KDL::Chain is

• a kinematic description of a serial chain of bodies connected by joints.
• built out of KDL::Segments.

A Chain has

• a default constructor, creating an empty chain without any segments.
• a copy-constructor, creating a copy of an existing chain.
• a =-operator is supported too.

Chain chain1;
Chain chain2(chain3);
Chain chain4 = chain5;

Chains are constructed by adding segments or existing chains to the end of the chain. All segments must have a different name (or "NoName"), otherwise the methods will return false and the segments will not be added. The functions add copies of the arguments, not the arguments themselves!

bool exit_value;
exit_value = chain1.addChain(chain2);

You can get the number of joints and number of segments (this is not always the same since a segment can have a Joint::None, which is not included in the number of joints):

unsigned int nj = chain1.getNrOfJoints();
unsigned int js = chain1.getNrOfSegments();

You can iterate over the segments of a chain by getting a reference to each successive segment. The method will return false if the index is out of bounds.

Segment segment3;
bool exit_value = chain1.getSegment(3, segment3);

You can also request a segment by name:

Segment segment3;
bool exit_value = chain1.getSegment("Segment 3", segment3);

The root and leaf segment can be requested, as well as all segments in the chain.

bool exit_value;
Segment root_segment;
Segment leaf_segment;
std::vector<Segment> segments;
exit_value = chain1.getRootSegment(root_segment);
exit_value = chain1.getLeafSegment(leaf_segment);
exit_value = chain1.getSegments(segments);

You can request a part of the chain between a certain root and a tip:

bool exit_value;
Chain part_chain;

exit_value = chain1.getChain_Including(1,3, part_chain);
exit_value = chain1.getChain_Including("Segment 1","Segment 3", part_chain);
//Segment 1 and Segment 3 are included in the new chain!

exit_value = chain1.getChain_Excluding(1,3, part_chain);
exit_value = chain1.getChain_Excluding("Segment 1","Segment 3", part_chain);
//Segment 1 is not included in the chain. Segment 3 is included in the chain.

There is a function to copy the chain up to a given segment number or segment name:

bool exit_value;
Chain chain_copy;
exit_value = chain1.copy(3, chain_copy);
exit_value = chain1.copy("Segment 3", chain_copy);
//Segment 3, 4,... are not included in the copy!

## KDL::Tree

A KDL::Tree is

• a kinematic description of a tree of bodies connected by joints.
• built out of KDL::Segments.

A Tree has

• a constructor that creates an empty tree without any segments and with the given name as its root name. The root name will be "root" if no name is given.
• a copy-constructor, creating a copy of an existing tree.
• a =-operator.

Tree tree1;
Tree tree2("RootName");
Tree tree3(tree4);
Tree tree5 = tree6;

Trees are constructed by adding segments, existing chains or existing trees to a given hook name. The methods will return false if the given hook name is not in the tree. These functions add copies of the arguments, not the arguments themselves!

bool exit_value;
exit_value = tree1.addTree(tree2,"root");

You can get the number of joints and number of segments (this is not always the same since a segment can have a Joint::None, which is not included in the number of joints):

unsigned int nj = tree1.getNrOfJoints();
unsigned int js = tree1.getNrOfSegments();

You can retrieve the root segment and the leaf segments:

bool exit_value;
std::map<std::string,TreeElement>::const_iterator root;
std::map<std::string,TreeElement> leafs;
exit_value = tree1.getRootSegment(root);
exit_value = tree1.getLeafSegments(leafs);

You can also retrieve a specific segment in a tree by its name:

std::map<std::string,TreeElement>::const_iterator segment3;
bool exit_value = tree1.getSegment("Segment 3",segment3);

You can retrieve the segments in the tree:

std::map<std::string,TreeElement> segments;
bool exit_value = tree1.getSegments(segments);

It is possible to request the chain in a tree between a certain root and a tip:

bool exit_value;
Chain chain;
exit_value = tree1.getChain("Segment 1","Segment 3",chain);
//Segment 1 and segment 3 are included but segment 1 is renamed.
Chain chain2;
exit_value = tree1.getChain("Segment 3","Segment 1",chain2);
//Segment 1 and segment 3 are included but segment 3 is renamed.

This chain can also be requested in a tree structure with the given root name ("root" if no name is given).

bool exit_value;
Tree tree;
exit_value = tree1.getChain("Segment 1","Segment 3",tree,"RootName");
Tree tree2;
exit_value = tree1.getChain("Segment 3","Segment 1",tree2,"RootName");

There is a function to copy a tree excluding some segments and all their decendants.

bool exit_value;
Tree tree_copy;
exit_value = tree1.copy("Segment 3", tree_copy);
//tree1 is copied up to segment 3 (excluding segment 3).
std::vector<std::string> vect;
vect.push_back("Segment 1");
vect.push_back("Segment 7");
exit_value = tree1.copy(vect,tree_copy);

# Kinematic and Dynamic Solvers

KDL contains for the moment only generic solvers for kinematic chains. They can be used (with care) for every KDL::Chain.

The idea behind the generic solvers is to have a uniform API. We do this by inheriting from the abstract classes for each type of solver:

• ChainFkSolverPos
• ChainFkSolverVel
• ChainIkSolverVel
• ChainIkSolverPos

A seperate solver has to be created for each chain. At construction time, it will allocate all necessary resources.

A specific type of solver can add some solver-specific functions/parameters to the interface, but still has to use the generic interface for it's main solving purpose.

The forward kinematics use the function JntToCart(...) to calculate the Cartesian space values from the Joint space values. The inverse kinematics use the function CartToJnt(...) to calculate the Joint space values from the Cartesian space values.

## Recursive forward kinematic solvers

For now we only have one generic forward and velocity position kinematics solver.

It recursively adds the poses/velocity of the successive segments, going from the first to the last segment, you can also get intermediate results by giving a Segment number:

ChainFkSolverPos_recursive fksolver(chain1);
JntArray q(chain1.getNrOfJoints);
q=...
Frame F_result;
fksolver.JntToCart(q,F_result,segment_nr);

# KDL-Examples

Some small examples for usage.

# Kuka LBR user group

This page collects all useful information for the User Group for the KUKA Light-Weight-Robot.

The following institutes are currently involved:

• Katholieke Universiteit Leuven (KULeuven), division PMA

At K.U.Leuven we released Orocos Components for communicating with the LBR using RSI and FRI interfaces. The RSI component should be usable for all KUKA Robots that offer RSI.

The FRI interface software can be found at: http://git.mech.kuleuven.be/robotics/kuka_robot_hardware.git

The RSI interface software can be found at: http://svn.mech.kuleuven.be/repos/orocos/orocos-apps/public_release/Kuka_RSI At KU Leuven RSI is currently not actively used.

The FRI and RSI interface, provide you with an Orocos component that you can add to your robot application to handle the communication with the robot controller.

A readme file with the main installation steps is provided with the code (git or svn checkout). All comments, discussions, questions and suggestions are very welcome at the mailing list: see http://lists.mech.kuleuven.be/mailman/listinfo/kuka-lwr for info on how to subscribe.

# OCL wiki

This wiki has only information for the OCL 1.x releases. For OCL 2.x, look at the 'Toolchain' wiki.

In order to have readline tab-completion in the taskbrowser, you'll need OCL 1.12.0 or 2.1.0 or later.

homepage:

It is advised to keep copies/backups of these files on your own site, since they are not official readline releases, but patched to work on Windows.

and then open the solution in the directory:

The build will place a static readline.lib in the ../lib directory.

## Configuring OCL

 set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "C:/Documents and Settings/virtual/My documents/readline5.2/include") set(CMAKE_LIBRARY_PATH${CMAKE_LIBRARY_PATH} "C:/Documents and Settings/virtual/My documents/readline5.2/lib")

Where 'C:/Documents and Settings/virtual/My documents/' is the directory where you unpacked the downloads.

Continue to configure OCL in the cmake GUI by turning off the NO_GPL flag (by default on on Windows). It will then try to link the taskbrowser with the readline.lib file, which should succeed. After installing ocl, readline should work as on Linux, but only on the standard cygwin or cmd.exe prompts, not on rxvt

# quotes from "Really Reusable Robot Code and the Player/Stage Project"

Further some significants parts of the paper "Really Reusable Robot Code and the Player/Stage Project" have been copied. The purpose it to present a possible philosophy to drive the development of OCL 2.0 (it is recommended to read the entire paper). Feel free to discuss these concepts in the forum.

Our design philosophy is heavily influenced by the operating systems (OS) community, which has already solved many of the same problems that we face in robotics research. For example, the principle function of an operating system is to hide the details of the underlying hardware, which may vary from machine to machine. Similarly, we want to hide the details of the underlying robot. Just as I expect my web browser to work with any mouse, I want my navigation system to work with any robot. Where OS programmers have POSIX, we want a common development environment for robotic applications. Operating systems are equipped with standard tools for using and inspecting the system, such as (in UNIX variants) top, bash, ls, and X11. We desire a similar variety of high-quality tools to support experimental robotics.
Operating systems also support virtually any programming language and style. They do this by allowing the low-level OS interface (usually written in C) to be easily wrapped in other languages, and by providing language-neutral interfaces (e.g., sockets, files) when possible. Importantly, no constraints or normative judgments are made on how best to structure a program that uses the OS. We take the same approach in building robotics infrastructure. Though not strictly part of the OS, another key feature of modern development environments is the availability of standard algorithms and related data structures, such as qsort(), TCP, and the C++ Standard Template Library. We follow this practice of incorporating polished versions of established algorithms into the common code repository, so that each researcher need not re-implement, for example, Monte Carlo localization. Finally, an important but often over-looked aspect of OS design is that access is provided at all levels. While most C programmers will manage memory allocation with the library functions malloc() and free(), when necessary they can dig deeper and invoke the system call brk() directly. We need the same multi-level access for robots; while one researcher may be content to command a robot with high-level “goto” commands, another will want to directly control wheel velocities.
Player comprises four key abstractions: The Player Abstract Device Interface (PADI), the message protocol, the transport mechanism, and the implementation. Each abstraction represents a reusable and separable layer. For example, the TCP client/server transport could be replaced by a CORBA
The central abstraction that enables portability and code re-use in Player is the PADI speci?cation. The PADI defines the syntax and semantics of the data that is exchanged between the robot control code and the robot hardware. For ease of use, the PADI is currently specified as a set of C message structures; the same information could instead be written in an Interface Definition Language (IDL), such as the one used in CORBA systems. The PADI’s set of abstract robot control interfaces constitutes a virtual machine, a target platform for robot controllers that is instantiated at run time by particular devices. The goal of the PADI is to provide a virtual machine that is rich enough to support any foreseeable robot control system, but simple enough to allow for an e?cient implementation on a wide array of robot hardware. The key concepts used in the PADI, both borrowed from the OS community, are the character device model and the driver/interface model.
The interface/driver model groups devices by logical functionality, so that devices which do approximately the same job appear identical from the user’s point of view. An interface is a specification for the contents of the data stream, so an interface for a robotic character device maps the input stream into sensor readings, output stream into actuator commands, and ioctls into device configurations. The code that implements the interface, converting between a device’s native formats and the interface’s required formats is called a driver. Drivers are usually speci?c to a particular device, or a family of devices from the same vendor. Code that is written to target the interface rather than any specific device is said to be device independent. When multiple devices have drivers that implement the same interface, the controlling code is portable among those devices. Many hardware devices have unique features that do not appear in the standard interface. These features are accessed by device-specific ioctls, while the read and write streams are generally device independent. Interfaces should be designed to be suficiently complete so as to not require use of device-specific ioctls in normal operation, in order to maintain device independence and portability. There is not a one-to-one mapping between interface definitions and physical hardware components. For example, the Pioneer’s native P2OS interface bundles odometry and sonar data into the same packet, but a Player controller that only wants to log the robot’s position does not need the range data. For portability, Player separates the data into two logical devices, decoupling the logical functionality from the details of the Pioneer’s implementation. The pioneer driver controls one physical piece of hardware, the Pioneer microcontroller, but implements two different devices: position2d and sonar. These two devices can be opened, closed, and controlled independently, relieving the user of the burden of remembering details about the internals of the robot.
In order to more conveniently support different devices, we introduced the interface/driver distinction to Player. An interface, such as sonar, is a generic specification of the format for data, command, and configuration interactions that a device allows. A driver, such as pioneer-sonar, specifies how the low-level device control will be carried out. In general, more than one driver may support a given interface; conversely, a given driver may support multiple interfaces. Thus we have extended to robot control the device model that is used in most operating systems, where, for example, a wide variety of joysticks all present the same “joystick” interface to the programmer.
The primary cost of adherence to a generic interface for an entire class of devices is that the features and functionality that are unique to each device are ignored. Imagine a fiducial-finder interface whose data format includes only the bearing and distance to each fiducial. In order to support that interface, a driver that can also determine a fiducial’s identity will be under-utilized, some of its functionality having been sacrificed for the sake of portability. This issue is usually addressed by either adding configuration requests to the existing interface or defining a new interface that exposes the desired features of the device. Consider Player’s Monte-Carlo localization driver amcl; it can support both the sophisticated localization interface that includes multiple pose hypotheses, and the simple position2d interface that includes one pose and is also used by robot odometry systems.
These higher-level drivers use other drivers, instead of hardware, as sources of data and sinks for commands. The amcl driver, for example, is an adaptive Monte Carlo localization system [TFBD00] that takes data from a position2d device, a laser device, and a map device, and in turn provides robot pose estimates via the localize interface (as mentioned above, amcl also supports the simpler position2d interface, through which only the most likely pose estimate is provided). Other Player drivers perform functionality such as path-planning, obstacle avoidance, and various image-processing tasks. The development of such higher-level drivers and corresponding interfaces yields three key benefits. First, we save time and effort by implementing well-known and useful algorithms in such a way that they are immediately reusable by the entire community. Just as C programmers can call qsort() instead of reimplementing quicksort, robotics students and researchers students should be able to use Player’s vfh driver instead of reimplementing the Vector Field Histogram navigation algorithm [UB98]. The author of the driver benefits by having her code tested by other scientists in environments and with robots to which she may not have access, which can only improve the quality of the algorithm and its implementation. Second, we create a common development environment for implementing such algorithms. Player’s C++ Driver API clearly defines the input/output and startup/shutdown functionality that a driver must have. Code that is written against this API can enter a community repository where it is easily understood and can be reused, either in whole or in part. Finally, we create an environment in which alternative algorithms can be easily substituted. If a new localization driver implements the familiar localize interface, then it is a drop-in replacement for Player’s amcl. The two algorithms can be run in parallel on the same data and the results objectively compared.

# RTT wiki

This wiki has only information for the RTT 1.x releases. For RTT 2.x, look at the Toolchain wiki.

# Documentation suggestions

From recent discussion on ML, simply a place to put down ideas before we forget them ...

• Use Wiki for FAQ instead of XML doc

## FAQ

• My shared libraries won't load
• The deployer won't load my plugins
• Can I use dynamic memory allocation, and where?
• How do I run in real time? ie how do I configure my system to allow

Orocos to run in real-time

• Why do I have periodic delays when attaching a remote deployer?
• Configuring OmniORB instead of TAO
• OmniORB options for IDL
• How do I set a client application using pyOmniOrb (OmniORB python bindings)?

<quote> Actually it's an option of the omniidl compiler... the command to use is

omniidl -bcxx -Wba myIdlFile.idl

This will become definately a FAQ item :-) <quote>

• My wiki page is blank

<quote> When your text is not appearing on your wiki page, it's because you ended your wiki page with an indented line. So if your last line is:

this is my last line

the wiki code clears the whole page. It's clearly a Drupal/wiki module thing/bug. <quote>

• Is it possible to log messages from scripts and state machines?

Check out OCL's HmiConsoleOutput component.

• What is the coding style used by Orocos?

You can take a look at the CODING_STYLE.txt file. Also, we worked out the indentation rules for Eclipse and Emacs.

## Tutorials

• Seems like I have to first read up on Activities. Can you point me to a good example for such a component test and mockobject in rtt/tests?
• Hello world as an application
• Hello world with the deployer
• Use of reporting component
• CMake and non-standard install location for Orocos
• Distributed deployment - ie how to use more than one deployer, and setting up CORBA
• Changing ReadDataPort/WriteDataPort to DataPort for CORBA-based deployment

## System examples

• Robotics
Examples like Stephen proposed earlier
Focus on : Kinematics, path planning, HMI/interfacing
• Machine control
Similar to Robotics with without kinematics or path planning
Master controller that controls slave devices through a state machine
Focus on : state machines & events
• Distributed
Sensor data processing using various distributed components
Focus on : Data flow

end

# Examples and Tutorials

The tutorials and example code are split in two parts, one for new users and one for experienced users of the RTT.

There are several sources where you can find code and tutorials. Some code is listed in wiki pages, other is downloadable in a separate package and finally you can find code snippets in the manuals too.

## Simple examples

RTT Examples Get started with simple, ready-to-compile examples of how to create a component

Naming connections, not ports: Orocos' best kept secret

Using omniORBpy to interact with a component from Python

Using plugins and toolkits to support custom data types

Using non-periodic components to implement a simple TCP client

Using XML substitution to manage complex deployments

Using real-time logging

# Developing plugins and toolkits

This is a work in progress and only for RTT 1.x !

## Rationale

Problem: You want to pass custom types between distributed components, be able to see the value(s) of your custom type with in a deployer, and be able to read/write the custom type to/from XML files.

<!-- break -->

## Assumptions

• The build directory is within the source directory. This helps with dynamic library loading.

## Compatabilitry

Tested on v1.8 trunk on Mac OS X Leopard with omniORB from MacPorts, and Ubuntu Jaunty with ACE/TAO.

## Overview

An RTT toolkit plugin provides information to Orocos about one or more custom types. This type of plugin allows RTT to display your types values in a deployer, load/save your types to/from XML files, and provides constructors and operators that can be used to manipulate your types within program scripts and state machines.

An RTT transport plugin provides methods to transport your custom types across CORBA, and hence between distributed Orocos components.

This is a multi-part example demonstrating plugins for two boost::posix_time types: ptime and time_duration.

• Part 1 Without the plugin creates components that use your custom type, and demonstrates that Orocos does not know anything about these types
• Part 2 Toolkit plugin demonstrates an Orocos plugin that makes the types available to Orocos. In a deployer, you can now see the values of your custom types
• Part 3 Transport plugin demonstrates an Orocos transport plugin making your custom types available across CORBA. Now you can pass types between deployers.
• TBD Part 4 will demonstrate XML manipulation
• TBD Part 5 will demonstrate accesors and manipulators for use in scripts and state machines

For additional information on plugins and their development, see [1].

Also, the KDL toolkit and transport plugins are good examples. See src/bindings/rtt in the KDL source.

## Structure

The overall structure of this examples is show below
.
|-- BoostToolkit.cpp
|-- BoostToolkit.hpp
|-- CMakeLists.txt
|-- config
|   |-- FindACE.cmake
|   |-- FindCorba.cmake
|   |-- FindOmniORB.cmake
|   |-- FindOrocos-OCL.cmake
|   |-- FindOrocos-RTT.cmake
|   |-- FindTAO.cmake
|   |-- UseCorba.cmake
|   -- UseOrocos.cmake
|-- corba
|   |-- BoostCorbaConversion.hpp
|   |-- BoostCorbaToolkit.cpp
|   |-- BoostCorbaToolkit.hpp
|   |-- BoostTypes.idl
|   |-- CMakeLists.txt
|   -- tests
|       |-- CMakeLists.txt
|       |-- corba-combined.cpp
|       |-- corba-recv.cpp
|       -- corba-send.cpp
-- tests
|-- CMakeLists.txt
|-- combined.cpp
|-- no-toolkit.cpp
|-- recv.cpp
|-- recv.hpp
|-- send.cpp
-- send.hpp

The toolkit plugin is in the root directory, with supporting test files in the tests directory.

CMake support files are in the config directory.

The transport plugin is in the corba directory, with supporting test files in the corba/tests directory.

## Limitations

Currently, this example does
• Show how to write a plugin telling Orocos about your custom types
• Show how to write a transport plugin allowing Orocos to move your custom types between deployers/processes.
• Demonstrate how to test said plugins.
• Use either ACE/TAO or OmniORB for CORBA support

Currently, this example does not yet

• Show how to read/write the custom types to/from XML file
• Provide manipulators and/or accessors of your custom types, that can be used in scripts and state machines.
• Does not demonstrate testing of the CORBA transport plugin within a single deployer, using two components. An optimization in RTT bypasses the CORBA mechanism in this case, rendering the test useless.
• Does not deal with all intricacies of the boost types (eg all of the special values).

NB I could not find a method to get at the underlying raw 64-bit or 96-bit boost representation of ptime. Hence, the transport plugin inefficiently transports a ptime type using two separate data values. If you know of a method to get at the raw representation, I would love to know. Good luck in template land ...

## References

[1] Extending the Real-Time Toolkit
AttachmentSize
BoostToolkit.hpp2.64 KB
BoostToolkit.cpp3.58 KB
CMakeLists.txt1.83 KB
corba/BoostCorbaToolkit.hpp934 bytes
corba/BoostCorbaToolkit.cpp1.34 KB
corba/QBoostCorbaConversion.hpp5.18 KB
corba/CMakeLists.txt738 bytes
plugins.tar_.bz214.24 KB

# Part 1 Without the plugin

This is a work in progress

This part creates components that use your custom type, and demonstrates that Orocos does not know anything about these types.

## Files

See the attachments at the bottom of Developing plugins and toolkits.

## To build

In a shell

cd /path/to/plugins
mkdir build
cd build
cmake .. -DOROCOS_TARGET=macosx -DENABLE_CORBA=OFF
make

For other operating systems substitute the appopriate value for "macosx" when setting OROCOS_TARGET (e.g. "gnulinux").

Tested in Mac OS X Leopard 10.5.7.

## To run

In a shell

cd /path/to/plugins/build
./no-toolkit

This starts a test case that uses an OCL taskbrowser to show two components: send and recv. If you issue a "ls" or "ls Send" command, you will get output similar to the following:

 Data Flow Ports:
RW(C)   unknown_t ptime          = (unknown_t)
RW(C)   unknown_t timeDuration   = (unknown_t)

Each component has two ports, named ptime and time_duration. Notice that both ports are connected "(C)", but that Orocos considers each an unknown type with unknown value.

Part 2 Toolkit plugin will build a toolkit plugin that allows Orocos to understand these types.

# Part 2 Toolkit plugin

This is a work in progress

This part creates a toolkit plugin making our types known to Orocos.

## Files

See the attachments at the bottom of Developing plugins and toolkits

## To build

Everything needed for this part was built in Part 1.

## To run

In a shell

cd /path/to/plugins/build
./combined

The combined tests uses an OCL taskbrowser to show two components: send and recv. Typing an "ls" or "ls Send" command, as in Part 1, you will get something like the following:

RW(C) boost_ptime ptime          = 2009-Aug-09 16:14:19.724622
RW(C) boost_timeduration timeDuration   = 00:00:00.200005

Note that Orocos now knows the correct types (eg boost_ptime) and can display each ports value. Issue multiple ls commands and you will see the values change. The ptime is simply the date and time at which the send component set the port value, and the duration is the time between port values being set on each iteration (ie this should approximately be the period of the send component).

## Toolkit plugin

The toolkit plugin is defined in BoostToolkit.hpp.

namespace Examples
{
/// \remark these do not need to be in the same namespace as the plugin

/// put the time onto the stream
std::ostream& operator<<(std::ostream& os, const boost::posix_time::ptime& t);
/// put the time onto duration the stream
std::ostream& operator<<(std::ostream& os, const boost::posix_time::time_duration& d);
/// get a time from the stream
std::istream& operator>>(std::istream& is, boost::posix_time::ptime& t);
/// get a time duration from the stream
std::istream& operator>>(std::istream& is, boost::posix_time::time_duration& d);
The toolkit plugin is contained in an Examples namespace. First up we define input and output stream operators for each of our types.

    class BoostPlugin : public RTT::ToolkitPlugin
{
public:
virtual std::string getName();

};

/// The singleton for the Toolkit.
extern BoostPlugin BoostToolkit;
The actual plugin class and singleton object are then defined. The plugin provides a name that is unique across all plugins, and contains information on the types, constructors and operators for each or our custom types.

    /// provide ptime type to RTT type system
/// \remark the 'true' argument indicates that we supply stream operators
struct BoostPtimeTypeInfo :
public RTT::TemplateTypeInfo<boost::posix_time::ptime,true>
{
BoostPtimeTypeInfo(std::string name) :
RTT::TemplateTypeInfo<boost::posix_time::ptime,true>(name)
{};
bool decomposeTypeImpl(const boost::posix_time::ptime& img, RTT::PropertyBag& targetbag);
bool composeTypeImpl(const RTT::PropertyBag& bag, boost::posix_time::ptime& img);
};

/// provide time duration type to RTT type system
/// \remark the 'true' argument indicates that we supply stream operators
struct BoostTimeDurationTypeInfo :
public RTT::TemplateTypeInfo<boost::posix_time::time_duration,true>
{
BoostTimeDurationTypeInfo(std::string name) :
RTT::TemplateTypeInfo<boost::posix_time::time_duration,true>(name)
{};
bool decomposeTypeImpl(const boost::posix_time::time_duration& img, RTT::PropertyBag& targetbag);
bool composeTypeImpl(const RTT::PropertyBag& bag, boost::posix_time::time_duration& img);
};

} // namespace Exampels
We then provide a type information class for each of our two custom types. These type info classes are the mechanism for Orocos to work with XML and our custom types.NB the true boolean value to each TypeInfo class indicates that stream operators are available (as defined above).

The toolkit plugin implementation is in the BoostToolkit.cpp file.

namespace Examples
{
using namespace RTT;
using namespace RTT::detail;
using namespace std;

std::ostream& operator<<(std::ostream& os, const boost::posix_time::ptime& t)
{
os << boost::posix_time::to_simple_string(t);
return os;
}

std::ostream& operator<<(std::ostream& os, const boost::posix_time::time_duration& d)
{
os << boost::posix_time::to_simple_string(d);
return os;
}

std::istream& operator>>(std::istream& is, boost::posix_time::ptime& t)
{
is >> t;
return is;
}

std::istream& operator>>(std::istream& is, boost::posix_time::time_duration& d)
{
is >> d;
return is;
}
After picking up some RTT workspaces, we declare the stream operators to use the underlying boost stream operators. TODO explain why need these stream operators.

    BoostPlugin BoostToolkit;

std::string BoostPlugin::getName()
{
return "Boost";
}
Next we create the singleton instance of the plugin as BoostToolkit. TODO explain naming scheme. Then we declare the unique name of this plugin, "Boost".

    bool BoostPlugin::loadTypes()
{
TypeInfoRepository::shared_ptr ti = TypeInfoRepository::Instance();

/* each quoted name here (eg "boost_ptime") must _EXACTLY_ match that
in the associated TypeInfo::composeTypeImpl() and
TypeInfo::decomposeTypeImpl() functions (in this file), as well as
the name registered in the associated Corba plugin's
registerTransport() function (see corba/BoostCorbaToolkit.cpp)
*/

return true;
}
The loadTypes() method provides the actual association for Orocos, from a type name to a TypeInfo class. This is how Orocos identifies a type at runtime. The choice of name is critical - it is what is shown in the deployer for an items type, and should make immediate sense when you see it. It probably also should not be too long, to keep things readable within the deployer and taskbrowser. The name you use here for each type is very important and must match with names in other places (TODO list the other places?).

    bool BoostPlugin::loadConstructors()
{
// no constructors for these particular types

return true;
}

{
// no operators for these particular types

return true;
}
Currently this example does not provide any constructors or operators, useable in program scripts and state machines. TODO update this.

    bool BoostPtimeTypeInfo::decomposeTypeImpl(const boost::posix_time::ptime& source,
PropertyBag& targetbag)
{
targetbag.setType("boost_ptime");
assert(0);
return true;
}

bool BoostPtimeTypeInfo::composeTypeImpl(const PropertyBag& bag,
boost::posix_time::ptime& result)
{
if ( "boost_ptime" == bag.getType() ) // ensure is correct type
{
// \todo
assert(0);
}
return false;
}
The implementation of a TypeInfo class for one of our custom types must use the same type name as use in loadTypes() above. These functions would also provide the mechanism to load/save the type to/from XML. TODO update this.

ORO_TOOLKIT_PLUGIN(Examples::BoostToolkit)
This macro (lying outside the namespace!) takes the fully qualified singleton, and makes it available to the RTT type system at runtime. It basically makes the singleton identifiable as an RTT toolkit plugin, when Orocos loads the dynamic library formed from this toolkit.

## Build system

Now the build system takes this .cpp file, and turns it into a dynamic library. We are going to examine the root CMakeLists.txt to see how to create this library, but for now, we will ignore the corba parts of that file.

cmake_minimum_required(VERSION 2.6)

# pick up additional cmake package files (eg FindXXX.cmake) from this directory
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/config") First we enforce the minimum CMake version we require, and ensure that we can pick up FindXXX.cmake files from our config directory find_package(Orocos-RTT 1.6.0 REQUIRED corba) find_package(Orocos-OCL 1.6.0 REQUIRED taskbrowser) We have to find both Orocos RTT and Orocos OCL, and we require the additional corba component from RTT and the additional taskbrowser component from OCL. include(${CMAKE_SOURCE_DIR}/config/UseOrocos.cmake)
The UseOrocos.cmake file makes RTT and OCL available to us, and provides us with some useful macros (eg create_component).

create_component(BoostToolkit-${OROCOS_TARGET} VERSION 1.0.0 BoostToolkit.cpp) TARGET_LINK_LIBRARIES(BoostToolkit-${OROCOS_TARGET}
boost_date_time)
The create_component macro makes an Orocos shared library for us. This library will contain only our toolkit plugin. Note that we make the library name dependent on the Orocos target we are building for (eg macosx or gnulinux). This allows us to have plugins for multiple architectures on the same machine (typically, gnulinux and xenomai, or similar). We also have to link the shared library against the boost "date time" library, as we are using certain boost functionality that is not available in the header files.

SUBDIRS(tests)
Lastly we also build the 'test' directory.

## Tests

There are two very simply test components that communicate each of our custom types between them. Tests are very important when developing plugins. Trying to debug a plugin within a complete system is a daunting challenge - do it in isolation first.

The send component regularly updates the current time on its ptime port, and the duration between ptime port updates on its timeDuration port.

class Send : public RTT::TaskContext
{
public:
RTT::DataPort<boost::posix_time::ptime>                ptime_port;
RTT::DataPort<boost::posix_time::time_duration>    timeDuration_port;
public:
Send(std::string name);
virtual ~Send();

virtual bool startHook();
virtual void updateHook();
protected:
boost::posix_time::ptime    lastNow;
};

The implementation is very simple, and will not be discussed in detail here.

#include "send.hpp"

Send::Send(std::string name) :
ptime_port("ptime"),
timeDuration_port("timeDuration")
{
}

Send::~Send()
{
}

bool Send::startHook()
{
// just set last to now
lastNow            = boost::posix_time::microsec_clock::local_time();
return true;
}

void Send::updateHook()
{
boost::posix_time::ptime            now;
boost::posix_time::time_duration    delta;

// send the current time, and the duration since the last updateHook()
now        = boost::posix_time::microsec_clock::local_time();
delta   = now - lastNow;

ptime_port.Set(now);
timeDuration_port.Set(delta);

lastNow = now;
}

The recv component has the same ports but does nothing. It is simply an empty receiver component, that allows us to view its ports within the deployer.

class Recv : public RTT::TaskContext
{
public:
RTT::DataPort<boost::posix_time::ptime>                ptime_port;
RTT::DataPort<boost::posix_time::time_duration>        timeDuration_port;

public:
Recv(std::string name);
virtual ~Recv();
};

And the recv implementation.

#include "recv.hpp"

Recv::Recv(std::string name) :
ptime_port("ptime"),
timeDuration_port("timeDuration")
{
}

Recv::~Recv()
{
}

Now the combined test program just combines one of each test component directly within the same executable.

#include <rtt/RTT.hpp>
#include <rtt/PeriodicActivity.hpp>
#include <rtt/os/main.h>
#include <rtt/Ports.hpp>

#include "send.hpp"
#include "recv.hpp"
#include "../BoostToolkit.hpp"

using namespace std;
using namespace Orocos;

int ORO_main(int argc, char* argv[])
{
RTT::Toolkit::Import(Examples::BoostToolkit);
This forcibly loads our toolkit plugin.


Recv                recv("Recv");
PeriodicActivity    recv_activity(ORO_SCHED_OTHER, 0, 0.1, recv.engine());
Send                 send("Send");
PeriodicActivity    send_activity(ORO_SCHED_OTHER, 0, 0.2, send.engine());

if ( connectPeers( &send, &recv ) == false )
{
log(Error) << "Could not connect peers !"<<endlog();
return -1;
}
if ( connectPorts( &send, &recv) == false )
{
log(Error) << "Could not connect ports !"<<endlog();
return -1;
}
Connect the ports of the two components, and makes them peers

    send.configure();
recv.configure();
send_activity.start();
recv_activity.start();

browser.loop();
Configures and starts both compents, and then runs an OCL::TaskBrowser over the receive component.

    send_activity.stop();
recv_activity.stop();

return 0;
}
Stops and exits cleanly.

The differences between the combined and no-toolkit test programs will be covered in Part 2, but essentially amounts to not loading the toolkit.

Part 3 Transport plugin will build a transport plugin allowing Orocos to communicate these types across CORBA.

# Part 3 Transport plugin

'This is a work in progress''

This part builds a transport plugin allowing Orocos to communicate these types across CORBA.

## Files

See the attachments at the bottom of Developing plugins and toolkits

## To build

In a shell

cd /path/to/plugins
mkdir build
cd build
cmake .. -DOROCOS_TARGET=macosx -DENABLE_CORBA=ON
make

The only difference from building in Part 1, is to turn ON CORBA.

For other operating systems substitute the appopriate value for "macosx" when setting OROCOS_TARGET (e.g. "gnulinux").

Tested in Mac OS X Leopard 10.5.7.

## To run

In a shell

cd /path/to/plugins/build/corba/tests
./corba-recv

In a second shell

cd /path/to/plugins/build/corba/tests
./corba-send

Now the same exact two test components of Parts 1 and 2 are in separate processes. Typing ls in either process will present the same values (subject to network latency delays, which typically are not human perceptible) - the data and types are now being communicated between deployers.

Now, the transport plugin is responsible for communicating the types between deployers, while the toolkit plugin is responsible for knowing each type and being able to display it. Separate responsibilities. Separate plugins.

NB for the example components, send must be started after recv. Starting only corba-recv and issuing ls will display the default values for each type. Also, quitting the send component and then attempting to use the recv component will lockup the recv deployer. These limitations are not due to the plugins - they are simply due to the limited functionality of these test cases.

### Without the transport plugin

Running the same two corba test programs but without loading the transport plugin, is instructive as to what happens when you do not match up certain things in the toolkit sources. This is very important!

In a shell

cd /path/to/plugins/build/corba/tests
./corba-recv-no-toolkit
An ls in the recv component now gives
 Data Flow Ports:
RW(U) boost_ptime ptime          = not-a-date-time
RW(U) boost_timeduration timeDuration   = 00:00:00
This is expected, as we have not connected the send component yet and so recv has default values.

In a second shell

cd /path/to/plugins/build/corba/tests
./corba-send-no-toolkit

The send component without the transport plugin fails to start, with:

$./build/corba/tests/corba-send-no-toolkit 0.008 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Forcing priority (0) of thread to 0. 0.008 [ Warning][PeriodicThread] Forcing priority (0) of thread to 0. 0.027 [ Warning][SingleThread] Forcing priority (0) of thread to 0. 5.078 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] ControlTask 'Send' already bound \ to CORBA Naming Service. 5.078 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Trying to rebind... done. New \ ControlTask bound to Naming Service. 5.130 [ Warning][./build/corba/tests/corba-send-no-toolkit::main()] Can not create a proxy for data \ connection. 5.130 [ ERROR ][./build/corba/tests/corba-send-no-toolkit::main()] Dynamic cast failed \ for 'PN3RTT14DataSourceBaseE', 'unknown_t', 'unknown_t'. Do your typenames not match? Assertion failed: (doi && "Dynamic cast failed! See log file for details."), function createConnection, \ file /opt/install/include/rtt/DataPort.hpp, line 462. Abort trap The culprit here is that we tried to pass unknown types through CORBA. While the toolkit plugin tells Orocos about a type, it takes a transport plugin to tell Orocos how to communicate the type. The above failure indicates that Orocos came across a type named unknown_t and did not know how to deal with it. We will cover this more later in the tutorial, and specifically where and why this occurs. As a matter of interest, comparing the sources of corba/tests/corba-recv.cpp and corba/tests/corba-recv-no-toolkit.cpp, the differences are *** corba/tests/corba-recv.cpp 2009-07-29 22:08:32.000000000 -0400 --- corba/tests/corba-recv-no-toolkit.cpp 2009-08-09 16:32:03.000000000 -0400 *************** *** 11,17 **** #include <rtt/os/main.h> #include <rtt/Ports.hpp> - #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" // use Boost RTT Toolkit test components --- 11,16 ---- *************** *** 27,33 **** int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); - RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin ); Recv recv("Recv"); PeriodicActivity recv_activity( --- 26,31 ---- We simply did not load the transport plugin. ## Transport plugin The transport plugin implementation spans three files. We will cover them in turn ### Defining CORBA types We define the CORBA types in corba/BoostTypes.idl. This is a file in CORBA's Interface Description Language (IDL). There are plenty of references on the web, for instance [1]. // must be in RTT namespace to match some rtt/corba code module RTT { module Corba { These structures must be in the RTT::Corba namespace.  struct time_duration { short hours; short minutes; short seconds; long nanoseconds; }; We send a time duration as individual time components. Note that we avoid boost's fraction_secionds fiasco, and always send nanoseconds even if the sender or receiver implementations only support microseconds.  // can't get at underlying type, so send this way (yes, more overhead) // see BoostCorbaConversion.hpp::struct AnyConversion<boost::posix_time::ptime> // for further details. struct ptime { // julian day long date; time_duration time_of_day; }; }; }; I was not able to find a way to get to the native 64 or 96 bits that define a ptime value. Consequently, we inefficiently send a ptime as a julian day and a time duration within the day. Adequate for an example, but definitely more data than we would like to send. Note that CORBA IDL knows about certain types already, e.g. short and long, and that we can use our time_duration structure in later structures. We will come back to this IDL file during the build process. ### The transport plugin The actual plugin is defined in corba/BoostCorbaToolkit.hpp. This is the equivalent of the BoostToolkit.hpp file, except for a transport plugin. namespace Examples { namespace Corba { class CorbaBoostPlugin : public RTT::TransportPlugin { public: /// register this transport into the RTT type system bool registerTransport(std::string name, RTT::TypeInfo* ti); /// return the name of this transport type (ie "CORBA") std::string getTransportName() const; /// return the name of this transport std::string getName() const; }; // the global instance extern CorbaBoostPlugin corbaBoostPlugin; // namespace } } The transport plugin provides its name, the name of its transport mechanism, and a function to register the transport into Orocos. Note that no types are mentioned here as that is taken care of by the toolkit plugin. A transport plugin without a corresponding toolkit plugin is useless. Orocos will not know about the types and hence will not even make it to looking up transports for a given type. The implementation of the plugin is in corba/BoostCorbaToolkit.cpp, and is very straight forward. namespace Examples { namespace Corba { bool CorbaBoostPlugin::registerTransport(std::string name, TypeInfo* ti) { assert( name == ti->getTypeName() ); // name must match that in plugin::loadTypes() and // typeInfo::composeTypeInfo(), etc if ( name == "boost_ptime" ) return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new CorbaTemplateProtocol< boost::posix_time::ptime >() ); if ( name == "boost_timeduration" ) return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new CorbaTemplateProtocol< boost::posix_time::time_duration >() ); return false; } Registering a transport registers each type for a given transport protocol (the ORO_CORBA_PROTOCOL_ID above, defined in rtt/src/corba/CorbaLib.hpp). Each type of transport must have a unique protocol ID, though currently Orocos only supports one, CORBA. Registration occurs automatically when the transport is loaded. std::string CorbaBoostPlugin::getTransportName() const { return "CORBA"; } std::string CorbaBoostPlugin::getName() const { return "CorbaBoost"; } The plugin's name is CorbaBoost, and must be unique within all plugins (transport and toolkit, I believe). We choose to prefix the name of our toolkit plugin with Corba, to keep them recognizable. For a CORBA transport plugin, the name returned by getTransportName() should be CORBA. CorbaBoostPlugin corbaBoostPlugin; // namespace } } ORO_TOOLKIT_PLUGIN(Examples::Corba::corbaBoostPlugin); Finally, the plugin itself is instantiated, and the appropriate macro is used so that Orocos can identify this as a plugin when loading it from a dynamic library.   ### Converting types I will only cover the code for converting one of the types. The other is very similar - you can examine it yourself in the source file. #include "BoostTypesC.h" #include <rtt/corba/CorbaConversion.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> // no I/O Here we pick up some standard RTT types, and the I/O operators for our custom boost types. We also pick up BoostTypesC.h. This is a file that CORBA generates from our BoostTypes.idl file above, and contains CORBA-specific code. Ignore its contents, but just realise that it is generated from the .idl file. // must be in RTT namespace to match some rtt/corba code namespace RTT { For some historical reason, I believe this has to be in the RTT namespace. Not sure if that is still true, but ... maybe it is to match the generated output from the .idl file? template<> struct AnyConversion< boost::posix_time::time_duration > { // define the Corba and standard (ie non-Corba) types we are using typedef Corba::time_duration CorbaType; typedef boost::posix_time::time_duration StdType; Here we define some shorthand types, to make typing easier. I also find that having these two types names this way, Corba vs Std, makes it easier to read some of the later code. The actual Corba::timer_duration type comes from the files generated from our .idl file. The last four of the following six functions are required by the CORBA library, to enable conversion between the CORBA and non-CORBA types. The two convert functions are their for convenience, and to save replicating code.  // convert CorbaType to StdTypes static void convert(const CorbaType& orig, StdType& ret) { ret = boost::posix_time::time_duration(orig.hours, orig.minutes, orig.seconds, orig.nanoseconds); } // convert StdType to CorbaTypes static void convert(const StdType& orig, CorbaType& ret) { ret.hours = orig.hours(); ret.minutes = orig.minutes(); ret.seconds = orig.seconds(); ret.nanoseconds = orig.fractional_seconds(); } The above two functions do the actual work of converting data to/from the CORBA and standard types. In this case we can basically copy individual data members - more complicated types may require further conversions, manipulation, etc.  static CorbaType* toAny(const StdType& orig) { CorbaType* ret = new CorbaType(); convert(orig, *ret); return ret; } static StdType get(const CorbaType* orig) { StdType ret; convert(*orig, ret); return ret; } static bool update(const CORBA::Any& any, StdType& ret) { CorbaType* orig; if ( any >>= orig ) { convert(*orig, ret); return true; } return false; } static CORBA::Any_ptr createAny( const StdType& t ) { CORBA::Any_ptr ret = new CORBA::Any(); *ret <<= toAny( t ); return ret; } }; The above four functions are, as previously mentioned, the standard interface to convert types to/from CORBA types. While the syntax might appear a little strange to you (e.g "<<=" operator), you can just copy the above to your own custom types (I copy these between transport plugins, an advantage of creating CORBA and standard types at top). Note well the one dynamic allocation in the toAny() function: transport plugins are most definitely not real-time capable. The same six functions then follow for our boost::ptime type. They are not covered in detail here. ## Build system IF (ENABLE_CORBA) INCLUDE(${CMAKE_SOURCE_DIR}/config/UseCorba.cmake)
This include ensures we know about the CORBA library, and also picks up some CMake macros we need.

  FILE( GLOB IDLS [^.]*.idl )
FILE( GLOB CPPS [^.]*.cpp )
ORO_ADD_CORBA_SERVERS(CPPS HPPS ${IDLS} ) The ORO_ADD_CORBA_SERVERS CMake macro we go from UseCorba.cmake, takes a list of source files (CPPS), a list of header files (HPPS - we have none here) and a list of interface description files (IDLS), and creates the necessary CMake code to generate the CORBA files from the IDL files. Basically, this takes our BoostTypes.idl file and produces header and source files to deal with that CORBA type. Note that this macro appends to the existing files listed in CPPS and HPPS - we'll need them shortly.  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/. )
We now have our own source files in the source directory, as well as source files generated into the build directory. This ensures we can pick up the source files from the build directory as well.

  CREATE_COMPONENT(BoostToolkit-corba-${OROCOS_TARGET} VERSION 1.0.0${CPPS})
TARGET_LINK_LIBRARIES(BoostToolkit-corba-${OROCOS_TARGET}${OROCOS-RTT_CORBA_LIBRARIES}
${CORBA_LIBRARIES}) Here we create a componet shared library, that contains only the transport plugins. Note that the library contains all the source files in the CPPS CMake variable, which now contains all the .cpp files in this directory (due to the "FILE(GLOB ...) statement) as well as the source files generated from the ORO_ADD_CORBA_SERVERS macro. These make up our transport toolkit. Fundamentally, the transport toolkit shared library is no different than a shared library of standard components, except for a tiny bit of C++ code that comes out of the ORO_TOOLKIT_PLUGIN() macro at the end of the BoostCorbaToolkit.cpp'' file. RTT then recognizes this shared library as containing a transport plugin.  SUBDIRS(tests) ENDIF (ENABLE_CORBA) And lastly, pick up the tests. ## Tests The corba test programs contain one component each, to distribute the two components and hence require the CORBA transport plugin. The exact same send and receive test components are used from Part 2. The corba-send test program instantiates a send component, and uses an RTT ControlTaskProxy to represent the remote receive component. #include <rtt/corba/ControlTaskServer.hpp> #include <rtt/corba/ControlTaskProxy.hpp> #include <rtt/RTT.hpp> #include <rtt/PeriodicActivity.hpp> #include <rtt/TaskContext.hpp> #include <rtt/os/main.h> #include <rtt/Ports.hpp> #include <ocl/TaskBrowser.hpp> #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" #include "../../tests/send.hpp" using namespace std; using namespace Orocos; using namespace RTT::Corba; int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin ); Import both the toolkit and transport plugins.  Send send("Send"); PeriodicActivity send_activity( ORO_SCHED_OTHER, 0, 1.0 / 10, send.engine()); // 10 Hz // start Corba and find the remote task ControlTaskProxy::InitOrb(argc, argv); ControlTaskServer::ThreadOrb(); Initialize the CORBA Orb, and then thread it (yes, this does use Proxy and Server functions - this is ok). This puts the CORBA Orb in a background thread, allowing us to run the taskbrowser (below) in the main thread.  TaskContext* recv = ControlTaskProxy::Create( "Recv" ); assert(NULL != recv); Creates a proxy task context for a remote component named "Recv". This will use the name service (by default) to find this component.  if ( connectPeers( recv, &send ) == false ) { log(Error) << "Could not connect peers !"<<endlog(); } // create data object at recv's side if ( connectPorts( recv, &send) == false ) { log(Error) << "Could not connect ports !"<<endlog(); } Connect the local send component to the proxy recv component.  send.configure(); send_activity.start(); log(Info) << "Starting task browser" << endlog(); OCL::TaskBrowser tb( recv ); tb.loop(); send_activity.stop(); Start a task browser on the proxy component. We are, after all, interested in the reception of the data. You could have instead run the taskbrowser on the send component.  ControlTaskProxy::DestroyOrb(); return 0; } Cleanly shutdown the orb and exit. The receive test program has a similar structure to the send test program. #include <rtt/corba/ControlTaskServer.hpp> #include <rtt/corba/ControlTaskProxy.hpp> #include <rtt/RTT.hpp> #include <rtt/PeriodicActivity.hpp> #include <rtt/TaskContext.hpp> #include <rtt/os/main.h> #include <rtt/Ports.hpp> #include "../BoostCorbaToolkit.hpp" #include "../../BoostToolkit.hpp" #include "../../tests/recv.hpp" #include <ocl/TaskBrowser.hpp> using namespace std; using namespace Orocos; using namespace RTT::Corba; int ORO_main(int argc, char* argv[]) { RTT::Toolkit::Import( Examples::BoostToolkit ); RTT::Toolkit::Import( Examples::Corba::corbaBoostPlugin ); Recv recv("Recv"); PeriodicActivity recv_activity( ORO_SCHED_OTHER, 0, 1.0 / 5, recv.engine()); // 5 Hz // Setup Corba and Export: ControlTaskServer::InitOrb(argc, argv); ControlTaskServer::Create( &recv ); ControlTaskServer::ThreadOrb(); We make the receive component a CORBA server, meaning that the send component will connect to this component. It could have been done the other way around - in this example, it simply impacts which test program has to be started first (the server must be running for the client to connect to it). Again we thread the ORB to put it in its own background thread.  // Wait for requests: recv.configure(); recv_activity.start(); OCL::TaskBrowser tb( &recv ); tb.loop(); recv_activity.stop(); Run the taskbrowser on the recieve component (in the main thread). Note that the send component is not mentioned anywhere. The "server" does not know about any "clients", but the "clients" do need to know about the server.  // Cleanup Corba: ControlTaskServer::ShutdownOrb(); ControlTaskServer::DestroyOrb(); return 0; } Cleanly shutdown and destroy the CORBA Orb and exit. The no-toolkit versions of the test programs are identical, except they simply do not load the transport plugin, making it impossible to transport the boost types over CORBA. ## References [1] http://www.iona.com/support/docs/manuals/orbix/33/html/orbix33cxx_pguide/IDL.html # Experienced Users Now located at http://orocos.org/wiki/rtt/examples-and-tutorials # Name connections, not ports (aka Orocos' best kept secret) ## Rationale Problem: How to reuse a component when you need the ports to have different names? Solution: Name the connection between ports in the deployer. This essentially allows you to rename ports. Unfortunately, this extremely useful feature is not documented anywhere (as of July, 2009). <!-- break --> ## Assumptions • The build directory is within the source directory. Click below to read the rest of this post.== Rationale == Problem: How to reuse a component when you need the ports to have different names? Solution: Name the connection between ports in the deployer. This essentially allows you to rename ports. Unfortunately, this extremely useful feature is not documented anywhere (as of July, 2009). <!-- break --> ## Assumptions • The build directory is within the source directory. This helps with dynamic library loading. • Admittedly, this is contrived example but the structure is very useful and occurs more frequently than you may realise (say using N copies of a camera component, deploying components for both a left and a right robot arm within the same deployer, etc). ## Files HMI.hpp Robot.hpp OneAxisFilter.hpp HMI.cpp Robot.cpp OneAxisFilter.cpp Connect-1.xml Connect-2.xml Connect-3.xml Buildable tarball ## Example overview This example occurs in three parts 1. A Human-Machine-Interface (HMI) component connects to a Robot component, and provides a desired cartesian position. 2. A one-axis filter is placed between the HMI and Robot component, to zero out one axis (say, you did not want the robot to move in one direction due to an obstacle or something similar) 3. A second one-axis filter is placed between the first filter and the Robot. The two filters are the exact same component with the same named ports. ## Components class HMI : public RTT::TaskContext { protected: // *** OUTPUTS *** /// desired cartesian position RTT::WriteDataPort<KDL::Frame> cartesianPosition_desi_port; public: HMI(std::string name); virtual ~HMI(); protected: /// set the desired cartesian position to an initial value /// \return true virtual bool startHook(); }; The HMI provides one output port that specified the desired cartesian position. This is set to an initial value in startHook(). class Robot : public RTT::TaskContext { protected: // *** INPUTS *** /// desired cartesian position RTT::ReadDataPort<KDL::Frame> cartesianPosition_desi_port; public: Robot(std::string name); virtual ~Robot(); }; The robot accepts a desired cartesian position as input (but in this example does nothing with it). class OneAxisFilter : public RTT::TaskContext { protected: // *** INPUTS *** /// desired cartesian position RTT::ReadDataPort<KDL::Frame> inputPosition_port; // *** OUTPUTS *** /// desired cartesian position RTT::WriteDataPort<KDL::Frame> outputPosition_port; // *** CONFIGURATION *** /// specify which axis to filter (should be one of "x", "y", or "z") RTT::Property<std::string> axis_prop; public: OneAxisFilter(std::string name); virtual ~OneAxisFilter(); protected: /// validate axis_prop value /// \return true if axis_prop value is valid, otherwise false virtual bool configureHook(); /// filter one translational axis (as specified by axis_prop) virtual void updateHook(); }; The OneAxisFilter component takes an input cartesian position, zeroes out one axis (the axis of interest is specified in a property), and then outputs the filtered cartesian position. ## Component implementation The component implementations are not given in this example, as they are not the interesting part of the solution, but are available in the Files section above. The interesting part is in the deployment files ... ## Deployment ### Part 1: HMI and Robot This part simply connects the HMI and robot together (see deployment file Connect-1.xml). <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "cpf.dtd"> <properties> <simple name="Import" type="string"> <value>liborocos-rtt</value> </simple> <simple name="Import" type="string"> <value>liborocos-kdl</value> </simple> <simple name="Import" type="string"> <value>liborocos-kdltk</value> </simple> <simple name="Import" type="string"> <value>libConnectionNaming</value> </simple> The first section of the deployment file simply loads the Orocos libaries we use (including the KDL toolkit, so that we can inspect and modify KDL types within the deployer), and then loads our shared libary (libConnectionNaming).  <struct name="HMI" type="HMI"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>cartesianPosition_desi</value></simple> </struct> </struct> The next section creates an HMI component, with the connection for its output port named "cartesianPosition_desi" (ie the same as the port name). The syntax for port/connection naming is:  <simple name="portName" type="string"> <value>connectionName</value> </simple> which makes port portName part of connection connectionName.  <struct name="Robot" type="Robot"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>HMI</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>cartesianPosition_desi</value></simple> </struct> </struct> </properties> Lastly, the robot component is created with its input port on a connection named cartesianPosition_desi. Now, the deployer uses connection names when connecting components between peers, not port names. So it attempts to connect a Robot.cartesianPosition_desi connection to a Vehicle. cartesianPosition_desi connection (which in this part, matches the port names). Build the library, and then run this part with cd /path/to/ConnectionNaming/build deployer-macosx -s ../Connect-1.xml Examine the HMI and Robot components, and note that each has a connected port, and the port values match. ### Part 2: HMI, one filter and a robot This part adds a filter component between the HMI and the robot (see Connect-2.xml) As with Part 1, the first part of the file loads the appropriate libraries (left out here, as it is identical to Part 1).  <struct name="HMI" type="HMI"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>unfiltered_cartesianPosition_desi</value></simple> </struct> </struct> Again an HMI component is deployed, except this time the deployer will connect the cartesianPosition_desi port as part of a connection named unfiltered_cartesianPosition_desi.  <struct name="Filter" type="OneAxisFilter"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>HMI</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="inputPosition" type="string"> <value>unfiltered_cartesianPosition_desi</value></simple> <simple name="outputPosition" type="string"> <value>filtered_cartesianPosition_desi</value></simple> </struct> <simple name="PropertyFile" type="string"> <value>../Filter1.cpf</value></simple> </struct> The Filter component is deployed with its input port being part of a connection named unfiltered_cartesianPosition_desi, while its output port is part of a connection named filtered_cartesianPosition_desi. Comparison with the HMI port/connections above, and the Robot port/connections below, you can see that the Filter's input port is connected to the HMI and the output port is connected to the Robot.  <struct name="Robot" type="Robot"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>Filter</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>filtered_cartesianPosition_desi</value></simple> </struct> </struct> The robot component is the same as Part 1, except that its input port is part of a connection named filtered_cartesianPosition_desi (ie connected to the Filter). Run this part with cd /path/to/ConnectionNaming/build deployer-macosx -s ../Connect-2.xml Examine all three components, and note that all ports are connected, and in particular, that the HMI and Filter.inputPosition ports match while the Filter.outputPosition and Vehicle ports match (ie they have the 'x' axis filtered out). Using connection naming allows us to connect ports of different names. This is particularly useful with a generic component like this filter, as in one deployment it may connect to a component with ports named cartesianPosition_desi, while in another deployment it may connect to ports named CartDesiPos, or any other names. The filter component is now decoupled from the actual port names used to deploy it. ### Part 3: HMI, two filters and a robot This part adds a second filter between the first filter and the robot. As with Parts 1 and 2, the libraries are loaded first.  <struct name="HMI" type="HMI"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>unfiltered_cartesianPosition_desi</value></simple> </struct> </struct> There is no change in the HMI from Part 2.  <struct name="Filter1" type="OneAxisFilter"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>HMI</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="inputPosition" type="string"> <value>unfiltered_cartesianPosition_desi</value></simple> <simple name="outputPosition" type="string"> <value>filtered_cartesianPosition_desi</value></simple> </struct> <simple name="PropertyFile" type="string"> <value>../Filter1.cpf</value></simple> </struct> There is no change in the first filter from Part 2.  <struct name="Filter2" type="OneAxisFilter"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>HMI</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="inputPosition" type="string"> <value>filtered_cartesianPosition_desi</value></simple> <simple name="outputPosition" type="string"> <value>double_filtered_cartesianPosition_desi</value></simple> </struct> <simple name="PropertyFile" type="string"> <value>../Filter2.cpf</value></simple> </struct> The second filter has its input port part of a connection named filtered_cartesianPosition_desi (ie it is connected to Filter1's output port), and the second filter's output port is part of a connecton named double_filtered_cartesianPosition_desi (which as you will see, is connected to the robot's input port).  <struct name="Robot" type="Robot"> <struct name="Activity" type="PeriodicActivity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>Filter2</value></simple> </struct> <struct name="Ports" type="PropertyBag"> <simple name="cartesianPosition_desi" type="string"> <value>double_filtered_cartesianPosition_desi</value></simple> </struct> </struct> The only change in the robot component, from Part 2, is to change its peer to Filter2 and to use a connection named double_filtered_cartesianPosition_desi (ie connect it to Filter2). Run this part with cd /path/to/ConnectionNaming/build deployer-macosx -s ../Connect-3.xml Examine all components, and note which ports are connected, and what their values are. Note that the vehicle has two axes knocked out (x and y). ## Points to note 1. WARNING The deployer displays port names for ports within components, while the OCL reporting component also uses port names. Only the act of connecting ports between peers when deploying a component network, makes use of the connection naming shown above. 2. Using connection naming allows us to reuse a component without resorting to renaming its ports or modifying its code in any way. This is an example of deployment-time configuration. Note that there are certainly instances where run-time configuration of port-names may be needed (eg the component has to name its ports based on the component name itself), but in our experience, deployment-time configuration is more frequent and decouples components better. 3. Note that as many filters as are required could be chained together in this manner, and that none of the input, output, nor filter components need know that they are connected in such a fashion. Decoupling is your friend, and allowed the Filter component writer to simply concentrate on writing a component that did one thing well: filtered a cartesian position (yes, a trivial example, but a valid point nonetheless). 4. You may notice that the deployment files do not specify peer combinations in pairs. The peers are mentioned in one direction only. We use this to decouple (yet again) a component from knowing what peers it is connected to, where possible. For example, Filter1 in both Parts 2 and 3 does not now what component is down-stream from it. It doesn't know, nor does it care, whether it is being filtered again, connected to a robot, or whatever. Again, decoupling. This can dramatically help when deploying large systems. ## To build In a shell cd /path/to/ConnectionNaming mkdir build cd build cmake .. -DOROCOS_TARGET=macosx make For other operating systems substitute the appopriate value for "macosx" when setting OROCOS_TARGET (e.g. "gnulinux"). Tested in Mac OS X Leopard 10.5.7. AttachmentSize HMI.hpp2.16 KB Robot.hpp1.99 KB OneAxisFilter.hpp2.52 KB HMI.cpp2.04 KB Robot.cpp1.94 KB OneAxisFilter.cpp2.92 KB Connect-1.xml1.96 KB Connect-2.xml2.91 KB Connect-3.xml3.85 KB connectionNaming.tar_.bz27.01 KB # Simple Examples Now located at http://orocos.org/wiki/rtt/examples-and-tutorials # Simple TCP client using non-periodic component Table of Contents ## Rationale Problem: You want a component that connects to a remote TCP server, and reads data from it (this example could easily write, instead of reading). This component will block for varying amounts of time when reading. Solution: Use a non-periodic component. This example outlines one method to structure the component, to deal with the non-blocking reads while still being responsive to other components, being able to run a state machine, etc. <!-- break --> ## Assumptions • Uses Qt sockets to avoid operating-system intracacies and differences when using actual sockets. The code can easily be modified to use bind(), accept(), listen(), etc. instead. It is the structure of the solution that we are interested in. • The build directory is within the source directory. This helps with dynamic library loading. • Does not attempt reconnection if unable connect on first attempt. • Non-robust error handling. • Does not validate property values (a robust component would validate that the timeouts were valid, eg. not negative, within a configureHook()). ## Files SimpleNonPeriodicClient.cpp SimpleNonPeriodicClient.hpp SimpleNonPeriodicClient.xml SimpleNonPeriodicClient.cpf Buildable tarball The .cpf file has a .txt extension simply to keep the wiki happy. To use the file, rename it to SimpleNonPeriodicClient.cpf. ## Component definition This is the class definition class SimpleNonPeriodicClient : public RTT::TaskContext { protected: // DATA INTERFACE // *** OUTPUTS *** /// the last read data RTT::WriteDataPort<std::string> lastRead_port; /// the number of items sucessfully read RTT::Attribute<int> countRead_attr; // *** CONFIGURATION *** // name to listen for incoming connections on, either FQDN or IPv4 addres RTT::Property<std::string> hostName_prop; // port to listen on RTT::Property<int> hostPort_prop; // timeout in seconds, when waiting for connection RTT::Property<int> connectionTimeout_prop; // timeout in seconds, when waiting to read RTT::Property<int> readTimeout_prop; public: SimpleNonPeriodicClient(std::string name); virtual ~SimpleNonPeriodicClient(); protected: /// reset count and lastRead, attempt to connect to remote virtual bool startHook(); /// attempt to read and process one packet virtual void updateHook(); /// close the socket and cleanup virtual void stopHook(); /// cause updateHook() to return virtual bool breakUpdateHook(); /// Socket used to connect to remote host QTcpSocket* socket; /// Flag indicating to updateHook() that we want to quit bool quit; }; The component has a series of properties specifying the remote host and port to connect to, as well as timeout parameters. It also uses an RTT Attribute to count the number of successful reads that have occurred, and stores the last read data as a string in a RTT data port. ## Component implementation #include "SimpleNonPeriodicClient.hpp" #include <rtt/Logger.hpp> #include <ocl/ComponentLoader.hpp> #include <QTcpSocket> The class definition is included as well as the RTT logger, and importantly, the OCL component loader that turns this class into a deployable componet in a shared library. Most importantly, all Qt related headers come after all Orocos headers. This is required as Qt redefines certain words (eg "slot", "emit") which when used in our or Orocos code cause compilation errors. SimpleNonPeriodicClient::SimpleNonPeriodicClient(std::string name) : RTT::TaskContext(name), lastRead_port("lastRead", ""), countRead_attr("countRead", 0), hostName_prop("HostName", "Name to listen for incoming connections on (FQDN or IPv4)", ""), hostPort_prop("HostPort", "Port to listen on (1024-65535 inclusive)", 0), connectionTimeout_prop("ConnectionTimeout", "Timeout in seconds, when waiting for connection", 0), readTimeout_prop("ReadTimeout", "Timeout in seconds, when waiting for read", 0), socket(new QTcpSocket), quit(false) { ports()->addPort(&lastRead_port); attributes()->addAttribute(&countRead_attr); properties()->addProperty(&hostName_prop); properties()->addProperty(&hostPort_prop); properties()->addProperty(&connectionTimeout_prop); properties()->addProperty(&readTimeout_prop); } The constuctor simply sets up the data interface elements (ie the port, attribute and properties), and gives them appropriate initial values. Note that some of these initial values are illegal, which would aid in any validation code in a configureHook() (which has not been done in this example). SimpleNonPeriodicClient::~SimpleNonPeriodicClient() { delete socket; } The destructor cleans up by deleting the socket we allocated in the constructor. Now to the meat of it bool SimpleNonPeriodicClient::startHook() { bool rc = false; // prove otherwise std::string hostName = hostName_prop.rvalue(); int hostPort = hostPort_prop.rvalue(); int connectionTimeout = connectionTimeout_prop.rvalue(); quit = false; // attempt to connect to remote host/port log(Info) << "Connecting to " << hostName << ":" << hostPort << endlog(); socket->connectToHost(hostName.c_str(), hostPort); if (socket->waitForConnected(1000 * connectionTimeout)) // to millseconds { log(Info) << "Connected" << endlog(); rc = true; } else { log(Error) << "Error connecting: " << socket->error() << ", " << socket->errorString().toStdString() << endlog(); // as we now return false, this component will fail to start. } return rc; } The startHook() uses the properites loaded from the SimpleNonPeriodicClient.cpf file, to attempt to connect to the remote host. If the remote port is not ready, the attempted connection will timeout. If the connection does not occur successfully, then startHook() will return false which prevents the component from actually being started. No reconnection is attempted (see Assumptions above) void SimpleNonPeriodicClient::updateHook() { // wait for some data to arrive, timing out if necessary int readTimeout = readTimeout_prop.rvalue(); log(Debug) << "Waiting for data with timeout=" << readTimeout << " seconds" << endlog(); if (!socket->waitForReadyRead(1000 * readTimeout)) { log(Error) << "Error waiting for data: " << socket->error() << ", " << socket->errorString().toStdString() << ". Num bytes = " << socket->bytesAvailable() << endlog(); log(Error) << "Disconnecting" << endlog(); // disconnect socket, and do NOT call this function again // ie no engine()->getActivity()->trigger() socket->disconnectFromHost(); return; } // read and print whatever data is available, but stop if instructed // to quit while (!quit && (0 < socket->bytesAvailable())) { #define BUFSIZE 10 char str[BUFSIZE + 1]; // +1 for terminator qint64 numRead; numRead = socket->read((char*)&str[0], min(BUFSIZE, socket->bytesAvailable())); str[BUFSIZE] = '\0'; // forcibly terminate if (0 < numRead) { log(Info) << "Got " << numRead << " bytes : '" << &str[0] << "'" << endlog(); countRead_attr.set(countRead_attr.get() + 1); lastRead_port.Set(&str[0]); } } // if not quitting then trigger another immediate call to this function, to // get the next batch of data if (!quit) { engine()->getActivity()->trigger(); } } The updateHook() function attempts to wait until data is available, and then reads the data BUFSIZE characters at a time. If it times out waiting for data, then it errors out and disconnects the port. This is not a robust approach and a real algorithm would deal with this differently. As data may be continually arriving and/or we get more than BUFSIZE characters at a time, the while loop may iterate several times. The quit flag will indicate if the user wants to stop the component, and that we should stop reading characters. Of particular note is the last line engine()->getActivity()->trigger(); This causes updateHook() to be called again immediately by the execution engine. Essentially, this makes the non-periodic component act as a periodic component with a varying period. Of course, this is not called if the component is being stopped (ie quit==true). void SimpleNonPeriodicClient::stopHook() { if (socket->isValid() && (QAbstractSocket::ConnectedState == socket->state())) { log(Info) << "Disconnecting" << endlog(); socket->disconnectFromHost(); } } The stopHook() simply disconnects the socket if it is currently connected. bool SimpleNonPeriodicClient::breakUpdateHook() { quit = true; return true; } The breakUpdateHook() is very important, as it is the only way to inform a blocked updateHook() that it is time to return and quit. In this example we set the quit flag and return true. The quit flag will be picked up by updateHook() when it finishes waiting for data (in socket->waitForReadyRead()). Returning true from breakUpdateHook() tells the execution engine that we successfully told updateHook() to return and that it should wait (one second, hardcoded) for updateHook() to complete and return. If we returned false, then stop would also return false. We could have also done something like socket->abort() to forcibly terminate any blocked socket->waitForReadyRead() calls. When using system calls (e.g. read() ) instead of Qt classes you could attempt to send a signal to interrupt the system call, however, this might not have the desired effect when the component is deployed ... the reader is advised to be careful here. ORO_CREATE_COMPONENT(SimpleNonPeriodicClient) This line of code creates a deployable component for the SimpleNonPeriodicClient) class, that the deployer can load from a shared library. ## To build In a shell cd /path/to/SimpleNonPeriodicClient mkdir build cd build cmake .. -DOROCOS_TARGET=macosx make For other operating systems substitute the appopriate value for "macosx" when setting OROCOS_TARGET (e.g. "gnulinux"). Tested in Mac OS X Leopard 10.5.7, and Ubuntu Jaunty Linux. ## To run Start one shell and run netcat to act as the server (NB 50001 is the HostPort value from your SimpleNonPeriodicClient.cpf file) nc -l 50001 Start a second shell and deploy the SimpleNonPeriodicClient component cd /path/to/SimpleNonPeriodicClient/build deployer-macosx -s ../SimpleNonPeriodicClient.xml Now type in the first shell and when you hit enter, then netcat will send the data and it will be printed by the SimpleNonPeriodicClient component (where N is the size of the buffer in updateHook()). Points to note: 1. The SimpleNonPeriodicClient component will time out if you do not hit enter within ReadTimeout seconds (as specified in the SimpleNonPeriodicClient.cpf file). 2. Setting the ORO_LOGLEVEL environment variable to 5 or 6, or running the deployer with -linfo or -ldebugoptions, will generate additional debugging statements. 3. The component will take up to ReadTimeout seconds to respond to the user typing quit in the deployer, as breakUpdateHook() does not forcibly exit the socket->waitForReadyRead() call. AttachmentSize SimpleNonPeriodicClient.cpp7.42 KB SimpleNonPeriodicClient.hpp3.11 KB SimpleNonPeriodicClient.xml1 KB SimpleNonPeriodicClient-cpf.txt748 bytes SimpleNonPeriodicClient.tar_.bz27.72 KB # Sample output ## Sample output The netcat shell, with the text the user typed in. nc -l 50001 The quick brown fox jumps over the lazy dog.  The deployer shell, showing the text read in chunks, as well as the updated port and attribute within the component. deployer-macosx -s ../SimpleNonPeriodicClient.xml -linfo 0.009 [ Info ][deployer-macosx::main()] No plugins present in /usr/lib/rtt/macosx/plugins 0.009 [ Info ][DeploymentComponent::loadComponents] Loading '../SimpleNonPeriodicClient.xml'. 0.010 [ Info ][DeploymentComponent::loadComponents] Validating new configuration... 0.011 [ Info ][DeploymentComponent::loadLibrary] Storing orocos-rtt 0.011 [ Info ][DeploymentComponent::loadLibrary] Loaded shared library 'liborocos-rtt-macosx.dylib' 0.054 [ Info ][DeploymentComponent::loadLibrary] Loaded multi component library 'libSimpleNonPeriodicClient.dylib' 0.054 [ Warning][DeploymentComponent::loadLibrary] Component type name SimpleNonPeriodicClient already used: overriding. 0.054 [ Info ][DeploymentComponent::loadLibrary] Loaded component type 'SimpleNonPeriodicClient' 0.055 [ Info ][DeploymentComponent::loadLibrary] Storing SimpleNonPeriodicClient 0.058 [ Info ][DeploymentComponent::loadComponent] Adding SimpleNonPeriodicClient as new peer: OK. 0.058 [ Warning][SingleThread] Forcing priority (0) of thread to 0. 0.058 [ Info ][NonPeriodicActivity] SingleThread created with priority 0 and period 0. 0.058 [ Info ][NonPeriodicActivity] Scheduler type was set to 4'. 0.059 [ Info ][PropertyLoader:configure] Configuring TaskContext 'SimpleNonPeriodicClient' with '../SimpleNonPeriodicClient.cpf'. 0.059 [ Info ][DeploymentComponent::configureComponents] Configured Properties of SimpleNonPeriodicClient from ../SimpleNonPeriodicClient.cpf 0.059 [ Info ][DeploymentComponent::configureComponents] Re-setting activity of SimpleNonPeriodicClient 0.059 [ Info ][DeploymentComponent::configureComponents] Configuration successful. 0.060 [ Info ][DeploymentComponent::startComponents] Connecting to 127.0.0.1:50001 0.064 [ Info ][DeploymentComponent::startComponents] Connected 0.065 [ Info ][DeploymentComponent::startComponents] Startup successful. 0.065 [ Info ][deployer-macosx::main()] Successfully loaded, configured and started components from ../SimpleNonPeriodicClient.xml Switched to : Deployer 0.066 [ Info ][SimpleNonPeriodicClient] Entering Task Deployer This console reader allows you to browse and manipulate TaskContexts. You can type in a command, event, method, expression or change variables. (type 'help' for instructions) TAB completion and HISTORY is available ('bash' like) In Task Deployer[S]. (Status of last Command : none ) (type 'ls' for context info) :4.816 [ Info ][SimpleNonPeriodicClient] Got 10 bytes : 'The quick ' 4.816 [ Info ][SimpleNonPeriodicClient] Got 10 bytes : 'brown fox ' 7.448 [ Info ][SimpleNonPeriodicClient] Got 10 bytes : 'jumps over' 7.448 [ Info ][SimpleNonPeriodicClient] Got 10 bytes : ' the lazy ' 12.448 [ ERROR ][SimpleNonPeriodicClient] Error waiting for data: 5, Network operation timed out. Num bytes = 5 12.448 [ ERROR ][SimpleNonPeriodicClient] Disconnecting In Task Deployer[S]. (Status of last Command : none ) (type 'ls' for context info) :ls SimpleNonPeriodicClient Listing TaskContext SimpleNonPeriodicClient : Configuration Properties: string HostName = 127.0.0.1 (Name to listen for incoming connections on (FQDN or IPv4)) int HostPort = 50001 (Port to listen on (1024-65535 inclusive)) int ConnectionTimeout = 5 (Timeout in seconds, when waiting for connection) int ReadTimeout = 5 (Timeout in seconds, when waiting for read) Execution Interface: Attributes : int countRead = 4 Methods : activate cleanup configure error getErrorCount getPeriod getWarningCount inFatalError inRunTimeError inRunTimeWarning isActive isConfigured isRunning resetError start stop trigger update warning Commands : (none) Events : (none) Data Flow Ports: W(U) string lastRead = the lazy Task Objects: this ( The interface of this TaskContext. ) scripting ( Access to the Scripting interface. Use this object in order to load or query programs or state machines. ) engine ( Access to the Execution Engine. Use this object in order to address programs or state machines which may or may not be loaded. ) marshalling ( Read and write Properties to a file. ) lastRead ( (No description set for this Port) ) Peers : (none) In Task Deployer[S]. (Status of last Command : none ) (type 'ls' for context info) :quit 18.089 [ Info ][DeploymentComponent::stopComponents] Stopped SimpleNonPeriodicClient 18.089 [ Info ][DeploymentComponent::cleanupComponents] Cleaned up SimpleNonPeriodicClient 18.090 [ Info ][DeploymentComponent::startComponents] Disconnected and destroyed SimpleNonPeriodicClient 18.090 [ Info ][DeploymentComponent::startComponents] Kick-out successful. 18.091 [ Info ][Logger] Orocos Logging Deactivated. # Using XML substitution to manage complex deployments Table of Contents ## Rationale Problem: You deploy multiple configurations of your system, perhaps choosing between a real and simulated robot, some real and simulated device, etc. You want to parameterize the deployments to reduce the number of files you have to write for the varying configuration combinations Solution: Use the XML ENTITY element. ## Assumptions • Works with Xerces only (v2 tested, v3 should also support this). Will not work with the default TinyXML processor. ## Compatabilitry Tested on v1.x trunk on Mac OS X Snow Leopard. These instructions should apply identically to RTT 2.x installations. ## Files See the attachments at the bottom of this page. ## Approach This simple example demonstrates how to deploy a tiny system in two configurations, by simply changing the name of the deployed component. This approach can be (and has been) used to manage deployments with many system configurations. There is a top-level file per configuration, which specifies all the parameters. Each top-level file then includes a child file which instantiates components, etc. One top level file <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "cpf.dtd" [ <!-- internal entities for substituion --> <!ENTITY name "Console"> <!ENTITY lib "liborocos-rtt"> <!-- external entity for file substitution --> <!ENTITY FILE_NAME SYSTEM "test-entity-child.xml"> ] > <properties> &FILE_NAME; </properties> The internal entity values are used to substitute component names, and other basic parameters. The external entity value (&FILE_NAME) is used to include child files, so that the entity values defined in the top-level file are available within the child file. Using the Orocos' builtin include statement does not make the top-level entity values available within the child file. The child file simply substitutes the two internal entities for a library name, and a component name. <properties> <simple name="Import" type="string"> <value>&lib;</value> </simple> <simple name="Import" type="string"> <value>liborocos-ocl-common</value> </simple> <struct name="&name;" type="OCL::HMIConsoleOutput"> </struct> </properties> The other top level file differs from the first top level file only in the name of the component. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "cpf.dtd" [ <!ENTITY name "Console2"> <!ENTITY lib "liborocos-rtt"> <!ENTITY file SYSTEM "test-entity-child.xml"> ] > <properties> &file; </properties> You can use relative paths within the external entity filename. I have had inconsistent success with this - sometimes the relative path is needed, and other times it isn't. I think that it only needs the path relative to the file being included from, so if that file is already loaded on a relative path then you need to specify the child file only relative to the parent file, and not the current working directory that you started the deployment in. AttachmentSize test-entity.xml1.56 KB test-entity2.xml278 bytes test-entity-child.xml307 bytes # Using real-time logging This page collects notes and issues on the use of real-time logging. Its contents will eventually become the documentation for this feature. This feature has been integrated in the Orocos 1.x and 2.x branches but is still considered under development. However, if you need a real-time logging infrastructure (ie text messages to users), this is exactly where you need to be. If you need a real-time data stream logging of ports, use the OCL's Reporting NetCDFReporting Component instead. It is noted in the text where Orocos 1.x and 2.x differ. ## Restrictions and issues ### Restrictions Startup the logging components first: Logging events prior to logging service's configure() will be dropped. The problem is that the logging service connects categories and appenders, and is it itself a component. So until it is configured, and the connections are all made, no appenders are available to deal with the event. Therefore you are suggested to put your appender components and the logging service in a separate deployment XML or script file which is loaded first. This will allow your application components to use logging from the start (component creation). See the ocl/logging/tests/data availablility XML deployment files for examples. OCL's deployer can execute in order multiple XML or script files. Categories can not be created in real-time: They live on the normal heap via new/delete. Create all categories in your component's constructor or during configureHook() or similar. NDC's are not supported. They involve std::string and std::vector which we currently can't replace. Works only with OCL's deployers: If you use a non-deployer mechanism to bring up your system, you will need to add code to ensure that the log4cpp framework creates our OCL::Category objects, and not the default (non-real-time) log4cpp::Category objects. This should be done early in your application, prior to any components and categories being created.  log4cpp::HierarchyMaintainer::set_category_factory( OCL::logging::Category::createOCLCategory); ### Issues On the ML it was requested to log when events have been lost. There are two places that this would need to be implemented, both annotated with TODO's in the code. • When creation of the OCL::String objects in a LoggingEvent exhausts the memory pool • When the buffer between a category and its appenders is full This is not currently dealt with, but could be in future implementations. In RTT/OCL 1.x, multiple appenders connected to the same category will, receive only some of the incoming logging events. This is as each appender will pop different elements from the category's buffer. This issue has been solved in 2.x. The size of the buffer between a category and its appenders is currently fixed (see ocl/logging/Category.cpp). This will be fixed lateron on the 2.x branch. Note that that fixed size plus the default consumption rate of the FileAppender means you can exhaust the default TLSF memory pool in very short order. For a complex application (~40 components, 400 Hz cycle rate) we increased the default buffer size to 200, increased the memory pool to 10's of kilobytes (or megabytes) and increased the FileAppender consumption rate to 500 messages per second. ## Viewing logs We can use standard log viewers for Log4j in two ways: 1. Use FileAppender which writes log lines to a file and let the viewers read that file 2. Use Log4cxxAppender which creates a network socket to which Log4cxx/Log4j viewers can connect. These log viewers are compatible: ## Complete application example As at October 2010, assumes you are using for RTT 1.x: And for RTT 2.x, use the Orocos Toolchain 2.2 or later from : then build in the following order, with these options ON: • log4cpp (default options) • RTT: ENABLE_RT_MALLOC, OS_RT_MALLOC • OCL: BUILD_RTALLOC, BUILD_LOGGING The deployer now defaults to a 20k real-time memory pool (see OCL CMake option ORO_DEFAULT_RTALLOC_SIZE), all Orocos RTT::Logger calls end up inside of log4cpp, and the default for RTT::Logger logging events is to log to a file "orocos.log". Same as always. But now you can configure all logging in one place! IMPORTANT Be aware that there are two logging hierarchies at work here: 1. a non-real-time, log4cpp-based logging in use by RTT::Logger (currently only for RTT 1.x) 2. a real-time, OCL::Logging-based (with log4cpp underneath) in use by application code In time, hopefully these two will evolve into just the latter. ### Required Build flags We're assuming here that you used 'orocreate-pkg' to setup a new application. So you're using the UseOrocos CMake macros. 1. Your application's manifest.xml must depend on ocl. 2. Your application's CMakeLists.txt must include the line : orocos_use_package(ocl-logging) Both steps will make sure that your libraries link with the Orocos logging libraries and that include files are found. ### Configuring real-time memory pool size The deployer's have command line options for this deployer-macosx --rtalloc-mem-size 10k deployer-corba-macosx --rtalloc-mem-size 30m deployer-corba-macosx --rtalloc 10240 # understands shortened, but unique, options See note at top of file regarding TLSF's bookkeeping overhead. The pool needs to be larger than that value. ### Configuring RTT::Logger logging NOTE: this feature is not available on the official release. Skip to the next section (Configuring OCL::logging) if you're not using the log4cpp branch of the RTT You can use any of log4cpp's configurator approaches to configure, but the deployer's already know about PropertyConfigurator's. You can pass a log4cpp property file to the deployer and that will be used to configure the first of the hierarchies above - the non-real-time, logging used by RTT::Logger. For example deployer-macosx --rtt-log4cpp-config-file /z/l/log4cpp.conf where the file /z/l/log4cpp.conf is something like # root category logs to application (this level is also the default for all # categories who's level is NOT explicitly set in this file) log4j.rootCategory=DEBUG, applicationAppender # orocos setup log4j.category.org.orocos.rtt=INFO, orocosAppender log4j.additivity.org.orocos.rtt=false # do not also log to parent categories log4j.appender.orocosAppender=org.apache.log4j.FileAppender log4j.appender.orocosAppender.fileName=orocos-log4cpp.log log4j.appender.orocosAppender.layout=org.apache.log4j.PatternLayout log4j.appender.orocosAppender.layout.ConversionPattern=%d{%Y%m%dT%T.%l} [%-5p] %m%n This configuration file simply changes the output filename and format. You could also add additional appenders (e.g. to stdout, to socket) and change the logging level for sub-categories, if RTT supported them (e.g. scripting.rtt.orocos.org). IMPORTANT Note the direction of the category name, from org to rtt. This is specific to log4cpp and other log4j-style frameworks. Using a category "rtt.orocos.org" and sub-category "scripting.rtt.orocos.org" won't do what you, nor log4cpp, expect. ### Configuring OCL::logging (XML) This is how you would setup logging from a Deployer XML file. If you prefer to use a script, see the next section. See ocl/logging/tests/xxx.xml for complete examples and more detail, but in short <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "cpf.dtd"> <properties> <simple name="Import" type="string"> <value>liborocos-logging</value> </simple> <simple name="Import" type="string"> <value>libTestComponent</value> </simple> <struct name="TestComponent" type="OCL::logging::test::Component"> <struct name="Activity" type="Activity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> </struct> <struct name="AppenderA" type="OCL::logging::FileAppender"> <struct name="Activity" type="Activity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Properties" type="PropertyBag"> <simple name="Filename" type="string"><value>appendera.log</value></simple> <simple name="LayoutName" type="string"><value>pattern</value></simple> <simple name="LayoutPattern" type="string"><value>%d [%t] %-5p %c %x - %m%n</value></simple> </struct> </struct> <struct name="LoggingService" type="OCL::logging::LoggingService"> <struct name="Activity" type="Activity"> <simple name="Period" type="double"><value>0.5</value></simple> <simple name="Priority" type="short"><value>0</value></simple> <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple> </struct> <simple name="AutoConf" type="boolean"><value>1</value></simple> <simple name="AutoStart" type="boolean"><value>1</value></simple> <struct name="Properties" type="PropertyBag"> <struct name="Levels" type="PropertyBag"> <simple name="org.orocos.ocl.logging.tests.TestComponent" type="string"><value>info</value></simple> </struct> <struct name="Appenders" type="PropertyBag"> <simple name="org.orocos.ocl.logging.tests.TestComponent" type="string"><value>AppenderA</value></simple> </struct> </struct> <struct name="Peers" type="PropertyBag"> <simple type="string"><value>AppenderA</value></simple> </struct> </struct> </properties> which creates one component that logs to a org.orocos.ocl.logging.tests.TestComponent category, and that category is connected to one appender that logs to a file appendera.log. Run this XML file, save it in 'setup_logging.xml' and use it:  deployer-gnulinux -s setuplogging.xml ### Configuring OCL::logging (Lua) This is how you would setup logging from a Lua script file. If you prefer to use a XML, see the previous section. require("rttlib") -- Set this to true to write the property files the first time. write_props=false tc = rtt.getTC() depl = tc:getPeer("deployer") -- Create components. Enable BUILD_LOGGING and BUILD_TESTS for this to -- work. depl:loadComponent("TestComponent","OCL::logging::test::Component") depl:setActivity("TestComponent", 0.5, 0, 0) depl:loadComponent("AppenderA", "OCL::logging::FileAppender") depl:setActivity("AppenderA", 0.5, 0, 0) depl:loadComponent("LoggingService", "OCL::logging::LoggingService") depl:setActivity("LoggingService", 0.5, 0, 0) test = depl:getPeer("TestComponent") aa = depl:getPeer("AppenderA") ls = depl:getPeer("LoggingService") depl:addPeer("AppenderA","LoggingService") -- Load marshalling service to read/write components depl:loadService("LoggingService","marshalling") depl:loadService("AppenderA","marshalling") if write_props then ls:provides("marshalling"):writeProperties("logging_properties.cpf") aa:provides("marshalling"):writeProperties("appender_properties.cpf") print("Wrote property files. Edit them and set write_props=false") os.exit(0) else ls:provides("marshalling"):loadProperties("logging_properties.cpf") aa:provides("marshalling"):loadProperties("appender_properties.cpf") end test:configure() aa:configure() ls:configure() test:start() aa:start() ls:start() To run this script, save it in 'setup_logging.lua' and do: rttlua-gnulinux -i setup_logging.lua ### Using OCL::Logging in C++ The component itself uses logging like the following simplified example // TestComponent.hpp #include <ocl/LoggingService.hpp> #include <ocl/Category.hpp> class Component : public RTT::TaskContext { ... /// Our logging category OCL::logging::Category* logger; }; // TestComponent.cpp #include <rtt/rt_string.hpp> Component::Component(std::string name) : RTT::TaskContext(name), logger(dynamic_cast<OCL::logging::Category*>( &log4cpp::Category::getInstance("org.orocos.ocl.logging.tests.TestComponent"))) { } bool Component::startHook() { bool ok = (0 != logger); if (!ok) { log(Error) << "Unable to find existing OCL category '" << categoryName << "'" << endlog(); } return ok; } void Component::updateHook() { // RTT 1.X logger->error(OCL::String("Had an error here")); logger->debug(OCL::String("Some debug data ...")); // RTT 2.X logger->error(RTT::rt_string("Had an error here")); logger->debug(RTT::rt_string("Some debug data ...")); logger->getRTStream(log4cpp::Priority::DEBUG) << "Some debug data and a double value " << i; } IMPORTANT YOu must dynamic_cast to an OCL::logging::Category* to get the logger, as shown in the constructor above. Failure to do this can lead to trouble. You must also use explicitly use OCL::String() syntax when logging. Failure to do this produces compiler errors, as otherwise the system defaults to std::string and then you are no longer real-time. See the FAQ below for more description. And the output of the above looks something like this: // file orocos.log, from RTT::Logger configured with log4cpp 20100414T09:50:11.844 [INFO] ControlTask 'HMI' found CORBA Naming Service. 20100414T09:50:11.845 [WARN] ControlTask 'HMI' already bound to CORBA Naming Service. and from a deployer with OCL::logging (note that here the categories are set as components.something) 20100414T21:41:22.539 [INFO ] components.HMI Started servicing::HMI 20100414T21:41:23.039 [DEBUG] components.Robot Motoman robot started 20100414T21:41:42.539 [INFO ] components.ConnectionMonitor Connected and if you combine RTT::Logger and your own log4cpp-logging, say in a GUI application 20100414T21:41:41.982 [INFO ] org.orocos.rtt Thread created with scheduler type '1', priority 0 and period 0. 20100414T21:41:41.982 [INFO ] org.orocos.rtt Creating Proxy interface for HMI 20100414T21:41:42.016 [DEBUG] org.me.myapp Connections made successfully 20100414T21:41:44.595 [DEBUG] org.me.myapp.Robot Request position hold The last one is the most interesting. All RTT::Logger calls have been sent to the same appender as the application logs to. This means you can use the exact same logging statements in both your components (when they use OCL::Logging) and in your GUI code (when they use log4cpp directly). Less maintenance, less hassle, only one (more) tool to learn. The configuration file for the last example looks something like # root category logs to application (this level is also the default for all # categories who's level is NOT explicitly set in this file) log4j.rootCategory=DEBUG, applicationAppender # orocos setup log4j.category.org.orocos.rtt=INFO, applicationAppender log4j.additivity.org.orocos.rtt=false # do not also log to parent categories # application setup log4j.category.org.me =INFO, applicationAppender log4j.additivity.org.me=false # do not also log to parent categories log4j.category.org.me.gui=WARN log4j.category.org.me.gui.Robot=DEBUG log4j.category.org.me.gui.MainWindow=INFO log4j.appender.applicationAppender=org.apache.log4j.FileAppender log4j.appender.applicationAppender.fileName=application.log log4j.appender.applicationAppender.layout=org.apache.log4j.PatternLayout log4j.appender.applicationAppender.layout.ConversionPattern=%d{%Y%m%dT%T.%l} [%-5p] %c %m%n ## Technical details • We rely on a real-time allocator called TLSF. • There is a several kilobyte overhead for TLSF's bookkeeping (~3k on 32-bit Ubuntu, ~6k on 64-bit Snow Leopard). You must take this into account, although the standard OCL TLSF pool size (256k) should cover your needs. • Only the OCL::String (in 1.x) and RTT::rt_string (in 2.x) objects in OCL::logging::LoggingEvent objects use the real-time memory pool. • When you create a category, all parent categories up to the root are created. For example, "org.me.myapp.cat1" causes creation of five (5) categories: "org.me.myapp.cat1", "org.me.myapp", "org.me", "org", and "" (the root category) (presuming none of these already exist). These all occur on the normal heap (see below). • Currently, exhausting the real-time memory pool results in logging events being silently dropped (also, see next item). • For real-time performance, ensure that TLSF is built with MMAP and SBRK support OFF in RTT's CMake options (-DOS_RT_MALLOC_MMAP=OFF -DOS_RT_MALLOC_SBRK=OFF). • TLSF use with multiple threads is currently supported only for non-macosx platforms. Use on macosx will exhibit (understandable) corruption in the TLSF bookkeeping (causes assert's). ## FAQ ### Logging statements are not recorded Q: You are logging and everything seems fine, but you get no output to file/socket/stdout (depending on what your appender is). A: Make sure you are using an OCL::logging::Category* and not a log4cpp::Category. The latter will silently compile and run, but it will discard all logging statements. This situation can also mask the fact that you are accidentally using std::string and not OCL::String. For example log4cpp::Category* logger = log4cpp::Category::getInstance(name); logger->debug("Hello world") When the above is used within the OCL real-time logging framework, no logging statements are recorded and it is not running in real-time. Changing the above to OCL::logging::Category* logger = dynamic_cast<OCL::logging::Category*>(&log4cpp::Category::getInstance(name)); logger->debug("Hello world") will cause a compile error of /path/to/log4cpp/include/log4cpp/Category.hh: In member function ‘virtual bool MyComponent::configureHook()’: /path/to/log4cpp/include/log4cpp/Category.hh:310: error: ‘void log4cpp::Category::debug(const char*, ...)’ is inaccessible /path/to/my/source/MyComponent.cpp:64: error: within this context because the "Hello world" string is being treated as a std::string, which you can not use with OCL::logging::Category. Finally, correct the code to OCL::logging::Category* logger = dynamic_cast<OCL::logging::Category*>(&log4cpp::Category::getInstance(name)); logger->debug(OCL::String("Hello world")) and the code compiles and runs, and now logging statements are recorded. # omniORBpy - python binding for omniORB This page describes a working example of using omniORBpy to interact with an Orocos component. The example is very simple, and is intended for people who do not know where to start developing a CORBA client. Your first stop is: http://omniorb.sourceforge.net/omnipy3/omniORBpy/ The omniORBpy version 3 User’s Guide. Read chapters 1 and 2. Optionally read chapter 6. The example works with and without naming services. Once you are comfortable with omniORBpy, do the following (I assume you are kind enough to be a Linux user working on a console): 1. download the rtt examples, and compile the smallnet orocos component (you might need first to fix the Makefile paths): • wget http://www.orocos.org/stable/examples/rtt/rtt-examples-1.10.0.tar.gz tar xf rtt-examples-1.10.0.tar.gz cd rtt-examples-1.10.0/corba-example/ make smallnet 2. download the corba idls and, for simplicity's sake, copy the IDLs to a new empty directory: • svn co http://svn.mech.kuleuven.be/repos/orocos/trunk/rtt/src/corba/ mkdir omniclt cp corba/*idl omnictl/ cd omniclt 3. generate the Python stubs, two new directories should appear (namely RTT and RTT__POA) • omniidl -bpython *idl  4. download the attached python file (orocosclient.py) to your home directory and copy it where your IDLs are (current directory) • cp ~/orocosclient.py . 5. open a new console and run your smallnet application • sudo ../smallnet If you get something like 0.011 [ Warning][SmallNetwork] ControlTask 'ComponentA' could not find CORBA Naming Service. 0.011 [ Warning][SmallNetwork] Writing IOR to 'std::cerr' and file 'ComponentA.ior' IOR:0...10100 it means your omniNames is either not configured or not running. Try: sudo ../smallnet -ORBInitRef NameService=corbaname::127.0.0.1 if this work (you see a line like: 0.011 [ Info ][SmallNetwork] ControlTask 'ComponentA' found CORBA Naming Service.) then you need to modify the paremeter InitRef in your omniORB4.cfg (or similar, which is usually in /etc/) and make it read like: InitRef=NameService=corbaname::127.0.0.1 • finally run the python application • python orocosclient.py  If you are not able to make your naming service work, try using the component's IOR. After running you smallnet server, copy the complete IOR printed on screen and paste it as argument to the python program (including the word "IOR:") python orocosclient.py IOR:0...10100 Look at the IDLs and the code to understand how things work. I am no python expert, so if the coding style looks weird to you, my apologies. Good luck! AttachmentSize orocosclient.py_.txt1.99 KB # Frequently asked questions (FAQ) Future home of FAQ # How to build Debian packages ## Rationale You want to build debian packages once, so that you can install on multiple machines without building from source on each. ## Assumptions 1. You are building for gnulinux only. 2. You have "svn-b", etc, alises setup (see "man svn-buildpackage"). 3. You are using Synaptic as your package manager. 4. Example code is for Orocos v1.8, but also applies to later versions, including 2.x 5. BASE_DIR is whatever directory you want to put everything into. ## To build the Orocos RTT packages (1.x) cd BASE_DIR svn co ... cd rtt debchange -v 1.8.0-0 cd debian ./create-control.sh gnulinux # optionally add "lxrt", "xenomai" svn add *1.8*install cd .. export DEB_BUILD_OPTIONS="parallel=2" # or 4, 8, depending on your computer svn-br # or svn-b Packages are built into BASE_DIR/build-area. ## To build the Orocos RTT packages (2.x) cd BASE_DIR git clone http://git.gitorious.org/orocos-toolchain/rtt.git cd rtt debchange -v 2.3.0-1 cd debian ./create-control.sh gnulinux # optionally add "lxrt", "xenomai" git add *2.3*install git commit -sm"2.3 release install files" cd .. export DEB_BUILD_OPTIONS="parallel=2" # or 4, 8, depending on your computer git-buildpackage --git-upstream-branch=origin Packages are built into BASE_DIR/build-area. ## Make the packages available to your package manager Create your own repository cd BASE_DIR dpkg-scanpackages build_area /dev/null | gzip -9c > Packages.gz Now open/etc/apt/sources.list in your favorite editor, and append the following lines to the bottom (substituting the full path to your repos for /path/to/BASE_DIR/). # Orocos packages deb file:///path/to/BASE_DIR/ ./ Open Synaptic, reload, search for orocos and install. ## KDL and OCL Follow the same basic approach first for KDL, then for OCL 1. build packages 2. update the repository doing just the "dpkg-scanpackages" line again 3. install NB KDL and OCL will happily both build into "build_area" alongside RTT. ## Test installed packages • 1.x: Build the quicky components. Requires OCL (install at least the orocos-ocl-gnulinux1.8-bin and liborocos-ocl-gnulinux1.8-dev packages). # 1.x: svn co ... cd quicky mkdir build && cd build cmake .. make # one of the following two exports, depending on your situation export LD_LIBRARY_PATH=. export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:.

deployer-gnulinux -s ../quicky.xml
ls Quicky   # you should see Data_W != 0

• 2.x: Run 'orocreate-pkg testme' and try to build the testme package.

orocreate-pkg testme
cd testme

#non-ROS:
make install
#ROS:
make

deployer-gnulinux
> import("testme")
> displayComponentTypes()

### Test CORBA deployer

These instructions test inter-process communication on the same machine. See [2] for more details on running CORBA-based deployers between computers.

In the first shell start the naming service and the deployer

Naming_Service -m 0 -ORBDottedDecimalAddresses 1 -ORBListenEndpoints  iiop://127.0.0.1:2809 -ORBDaemon
export NameServiceIOR=corbaloc:iiop:127.0.0.1:2809/NameService
deployer-corba-gnulinux -s ../quicky.xml -- -ORBDottedDecimalAddresses 1
ls Quicky   # you should see Data_W != 0

In the second shell run the taskbrowser and see the Quicky component running in the deployer

export NameServiceIOR=corbaloc:iiop:127.0.0.1:2809/NameService
ls Quicky   # you should see Data_W != 0

## Gotchas

If the v1.8 files have already been committed to the repository, then you don't need the debchange and svn add commands when building the packages.

See [1] below.

# How to re-build Debian packages

This page describes how to re-build debian packages for another Debian/Ubuntu release than they were prepared for.

Note1: This only applies if you want to use the same version as the version in the package repository. If you want a newer version, consult How to build Debian packages.

Note2: The steps below will rebuild Orocos for all targets in the repository, so lxrt, xenomai and gnulinux. If you care only for one of these targets, see also How to build Debian packages.

First, make sure you added this deb-src line to your sources.list file:

deb-src http://www.fmtc.be/debian etch main
Next, type from your 'HOME/src' directory:

sudo apt-get update
apt-get source orocos-rtt
sudo apt-get build-dep orocos-rtt
sudo apt-get install devscripts build-essential fakeroot dpatch
cd orocos-rtt-1.6.0
dpkg-buildpackage -rfakeroot -uc -us
cd ..
for i in *.deb; do sudo dpkg -i \$i; done You can repeat the same process for orocos-ocl. # Using CORBA Table of Contents Outlines how to use CORBA to distribute applications. Differs by CORBA implementation and whether you are using DNS names or IP addresses. Examples below support the ACE/TAO and OmniORB CORBA implementations. Sample system: • Deploying components in demo.xml with deployer-corba, on machine1.me.home with IP address 192.168.12.132 • Running a GUI program demogui to connect to deployer components, on machine2.me.home with IP address 192.168.12.133 • Use a name server without multi-casting[1], on machine1.me.home. • Using a bash shell. • Both machines are gnulinux (though this has been verified with macosx, and mixing macosx and gnulinux) ## Working DNS If you have working forward and reverse DNS entries (ie dig machine1.me.home returns 192.168.12.132, and dig -x 192.168.12.132 returns machine1.me.home) ### ACE/TAO machine1 \$ Naming_Service -m 0 -ORBListenEndpoints iiop://machine1.me.home:2809 \
-ORBDaemon &
machine1 \$export NameServiceIOR=corbaloc:iiop:machine1.me.home:2809/NameService machine1 \$ deployer-corba-gnulinux -s demo.xml

machine2 \$export NameServiceIOR=corbaloc:iiop:machine1.me.home:2809/NameService machine2 \$ ./demogui

### OmniORB

OmniORB does not support the NameServiceIOR environment variable

machine1 \$omniNames -start & machine1 \$ deployer-corba-gnulinux -s demo.xml

machine2 \$./demogui -ORBInitRef NameService=corbaloc:iiop:machine1.me.home:2809/NameService Note that if you swap which machines run the deployer and demogui, then change the above to machine1 \$ omniNames -start &

machine2 \$deployer-corba-gnulinux -s demo.xml -- \ -ORBInitRef NameService=corbaloc:iiop:machine1.me.home:2809/NameService machine1 \$ ./demogui 

## Non-working DNS or you must use IP addresses

If you don't have DNS or you must use IP addresses for some reason.

### ACE/TAO

machine1 \$Naming_Service -m 0 -ORBDottedDecimalAddresses 1 \ -ORBListenEndpoints iiop://192.168.12.132:2809 -ORBDaemon & machine1 \$ export NameServiceIOR=corbaloc:iiop:192.168.12.132:2809/NameService
machine1 \$deployer-corba-gnulinux -s demo.xml -- -ORBDottedDecimalAddresses 1 machine2 \$ export NameServiceIOR=corbaloc:iiop:192.168.12.132:2809/NameService
machine2 \$./demogui -ORBDottedDecimalAddresses 1 For more information on the ORBListenEndPoints syntax and possibilities, see http://www.dre.vanderbilt.edu/~schmidt/DOC_ROOT/TAO/docs/ORBEndpoint.html ### OmniORB machine1 \$ omniNames -start &
machine1 \$deployer-corba-gnulinux -s demo.xml machine2 \$ ./demogui -ORBInitRef NameService=corbaloc:iiop:192.168.12.132:2809/NameService

And the reverse

machine1 \$omniNames -start & machine2 \$ deployer-corba-gnulinux -s demo.xml  -- \
-ORBInitRef NameService=corbaloc:iiop:192.168.12.132:2809/NameService

machine1 \$./demogui  ## Localhost Certain distro's and certain CORBA versions will exhibit problems even with localhost only scenarios (demonstrated with OmniORB under Ubuntu Jackaloupe). If you can not connect to the name service running on the same machine, substitue the primary network interface's IP address for localhost in any NameService value. For example, instead of machine1 \$ omniNames -start &

machine2 \$deployer-corba-gnulinux -s demo.xml  or even machine1 \$ omniNames -start &

machine2 \$deployer-corba-gnulinux -s demo.xml -- \ -ORBInitRef NameService=corbaloc:iiop:localhost:2809/NameService use machine1 \$ omniNames -start &

machine2 \$deployer-corba-gnulinux -s demo.xml -- \ -ORBInitRef NameService=corbaloc:iiop:192.168.12.132:2809/NameService NB as of RTT v1.8.2 and OmniORB v4.1.0, programs like demogui (which use RTT::ControlTaskProxy::InitOrb() to initialize CORBA) do not support -ORBDottedDecimalAddresses (in case you try to use it). ## Multi-homed machines Computers that have multiple network interfaces present additional problems. The following is for omniORB (verified with a mix of v4.1.3 on Mac OS X, andv v4.1.1 on Ubuntu Hardy), for a system running a name server, a deployer, and a GUI. The example system has a 192.168.1.0 wired subnet and a 10.0.10.0 wireless subnet, and you have a mobile vehicle that has to communicate over the wireless subnet but it also has a wired interface. The problem may appear as one of • The vehicle can not contact the name server when the wired interface is disconnected but it is up (NB on rare occasions, we've seen this even with the wired interface disconnected and down!) • Your GUI can connect to the deployer, but then locks up or throws a CORBA exception when trying to connect to certain remote Orocos items (we had this happen specifically for methods with parameters). The solution is to forcibly specify the endPoint parameter to the name server. In the omniorb.cfg file on the computer running the name server, add (for the example networks above) endPoint = giop:tcp:10.0.10.14: where 10.0.10.14 is the IP adrs of that computer. This forces the name server to publish end points on the wireless network first. Despite this, it will still publish the wired interface but it will come after the wireless. Specifying the endPoint parameter on the command line (instead of the config file) will not work, as then the name sever publishes the wired network first, and then the wireless network second. If the above still does not work, then set the endPoint parameter in all computer's config files (note that the end point is the IP adrs of each computer, so it will be (say) 10.0.10.14 for the computer running the name server and the deployer, and (say) 10.0.10.21 for the computer running the GUI). This will force everyone onto the wireless network, instead of relying on what the name server is publishing. To debug this problem see the debugging section below, but after starting the name server you will see it output its published endpoints (right after the configuration dump). Also, if you get the lockup then adding the debug settings will cause the GUI or deployer to output each message and what direction/IP it is going on. If they have strayed on to the wired network it will be visibly obvious. NB we found that the clientTransportRule and serverTransportRule parameters had no affect on this problem. NB the above solution works now matter which computer the name server is running on (ie with the deployer, or with the GUI). ## Debugging Add the following to the omniorb.cfg file dumpConfiguration = 1 traceLevel = 25 and get ready for lots of output. ## See also # Installation For general installation instructions specific to each software version, see the top level wiki page for each project (eg. RTT, KDL, etc) and look for Installation in the left toolbar. See below for specific additional instructions. ## Installing from binaries / package managers Installing via Macports on Mac OS X How to build Debian packages ## Installing from source To install from source on *NIX systems such as Linux and Mac OS X, see the installation page specific to your software version (e.g. v1.8 RTT). To install from source on Windows, see the following wiki pages (also check the forums, a lot of good material is in there also). # Debian Etch installation from public repositories (x86 only!) The Orocos Real-Time Toolkit and Component Library have been prepared as Debian packages for Debian Etch. The pages How to build Debian packages How to re-build Debian packages contain instructions for building your own packages on other distributions, like Ubuntu. Copy/paste the following commands, and enter your password when asked (only works in Ubuntu Feisty or later and Debian Etch or later): wget -q -O - http://www.orocos.org/keys/psoetens.gpg | sudo apt-key add - sudo wget -q http://www.fmtc.be/debian/sources.list.d/fmtc.list -O /etc/apt/sources.list.d/fmtc.list These commands install the GPG key and the repository location of the Orocos packages. Next, for Debian Etch, type: sudo apt-get update sudo apt-get install liborocos-rtt-corba-gnulinux1.8-dev You can install Orocos for additional targets (or versions) by replacing gnulinux1.8 by another target name (or version). All target libraries can be installed at the same time, the -dev header files only for a single target and version at a time. For your application development, you'll most likely use the Orocos Component library as well: sudo apt-get install orocos-ocl-gnulinux1.8-dev orocos-ocl-gnulinux1.8-bin Again, you may install additional targets and/or versions. We recommend using the pkg-config tool to discover the compilation flags required to compile your application with the RTT or OCL. This is described in the installation manual. # Installing via Macports on Mac OS X These are instructions to install the latest version of each of RTT, KDL, BFL and OCL, on Mac OS X using Macports. Macports does not have official ports for these Orocos projects, however, the approach below is the recommended way to load unofficial ports in to Macports. [1] ## Installation These instructions use /opt/myports to hold the Orocos port files. You can substitute any other directory for MYPORTDIR (ie /opt/myports). Instructions are for bash shell - change appropriately for your own shell. 1. Download the Portfile files from this page's Attachments (at bottom of page). 2. Execute the following commands (substituting /opt/myports for the location you wish to store the Orocos port files, and ~/Downloads for the directory you downloaded the portfiles to) export MYPORTDIR=/opt/myports export DOWNLOADDIR=~/Downloads mkdir \$MYPORTDIR
cd \$MYPORTDIR mkdir devel cd devel mkdir orocos-rtt orocos-kdl orocos-bfl orocos-ocl cp \$DOWNLOADDIR/orocos-rtt-Portfile.txt orocos-rtt/Portfile
cp \$DOWNLOADDIR/orocos-kdl-Portfile.txt orocos-kdl/Portfile cp \$DOWNLOADDIR/orocos-bfl-Portfile.txt orocos-bfl/Portfile
cp \$DOWNLOADDIR/orocos-ocl-Portfile.txt orocos-ocl/Portfile And for the RTT patch file cd \$MYPORTDIR/devel
mkdir orocos-rtt/files
cp \$DOWNLOADDIR/rtt-patch-config-check_depend.cmake.diff orocos-rtt/files/patch-config-check_depend.cmake.diff You should now have a tree that looks like tree /opt/myports/ /opt/myports/ -- devel |-- orocos-bfl | -- Portfile |-- orocos-kdl | -- Portfile |-- orocos-ocl | -- Portfile -- orocos-rtt |-- Portfile -- files -- patch-config-check_depend.cmake.diff 3. Edit /opt/local/etc/macports/sources.conf with superuser privileges (ie via sudo), and add the follwing line before the rsync:///rsync.macports.org/...' line. # (substitute your ''MYPORTDIR'' value from above) file:///opt/myports 4. Execute these commands to tell Macports about your new ports. cd \$MYPORTDIR
sudo portindex

5. Now install each port with the following commands (the following commands add the optional CORBA support, via omniORB in Macports, as well as the helloworld and other useful parts of OCL)

sudo port install orocos-rtt +corba
sudo port install orocos-kdl +corba
sudo port install orocos-bfl
sudo port install orocos-ocl +corba+deployment+motion_control+reporting+taskbrowser+helloworld

deployer-macosx -s /path/to/test-macports.xml
This should succesfully load and start the OCL HelloWorld component within the taskbrowser. You may need to specify the paths to the dynamic libraries, for this to work
export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib
Yes, it is DYLD_FALLBACK_LIBRARY_PATH and not DYLD_LIBRARY_PATH. Search the forum if you want to know why ...

export CMAKE_PREFIX_PATH=/opt/local
If you already have CMAKE_PREFIX_PATH set, then append "/opt/local" to your existing entry.

If you use Makefiles or autoconf to build your project, you'll need to tell those build systems to find Orocos headers, libraries and binaries under /opt/local. Instructions are not provided here for that.

export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/rtt/macosx/plugins
If you already have DYLD_FALLBACK_LIBRARY_PATH set, then append the above to your existing entry.

## Updating an existing installation

(Not yet tested)

2. Uninstall each of the old ports

...
sudo port uninstall orocos-rtt
1. Copy the new portfiles on top of the old ones
2. Regenerate the port index
3. Reinstall

## Limitations and issues

Current limitations

• CORBA support assumes omniorb
• KDL supports the Python plugin for Python 2.5, but not Python 2.6.
• BFL defaults to boost
• OCL could use more variants to provide finer control

### Issues

• If when running an Orocos executable, or your own executable linked with Orocos, you get the following error

dyld: Symbol not found: __cg_jpeg_resync_to_restart
Referenced from:
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/\
Frameworks/ImageIO.framework/Versions/A/ImageIO
Expected in: /opt/local/lib/libJPEG.dylib 
then ensure you have the DYLD_FALLBACK_LIBRARY_PATH set and not DYLD_LIBRARY_PATH. See [2] for further details.

## References

[1] Macports guide with detailed information on the port system.

AttachmentSize
orocos-rtt-Portfile.txt1.82 KB
orocos-kdl-Portfile.txt2.13 KB
orocos-bfl-Portfile.txt1.36 KB
orocos-ocl-Portfile.txt2.67 KB
rtt-patch-config-check_depend.cmake_.diff607 bytes
test-macports.xml472 bytes

# Orocos packages on Peter's Ubuntu PPA

A PPA is a 'Personal Package Archive' which the Ubuntu community offers such that non-Ubuntu developers can test, build and upload their packages to an Ubuntu server.

Currently, only Ubuntu Jaunty is supported.

Peter's packages are located at:

deb http://ppa.launchpad.net/peter-soetens/ppa/ubuntu jaunty main

These packages are signed. You'll want to install his GPG key using this line:

wget -q -O - http://www.orocos.org/keys/psoetens-ppa.gpg | sudo apt-key add -

# RTT Dictionary

## RTT Dictionary

This page containts a list of terms used in RTT (and Orocos in general).

### Activity

• An Activity object executes the ExecutionEngine, which in turn executes programs, state machines, processes, incoming commands, incoming events and finally executes the user code.
• An activity can be: activity, periodic activity, non-periodic activity, sequential activity, slave activity...

### Attribute

• Attributes are solely for run-time values.
• You can alter the attributes of any task, program or state machine. The TaskBrowser will confirm validity of the assignment with 'true' or 'false'

### Command

• Commands are 'sent' by other components to instruct the receiver to 'reach a goal'
• When a command is entered, it is sent to the component, which will execute it in its own thread on behalf of the sender. The different stages of its lifetime are displayed by the prompt. Hitting enter will refresh the status line.
• A Command might be rejected (return false) in case it received invalid arguments.
• A command has a designated reciever.
• A command cannot, in general, be completely executed instantaneously, so the caller should not block and wait for its completion.
• But the Command object offers all functionalities to let the caller know about the progress in the execution of the command.
• Commands are used for actions taking time and setpoints.

### Component

• Components are implemented by the TaskContext class.
• It is useful speaking of a context because it defines the context in which an activity (a program) operates. It defines the interface of the component, its properties, its peer components and uses its ExecutionEngine to execute its programs and to process commands and events.
• A task's interface consists of members.

### Data-Flow Ports

• Data-Flow Ports are a thread-safe data transport mechanism to communicate buffered or un-buffered data between components.
• When a value is Set(), it is sent to whatever is connected to that port. Use Get() to read the port.
• The advantage of using ports is that they are completely thread-safe for reading and writing, without requiring user code.

### Events

• Events are related to commands, but allow broadcasting of data, while a command has a designated receiver.
• Events allows functions to be executed when a change in the system occurs.
• eg. alarms, publishing state changes

### Members

• Members are: Commands, Methods, Ports, Attributes and Properties and Events, which are all public.

### Method

• Methods are used for algorithms and complex configurations.
• Methods are callable by other components to 'calculate' a result immediately, just like a 'C' function.

### Peer

• The peers of a component are the components which are known, and may be used, by this component.

### Property

• Properties are meant for persistent configuration and can be written to disk.
• Properties are run-time modifiable parameters, stored in XML files..

# RTT on MS Windows

This page collects all the documentation users collected for building and using RTT on Windows. Note that Native Windows support is available from RTT 1.10.0 on, and that you might no longer need some of the proposed workarounds (such as using mingw or cygwin).

The recommended way of compiling the RTT on Windows is by using the Compiling on Windows with Visual Studio instructions.

# Compiling RTT in Windows/MinGW + pthreads-32

This page describes the steps to take in order to compile the real-time toolkit (RTT) on a Windows machine, under MinGW and pthreads-32.

The following has been tested on Windows XP, running in a virtual machine on Mac OS X Leopard.

### Outstanding issues

• Not all RTT tests pass
• TAO does not completely build
• CORBA support in RTT untested due to the above

Warning: the default GCC 3.4.5 compiler in MinGW outputs a lot of warnings when compiling RTT. Mostly they are "foo might be used uninitialized in this function" in STL code.

### Install MinGW

See the following links for the basic approach

See detailed instructions in URL's above and below, but basically (unless otherwise noted, all actions are in MSys Unix shell, and, all unix-built items are installed in /mingw (which is c:\msys\1.0\mingw in DOS prompt) )

1. Install MinWG - base, C++, make (then add c:\minwg\bin to system PATH)
2. Install msysxxx.exe
3. Install msys DTK
4. Install bash (i386 versions) by untarring in / in msys
5. Install coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz by untar'ing and then manually copying contents to / (have to mv the bin/cp command)
6. Download autoconf, automake and libtool: untar, configure with --prefix=/mingw, and build/install
7. Set env vars in /etc/profile: CFLAGS, PKG_CONFIG_PATH
8. Install glib2, gettext and pkg-config from gtk URL. Extract into /mingw

### Install dependancy packages

Compile CMake from unix source (in build dir)
 cmake-xxx/bootstrap --prefix=/mingw --no-qt-gui
make && make install
    - manually copy pre-built/include/* to /c/mingw/include    (C:\mingw/include)
- manually copy pre-built/lib/*GC2* to /c/mingw/lib        (C:\mingw/lib)
- to run pthreads tests, need to copy prebuilt .a/.dll into .. dir, and copy queueuserapcex to ../.. 
Boost (as at 2009-Jan, use v1.35 not v1.37 until we fix RTT for v1.37)
    *** DOS shell ***
cd boost-jam-xxx
.\build.bat gcc        ** won't build in unix shell with build.sh **
*** unix shell ***
cd boost-jam-xxx
cp binntx86/bjame.exe /mingw/bin
cd ~/software/build/boost_1_35
bjam --toolset=gcc --layout=system --prefix=/mingw --with-date_time --with-graph \
--with-system --with-function_types  --with-program_options  install

Cppunit, get tarball from sourceforge
    untar and configure with --prefix=/mingw
correct line 7528 in libtool, to be c:/MinGW/bin../lib/dllcrt2.o for first item
make && make install


### Build RTT

Get trunk of RTT, patch with this file, configure (ensure set OROCOS_TARGET=win32), make, and install as usual.
 cd /path/to/rtt; patch -p0 < patch-rtt-mingw-1.patch


Ensure your PATH in the MSYS shell contains /mingw/bin and /mingw/lib.

Next test your setup with a 'make check'. Currently 4 of 8 tests fail ... more work to do here.

### Partial ACE/TAO CORBA build

This gets most of ACE/TAO to build, but not yet all.
    download, follow MinGW build instructions on the website.
add "#undef ACE_LACKS_USECONDS_T" to ace/config-win32-mingw.h" before compiling
copy ace/libACE.dll to /mingw/lib
make TAO ** this fails
You can build all we need by manually doing ''make'' in the following directories. Note that the last couple of TAO dir's have problems.
ace, ace/protocols, kokyu, tao, tao/TAO_IDL, tao/orbsvcs

NB Can parallel build ace but not its tests nor tao.

NB Not all tests pass. At least one of the ACE tests fail.

# Compiling RTT in Windows/MinGW Native API

## Status

The native API port of RTT can be found in the 1.10.x releases. Only minor issues are left and most unit tests pass.
• it is based solely on mingw32, no msys or cygwin.
• only ports RTT, CORBA is supported as well.
• it uses the fixes that Peter made regarding 'weak' symbol handling.
• 1 unit test is till doing difficult
• Priorities are supported on thread level, though creating new processes could help get better "realtime" support.

This document is slightly outdated.

## Compiling RTT

• CMake is used to generate the project: cmake -G"MinGW Makefiles" .

## Linking and Compiling an application

To have something sort of working under native Win32/Visual Studio 2003 using the MinGW Compiled RTT (with the patches).

Using the info here: http://www.mingw.org/old/mingwfaq.shtml#faq-msvcdll

I managed to create DEF files, and use Microsofts LIB tool to turn the library it into something MSVC likes.

I'm no CMake expert, and don't have the time to learn **another** build scripting language, however I created the CMake files in the usual way, built RTT and ensure it compiled cleanly. I hacked the created makefiles by a search of my source tree for "--out-implib" and found that link.txt that lives in build\src\CMakeFiles\orocos-rtt-dynamic_win32.dir had that string in it. So I added the --output-def,..\..\libs\liborocos-rtt-win32.dll.def, to create the def file, and rebuilt RTT, this created the DEF file, I than ran it through the Microsoft LIB tool as described.

I then created a MSVC project, added the library to my linker settings, and made a very simply MSVC console application:

#include "rtt\os\main.h"
#include "rtt\rtt-config.h"

int ORO_main(int, char**)
{
return 0;
}

I also needed to setup my MSVC preprocessor definitions:

NDEBUG
_CONSOLE
__i386__
__MINGW32__
OROCOS_TARGET=win32

Hopefully I am now at a stage when I can actually start to evaluate RTT :-) If anyone has any ideas on how to properly get the CMakeList.txt to generate the DEF files without nasty post-CMake hacks, then I would love to hear it...

# Compiling on Windows with Visual Studio

This page summarizes how to compile RTT with Microsoft Visual Studio, using the native win32 api. RTT supports Windows out of the box from RTT 1.10.0 and 2.3.0 on. OCL is supported from 1.12.0 and 2.3.0 on.

This tutorial assumes you extracted the Orocos sources and all its dependencies in c:\orocos

For new users, RTT/OCL v2.3.x or later is recommended, included in the Orocos Toolchain v2.3.x.

## Rationale

We only support Visual Studio 2008 and 2005. Support for 2010 is on its way. You're invited to try VS2010 out and suggest patches to the orocos-dev mailing list.

Orocos does not come with a Visual Studio Solution. You need to generate one using the CMake tool which you can download from http://www.cmake.org. The most important step for CMake is to set the paths to where the dependencies of Orocos are installed. So before you can get to building Orocos, you need to build its dependencies, which don't use CMake, but their own build mechanism.

Only RTT and OCL of the toolchain are supported on Windows. The ruby based 'orogen' and 'typegen' tools, part of the toolchain, are not supported. Also ROS integration is not supported on Windows.

## Important notice about Release or Debug

Debug and Release builds can not be mixed in Visual Studio's C++ compiler (you will have crashes when mixing a Debug and Release DLL that has a C++ API). By convention, a Debug .DLL can be recognized because it ends with ....d.dll. We recommend that you do Release builds when evaluating the Orocos toolchain and on production systems. Debug builds are considerably larger than Release builds.

## RTT Dependencies

There are two major libraries required by RTT: Boost C++ and a CORBA transport library (if you require one).

### CORBA using ACE/TAO (optional)

In case you require distributed Orocos components, you need to setup ACE/TAO, which does the work under the hood for RTT. Download the latest TAO version, extract it and open the solution (ACE_wrappers/TAO/TAO_ACE_vc8.sln) file with Visual Studio. Build the 'Naming_Service_vc8' project, and make sure that you choose the configuration (Debug/Release) that fits your purpose. The Naming_Service project builds automatically the right components we need to build RTT. Check the TAO build instructions in case you encounter problems.

You must have this set as system environment variables:

set ACE_ROOT=c:\orocos\ACE_wrappers
set TAO_ROOT=%ACE_ROOT%\tao
set PATH=%PATH%;%ACE_ROOT%\bin;%ACE_ROOT%\lib

You can also set these using Configuration -> System -> Advanced -> Environment Variables

Untested.

### Boost (required)

While TAO builds, you might want to check out the pre-built boost libraries from boost-pro consulting on http://www.boostpro.com/download . Alternatively, get boost from http://www.boost.org and follow their build instructions. RTT 1.10.0 requires Boost 1.36.0 at least, since it requires the intrusive containers added in that release. RTT 2.3.0 or later require Boost 1.40.0. Note this bug for Boost 1.44.0: https://svn.boost.org/trac/boost/ticket/4487

We recommend Boost 1.40.0 for Windows. Also, unzip Boost with 7Zip or similar, but not with the default Windows unzip program, which is extremely slow.

Make sure to install these components: program_options, thread, unit_test_framework, filesystem, system.

set PATH=%PATH%;c:\orocos\boost_1_40\lib

### CMake (required)

Download and install cmake (http://www.cmake.org). We're going to use cmake-gui.exe to configure our build system. Use CMake version 2.6.3 or newer.

### XML Parser

RTT will use its internal 'tinyxml' parser on Windows. No need to install anything for this.

## Setting up CMake

First you need to add two 'PATH' entries for telling cmake where Boost and TAO are installed. In the top RTT directory, there is a file named orocos-rtt.default.cmake. Copy it to orocos-rtt.cmake (in the same directory) and add these two lines:
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "c:/orocos/boost_1_40;c:/orocos/ACE_wrappers;c:/orocos/ACE_wrappers/TAO;c:/orocos/ACE_wrappers/TAO/orbsvcs") set(CMAKE_LIBRARY_PATH${CMAKE_LIBRARY_PATH}
"c:/orocos/boost_1_40/lib;c:/orocos/ACE_wrappers/lib")
(note the forward-slashes, even on Windows!) Edit these lines to use your Boost version and install location. The OROCOS_TARGET should be automatically detected. If not, add:
set( OROCOS_TARGET win32 CACHE STRING
"The Operating System target. One of [lxrt gnulinux xenomai macosx win32]")
and remove the lines with references to other OROCOS_TARGET settings.

Start the cmake-gui and set your source and build paths ( For example, c:\orocos\orocos-rtt-1.10.0 and c:\orocos\orocos-rtt-1.10.0\build ). Now click 'Configure' at the bottom. Check that there are no errors. If components are missing, you probably need to fix the above PATHs.

• For RTT 1.12, turn OS_NO_ASM ON, for RTT 2.3.0, turn OS_NO_ASM OFF.

You probably need to click Configure again and then click 'Generate', which will generate your Visual Studio solution and project files in the 'build' directory.

Open the generated solution in MSVS and build the 'ALL_BUILD' target, which will build the RTT (and the unit tests if you enabled them).

## Unit tests (Optional)

In order to enable unit tests, you need to turn on BUILD_TESTING in the CMake GUI.

The unit tests will fail if the required DLLs are not in your path. In your system settings, or on the command prompt of Windows, add c:\orocos\boost_1_40\lib and c:\orocos\ACE_wrappers\lib to your PATH environment (reboot if necessary).

Next, run a 'make install' and add the c:\orocos\bin directory to your PATH (or whatever you used as install path.) In RTT 2.3.0, the default install path is c:\Program Files\orocos (so add c:\Program Files\orocos\bin to PATH). It is recommended to keep this default, since OCL uses that too.

Now you should be able to run the unit tests. The process could be a bit streamlined more and may be improved in later releases.

## Installing RTT

Once everything is build, you can use the 'INSTALL' project to copy the files into the correct installation directories. This is necessary for OCL and the other Orocos applications such that they find headers and libraries in the expected locations.

## Building OCL

Building OCL on Windows follows a similar path as RTT. Start CMake and point it to your OCL source tree and create a 'build' directory in there.

### OCL Dependencies

Also OCL needs to know where Boost, TAO and other dependencies are installed. There's again an orocos-ocl.default.cmake file which you can copy to orocos-ocl.cmake.

## Missing Features on Windows

• The RTT on Windows is not real-time ! It exists for convenience, simulation, and use by Windows GUIs.

# Compiling the RTT on Windows/cygwin

This page describes all steps you need to take in order to compile the real-time toolkit on a Windows machine. We rely on the Cygwin libraries and tools to accomplish this. Visual Studio or mingw32 are as of this writing not yet supported. Also CORBA is not yet ported.

You can get it from http://www.cygwin.com and use the setup.exe script. Make sure you additionally install:
• From 'Devel': binutils, boost, boost-devel, cmake, cppunit, gcc4-g++, libncurses-devel, make, readline, subversion

The official RTT release does not yet include cygwin support. You need to download the RTT 1.6 sources in your cygwin environment and apply the patch in attachment:
 cd orocos-rtt; patch -p1 < orocos-rtt-cygwin.patch
The patch can be found here: https://www.fmtc.be/bugzilla/orocos/show_bug.cgi?id=605

### Build RTT

Create a build directory in the orocos-rtt directory (mkdir build) and run:
 cmake .. -DBOOST=/usr/include/boost-1_33_1
make
This is a slow process. If you have multiple CPU cores, use 'make -j2' or -jN with N cores. In case you want to change more option, type 'ccmake ..' between the cmake and make commands.