Rock

the Robot Construction Kit

Setting Up Data Logging

This page presents the most essential step for data analyis, which is showing how to save the data from ports into log files. The next page will present how to read that data to analyze it.

The setup

The following elements play a major role for logging in oroGen projects:

  • a marshaller that is generated by oroGen,
  • a generic, high-performance, logging component that is able to log any type that orogen understands.
  • finally, a command line tool to export the generated data, and a Ruby library that allows to manipulate the data in log files.

The two most important features of that logging setup are that:

  • it is very low overhead (see below), and
  • generated log files are self-contained

That means that, once a log file has been generated, it can be read and analyzed even though you know absolutely nothing about the software that generated it.

Making oroGen deployments logging-ready

If you are using manually created deployments, the only step needed to make an oroGen deployment “ready to log” is to add an add_default_logger statement to your deployment specification. For instance:

deployment "test_imu" do
  task "imu", "imu::Task"
  add_default_logger
end

If you are using the model_name => task_name syntax in orocos.rb, as for instance

Orocos.run 'xsens_imu::Task' => 'imu' do
end

then logging is already available.

Logging all output ports using Orocos.rb

If you are starting up your system using orocos.rb, the simplest way to start logging is to add

Orocos.log_all

after all the deployments have been started. It will setup each deployment’s logger to log all output ports of the deployment. Additionally, it will setup a log file storing the task’s property values and their changes, for the properties that are accessed from the Ruby process.

Port logs are stored in a “name.index.log” file, where name is the deployment’s name and index is incremented to avoid overwriting old log files (i.e. name.0.log by default, but if name.0.log exists, then name.1.log and so on). A complete example would be:

Property logs are all stored in a single file called “properties.index.log”.

How to extract data from the log files is the subject of the following documentation pages.

# Start the test_imu deployment
Orocos.run 'xsens_imu::Task' => 'imu' do
  # Set up logging
  Orocos.log_all

  # Start the IMU task
  task = Orocos.name_service.get 'imu'
  task.start

  # Poll the task's states every 100 ms and report state changes.
  Orocos.watch(task)
end

If you don’t want to log some types, then use the exclude_types option. That option takes a regular expression, all ports whose type matches that regex will not be logged. For instance:

Orocos.log_all :exclude_types => /can\/Message/

will log all ports whose typename does not contain “can/Message”

Hand-made logging using orocos.rb

The logfile format is based on the following schema:

  • the logger is multiplexing data streams into a single file
  • the logger component associates each of its input ports with a single stream in the file
  • data coming on one of the input ports is saved into the associated stream

So, to log a port, you basically need to

  • ask the logger to create a new stream along with an associated (input) port by using the createPort method
  • connect the output port you want to log to this new logger port.

With orocos.rb, this is done with:

Orocos.run 'xsens_imu::Task' => 'imu' do
  logger = Orocos.name_service.get 'imu_Logger'
  imu    = Orocos.name_service.get 'imu'

  logger.file = "logfile.log"

  # Add a stream in the log file named "imu.orientation_samples", and connect
  # the imu port to it with a buffer of size 200
  logger.log(imu.orientation_samples, 200)

  # Another way is to use the raw API on the logger task. createLoggingPort
  # creates the stream on the log file as well ass the logger input. You just
  # have to connect the output port to the logger's input
  logger.createLoggingPort('orientations', '/base/samples/RigidBodyState', [])
  imu.orientation_samples.connect_to(logger.orientations, :type => :buffer, :size => 200)

  imu.configure
  imu.start

  # Don't forget to start your logger
  logger.start
end