Skip to main content

Tuning

After you have written your software, you will most likely want to tune it. Tuning is the process of modifying parameters that your code uses to optimize for your given challenge. Consider our Camera module from the running example: we would like to tune the brightness and contrast so that the module optimally detects the track edges.

One of the key principles in the tuning process is to separate your compile-time configuration from your runtime configuration. As you might have expected, tuning parameters are a prime example of a runtime configuration. This means that you can tune parameters in your code, without the need to recompile. In the ASE labs, we go one step further by allowing you to change runtime parameters during execution, Over the Air (OTA). We will explore this in detail in this section.

Why you should avoid compile-time configuration

We know, it is tempting when building a new service: just introduce one more parameter in your code. You're sure it will be fine. For example, in our Camera module from the running example, we might do this:

int cameraBrightness = 50;
// ... the rest of your code

But the problem is, there will always be a new parameter to introuce and at some point, you will lose overview of all tuning parameters that exist. Besides:

  • You now need to recompile your entire service, even if you want to change just one parameter
    • This also holds for interpreted languages, you still need to modify code
  • Your teammates do not know of all tuning parameters that can be used, you need to create a convention that cannot be enforced by the compiler/interpreter
  • You cannot "swap" configurations. In the best case, you can backtrack your git commits but more likely you will need to document tuning parameters and edit them in code manually
  • You cannot build multiple configurations in parallel. The closest approach would be to uncomment lines, but this is not desired for obvious reasons:
// for use outside
int cameraBrightness = 50;

// uncomment if using LED lights
// cameraBrightness = 100;

// uncomment if in a dimly lit room
// cameraBrightness = 200;

// uncomment if in a really dark room
// cameraBrightness = 220;

// see the problem yet?

That being said, having your contribution fail due to invalid tuning parameters or related problems is not a valid excuse. On the other hand, using the tools provided by the ASE labs to incorporate tuning, will positively impact your contribution grade advice. We'll explore how this is done in the next section.

Runtime configurations using service.yaml files

Recall that we have introduced service.yaml files, which define outputs, dependencies and options for a service. They serve as the blueprint for a given service: in just a glance you can understand how a service behaves, where data is outputted and which options are available for tuning. Let's consider our Camera module service.yaml again:

mod-Camera/service.yaml
# Service identifying information
name: camera
description: camera module to convert RGB images to an array of track edge X,Y-coordinates

# No dependencies, this is the first service in the pipeline
dependencies: []

# Output streams by the Camera service
outputs:
- name: trackdata
address: tcp://localhost:5050
- name: metrics
address: ipc://dev/metrics

# Options that can be accessed during runtime and tuned Over the Air (OTA) when applicable
options:
# the camera device to use, coresponding to /dev/video1 in this case
- name: camera-index
type: int
mutable: false # don't allow OTA changes
default: 1
# brightness to capture images with
- name: brightness
type: int
mutable: true # allow tuning the brightness OTA
default: 50
# contrast to capture images with
- name: contrast
type: int
mutable: true # allow tuning the contrast OTA
default: 20

Let's focus on the options part of this YAML file. You should already notice how this houses all runtime parameters. Because the service.yaml file is read at the start of a service run, you can modify configurations here and try them without recompiling your code.

info

This only applies if you use either pkg-ServiceRunner when building your service in Golang, or the mod-ServiceWrapper binary for any other language. This is highly recommended but not required.

Every "configuration field" has four properties:

  • name (string) : this is the name of the tuning parameter. It is case-sensitive and referenced in your code. Names should be unique
  • type ('int' | 'float' | 'string'): the data type of the primitive. Supported types are int, float and string. The value will be parsed according to this type
  • mutable (boolean): whether or not this value can be mutated during service execution
  • default (int | float | string | null): the default value of this parameter. When mutable is set to false, this will be the actual value of the parameter during execution.

With this in mind, try to understand why the following two statements hold:

  1. When setting mutable to false, it is forbidden to omit a default value
  2. When setting mutable to true, it is allowed to omit a default value

Recall that (unless explicitly stated), a module will look for its service.yaml file in the current working directory. In all services provided by the ASE framework, you will hence see the following file structure:

mod-Camera
├── mod-Camera
├── service.yaml

When starting the mod-Camera module, it will thus use the service.yaml file:

# no service.yaml was specified explicitly, so we just use service.yaml
./mod-Camera

But this behavior can be overriden by explicitly passing the -service-yaml flag when starting your service:

./mod-Camera -service-yaml service.yaml

This allows you to have multiple configurations in parallel, which you can just swap as needed, depending on the environment you are running your code in. Consider different brightness configurations, depending on the lighting situation. We might now tackle this with the following file structure:

mod-Camera
├── mod-Camera
├── service.yaml
├── brightroom.yaml
├── dimlitroom.yaml
...

You can just start the different configurations by switching flags:

# we are in a bright room
./mod-Camera -service-yaml brightroom.yaml
# we are in a dim lit room
./mod-Camera -service-yaml dimlitroom.yaml

Naturally, it is always better to make your service as resillient as possible and writing calibration software to help in different lightning enviornments would be the best option. But when this is not feasible, using runtime tunings using service.yaml files is the second-best option.

Acessing tuning parameters at runtime

When the options are defined in your service YAML, you will need to read them into your code at runtime. The ASE framework tackles this for you, depending on which option you choose to run your binaries.

mod-ServiceWrapper will inject all values and tuning parameters into your shell environment. On this page you can read about how this is done and which values you can read at runtime.