Designing an experiment

Designing an experiment#

In this tutorials, you are learn how to design a navigation experiment.

Step by step, we will describe the experiment in YAML. This description can then be processed by navground to collect experimental data.

Scenario#

First, we need to decide in which kind of navigation scenario we are going to experiment. Some scenarios are already predefined. We can pick one of them, let’s say Corridor, or we can define our own scenario, which will be the subject of a different guide or tutorial.

# Experiment configuration
scenario:
  type: Corridor

Scenarios may have specific configuration variables, which we list using info

$ navground info --scenario Corridor --properties

Scenarios
---------

Corridor
  add_safety_to_agent_margin: 1 [bool]
  agent_margin: 0.1 [float]
  length: 10 [float]
  width: 1 [float]

Note

If you have installed navground from pip and don’t have the navground command, use navground_py instead.

Let’s make the corridor wider and shorter:

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2

Agents#

We now populate the scenario with groups of agents. Agents in the same group share the same configuration. To keep it simple, let’s add a single group of 10 agents.

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10

All configuration variables represent samplings: when the scenario is initialized, a sample from that sampling is used to initialize the agent. For example, let’s say we want the agents to have individual random radii picked uniformly between 0.5 and 1.0 meters, a common type picked randomly as either "type1" or "type2", and a control period of 0.1 s:

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: uniform
        from: 0.5
        to: 1.0
      control_step:
        sampler: constant
        value: 0.1
      type:
        sampler: choice
        values: ["type_1", "type_2"]
        once: true

Note

Note how we specify once: true for type to sample once per run and assign the same type value to all agents in the group; without it, values (e.g., radius or type) would sampled for each individual agent in the group. The scenario fields (e.g., radius, length and width) and the group number are always sampled per run instead.

To avoid unnecessary verbose configurations, navground supports more compact notations for some distributions, like just providing the value for constant distributions, therefore we can simplify as

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: uniform
        from: 0.5
        to: 1.0
      control_step: 0.1
      type:
        sampler: choice
        values: ["type_1", "type_2"]
        once: true

To finalize the configuration of agents, we need to fix their kinematics, behaviors, tasks, state estimations and initial poses. In fact, some may be already configured by the scenario. For instance, Corridor initializes agents at random poses inside the corridor, therefore there is no need to configure their initial poses separately. Similarly, Corridor ask each agents to travel along the corridor, therefore we can skip task. We still need to set the kinematics (here, omni-directional), behavior (here, HL) and state estimation (here, with a maximal range of 4 meters, i.e. half of the corridor length):

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: uniform
        from: 0.5
        to: 1.0
      type:
        sampler: choice
        values: ["type_1", "type_2"]
        once: true
      control_step: 0.1
      behavior:
        type: HL
      kinematics:
        type: Omni
        max_speed: 1.0
        max_angular_speed: 1.0
      state_estimation:
        type: Bounded
        range: 4.0

We can try to sample a world from such a scenario. Save all but the root element scenario in my_scenario.yaml and run

$ navground sample --seed 0 my_scenario.yaml

obstacles:
  []
walls:
  - line:
    -
      - -8
      - 0
    -
      - 16
      - 0
    uid: 10
  - line:
    -
      - -8
      - 2
    -
      - 16
      - 2
    uid: 11
