Rock

the Robot Construction Kit

... in oroGen

The first step, when using the transformer, is describing which transformations are needed your component’s processing. For instance, in the case of the laser filter, one would need the transformation between its input and the body that it has to filter out. One would therefore declare that the component needs a input-2-body transformation with:

task_context "Task" do
  input_port "lidar_samples", "/base/samples/LaserScan"
  output_port "filtered_samples", "/base/samples/LaserScan"

  transformer do
    transform "input", "body"
  end

  port_driven
end

A very important bit is that the names used in this declaration are local to the component: the “input” frame does not need to be an actual frame on the robot. It will be mapped to an actual frame at runtime.

In the C++ component implementation, the transformations are made available through an attribute object called _from_2_to (e.g. _input2body in the example above). This object has a “get(time, result, interpolate)” method that is used as follows:

Eigen::Affine3d transformation;
if (!_input2body.get(time, transformation, true))
    return;

The method returns true if the requested transformation is available and false otherwise. The last argument tells the transformer whether the caller is interested in the last transformation available or in a transformation interpolated up until \c time.

In order to generate the dynamic transformations “right”, the transformer must process its input “in order”. A transformer is therefore, behind the scenes, a stream aligner. What is means is that, instead of only specifying transformations, one usually also specifies which ports are going to be processed using the transformations, and that one must give a default max latency:

task_context "Task" do
  input_port "lidar_samples", "/base/samples/LaserScan"
  output_port "filtered_samples", "/base/samples/LaserScan"

  transformer do
    transform "input", "body"
    align_port "lidar_samples"
    max_latency 0.5
  end
end

In much the same way than with the stream aligner, the processing is then done in C++ callbacks of the form port_nameTransformerCallback(timestamp, sample) that are generated by oroGen

void lidar_samplesTransformerCallback(base::Time const& timestamp,
    base::samples::LaserScan const& sample)
{
  // process the sample here. One usually gets the transformations in these
  // callbacks using timestamp, e.g.:
  Eigen::Affine3d tf;
  if (!_input2body.get(timestamp, tf, false))
    return;
}

As usual with oroGen, if you add or remove aligned ports after the first code generation, you must update the files in tasks/ yourself, using if needed the templates generated in templates/tasks/

Latency Considerations

There is currently no way to tell a transformer which transformations are needed with interpolation and without interpolation (this is [ticket #98]({rock_ticket: “98”}). In effect, it means that all the dynamic transformation streams are configured with a period of zero. Or, in other words, that the transformer must wait to have a sample both before and after the processed samples before calling any callbacks.