Fachbereich Automatisierung & Informatik

OER AI Engineering

Modul Mobile Systeme & Telematik

Case Study Exoskelette

Prof. Dr. Fabian Transchel, Hochschule Harz, Oktober 2025


In [22]:
import pandas as pd
import plotly as px
import matplotlib
%matplotlib widget
print(matplotlib.get_backend())
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as pxgo
widget
In [23]:
data_file_name = 'AIEng Exo Motion Sensing\\23-07-2025Mocap2Gameplay-002.bvh'

data_file_ok = True
data_file_error = ""
data_start_line = 0
frames = 0
frame_time = 0
column_header = []
column_header_prefix = ""
In [24]:
# Daten in der Datei mittels Keyword "MOTION" suchen und Definitionen für die Generierung der Spaltenbezeichnungen einlesen
with open(data_file_name) as fp:
    lines = fp.readlines()
    i = -1
    for row in lines:
        '''
        Ich gehe davon aus, dass die Einträge ROOT/JOINT und CHANNELS sich abwechseln.
        ROOT/JOINT bildet das Präfix für alle folgenden CHANNELS bei der Spaltenbezeichnung.
        Schlielich wird noch nach MOTION gesucht, um den Einstiegspunkt für die einzulesenden
        Daten zu finden. Abschließend wird die Anzahl der Frames sowie die Framelänge gelesen. 
        Annahme: Angabe der Frame Time in [s]
        '''
        i+=1
        if row.find("ROOT") != -1 or row.find("JOINT") != -1:
            column_header_prefix = row.strip().split(" ")[1]
            # print (column_header_prefix)
            continue
        if row.find("CHANNELS") != -1:
            column_header = column_header + [ column_header_prefix +"_"+ item for item in row.strip().split(" ")[2:]  ]
            # print ([ column_header_prefix +"_"+ item for item in row.strip().split(" ")[2:]  ])
            continue
        if row.find('MOTION') != -1:
            data_start_line = i + 3
            frames = int(lines[i+1].strip().split(" ")[1])
            frame_time = float(lines[i+2].strip().split(" ")[2])
            break
        

if data_start_line == 0:
    data_file_ok = False
    data_file_error += "Keyword 'Motion' nicht gefunden. Keine Daten zum einlesen vorhanden."


if data_file_ok:
    # Daten einlesen
    df = pd.read_csv(data_file_name, sep=" ", skiprows=data_start_line, header=None, names=column_header)
            
    # Duplikate verwerfen
    df = df.drop_duplicates()
    
    # Index definineren
    df = df.set_index( pd.date_range('1/1/2025', periods=df.shape[0], freq=str(frame_time)) )

    # Daten mit neuer Zeitbasis resampeln
    df = df.resample("0.1s").mean()

    print("Daten eingelesen")

else:
    print("FEHLER\n" + data_file_error)


    
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File pandas/_libs/tslibs/offsets.pyx:4855, in pandas._libs.tslibs.offsets.to_offset()

ValueError: last element must be blank

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[24], line 42
     39 df = df.drop_duplicates()
     41 # Index definineren
---> 42 df = df.set_index( pd.date_range('1/1/2025', periods=df.shape[0], freq=str(frame_time)) )
     44 # Daten mit neuer Zeitbasis resampeln
     45 df = df.resample("0.1s").mean()

File d:\Anaconda\envs\MSuT\Lib\site-packages\pandas\core\indexes\datetimes.py:1008, in date_range(start, end, periods, freq, tz, normalize, name, inclusive, unit, **kwargs)
   1005 if freq is None and com.any_none(periods, start, end):
   1006     freq = "D"
-> 1008 dtarr = DatetimeArray._generate_range(
   1009     start=start,
   1010     end=end,
   1011     periods=periods,
   1012     freq=freq,
   1013     tz=tz,
   1014     normalize=normalize,
   1015     inclusive=inclusive,
   1016     unit=unit,
   1017     **kwargs,
   1018 )
   1019 return DatetimeIndex._simple_new(dtarr, name=name)

File d:\Anaconda\envs\MSuT\Lib\site-packages\pandas\core\arrays\datetimes.py:423, in DatetimeArray._generate_range(cls, start, end, periods, freq, tz, normalize, ambiguous, nonexistent, inclusive, unit)
    418 if com.count_not_none(start, end, periods, freq) != 3:
    419     raise ValueError(
    420         "Of the four parameters: start, end, periods, "
    421         "and freq, exactly three must be specified"
    422     )
--> 423 freq = to_offset(freq)
    425 if start is not None:
    426     start = Timestamp(start)

File pandas/_libs/tslibs/offsets.pyx:4791, in pandas._libs.tslibs.offsets.to_offset()

File pandas/_libs/tslibs/offsets.pyx:4954, in pandas._libs.tslibs.offsets.to_offset()