agents:
  - behavior:
      type: HL
      aperture: 3.14159274
      barrier_angle: 1.57079637
      epsilon: 0
      eta: 0.5
      resolution: 101
      tau: 0.125
      optimal_speed: 1
      optimal_angular_speed: 1
      rotation_tau: 0.5
      safety_margin: 0
      horizon: 5
      radius: 0.774406791
      heading: idle
      kinematics:
        type: Omni
        max_speed: 1
        max_angular_speed: 1
      social_margin:
        modulation:
          type: constant
        default: 0
    kinematics:
      type: Omni
      max_speed: 1
      max_angular_speed: 1
    state_estimation:
      type: Bounded
      range: 4
    position:
      - 2.43710041
      - 0.875406802
    orientation: 0
    velocity:
      - 0
      - 0
    angular_speed: 0
    radius: 0.774406791
    control_period: 0
    type: type_2
    id: 0
    uid: 0
  - behavior:
      type: HL
      aperture: 3.14159274
      barrier_angle: 1.57079637
      epsilon: 0
      eta: 0.5
      resolution: 101
      tau: 0.125
      optimal_speed: 1
      optimal_angular_speed: 1
      rotation_tau: 0.5
      safety_margin: 0
      horizon: 5
      radius: 0.857594669
      heading: idle
      kinematics:
        type: Omni
        max_speed: 1
        max_angular_speed: 1
      social_margin:
        modulation:
          type: constant
        default: 0
    kinematics:
      type: Omni
      max_speed: 1
      max_angular_speed: 1
    state_estimation:
      type: Bounded
      range: 4
    position:
      - 1.05219924
      - 0.95859468
    orientation: 3.14159274
    velocity:
      - 0
      - 0
    angular_speed: 0
    radius: 0.857594669
    control_period: 0
    type: type_2
    id: 0
    uid: 1
 [other 8 agents omitted]

Metrics#

What should we record? Let’s say we want to plot the agents trajectories … then we need to record their poses. We may want to record collisions too to perform some safety assessment and the initial state of the world. We should also set where to save data, for instance to the current directory.

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: uniform
        from: 0.5
        to: 1.0
      control_step: 0.1
      type:
        sampler: choice
        values: ["type_1", "type_2"]
        once: true
      behavior:
        type: HL
      kinematics:
        type: Omni
        max_speed: 1.0
        max_angular_speed: 1.0
      state_estimation:
        type: Bounded
        range: 4.0
save_directory: '.'
record_poses: true
record_colllisions: true
record_world: true

Warning

Recordings are disabled by default to be as efficient as possible. You need to enabled the data you want to record.

Runs#

Finally, we need to decide how many runs to execute and how long they are. Each run will be initialized from the same scenario. If the scenario has no randomization, all runs will result in the same results. In our case, Corridor does have random pose initialization and we also configured random radii, therefore each run will result in different trajectories (and possibly different number of collisions).

Let’s say that we are good with a statistics collected from 100 runs, each 20 second long (i.e., with 200 steps of 0.1 s).

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: uniform
        from: 0.5
        to: 1.0
      control_step: 0.1
      type:
        sampler: choice
        values: ["type_1", "type_2"]
        once: true
      behavior:
        type: HL
      kinematics:
        type: Omni
        max_speed: 1.0
        max_angular_speed: 1.0
      state_estimation:
        type: Bounded
        range: 4.0
save_directory: '.'
record_poses: true
record_colllisions: true
record_world: true
runs: 100
steps: 200
time_step: 0.1

Now we are ready to put the configuration in a file like my_config.yaml and to make navground execute the experiment.

$ navground run my_config.yaml

Experiment done
Duration: 1.64725 s
Saved to: "./experiment_2023-07-25_13-32-22/data.h5"

Sampling per run#

If we want to perform an experiment where we measure the impact of different group radii, we should switch to a radius sampler that sample once per run instead of once per agent, by specifying once: true. For instance, this experiment

# Experiment configuration
scenario:
  type: Corridor
  length: 8
  width: 2
  groups:
    - number: 10
      radius:
        sampler: regular
        from: 0.5
        to: 1.0
        number: 11
        once: true
      control_step: 0.1
      behavior:
        type: HL
      kinematics:
        type: Omni
        max_speed: 1.0
        max_angular_speed: 1.0
      state_estimation:
        type: Bounded
        range: 4.0
save_directory: '.'
record_poses: true
record_colllisions: true
record_world: true
runs: 11
steps: 200
time_step: 0.1

runs 11 times, assigning radius=0.5 to all agent the first time, radius=0.6 the second time and so on until radius=1.0 the last time.