Data recording and replay utilities for the Lunar Autonomy Challenge. Built for MAPLE.
Either:
- Add this repository as a dependency to your project:
uv add https://github.com/Robaire/lac-data.git
- Install this repository into a virtual environment or your system:
pip install git+https://github.com/Robaire/lac-data.git
By default, the recorder will capture all rover state information and images from any active cameras when called.
This can generate a significant amount of data if called frequently and if multiple cameras are active.
A max file size can be specified that once reached, data collection stops and the archive is saved to the disk.
The default max is 10 gigabytes.
The agent will continue running after the recorder is stopped.
If you want to stop the agent, query is_done() and stop the agent as desired.
from lac_data import Recorder
# Create a recorder
# Will save information to "./simulation.lac" with a maximum file size of 5 GB
# A directory can be specified instead of an exact file
recorder = Recorder(agent, "simulation_data.lac", 5)
# (Optional) Set a description
recorder.description("And example of using the data recorder.")
# Pause data collection
# Calls to `record_all()` will not do anything
recorder.pause()
# Resume data collection
recorder.resume()
# Stop data collection and save to disk
# The recorder cannot be resumed after being stopped
recorder.stop()
# Check if the recording has stopped
# Caused by either a call to `recorder.stop()`
# or because the max file size was reached
recorder.is_done() -> bool# Create the recorder object
recorder = Recorder(agent)
# Record all sensor data and images
# These two functions are equivalent, only call one of them
recorder(frame_index, input_data)
recorder.record_all(frame_index, input_data)Given the large amount of data generated by images, it may be desirable to record camera data less frequently than the general rover sensor and state data.
Do not call these either of these two methods in conjunction with record_all() otherwise duplicate data will be recorded.
# Create the recorder object
recorder = Recorder(agent)
# Record rover sensor and state information that is generated by the agent
# This includes information like IMU data, ground truth, etc...
recorder.record_sensors(frame_index)
# Record all the currently enabled cameras
# Warning: Camera data is not available every frame, so check that input_data is not empty,
# otherwise nothing will be saved
if frame_index % 20 == 0: # Record images at 1 Hz
recorder.record_cameras(frame_index, input_data)An interface is provided to record custom data during the simulation. Data must be formatted as a python dictionary.
# Create the recorder object
recorder = Recorder(agent)
# Record one row of data
frame_index = 1
custom_data = {
"location": "earth"
"accel": 9.81,
}
recorder.record_custom(frame_index, "gravity", custom_data)
# Record another row of data
frame_index = 2
custom_data = {
"location": "moon"
"accel": 1.62,
}
recorder.record_custom(frame_index, "gravity", custom_data)This would produce a csv file in the archive named custom/gravity.csv with the following data.
frame, location, accel
1, earth, 9.81
2, moon, 1.62
Sample data and an example playback script are included in examples.
Two core classes, FrameDataReader and CameraDataReader are provided to access the numeric and image data from a .lac file.
An additional PlaybackAgent is provided to mock agent behavior synchronized to a .lac file.
The FrameDataReader provides direct access to pandas DataFrames for numerical data.
reader = FrameDataReader("./examples/example.lac")
reader.initial -> dict
reader.frames -> pd.DataFrame
reader.camera_frames -> dict[str, pd.DataFrame]
reader.custom_records -> dict[str, pd.DataFrame]The CameraDataReader provides access to camera specific numerical data and image data.
Images are provided as numpy arrays.
reader = CameraDataReader("./examples/example.lac")
reader.get_frame("FrontLeft", 20) -> dict
reader.get_image("FrontLeft", 20, "grayscale") -> np.ndarray
# Get a LAC style input_data dictionary
reader.input_data(20) -> dict PlaybackAgent mocks the functionality of the AutonomousAgent base class from the Lunar Autonomy Challenge.
The agent provides most of the core functionality from the AutonomousAgent and can be used as a drop in replacement for any functions the query the agent directly for data.
The PlaybackAgent also includes control functions to set the currently active frame from the data set, set_frame(), and to step to the next frame step_frame().
input_data() will provide an input_data dictionary normally provided by the simulator to the run_step() method of the AutonomousAgent.
# Create a playback agent
agent = PlaybackAgent("examples/example.lac")
frame = 1
done = False
while not done:
# Get some data from the agent
imu_data = agent.get_imu_data()
# Do something with the data
print(f"Frame {frame}: {imu_data}")
# Get input data from the cameras
input_data = agent.input_data()
# Check if we reached the end of the recording
done = agent.at_end()
# Step the agent to the next frame
frame = agent.step_frame()
# Jump to a specific frame
agent.set_frame(120)
# Get camera specific data:
print(f"BackLeft Camera Enabled? {agent.get_camera_state('BackLeft')}")Unless a user defined file name is provided, data is collected into a .lac file.
These are .tar.gz archives and can be extracted with tar -xzf <file_name>.lac if desired.
The internal structure of the archive is defined below.
<file_name>.lac/
├── metadata.toml
├── initial.toml
├── frames.csv
├── images/
│ └── <camera>/
│ ├── <camera>_frames.csv
│ ├── grayscale/
│ │ └── <camera>_grayscale_<frame>.png
│ └── semantic/
│ └── <camera>_semantic_<frame>.png
└── custom/
└── <record_name>.csv
