Module reference · Cross-cutting
Data Capture & Logging
A simple study record. Choose the inputs, commands, state values, and events your study needs, record them with timestamps, and write them to the format you want to analyse.
What
Data Capture & Logging is where your study keeps a record of what happened. A capture implementation listens to the module outputs it needs and calls Record when a value or event should be saved.
Metric definitions describe the measures you report: their name, unit, calculation, thresholds, aggregation, and start and stop events. You do not need to record every signal or calculate the measure during the trial.
Why
A study measure often combines signals from different parts of the apparatus. For example, completion time needs a start event and an end event, while a pause measure may combine operator input with the configured uplink delay.
Keeping the raw observations and the metric definition together lets you inspect how a result was produced and recompute it later if needed.
How
Data Capture & Logging listens to the outputs and trial events a study selects, records them with timestamps, and turns the recorded data into analysis-ready metrics.
Record- Add a capture implementation and choose the outputs your study needs.
- Subscribe to those outputs in
OnCaptureInitialized. - Record current-tick values in
OnCaptureStepand the terminal event inOnTrialEnded. - Add a metric definition for each measure you plan to report.
You can start with one or two signals and add more as your study requires.
Boundary
Owns
- Selecting the signals, package events, state values, and trial events a study records.
- Recording timestamped observations and defining the study's output format.
- Storing metric definitions and coordinating the study capture implementation.
Does not own
- Producing inputs, commands, communication packages, assistance, or vehicle state.
- Controlling the teleoperation loop or deciding when a trial ends.
- Changing the apparatus it observes or silently deciding which signals a study must record.
Contract
Study code derives from DataCaptureImplementation. Most implementations use OnCaptureInitialized to subscribe, one capture hook for per-tick values, and OnTrialEnded for the final event. Call Record with a stable data id, value, timestamp, and unit.
public abstract class DataCaptureImplementation : MonoBehaviour
{
protected virtual void OnCaptureInitialized()
{
}
protected virtual void OnCaptureStep(
double timestampSeconds)
{
}
protected virtual void OnTrialEnded(
TrialEndedEvent trialEndedEvent)
{
}
protected void Record(
string dataId,
object value,
double timestampSeconds,
string unit = null)
{
}
}OnCaptureInitializedis where the implementation subscribes to selected module outputs and prepares its output.OnCaptureSteprecords values that belong to the current tick.OnTrialEndedrecords the terminal event and closes or flushes the study output.Recordcreates a timestamped observation owned by the capture module.
Examples
These examples show how TrialLogger records commands, vehicle state, and trial outcomes.
OnCaptureInitialized subscribes to the command-mapping output. That output invokes CaptureMappedCommand, which records the resulting wheel command.
public sealed partial class TrialLogger : DataCaptureImplementation
{
protected override void OnCaptureInitialized()
{
if (commandMapping) commandMapping.OutputProduced += CaptureMappedCommand;
}
private void CaptureMappedCommand(WheelVelocityCommand command)
{
EnsureTrialStarted(command.TimestampSeconds);
Record(
"mapped-command",
command,
command.TimestampSeconds,
"m/s");
AppendEvent(
command.TimestampSeconds,
"mapped-command",
EveryMoveStreams.Command,
command.SourceTimestampSeconds,
FormattableString.Invariant(
$"requested_left={command.RequestedLeft:F6};requested_right={command.RequestedRight:F6};dt={command.DeltaTime:F6}"),
"m/s,s");
}
}The authoritative state is sampled at the end of each kernel tick. The record keeps the capture timestamp and the state's own timestamp, allowing later analysis to distinguish when a value was observed from when it was produced.
public sealed partial class TrialLogger : DataCaptureImplementation
{
protected override void OnCaptureStep(double timestamp)
{
EnsureTrialStarted(timestamp);
DrainPackageEvents(timestamp);
if (!vehicle || !(vehicle.LatestState is TurtleBotState state))
return;
Record(
"authoritative-pose",
state,
timestamp);
AppendEvent(
timestamp,
"authoritative-state",
"vehicle-state",
state.TimestampSeconds,
FormattableString.Invariant(
$"x={state.Position.x:F6};z={state.Position.z:F6};heading={state.Heading:F6};left_velocity={state.LeftVelocity:F6};right_velocity={state.RightVelocity:F6}"),
"m,rad,m/s");
}
}The terminal event is recorded by the study implementation. The metric definitions below use the recorded event names as their start and stop boundaries.
public sealed partial class TrialLogger : DataCaptureImplementation
{
protected override void OnTrialEnded(TrialEndedEvent ended)
{
EnsureTrialStarted(ended.TimestampSeconds);
Record(
EveryMoveStreams.TrialEnded,
ended,
ended.TimestampSeconds);
AppendEvent(
ended.TimestampSeconds,
EveryMoveStreams.TrialEnded,
EveryMoveStreams.TrialEnded,
ended.TimestampSeconds,
$"outcome={ended.Outcome};reason={ended.Reason};criterion={ended.CriterionName}",
string.Empty);
FlushCsv();
Unsubscribe();
}
}public static class EveryMoveMetricDefinitions
{
public static DataMetricDefinition[] Create()
{
return new[]
{
new DataMetricDefinition(
"completion-time",
"s",
"Timestamp of target-region-entry minus trial-start timestamp.",
"Capped by the 300 s elapsed-time criterion.",
"Report per condition and aggregate across participants using the declared analysis plan.",
"trial-start",
"target-region-entry")
};
}
}Config
For a first study, configure one capture implementation and add metric definitions only for measures you will report. Use the Unity Inspector to choose the implementation, its inputs, and its output settings.
- Add one
DataCaptureAndLoggingModuleto theTeleroboticsAgentGameObject. - Add a study capture component derived from
DataCaptureImplementation, such asTrialLogger, to the same GameObject. - Assign the capture component in
Capture Implementations. - Assign the study's
DataMetricDefinitionvalues inMetric Definitions. - Configure the capture implementation's module references and output settings, such as the demo's
writeCsvOnTrialEnd.
The shipped TrialLogger writes its CSV under Application.persistentDataPath. A different study can write JSON, a database record, a network stream, or another analysis-ready format.