No project description provided
Project description
csv-session-logger
A small session-based CSV logger for robotics sims and bench tests, one file per run, channels aligned on wall-clock time.
Install
pip install csv-session-logger
Requires Python 3.10+.
Quick Start
from csv_session_logger import Logger
log = Logger(session="bench_test", output_dir="logs/", fill="none")
log.register("voltage", unit="V")
log.register("current", unit="A")
log.log("voltage", 12.1)
log.log("current", 0.45)
path = log.save()
print(path)
Channels must be registered before logging. I made that explicit so you decide upfront what goes in the CSV instead of accumulating mystery columns mid-run.
log() vs log_many()
Both append scalar samples to the buffer. The difference is timing.
**log(key, value)**: one channel, one call. Each call records its ownt_wallviatime.time(). If you logxand thenyseparately, they land on slightly different timestamps. That is fine when channels are independent or arrive asynchronously (e.g. a slow sensor vs a fast control loop).**log_many({...})**: several channels in one call. All keys in the dict share a singlet_walltimestamp, taken once at the start of the call. I use this when values belong to the same timestep and should line up in the CSV without NaN gaps, typical in a simulation loop or a synchronized sensor snapshot.
Simulation time is not special. If you want t_sim, register it like any other channel and include it in log_many():
log.register(["t_sim", "x", "x_dot"], ["s", "m", "m/s"])
log.log_many({"t_sim": t, "x": x, "x_dot": x_dot})
Fill strategies
When channels are logged at different t_wall times, save() merges them onto one time index. Missing entries become NaN before the fill step runs.
fill |
Behavior |
|---|---|
"none" |
Leave NaN as-is. Use when you want to see exactly which channel was missing at each timestamp. |
"ffill" |
Forward-fill with the last known value per column. Leading NaN (before the first sample) stay NaN. |
"mean" |
Linear interpolation between neighbours (pandas.interpolate()). Good for plotting continuous signals when samples are slightly misaligned. |
Pick the strategy at Logger(...) construction, it applies on every save().
Output format
The CSV index is t_wall (Unix seconds, float). Column headers use "key [unit]" when a unit was registered, otherwise plain key. Values are written with 8 decimal places.
Example (from a spring-mass-damper run):
t_wall,t_sim [s],x [m],x_dot [m/s],x_ref [m],error [m],F [N]
1781263948.99401498,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000
1781263948.99402165,0.01000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000
Auto-generated filenames look like {session}_{YYYY-MM-DD_HH-MM-SS}.csv. Pass save(filename="my_run.csv") to override.
CLI
Entry point: csl
csl info <csv>
Print channel names and duration (last t_wall minus first).
csl info logs/spring_mass_damper_2026-06-12_00-23-22.csv
csl plot <csv>
Quick matplotlib plots. Time axis is t_wall relative to the first row (starts at 0).
# All channels, stacked subplots
csl plot logs/spring_mass_damper_2026-06-12_00-23-22.csv
# Subset by column name (-t / --time)
csl plot logs/spring_mass_damper_2026-06-12_00-23-22.csv -t "x [m]" "F [N]"
# XY plot: first column is X, rest are Y (-x / --xy)
csl plot logs/spring_mass_damper_2026-06-12_00-23-22.csv -x "t_sim [s]" "x [m]" "error [m]"
-t and -x cannot be used together.
Full example: spring-mass-damper
Second-order plant with a P controller, Euler integration. Same as examples/sim_example.py:
import numpy as np
from csv_session_logger import Logger
m, c, k, Kp = 1.0, 0.5, 2.0, 10.0
dt, t_end = 0.01, 20.0
n_steps = int(t_end / dt)
def x_ref(t: float) -> float:
return 0.0 if t < 2.0 else 1.0
x, x_dot = 0.0, 0.0
log = Logger(session="spring_mass_damper", output_dir="logs/", fill="none")
log.register(["t_sim", "x", "x_dot", "x_ref", "error", "F"],
["s", "m", "m/s", "m", "m", "N"])
t = 0.0
for _ in range(n_steps):
ref = x_ref(t)
error = ref - x
F = Kp * error
x_ddot = (F - c * x_dot - k * x) / m
x_dot += x_ddot * dt
x += x_dot * dt
log.log_many({
"t_sim": t, "x": x, "x_dot": x_dot,
"x_ref": ref, "error": error, "F": F,
})
t += dt
path = log.save()
log.summary()
Run it: python examples/sim_example.py, then csl plot logs/spring_mass_damper_*.csv.
Why not rosbag / wandb / mlflow?
I built this during my bachelor's thesis when I needed a fast, lightweight way to log scalar data and hadn't worked with rosbag, wandb, or mlflow before. Rather than learning one of those, I wrote a minimal tool to register a few channels, log scalars in a loop and get a CSV.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file csv_session_logger-0.1.1.tar.gz.
File metadata
- Download URL: csv_session_logger-0.1.1.tar.gz
- Upload date:
- Size: 9.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b734294ebf9d3aca108ee5f5bb4b4c7980a309c4bc5f184f1c0ace2360969467
|
|
| MD5 |
cab8ae88668c0b9dd5d7f514fb607c5a
|
|
| BLAKE2b-256 |
07a6c97af653d1ed730c07170d6f897f471e9265dc38a6a8a251412ab96a1220
|
File details
Details for the file csv_session_logger-0.1.1-py3-none-any.whl.
File metadata
- Download URL: csv_session_logger-0.1.1-py3-none-any.whl
- Upload date:
- Size: 8.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93caafcaab6f4e81766aa8b806489c493c82227c508db1ce2c45bdf2e5eafde6
|
|
| MD5 |
44ef476d1d9ae00c75f9a502a1891247
|
|
| BLAKE2b-256 |
3eb653427a1dae4587298d343cb7a376e95f800b1087419a11e8fee63fa696f8
|