ValueError: Invalid frequency: 0.016667, failed to parse with error message: ValueError('last element must be blank')
In [25]:
df.head(20)
Out[25]:
Hips_Xposition Hips_Yposition Hips_Zposition Hips_Yrotation Hips_Xrotation Hips_Zrotation Chest_Yrotation Chest_Xrotation Chest_Zrotation Chest2_Yrotation ... LeftHip_Zrotation LeftKnee_Yrotation LeftKnee_Xrotation LeftKnee_Zrotation LeftAnkle_Yrotation LeftAnkle_Xrotation LeftAnkle_Zrotation LeftToe_Yrotation LeftToe_Xrotation LeftToe_Zrotation
0 0.0 0.995491 0.0 0.000000 6.340192 0.000000 0.000000 -6.340192 0.000000 0.000000 ... 0.000000 0.000000 1.905768 0.000000 0.000000 -4.658254 0.000000 0.0 0.0 0.0
1 0.0 1.250000 0.0 -129.519133 7.040608 -1.145609 1.671974 -5.508269 1.501748 0.689633 ... 1.679436 0.398210 11.530228 -0.110554 -2.813527 -9.878268 -1.547494 0.0 -0.0 0.0
2 0.0 1.250000 0.0 -129.631701 6.936906 -1.120711 1.676278 -5.451491 1.441430 0.693434 ... 1.560593 0.418050 11.311385 -0.125586 -2.871568 -9.819166 -1.408210 -0.0 -0.0 0.0
3 0.0 1.250000 0.0 -129.704610 6.852042 -1.094486 1.674093 -5.406982 1.388953 0.694141 ... 1.457488 0.438776 11.122996 -0.121332 -2.921298 -9.773088 -1.298656 0.0 0.0 -0.0
4 0.0 1.250000 0.0 -129.710698 6.796788 -1.063975 1.661378 -5.381869 1.347789 0.689853 ... 1.376957 0.460616 10.982769 -0.083128 -2.956303 -9.748521 -1.239878 0.0 -0.0 0.0
5 0.0 1.250000 0.0 -129.677129 6.760305 -1.032362 1.642162 -5.368912 1.314560 0.682444 ... 1.311873 0.483467 10.872982 -0.025577 -2.983026 -9.737142 -1.210721 -0.0 0.0 -0.0
6 0.0 1.250000 0.0 -129.645717 6.728973 -1.007798 1.622036 -5.358692 1.286974 0.674447 ... 1.256649 0.508034 10.770980 0.026858 -3.012495 -9.726953 -1.177458 0.0 -0.0 -0.0
7 0.0 1.250000 0.0 -129.630408 6.699372 -0.994375 1.602600 -5.349927 1.265212 0.666564 ... 1.210587 0.535335 10.669086 0.066194 -3.050177 -9.714889 -1.126979 -0.0 -0.0 0.0
8 0.0 1.250000 0.0 -129.617236 6.674939 -0.987983 1.582250 -5.343900 1.249093 0.658089 ... 1.174393 0.564398 10.574979 0.100416 -3.090620 -9.704001 -1.072404 0.0 -0.0 -0.0
9 0.0 1.250000 0.0 -129.606157 6.652380 -0.984450 1.561764 -5.335073 1.235769 0.649454 ... 1.144637 0.592919 10.489046 0.129005 -3.128498 -9.691433 -1.019375 -0.0 0.0 -0.0
10 0.0 1.250000 0.0 -129.597200 6.630917 -0.982954 1.540399 -5.321041 1.225236 0.640325 ... 1.120946 0.621741 10.410841 0.151669 -3.162967 -9.675991 -0.967511 -0.0 -0.0 -0.0
11 0.0 1.250000 0.0 -129.590332 6.611330 -0.984312 1.518894 -5.304207 1.217496 0.631033 ... 1.103684 0.650075 10.340826 0.168741 -3.194858 -9.658869 -0.917182 0.0 0.0 0.0
12 0.0 1.250000 0.0 -129.585419 6.592653 -0.986811 1.500244 -5.286250 1.209712 0.623006 ... 1.090520 0.673167 10.280761 0.180656 -3.221339 -9.640799 -0.875147 -0.0 0.0 0.0
13 0.0 1.250000 0.0 -129.582456 6.573590 -0.990264 1.486002 -5.266777 1.201089 0.616956 ... 1.081831 0.688932 10.233554 0.186428 -3.240684 -9.621623 -0.846054 -0.0 -0.0 0.0
14 0.0 1.250000 0.0 -129.581448 6.555438 -0.994855 1.474615 -5.246182 1.192426 0.612169 ... 1.077243 0.699495 10.196310 0.187080 -3.254592 -9.601506 -0.825237 0.0 -0.0 0.0
15 0.0 1.250000 0.0 -129.582278 6.541122 -0.999421 1.464417 -5.227329 1.183285 0.607930 ... 1.073302 0.706413 10.162076 0.186063 -3.265368 -9.581668 -0.805478 -0.0 -0.0 -0.0
16 0.0 1.250000 0.0 -129.586493 6.532947 -1.003906 1.455376 -5.211449 1.173282 0.604239 ... 1.069675 0.709135 10.131002 0.183341 -3.271568 -9.562270 -0.785250 0.0 0.0 0.0
17 0.0 1.250000 0.0 -129.592547 6.528608 -1.008366 1.447521 -5.197313 1.162803 0.601093 ... 1.066697 0.708217 10.102939 0.178955 -3.274634 -9.543153 -0.766072 0.0 0.0 -0.0
18 0.0 1.250000 0.0 -129.595692 6.524117 -1.011801 1.439288 -5.184094 1.152565 0.597774 ... 1.061912 0.706865 10.070482 0.176456 -3.281200 -9.525055 -0.745303 -0.0 -0.0 -0.0
19 0.0 1.250000 0.0 -129.593691 6.519215 -1.013571 1.429914 -5.171796 1.142711 0.593938 ... 1.054076 0.704535 10.029531 0.177350 -3.295863 -9.507830 -0.720525 -0.0 0.0 -0.0

20 rows × 72 columns

In [26]:
fig = pxgo.Figure()
features = ["Chest_Xrotation","Chest_Yrotation","Hips_Xrotation","Hips_Yrotation"]
for f in features:
    fig.add_trace(pxgo.Scatter(x=df.index, y=df[f],mode='lines',name=f))
fig.show()
In [ ]: