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#
[ ]:
from perda.analyzer import Analyzer
csvPath = "csv_files/20thMarch13-21.csv"
aly = Analyzer(csvPath)
[ ]:
# Print summary of the loaded data
print(aly)
Search Data#
[ ]:
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="us"
) # change source_time_unit to us if v2.0 logs
print()
# Print the same variable, but accessing the data structure using variable ID instead
data_instance_summary(aly.data[357], source_time_unit="us")
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)
[ ]:
# We can also plot small chunks of the data to prevent too much data points
aly.plot(variables, ts_start=150, ts_end=300)
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",
357, # We can also specify variables by variable ID
]
aly.plot(
var_1=variables_left,
var_2=variables_right,
ts_start=600,
ts_end=800,
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
print()
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
print()
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 bms.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)
Frequency Analysis#
[ ]:
# Retrieve DataInstances
aly.analyze_frequency("bms.stack.thermistors.temperature[38]")
CSV Compare (diff)#
[ ]:
from perda.analyzer import Analyzer
csvBase = "csv_files/09thMarch02-19_from_pi.csv"
csvIncom = "csv_files/9thMar06-19-18_from_dataserver.csv"
# Parse two csv files for compare
alyBase = Analyzer(csvBase)
alyIncom = Analyzer(csvIncom)
[ ]:
# Compare base and incoming data
graph = alyBase.diff(alyIncom.data, diff_rtol=1e-3, diff_atol=1e-3)
graph.show()
Concat CSVs#
[ ]:
from perda.analyzer import Analyzer, concat
# We can concate multiple csv files into one readings, start with base
alyBase = Analyzer("csv_files/22ndMar09-53-00")
# Setup logs we want to merge
paths = [
"csv_files/22ndMar10-08-00",
"csv_files/22ndMar10-20-00",
"csv_files/22ndMar11-16-00",
"csv_files/22ndMar11-48-00",
"csv_files/22ndMar11-52-00",
"csv_files/22ndMar12-00-00",
"csv_files/22ndMar12-52-00",
"csv_files/22ndMar14-16-00",
"csv_files/22ndMar14-22-00",
"csv_files/22ndMar14-25-00",
"csv_files/22ndMar14-27-00",
]
# This might take a while since we are parsing many runs
for path in paths:
# Parse incoming log
alyIncom = Analyzer(path)
# Concat the second analyzer after first analyzer
alyBase = concat(alyBase, alyIncom)
[ ]:
# Plot cell voltage over time for multiple runs
variables = [
"bms.stack.mma.cellV.min",
"bms.stack.mma.cellV.max",
"bms.stack.mma.cellV.avg",
]
alyBase.plot(variables)