Penn Electric Racing Data Analyzer: PERDA#

PERDA is PER’s in-house data analysis library designed to streamline the process of analyzing vehicle data logs.

Load Data (local csv)#

[ ]:
from perda.analyzer import Analyzer
from perda.utils import patch_ned_velocity

csvPath = "csv_files/16thApr10-52-00.csv"
aly = Analyzer(csvPath, preprocessing=[patch_ned_velocity])

# Print summary of the loaded data
print(aly)

Load Data (via server)#

[ ]:
from perda.analyzer import Analyzer
from perda.server import ServerClient

# Fill in our server address and password
client = ServerClient("http://198.74.62.28:5000/")
# Optional cache_dir to download and load logs
# client = ServerClient("http://198.74.62.28:5000/", cache_dir="~/.perda/cache/")

# Please fill in password, you should know what it is... :)
client.login("FILL")
[ ]:
# Find your log by changing the path
client.print_logs("REV 11/")
[ ]:
# Get/download analyzer via log path on server
aly = client.load("REV 11/2026-04-01/test.csv")

# Print summary of the loaded data
print(aly)

Search Data#

[ ]:
search_results = aly.search("vectornav")

Inspect Data#

[ ]:
from perda.utils import data_instance_summary

# Print individual variable's info
data_instance_summary(
    aly.data["bms.pack.current"], source_time_unit="ms"
)  # change source_time_unit to us if using car logs

# Print the same variable, but accessing the data structure using variable ID instead
data_instance_summary(aly.data[422], source_time_unit="ms")

Graph GPS#

[ ]:
from perda.utils import create_representative_gps_image

lat = aly.data["pcm.vnav.posLla.latitude"]  # Put latitude data here, if any
lon = aly.data["pcm.vnav.posLla.longitude"]  # Put longitude data here, if any
vel = aly.data[
    "pcm.wheelSpeeds.frontLeft"
]  # Put velocity data here, optional but recommended

create_representative_gps_image(lat, lon, vel)

Graph Variables (Single Y-Axis)#

[ ]:
# Variables to graph
variables = [
    "pcm.moc.motor.requestedTorque",
    "pcm.moc.motor.wheelSpeed",
    "bms.pack.power",
    "bms.pack.voltage",
]
aly.plot(variables)

Graph Variables (Dual Y-Axis)#

[ ]:
# Variables to graph
variables_left = [
    "pcm.wheelSpeeds.frontLeft",
    "pcm.wheelSpeeds.frontRight",
    "pcm.wheelSpeeds.backLeft",
    "pcm.wheelSpeeds.backRight",
]

variables_right = [
    "bms.stack.mma.temp.avg",
    "bms.stack.mma.dieTemp.avg",
    422,  # We can also specify variables by variable ID
]

aly.plot(
    var_1=variables_left,
    var_2=variables_right,
    title="Dual Y-Axis Example",
    y_label_1="Wheel Speeds (MPH)",
    y_label_2="Minion Temperature Data",
)

Data Manipulation#

The core of PERDA is the DataInstance class. Each variable is stored as a DataInstance object.

[ ]:
from perda.utils import data_instance_summary

# Retrieve DataInstances
current = aly.data["bms.pack.current"]
voltage = aly.data["bms.pack.voltage"]

# Print info for current
data_instance_summary(current)

# Basic arithmetic operators are overloaded for DataInstances
current_plus = current + 10  # Creates a new DataInstance that is
# 10 higher than current at every timestamp
current_plus.label = "current + 10"  # Assign our new DataInstance a label
data_instance_summary(current_plus)

# One example of a practical use case is calculating power from current and voltage
power_cal = (current * voltage) / 1000
power_cal.label = "power calculated"
[ ]:
# Let's compare calculated power with ams.pack.power
variables = [
    "bms.pack.power",
    power_cal,
]
aly.plot(var_1=variables)

Custom Variables#

[ ]:
from perda.analyzer import DataInstance
from perda.utils import data_instance_summary
import numpy as np

# We can also create our own DataInstances
my_ts = np.arange(2000000)
my_val = my_ts / 1e3 - 1000
my_di = DataInstance(timestamp_np=my_ts, value_np=my_val, label="Line_Data", var_id=1)
data_instance_summary(my_di)
[ ]:
aly.plot(var_1=my_di)