Rock

the Robot Construction Kit

Refining models - a leader/follower system

The result of this tutorial can be found in bundles/tutorials_scripts if you followed the instructions at the bottom of this page

Abstract

In this tutorial, you will learn one of the most important feature for model-based systems: how generic models can be refined. In object-oriented programming, this is more or less equivalent to subclassing.

This tutorial will guide through the process of:

  • changing the RockControl composition so that it can be adapted to the requirements of more complex motion control generators
  • how to transform a composition into a service, i.e. how to add ports to a composition

The leader-follower system

This tutorial will deploy a network in which:

  • one rock, controlled by the random motion generator, is a leader
  • another rock is following the first rock

The motion generator component for the follower is tut_follower::Task, included in Rock’s rock.tutorial package set. It requires an additional sensor component, tut_sensor::Task, that ‘senses’ the bearing and distance to the target.

The goal will be to reuse the same base model ‘Tutorials::RockControl’ for both deployments. The idea behind reusing in this case is:

  • you might have a diagnostics routine that is able to monitor the movement of a rock regardless of what controls it. Attaching that diagnostics routine to the RockControl composition will be sufficient, as it will apply to all refinements of this model.
  • it simplifies the usage of the compositions, as one only has to remember the name of a composition (Tutorials::RockControl) and give an intent (‘run the composition with X or Y’) instead of having to remember N composition names.

Reusing Models

The first thing that we will learn in this page is how to reuse common models. This is critical: one does not want to recreate every single data service or composition each time a new robot needs to be integrated. Moreover, it gives functional definition of generic complex behaviours, i.e. “how to run this particular SLAM on a four-wheeled skid-steered robot” can very well become a (reusable / refinable) definition.

One bundle on which every other bundle depends by default is the Rock bundle. For simplicity reasons, we removed this dependency in the starting tutorial. Let’s undo this now, and check that we indeed are using the rock bundle using bundle-info:

# mv config/bundle-yml.bak config/bundle.yml
# bundle-info
Bundles[INFO]: Active bundles: tutorial, rock

This is going to break the joystick ! The reason is contained in the explanations on devices.

For our purposes here, we will want to have a data service that generates a pose (in particular, the poses of both our leader and follower). Let’s have a look at what is available. Open the Syskit model browser

# syskit browse

And look into the ‘Base’ entry. You will find there a Base::PoseSrv service that fits:

Sykit browse

At the top of the service’s description, note the

require 'rock/models/blueprints/pose'

This is the line that needs to be used to make this particular data service available (when required).

Specializations

In Syskit, refinements of a composition model can be created with specializations. Specializations can be seen as conditional application of changes on compositions.

For instance, the following specialization specifies that when using Tutorials::RockControl, in cases where the command generator is a TutFollower::Task, one child and two connections must be added to the composition. Copy scripts/03_defines.rb to scripts/04_leader_follower.rb, declare the TutFollower::Task to provide the CommandGeneratorSrv service and add the code for the specialization after the declaration of the Tutorials::RockControl composition.

# Load common data services from the Rock bundle
require 'rock/models/blueprints/pose'
using_task_library 'tut_follower'
using_task_library 'tut_sensor'
TutFollower::Task.provides Tutorials::CommandGeneratorSrv, :as => 'cmd'
...

# This is the syntax to add specializations to existing composition models (i.e.
# after the class ... end definition)
Tutorials::RockControl.specialize Tutorials::RockControl.cmd_child => TutFollower::Task do
  # It will need some other task/composition to provide the target pose
  add Base::PoseSrv, :as => "target_pose"
  # And the sensor processing to compute the bearing/distance to target
  add TutSensor::Task, :as => 'sensor'

  # We must specify the port on the sensor child, since
  # there are two matches
  #
  # The rock child is already defined in the 'base' Tutorials::RockControl
  # composition
  target_pose_child.connect_to sensor_child.target_frame_port
  rock_child.connect_to sensor_child.local_frame_port
  sensor_child.connect_to cmd_child
end

We have to make the TutFollower and the TutSensor tasks’ oroGen projects available, and thus added the following lines to the includes at the top of the file:

using_task_library 'tut_follower'
using_task_library 'tut_sensor'

Once the specialization hase been added, it allows you to use the same composition for the random controller ‘TutBrownian::Task’ and the follower controller ‘TutFollower::Task’, without having to care about the details (or at least, not yet)

# Selects the "standard" composition
define 'random', Tutorials::RockControl.use(TutBrownian::Task)
# Selects the "specialized" composition
define 'follower', Tutorials::RockControl.use(TutFollower::Task)

However, for the second one to be valid, we need a provider for the Base::PoseSrv service: our ‘leader’ composition- which is a Tutorials::RockControl composition itself. For that to be possible, we need the ‘leader’ composition to provide the Base::PoseSrv service.

