Logging

SCRIMMAGE provides several tools for logging and playback of data from a simulation. A user can specify a SCRIMMAGE root logging directory in a SCRIMMAGE mission file with the log_dir XML tag. By default, the root logging directory is set to ~/.scrimmage/logs. Every time a new simulation is run, a new directory is created under the root logging directory based on the current time stamp (e.g., YYYY-MM-DD_HH-MM-SS). A SCRIMMAGE plugin can gain access to the current simulation’s logging directory through the MissionParse class’ log_dir() method and access to the root log directory though the root_log_dir() method. For example, from within a plugin, the log directories are accessed through the following statements:

1
2
3
4
5
6
7
8
//Need this include
#include <scrimmage/parse/MissionParse.h>

// Get the current simulation's log directory (time-stamped directory)
std::string mission_log_dir = parent_->mp()->log_dir();

// Get the root log directory
std::string root_log_dir = parent_->mp()->root_log_dir();

The types of files written to the current simulation’s logging directory depends on the output_type XML tags defined in Mission XML Tag Definitions. Generally, this consists of frames.bin (full state history of all entities in simulation), contact_visual.bin (entity visualization information: meshes, colors, etc.), utm_terrain.bin (which terrain was loaded), shapes.bin (which shapes were drawn during the simulation), summary.csv (output from metrics plugins), log.txt (contains mission pseudorandom seed value), mission.xml (the mission file that was executed), and runtime_seconds.txt (the runtime for the simulation).

Protocol Buffer Logging / Visualization

The plot_3d_fr.py python script can be used to plot 2D and 3D trajectories on entities from simulations. Plot the 3D trajectories by pointing to a directory containing a frames.bin file.

1
$ ./plot_3d_fr.py ~/.scrimmage/logs/latest

The 2D trajectories can be plotted with the --2d flag.

1
$ ./plot_3d_fr.py ~/.scrimmage/logs/latest --2d

CSV File Logging

The CSV class can be used in any plugin to log custom data in a comma separated format. The CSV class can also be used to read data from a comma separated file. For a full example of using the CSV class, see the TrajectoryRecordPlayback autonomy plugin. The following code examples briefly describe the CSV classes API.

CSV File Writing

Include the CSV class:

1
#include <scrimmage/common/CSV.h>

Create a file to write comma separated data:

1
2
3
4
5
6
7
8
9
scrimmage::CSV csv;

// Write the CSV file to the root log directory
std::string filename = parent_->mp()->root_log_dir() + "/my.csv";

// Create the file
if (!csv.open_output(filename)) {
   std::cout << "Couldn't create output file" << endl;
}

Specify the names for the column headers (t, x, y, z):

1
csv.set_column_headers("t, x, y, z")

Write data to the csv file:

1
2
3
4
5
csv.append(sc::CSV::Pairs{
   {"t", t},
   {"x", state_->pos()(0)},
   {"y", state_->pos()(1)},
   {"z", state_->pos()(2)}});

Close the output file when you are done writing data:

1
csv.close_output();

CSV File Reading

When reading in a CSV file, the CSV class will use the header column names for indexing columns. If the CSV file doesn’t have header columns, the CSV file can be indexed with integers.

First, read the CSV file:

1
2
3
4
if (!csv.read_csv(filename)) {
   cout << "Failed to read CSV file: " << filename
        << endl;
}

Print out the data from the CSV file:

1
2
3
4
5
6
for (int r = 0; r < csv.rows(); r++) {
   cout << "t: " << csv.at(r, "t") << endl;
   cout << "x: " << csv.at(r, "x") << endl;
   cout << "y: " << csv.at(r, "y") << endl;
   cout << "z: " << csv.at(r, "z") << endl;
}

Real-time Plotting from CSV

You can plot data from CSV files with the csv-plot script in the scrimmage/scripts directory. csv-plot can plot previously created CSV files or it can create real-time plots while SCRIMMAGE is running. For example, to plot the pitch, pitch_rate, yaw, and yaw_rate columns from a CSV file called my.csv, run the following command:

1
$ ./csv-plot.py -c my.csv -y pitch pitch_rate

The column headers can be assigned to different subplots by running the following command:

1
$ ./csv-plot.py -c my.csv -y pitch pitch_rate:1

The :X after the column header name specifies which subplot to use. If no subplot is given, the data is plotted on the first subplot.

The y-axis label of each subplot can be set by prefacing the column header name with the name of the subplot and an equal sign. For example to add a name to our example plots we would use:

1
$ ./csv-plot.py -c my.csv -y "pitch (deg)"=pitch "pitch rate (deg/s)"=pitch_rate:1

If no y-axis label is provided for a subplot, the default will be a list of the column headers used to generate that subplot.

Additionally, python expressions can be used to perform operations on the data before plotting. For example, to plot the error between measured pitch and actual pitch, run the following command:

1
$ ./csv-plot.py -c my.csv -y {measured_pitch - pitch}

To create a 3D plot, use the -x, -y, and -z flags:

1
$ ./csv-plot.py -c my.csv -x x -y y -z z -e

The -e flag tells the plotter to ensure that the x, y, and z axes have equal scales.

By default, the script looks for the CSV file located in the ~/.scrimmage/logs/latest directory, but the -l flag can be used to change the directory. The csv-plot.py script can also detect when SCRIMMAGE links the latest directory to the newest log directory and it will automatically start plotting data from the newest CSV file. Thus, during development, the plugin developer can leave the csv-plot.py script constantly running, while restarting SCRIMMAGE.

The script can be passed a -s or --static flag to disable automatic plotting of new data.

Tips for Naming CSV Files

Since your simulation may consist of multiple entities running the same plugins, which in turn, could be writing CSV files, it’s important to ensure that each entity writes to a uniquely named CSV file. It is advised the plugin developer includes the entity’s ID in the filename. For example, to create a CSV file that contains the entity’s ID and is written to the current simulation’s log directory, use the following code:

1
2
3
std::string csv_filename = parent_->mp()->log_dir() + "/"
                           + std::to_string(parent_->id().id())
                           + "-my.csv"