-
TIO 측정 데이터 분석 - mat 파일을 데이터프레임으로 변환하기 (mdf2df)application 2025. 9. 1. 12:26
이 글은 Jupyter 노트북으로 작성하였습니다.
tio_measure_data_analysis - how-to.ipynb9.08MB시작하기 전에
- TIO로 디지털 신호와 아날로그 신호를 측정했다.
- TIO는 CAN으로 통신하는 Tosun의 Input Output 장치이다. Digital IO, Analog IO, 저항, 릴레이 모듈들이 있다. 모듈들을 아래 사진처럼 TSMaster와 CAN으로 연결한다.

TIO 모듈들과 TSMaster를 CAN 으로 연결한다. 
TIO의 Digitial Input/Output, Analog Input/Output 모듈 - 위 실험 셋업에서 CAN 메시지로 Digital Output, Analog Output을 제어하며, Digitial Input, Aanlog Input 메시지로 수신하여 blf 파일로 저장하였다.
- 제어 요청과 디지털, 아날로그 신호의 실제 값 사이에 얼마의 시간 지연이 있는지, 신호는 얼마나 정확한지 분석하고자 한다.
- 이 분석을 TSMaster의 그래픽스 창에서 시각적으로 할 수 있다. 데이터를 처리하는 프로그램을 작성하여 할 수도 있다. 여기서는 파이썬으로 프로그램을 작성하여 해보려고 한다.
- 분석을 위해서 blf 파일에서 필요한 신호들을 추출여야 한다. blf는 메시지 단위로 데이터를 저장한다. 이는 개별 신호 분석에 불편하다. TSMaster의 Log Converter 기능을 이용하여 신호 단위로 데이터를 저장하는 mat 파일로 변환하였다.
- mat 파일을 데이터 분석에 많이 사용되는 파이썬 pandas 모듈의 df(DataFrame)으로 변환한다.
- 여기서는 mat to df 방법을 설명한다. 이 방법을 mat 파일을 데이터프레임으로 변환하고 feather 파일로 저장하기 :: hsl's tsmaster 사용기 에서 설명한 적이 있다. 신호들의 타임스탬프들을 동일하게 만들기 위해서 데이터 처리한 부분이 있다. 이 부분을 pandas의 resample 기능을 이용하여 쉽게 할 수 있다는 것을 배웠다. 이 방법을 소개한다.
개요
- 한 신호를 df로 변환하는 방법
- 타임스탬프가 동일한 경우, 신호들을 한 df로 변환하는 방법
- 타임스탬프가 다른 경우, 신호들을 한 df로 변환하는 방법
- 타임스탬프가 동일한 신호들끼리 한 df로 만들기
- df들 합치기
- 결측치 처리
- Resample
# import from pathlib import Path # 파일 경로를 다루기 위한 모듈 import h5py # .mat 파일을 읽기 위한 모듈 import pandas as pd # 데이터 분석을 위한 모듈 import numpy as np # 데이터 분석을 위한 모듈 import plotly.express as px # 데이터 시각화를 위한 모듈 from plotly.subplots import make_subplots # 데이터 시각화를 위한 모듈# 블로그에 올리기 위해 plotly renderer의 설정이 필요하다. import plotly.io as pio pio.renderers.default = "notebook_connected"# constant # 데이터 파일 저장 경로 k_dir_data = Path('data') k_dir_data.mkdir(exist_ok=True) # data 디렉토리의 첫 번째 .mat 파일을 대상으로 한다.(파일 이름을 기억하기가 귀찮다.^^) mats = list(Path(k_dir_data).rglob('*.mat')) if len(mats) == 0: print('No mat files found in data directory') else: print('mats =') for i, mat in enumerate(mats): print(f'{i}: {mat.name}')mats = 0: tio_2025_08_28_17_44_55.MAT# data 디렉토리의 첫 .mat 파일을 대상으로 한다. i_mat = 0 mat = mats[i_mat] print(f'{mat = }')mat = WindowsPath('data/tio_2025_08_28_17_44_55.MAT')mat 파일을 읽어서 데이터프레임으로 변환하는 방법 찾기
- digital output Request, digitial input, analog output Request, analog input 신호들을 pandas df(DataFrame)으로 만든다.
- 메시지별로 전송 주기가 다르다. 즉, 타임스탬프가 다르다. 타임스탬프를 맞춰야 계산이 편리하다.
- 메시지별로 df를 만들고, 데이터프레임들을 합치고, 합치는 과정에서 발생하는 결측치를 처리하고, Resample을 한다.
# mat 파일을 읽는다. trace = h5py.File(mat, 'r') # 파일에 포함된 신호(키)들을 확인한다. signals = list(trace.keys()) for signal in signals: print(signal)CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH9_Level CAN1_TIO9011_N01_SET_DO_LEVEL_REQ_TIO9011_Channel_Index CAN1_TIO9011_N01_SET_DO_LEVEL_REQ_TIO9011_IO_LEVEL CAN1_TIO9015_N02_REP_AI_CHN1_4_Voltage_CH2 CAN1_TIO9015_N02_REP_AO_CHN1_4_Voltage_CH1 CAN1_TIO9015_N02_SET_AO_VALUE_REQ_A_Value CAN1_TIO9015_N02_SET_AO_VALUE_REQ_Channel노트
- 신호의 정의는 아래와 같다.
디지털 신호
- 나는 디지털 아웃풋으로 CH7을 디지털 인풋으로 CH9를 사용했다.
- CAN1_TIO9011_N01_SETDO_LEVELREQTIO9011Channel_Index:
- SET 메시지에 있는 Digitial Output 채널 지정
- SET 메시지는 TSMaster가 TIO로 필요할 때마다 전송한다.
- df 컬럼명: do_ch_set
- CAN1_TIO9011_N01_SETDO_LEVELREQTIO9011_IOLEVEL:
- SET 메시지에 있는 Digitial Output 요청값
- SET 메시지는 TSMaster가 TIO로 필요할 때마다 전송한다.
- df 컬럼명: do_req_set
- CAN1_TIO9011_N01_REPDI_LEVEL_TIO9011_CH9Level: do_act_req
- REP 메시지에 있는 Digital Input 신호값
- REP 메시지는 TIO가 주기적으로 전송한다.
- REP 메시지의 전송 주기는 SET 메시지로 설정 가능하다.
- df 컬럼명: do_act_req
- CAN1_TIO9011_N01_REPDI_LEVEL_TIO9011_CH7Level:
- REP 메시지에 있는 Digital Output의 요청값 <-- 아웃풋으로 설정된 경우, Input 값이 아닌 Output 요청이다.
- REP 메시지는 TIO가 주기적으로 전송한다.
- REP 메시지의 전송 주기는 SET 메시지로 설정 가능하다.
- df 컬럼명: do_req_rep
아날로그 신호
- 나는 아날로그 아웃풋으로 CH1을 아날로그 인풋으로 CH2를 사용했다.
- CAN1_TIO9015_N02_SETAO_VALUEREQ_Channel:
- SET 메시지에 있는 Analog Output 채널 지정
- SET 메시지는 TSMaster가 TIO로 필요할 때마다 전송한다.
- df 컬럼명: ao_ch_set
- CAN1_TIO9015_N02_SETAO_VALUEREQAValue:
- SET 메시지에 있는 Analog Output 요청값
- SET 메시지는 TSMaster가 TIO로 필요할 때마다 전송한다.
- df 컬럼명: ao_req_set
- CAN1_TIO9015_N02_REP_AI_CHN1_4_Voltage_CH2:
- REP 메시지에 있는 Analog Input 신호값
- REP 메시지는 TIO가 주기적으로 전송한다.
- REP 메시지의 전송 주기는 SET 메시지로 설정 가능하다.
- df 컬럼명: ai_act_set
- CAN1_TIO9015_N02_REP_AO_CHN1_4_Voltage_CH1:
- REP 메시지에 있는 Analog Output의 요청값 <-- 아웃풋으로 설정된 경우, Input 값이 아닌 Output 요청이다.
- REP 메시지는 TIO가 주기적으로 전송한다.
- REP 메시지의 전송 주기는 SET 메시지로 설정 가능하다.
- df 컬럼명: ao_req_rep
한 신호를 df로 변환하는 방법
do_req_rep = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level'][:] df = pd.DataFrame(do_req_rep) df = df.T # 신호가 1개일 경우, df를 Transpose 해야 한다. df.columns = ['ts', 'do_req_rep'] # 열 이름 설정한다. df.head(3)ts do_req_rep 0 26.998460 1.0 1 27.008549 1.0 2 27.018482 1.0 발견 - trace[신호]와 trace[신호][:]의 차이
- trace[신호][:]라고 해야 데이터를 볼 수 있다.
do_req_rep = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level'] do_req_rep<HDF5 dataset "CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level": shape (2, 2966), type "<f8">do_req_rep = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level'][:] do_req_reparray([[26.99846 , 27.008549, 27.018482, ..., 56.628256, 56.638281, 56.648308], [ 1. , 1. , 1. , ..., 1. , 1. , 1. ]])타임스탬프가 동일한 경우, 신호들을 한 df로 변환하는 방법
# 두 신호를 각각 numpy array로 변환한다. do_req_rep = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH7_Level'][:] do_act_req = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH9_Level'][:]# 신호의 첫 번째 (인텍스 0) 컬럼은 타임스탬프이다. do_req_rep[0]array([26.99846 , 27.008549, 27.018482, ..., 56.628256, 56.638281, 56.648308])# 신호의 두 번째 (인덱스 1) 컬럼은 신호의 값이다. do_req_rep[1]array([1., 1., 1., ..., 1., 1., 1.])# timestamp가 동일한지 확인한다. # 동일하지 않으면, 하나의 df로 만들 수 없다. # shape을 비교한다. print(f'{do_req_rep.shape = } == {do_act_req.shape = }') print(f'{(do_req_rep.shape == do_act_req.shape) = }') # 값을 비교한다. try: print(f'{(do_req_rep[0] == do_act_req[0]).all() = }') except ValueError as e: print("ValueError:", e)do_req_rep.shape = (2, 2966) == do_act_req.shape = (2, 2966) (do_req_rep.shape == do_act_req.shape) = True (do_req_rep[0] == do_act_req[0]).all() = np.True_# 두 신호를 하나의 DataFrame으로 합치기 df = pd.DataFrame( { 'do_req_rep': do_req_rep[1], # 신호를 컬럼으로 한다. 'do_act_req': do_act_req[1] # 신호를 컬럼으로 한다. }, index=do_req_rep[0] # 타임스탬프를 인덱스로 한다. ) df.head(3)do_req_rep do_act_req 26.998460 1.0 1.0 27.008549 1.0 1.0 27.018482 1.0 1.0 타임스탬프가 다른 경우, 신호들을 한 df로 변환하는 방법
# 두 신호를 각각 numpy array로 변환 do_req_set = trace['CAN1_TIO9011_N01_SET_DO_LEVEL_REQ_TIO9011_IO_LEVEL'][:] do_act_req = trace['CAN1_TIO9011_N01_REP_DI_LEVEL_TIO9011_CH9_Level'][:]# timestamp가 동일한지 확인한다. # 동일하지 않으면, 하나의 df로 만들 수 없다. print(f'{do_req_set.shape = } {do_act_req.shape = }') print(f'{(do_req_set.shape == do_act_req.shape) = }') try: print(f'{(do_req_set[0] == do_act_req[0]).all() = }') except ValueError as e: print("ValueError:", e)do_req_set.shape = (2, 44) do_act_req.shape = (2, 2966) (do_req_set.shape == do_act_req.shape) = False ValueError: operands could not be broadcast together with shapes (44,) (2966,)# 두 신호를 각각의 DataFrame으로 만든다. df_do_req_set = pd.DataFrame(do_req_set.T, columns=['ts', 'do_req_set']) # 신호가 1개의 경우 Transpose를 해야한다. df_do_act_req = pd.DataFrame(do_act_req.T, columns=['ts', 'do_act_req']) # 신호가 1개의 경우 Transpose를 해야한다.print(df_do_req_set.ts.diff().describe()) df_do_req_set.head(3)count 43.000000 mean 0.500017 std 0.000298 min 0.499481 25% 0.499767 50% 0.499997 75% 0.500323 max 0.500507 Name: ts, dtype: float64ts do_req_set 0 34.738190 0.0 1 35.238565 1.0 2 35.738881 0.0 print(df_do_act_req.ts.diff().describe()) df_do_act_req.head(3)count 2965.000000 mean 0.010000 std 0.000156 min 0.008102 25% 0.009971 50% 0.009979 75% 0.010027 max 0.012109 Name: ts, dtype: float64ts do_act_req 0 26.998460 1.0 1 27.008549 1.0 2 27.018482 1.0 df_do_req_set['ts_diff'] = df_do_req_set['ts'].diff() px.line( df_do_req_set, x='ts', y='ts_diff', title='ts diff - df_do_req_set', range_y=[df_do_req_set.ts_diff.mean() * 1.05, df_do_req_set.ts_diff.mean() * 0.95], width= 600, height= 400, ).show() df_do_req_set.ts_diff.describe()
do_req_set 신호의 전송 주기는 500msec로 일정하다. count 43.000000 mean 0.500017 std 0.000298 min 0.499481 25% 0.499767 50% 0.499997 75% 0.500323 max 0.500507 Name: ts_diff, dtype: float64df_do_act_req['ts_diff'] = df_do_act_req['ts'].diff() px.line( df_do_act_req, x='ts', y='ts_diff', title='ts diff - df_do_act_req', range_y=[df_do_act_req.ts_diff.mean() * 1.2, df_do_act_req.ts_diff.mean() * 0.8], width= 600, height= 400, ).show() df_do_act_req.ts_diff.describe()
do_act_req 신호의 전송 주기는 10msec로 일정하다. count 2965.000000 mean 0.010000 std 0.000156 min 0.008102 25% 0.009971 50% 0.009979 75% 0.010027 max 0.012109 Name: ts_diff, dtype: float64발견
- 통계치의 mean을 보면,
- df_do_req_set 타임스탬프의 주기는 500msec이다.
- df_do_act_req 타임스탬프의 주기는 10msec이다.
- 통계치의 std, min, max를 보면, 메시지의 전송 주기가 매우 일정하다.
- 그래프를 보면, do_act_req는 do_req_set에 비해 들쭉날쭉하는 것으로 보인다. do_act_req의 경우, 메시지 전송 주기가 10msec로 do_req_set의 500msec에 비해 50배 짧다. 그래서 전송 주기가 약간만 늘어나거나 줄어들어도 크게 변동한 것처럼 보인다.
df 합치기
- 두 df들을 합혀서(merge) df_digital을 만든다.
- 합칠 때, 결측치 (missing value)가 생긴다.
- df_digital을 ts로 정렬한다.
df_digital = pd.merge(df_do_req_set, df_do_act_req, on='ts', how='outer', suffixes=('_do_req', '_do_act_req')) df_digital.tail(3) # 결측치 (Missing value)가 있는 것을 볼 수 있다.ts do_req_set ts_diff_do_req do_act_req ts_diff_do_act_req 3007 56.628256 NaN NaN 1.0 0.009956 3008 56.638281 NaN NaN 1.0 0.010025 3009 56.648308 NaN NaN 1.0 0.010027 결측치 처리
- ffill() (forward fill)을 이용하여 결측치를 채운다.
- 디지털 신호의 경우, 이번 메시지에서 다음 메시지까지 값이 일정하다.
# 결측치에 대해 ffill을 실시한다. df_digital = df_digital.ffill() # ffill을 통해 결측치를 채운 결과를 확인할 수 있다. # 결측치 (Missing value)가 해결 되었다. # 단, df 앞쪽에 있는 결측치는 해결되지 않았다. ffill은 이전 값을 이용해서 아래 결측치를 채우는 방식이기 때문이다. df_digital.tail(3)ts do_req_set ts_diff_do_req do_act_req ts_diff_do_act_req 3007 56.628256 1.0 0.500003 1.0 0.009956 3008 56.638281 1.0 0.500003 1.0 0.010025 3009 56.648308 1.0 0.500003 1.0 0.010027 ts_diff를 확인
- 두 df가 합쳐지는 과정에서 서로 주기가 다른 타임스탬프들이 합쳐졌다.
- 타임스탬프 사이의 간격이 각각의 df에서 처럼 일정하지 않다.
df_digital['ts_diff'] = df_digital['ts'].diff() px.line( df_digital, x='ts', y='ts_diff', title='ts diff - df들을 합친 후', width=600, height=400, ).show() df_digital.ts_diff.describe()
df들을 합친 후 타임스탬프의 주기가 들쭉날쭉하다. count 3009.000000 mean 0.009854 std 0.001141 min 0.000090 25% 0.009963 50% 0.009979 75% 0.010025 max 0.012109 Name: ts_diff, dtype: float64발견 - ts_diff
- 두 df 합쳐지면서 주기가 다른 타임스탬프들이 합쳐진다.
- 일정하던 ts_diff가 타임스탬프가 합쳐지는 곳 인근에서 인접하게 된다.
- 위 현상이 있은 후에 간혹 주기가 길어졌다가 짧아지는 현상이 있다.
- 메시지가 등간격 전송되는 경우, 어떤 이유에서건 주기가 한 번 길어진 후에 곧바로 한 번 짧아지는 현상이 나타나는 것은 당연하다. 이번 전송에서 주기가 늘어난 만큼 다음 주기에서 주기가 짧아져야 주기가 등간격이 된다.
- 애초에 왜 주기가 길어졌을까? 여러 메시지들이 전송되는 주기가 겹쳤을 때, 해당 메시지의 우선 순위가 낮아서 그렇지 않을까 추측한다.
- 주기를 일정하게 하면 좋겠다. pandas의 resample 기능을 이용한다.
Resample
- 들쭉날쭉한 타임스탬프의 주기를 일정하게 변환한다.
- pandas의 resample 기능을 이용한다.
# 기존 데이터를 유지하기 위해서 복사한다. df = df_digital.copy() # ts가 초 단위 실수다. # 타임스탬프 사이의 간격을 고려하여 보간 (interpolate)하려면, # ts를 pandas의 TimedeltaIndex 형식으로 변환해야 한다. df['ts_timedelta'] = pd.to_timedelta(df['ts'], unit='s') # 0.01초(10ms) 간격으로 리샘플링 후 보간한다. df = df.resample('10ms', on='ts_timedelta').mean().interpolate() # 타임스탬프 사이의 간격이 일정한지 확인한다. df['ts_diff'] = df.index.diff() # 현재 index가 ts이다. df['ts_diff'] = df['ts_diff'].dt.total_seconds() # ts_diff를 초 단위로 변환한다. px.line( df, x='ts', y='ts_diff', title='ts diff - after resampling', range_y=[df.ts_diff.mean()*0.5, df.ts_diff.mean()*1.5], width=600, height=400 ).show() df.ts_diff.describe()
resample 후 타임스탬프 주기는 10msec로 일정하다. count 2.964000e+03 mean 1.000000e-02 std 1.735016e-18 min 1.000000e-02 25% 1.000000e-02 50% 1.000000e-02 75% 1.000000e-02 max 1.000000e-02 Name: ts_diff, dtype: float64발견 - Resample
- 타임스탬프의 간격이 일정하게 (10msec)로 변환되었다.
신호 변환 결과 확인
# 'do_req_set', 'do_act_req'를 각각의 2 x 1 그래프에 그린다. fig = make_subplots(rows=2, cols=1, shared_xaxes=True) fig.add_trace(px.line(df, x='ts', y='do_req_set', title='DO Request').data[0], row=1, col=1) fig.add_trace(px.line(df, x='ts', y='do_act_req', title='DI Actual').data[0], row=2, col=1) fig.update_layout(height=400, width=600, title_text="Digital Output Request and Digital Input Actual over Time") fig.show()
Resample 후 디지털 출력 요청과 실제 디지털 입력 신호. 틱 바이 틱으로 봐야한다. # 신호 사이의 관계를 scatter 그래프의 매트릭스로 확인한다. import plotly.figure_factory as ff fig = ff.create_scatterplotmatrix( df[['do_req_set', 'do_act_req']], diag='histogram', title='Digital Output Request vs Digital Input Actual', width=600, height=600 ) fig.show()
resample 과정에서 신호 값을 보간(interpolate)한다. 디지털 신호인데 0, 1이 아닌 값들이 있다. 발견 - 신호 변환 결과 확인
- 리샘플링 중에 보간(interpolate)을 했다.
- 디지털 신호인데 0, 1이 아닌 값들이 생겼다. 보간 때문에 생긴 것이다.
Resample 2nd Trial
# 기존 데이터를 유지하기 위해서 복사한다. df = df_digital.copy() # ts가 초 단위 실수다. # 타임스탬프 사이의 간격을 고려하여 보간 (interpolate)하려면, # ts를 pandas의 TimedeltaIndex 형식으로 변환해야 한다. df['ts_timedelta'] = pd.to_timedelta(df['ts'], unit='s') # 0.01초(10ms) 간격으로 리샘플링 후 보간한다. df = df.resample('10ms', on='ts_timedelta').mean().interpolate() # do_req_set은 정수로 반올림한다. # do_act_req는 내림한다. # 서로 방법을 달리하는 이유는 다양한 방법이 있다는 것을 보이기 위해서다. df['do_req_set'] = df['do_req_set'].round() df['do_act_req'] = df['do_act_req'].apply(np.floor) # 타임스탬프 사이의 간격이 일정한지 확인한다. df['ts_diff'] = df.index.diff() df['ts_diff'] = df['ts_diff'].dt.total_seconds()px.line( df, x='ts', y='ts_diff', title='ts diff after resampling 2nd trial', range_y=[df.ts_diff.mean()*0.5, df.ts_diff.mean()*1.5], width=600, height=400 ).show()
다시 resampling 한다. 타임스탬프의 주기는 10msec로 일정하다. # 'do_req_set', 'do_act_req'를 각각의 2 x 1 그래프에 그린다. fig = make_subplots(rows=2, cols=1, shared_xaxes=True) fig.add_trace(px.line(df, x='ts', y='do_req_set', title='DO Request').data[0], row=1, col=1) fig.add_trace(px.line(df, x='ts', y='do_act_req', title='DI Actual').data[0], row=2, col=1) fig.update_layout(height=400, width=600, title_text="Digital Output Request and Digital Input Actual over Time") fig.show()
다시 resample 후 디지털 출력 요청과 실제 디지털 입력 신호. 틱 바이 틱으로 봐야한다. # 신호 사이의 관계를 scatter 그래프의 매트릭스로 확인한다. fig = ff.create_scatterplotmatrix( df[['do_req_set', 'do_act_req']], diag='histogram', title='Digital Output Request vs Digital Input Actual', width=600, height=600 ) fig.show()
resample 과정에서 신호 값을 보간(interpolate) 후 반올림 혹은 내림을 하여 0, 1이 아닌 값들을 0, 1로 대체하였다. 발견 - Resample 2nd Trial
- round(), floor(), ceil()로 데이터를 정수화처리하여 디지털 신호에 0, 1이 아닌 값들을 없앴다.
- do_req_set 히스토그램을 보면, 0, 1의 도수 차이가 작다.
- do_act_req 히스토그램을 보면, 0, 1의 도수 차이가 크다.
- 샘플링과 정수화가 do_act_req에 불리(?)하게 작동했다. 불리하지 않게 하는 방법이 있을까? 향후 생각해볼 이슈인가?
df를 feather 파일로 저장
- 위 변환을 다시 할 필요가 없도록 df를 파일로 저장한다.
- feather 포맷으로 저장한다.
# df 저장할 feather 파일 이름을 준비한다. feather = k_dir_data/(mat.stem + '_digital.feather') print(f'{feather = }') # df를 feather 파일로 저장한다. df.to_feather(feather) # df를 feather 파일로부터 읽어온다. df = pd.read_feather(feather) # df를 확인한다. df.sample(5)feather = WindowsPath('data/tio_2025_08_28_17_44_55_digital.feather')ts do_req_set ts_diff_do_req do_act_req ts_diff_do_act_req ts_diff ts_timedelta 0 days 00:00:52.638460 52.648286 1.0 0.499997 1.0 0.009953 0.01 0 days 00:00:55.728460 55.738317 1.0 0.500504 1.0 0.009972 0.01 0 days 00:00:30.978460 30.983470 NaN NaN 1.0 0.010010 0.01 0 days 00:00:32.998460 33.003449 NaN NaN 1.0 0.010010 0.01 0 days 00:00:37.498460 37.508338 1.0 0.500507 1.0 0.009972 0.01 결론
- mat 파일의 신호들을 pandas의 df(DataFrame)으로 만드는 방법을 설명하였다.
- 각 신호는 자신의 타임스탬프를 갖고 있다. 정확히는 CAN 메시지의 타임스탬프이다. 같은 메시지에 속한 신호들은 같은 타임스탬프를 갖는다.
- 측정 주기가 다른 신호들의 타임스탬프들은 당연히 다르다.
- 먼저, 타임스탬프가 같은 신호들끼리 df를 만들 수 있다.
- 여러 df를 합쳐(merge)서 한 df로 만들 수 있다.
- 이 경우, 타임스탬프의 주기가 일정하지 않다.
- 결측치가 발생한다.
- 결측치를 처리해야 한다.
- 디지털 신호의 경우 ffill()을 사용할 수 있다.
- 아날로그 신호의 경우 interpolate()을 사용할 수 있다. <-- 별도 포스트에서 설명할 예정이다.
- pandas의 resample 기능을 이용하여, 타임스탬프의 주기를 일정하게 할 수 있다.
- resample을 할 때 보간 (interpolate)을 하면, 디지털 신호에 0, 1이 아닌 값이 생길 수 있다. 이 값들은 반올림 등으로 처리할 수 있다.
- 위 과정을 반복할 필요 없도록 df를 파일로 저장하여 사용한다.
mat 파일을 데이터프레임으로 변환하고 feather 파일로 저장하기 :: hsl's tsmaster 사용기
'application' 카테고리의 다른 글
조향과 구동 조종을 위한 판넬을 만드는 법 (0) 2025.09.18 TIO 측정 데이터 분석 - 디지털 입출력 (0) 2025.09.02 UDS 기반 제어기 프로그래머 개발 - libTSCAN (5) 2025.08.11 ts_can_flash_programmer 개발 - libTSCAN (3) 2025.07.26 claude.ai와 코드, 문서 분석 및 코드 작성 (3) 2025.07.26