We therefore need:

  • the composition to have an output port of type base/samples/RigidBodyState
  • a declaration of the fact that the composition does provide the data service

Adding ports to compositions

Compositions cannot have their own ports. They can only export ports from their children. In our case, the rock’s pose is provided by the RockTutorialControl task’s pose_samples port. We will now change the original composition model so that it exports this port and we declare the composition as providing a Base::PoseSrv

module Tutorials
  class RockControl < Syskit::Composition
    ...
    export rock_child.pose_samples_port
    provides Base::PoseSrv, :as => 'pose'
  end
end

It is now possible to define our leader-follower system:

define 'leader',
  Tutorials::RockControl.use(TutBrownian::Task)
define 'follower',
  Tutorials::RockControl.use(TutFollower::Task, leader_def)

Try out the current definition:

# syskit instanciate scripts/04_leader_follower.rb follower_def!
= cannot deploy the following tasks
TutSensor::Task:0x4a92050{conf => [default]}[]
  child sensor of Tutorials::RockControl/[cmd.is_a?(TutFollower::Task)]:0x5bfa298{conf => [default]}[]
TutFollower::Task:0x5c05030{conf => [default]}[]
  child cmd of Tutorials::RockControl/[cmd.is_a?(TutFollower::Task)]:0x5bfa298{conf => [default]}[]
RockTutorial::RockTutorialControl:0x5d36d28{conf => [default]}[]
  child rock of Tutorials::RockControl/[cmd.is_a?(TutFollower::Task)]:0x5bfa298{conf => [default]}[]
TutSensor::Task:0x4a92050{conf => [default]}[]: no deployments available
TutFollower::Task:0x5c05030{conf => [default]}[]: no deployments available
RockTutorial::RockTutorialControl:0x5d36d28{conf => [default]}[]: some deployments exist, but they are already used in this network
  task rock_tutorial from deployment Deployments::RockTutorial on localhost
    already used by RockTutorial::RockTutorialControl:0x5df4f08{orocos_name => rock_tutorial, conf => [default]}[]: child rock of Tutorials::RockControl:0x5c8f0a0{conf => [default]}[]

Ah … yes, we need some extra deployments. The tut_deployment project contains all we need so let’s simply replace all the use_deployment lines:

Syskit.conf.use_deployment 'joystick'
Syskit.conf.use_deployment 'rock_tutorial'
Syskit.conf.use_deployment 'brownian'

by

Syskit.conf.use_deployments_from 'tut_deployment'

and re-run syskit instanciate.

cannot deploy the following tasks
RockTutorial::RockTutorialControl:0x50dcb90{conf => [default]}[]
  child rock of Tutorials::RockControl:0x3c00028{conf => [default]}[]
RockTutorial::RockTutorialControl:0x515dad8{conf => [default]}[]
  child rock of Tutorials::RockControl/[cmd.is_a?(TutFollower::Task)]:0x50128e0{conf => [default]}[]
RockTutorial::RockTutorialControl:0x50dcb90{conf => [default]}[]: multiple possible deployments, choose one with #use_deployments
  task rock_tutorial from deployment rock_tutorial defined in tut_deployment on localhost
  task target from deployment target defined in tut_deployment on localhost
  task follower from deployment follower defined in tut_deployment on localhost
RockTutorial::RockTutorialControl:0x515dad8{conf => [default]}[]: multiple possible deployments, choose one with #use_deployments
  task rock_tutorial from deployment rock_tutorial defined in tut_deployment on localhost
  task target from deployment target defined in tut_deployment on localhost
  task follower from deployment follower defined in tut_deployment on localhost

This time, the issue is different: there are more than one deployment available for a particular task, and we therefore need to tell Syskit which one should be used. This is done by providing a regular expression that matches the deployment names that are preferred for a particular subsystem. Let’s do this:

define 'joystick',    Tutorials::RockControl.
    use(Controldev::JoystickTask)
define 'random',      Tutorials::RockControl.
    use(TutBrownian::Task)
define 'random_slow', Tutorials::RockControl.
    use(TutBrownian::Task.with_conf('default', 'slow'))
define 'random_slow2', Tutorials::RockControl.
    use(TutBrownian::Task).with_conf('slow')
define 'leader', Tutorials::RockControl.
    use(TutBrownian::Task).
    use_deployments(/target/)
define 'follower', Tutorials::RockControl.
    use(TutFollower::Task, leader_def).
    use_deployments(/follower/)

Finally you can run and display the rocks using rock-display:

# syskit run scripts/04_leader_follower.rb follower_def!

Summary

In this tutorial, you:

  • learned how to use specializations to refine the compositon models
  • learned how to “create” ports on a composition

What’s left is to describe how all the bits and pieces are organized within a bundle folder structure. This is the subject of the next and last tutorial.