-
CAN 신호를 실시간으로 필터링 하기 - 미니프로그램과 aianalysis 2024. 10. 25. 01:57
시작하기 전에
CAN 신호들로 실시간 연산하기 - 미니프로그램으로 yaw_rate_ws 계산에서 CAN 버스의 뒷 바퀴 신호들 (WHL_SPD_RR, WHL_SPD_RL)에서 요-레이트(yaw_rate_ws)를 계산하고, 차량 센서가 측정하여 CAN 버스에 올린 요-레이트(YAW_RATE)와 그래픽 창에서 비교하는 방법을 설명하였다.
결과를 보면, YAW_RATE는 매끈한데 비해 yaw_rate_ws는 거칠다. yaw_rate_ws에 실시간으로 필터를 적용할 수 있을까? 하는 의문이 들었다.
사실 나는 '필터는 신호와 노이즈가 섞인 데이터에서 노이즈를 제거한다'는 개념적인 이해를 갖고 있으나 연산 측면에서 필터를 어떻게 코드로 작성할 수 있는 지는 모른다. claude.ai에게 요청하여 코드를 작성했다. 그 코드로 yaw_rate_ws에 필터를 적용하여 yaw_rate_ws_filt를 계산할 수 있었다.
아이디어가 생겨서 구현하기까지 전 과정이 2 시간도 안 걸렸다. 필터에 대한 내 지식, 내 코딩 능력, 내 학습 능력을 고려하면 40 시간은 걸려야 할 수 있는 일이라고 짐작한다.
ws(wheel speed)에서 실시간으로 계산한 요-레이트(yaw_rate_ws)에 실시간으로 필터를 적용하여 yaw_rate_ws_filt 를 계산하고 그래프로 표시하였다. 개요
- claude.ai에게 필터 코드를 요청하기
- 기존 yaw_rate_ws를 계산하는 미니프로그램에 필터 코드를 추가하기
- 그래픽에 yaw_rate_ws_filt 추가하기
- 실행
claude.ai에게 필터 코드를 요청하기
- 어디선가 "칼만(Kalman) 필터"라는 것을 들어본 적이 있다.
- yaw_rate_ws에서 노이즈를 제거하기 위해 적절한 필터인지 모르겠지만 이 필터를 적용해보기로 한다. (필터 적용 방법 설명이 주요 주제라서 필터의 적절성은 부차적이다.)
- 데이터를 어레이로 받아서 필터를 해야할 것으로 짐작했었다. ( claude.ai가 작성한 코드를 보니 내 짐작은 틀렸다. 어레이도 필요없다. 변수 1개면 된다.)
- 아래와 같이 요청하고 코드를 받았다.
ai에 요청하여 칼만 필터의 코드를 작성하였다. (ai가 "필터를 코드를 짜주세요"라는 문법 오류에도 불구하고 요청을 잘 알아들었다.) 기존 yaw_rate_ws를 계산하는 미니프로그램에 필터 코드를 추가하기
칼만 필터 사용법
- 칼만 필터 사용법은 간단하다.
- 칼만 필터 클래스를 정의한다. claude.ai가 만든 코드를 복붙한다.
- 칼만 필터 객체를 만든다.
- WHL_SPD11_1 메시지를 받을 때마다 on_can_rx_WHL_SPD11() 함수가 호출된다. on_can_rx_WHL_SPD11() 함수는 yaw_rate_ws를 계산한다.
- 이 계산 후에 yaw_rate_ws에 칼만 필터를 적용하여 yaw_rate_ws_filt를 계산하도록 코드를 수정한다.
- 함수 호출 때마다 칼만 필터를 새로 만들지 않도록 칼만 필터를 Global Definition에서 만든다.
- 칼만 필터가 적용된 yaw_rate_ws_filt를 그래프로 표시하기 위해서 시스템 변수에 저장한다. 이렇게 하기 위해서는 시스템 변수를 사전에 만들어 두어야 한다.
Global Definition 코드 수정
- 아래와 같이 코드를 수정한다.
- "# 칼만 필터" 이하 부분이 추가된 코드다.
- class KalmanFilter의 정의는 claude.ai가 작성한 코드를 복붙하였다.
- kalman_filter = KalmanFilter(...)은 내가 짠 코드 한 줄이다. (한 줄의 코드를 읽기 좋게 여러 줄로 분리했다.) KalmanFilter 클래스에서 kalman_filter 객체를 만든다.
# Databases.py에 dbc의 메시지들과 신호들이 정의됨 # 미니프로그램에서 불러서 사용할 수 있도록import함 import Databases as dbs # Databases.py에 TWHL_SPD11_1 이라는 클래스가 정의되어 있다. # WHL_SPD11_1 메시지에서 신호들을 읽기 위해 # TWHL_SPD11_1 클래스의 변수를 선언한다. WHL_SPD11_1 = dbs.TWHL_SPD11_1() # 칼만 필터 # claude.ai가 작성한 코드이다. # 칼만 필터 클래스를 정의한다. class KalmanFilter: def __init__(self, initial_state, initial_estimate_error, measurement_noise, process_noise): self.state = initial_state self.estimate_error = initial_estimate_error self.measurement_noise = measurement_noise self.process_noise = process_noise def update(self, measurement): # 예측 prediction = self.state prediction_error = self.estimate_error + self.process_noise # 업데이트 kalman_gain = prediction_error / (prediction_error + self.measurement_noise) self.state = prediction + kalman_gain * (measurement - prediction) self.estimate_error = (1 - kalman_gain) * prediction_error return self.state # 칼만 코드 객체를 만든다. kalman_filter = KalmanFilter( initial_state=0, initial_estimate_error=1, measurement_noise=0.1, process_noise=0.01 )시스템 변수 만들기
- yaw_rate_ws_filt를 그래프에 표시하기 위해서는 시스템 변수가 필요하다.
- 메인 메뉴/ Simulation/ System Variables 버튼을 클릭하여 System Variable Management 창을 열고, yaw_rate_ws_filt 변수를 추가한다. (상세한 방법은 CAN 신호들로 실시간 연산하기 - 미니프로그램으로 yaw_rate_ws 계산)의 yaw_rate_ws 사용자변수 만들기 부분을 참조해 주십시오.)
yaw_rate_ws_filt 변수를 위 그림과 같이 정의합니다. on_can_rx_WHL_SPD11() 변경하기
- 아래와 같이 코드를 수정한다.
- "# yaw_rate_ws를 필터링한다." 부분과 " # 사용자 변수 calc.yaw_rate_ws_filt에 위에서 계산한 yaw_rate_ws_filt를 넣는다." 두 부분만 추가된 코드다.
def on_can_rx_WHL_SPD11(ACAN: RawCAN) -> None: # global yaw_rate_ws_filt # global KalmanFilter global kalman_filter # if (ACAN.idx_chn != CH1): # if you want to filter channel # return # WHL_SPD11 메시지를 WHL_SPD11_1 변수에 넣는다. # WHL_SPD11_1 변수를 먼저 선언해주어야 함. # 변수 선언은 Global Definition에서 함 WHL_SPD11_1.FRawCAN = ACAN # yaw_rate_ws를 계산한다. whl_spd_rr_minus_rl = WHL_SPD11_1.WHL_SPD_RR - WHL_SPD11_1.WHL_SPD_RL # 베뉴의 rear track은1.555m이다. # wheel_spd_rr_minus_rl를 rear track으로 나눠서 yaw rate를 구한다. # wheel speed는 kph이다. mps로 변환하기위해 1000 / 36000을 곱한다. # radian을 deg로 변환하기 위해 180 / pi를 곱한다. # 위 연산을 하는 것은 whl_spd_rr_minus_rl에 10.27을 곱하는 것과 같다. yaw_rate_ws = whl_spd_rr_minus_rl * 10.27 # yaw_rate_ws를 필터링한다. yaw_rate_ws_filt = kalman_filter.update(yaw_rate_ws) # 사용자 변수 calc.yaw_rate_ws에위에서 계산한 yaw_rate_ws를 넣는다. app.set_system_var_double("calc.yaw_rate_ws", yaw_rate_ws) # 사용자 변수 calc.yaw_rate_ws_filt에위에서 계산한 yaw_rate_ws_filt를 넣는다. app.set_system_var_double("calc.yaw_rate_ws_filt", yaw_rate_ws_filt)그래프에 yaw_rate_ws_filt 추가하기
- 그래픽스 창의 신호 리스트에서 우클릭하여 Add System Variables 를 클릭하여 yaw_rate_ws_filt를 그래프에 추가한다.
- 나는 쉬운 비교를 위해서 화면에 스플릿을 여러 개 추가하였다. 그리고 각 스플릿에 비교할 신호들을 추가하였다.
실행
- 미니프로그램을 실행한다.
- 메인 메뉴/ Analysis에서 Start Logging 한다. 혹은 메인 메뉴/ Analysis에서 Bus Replay를 한다.
- 위 "시작하기 전에"에 있는 그래프 출력을 볼 수 있다.
결론
- 데이터 처리는 소프트웨어 개발 및 검증에 요긴하다. 특히 실시간 데이터 처리와 표시는 개발 시간 단축에 매우 요긴하다.
- TSMaster는 C와 Python을 지원한다. 두 언어는 ai 코드의 품질이 상당히 좋다. 이번 예제의 칼만 필터의 경우 코드를 단순 복붙하여 사용했다. 잘 작동한다.
- ai의 지원으로 코딩 시간이 극적으로 단축되었다.
- 위에 설명한 방법으로 실시간 FFT(Fast Fourier Transformation) 계산과 표시 방법을 개발하고 있다. 개발이 완료되면 방법을 설명하는 블로그를 작성할 것이다.
- 실시간으로 FFT 하기 - 미니프로그램과 ai :: hsl's tsmaster 사용기
TSMaster 프로젝트
- yaw_rate_ws_filt.zip은 이 블로그에서 설명한 칼만 필터를 적용하는 경우의 TSMaster 프로젝트이다.
- yaw_rate_ws_filt_ema.zip은 칼만 필터 대신에 EMA (Exponential Moving Average) 필터를 적용한 경우의 TSMaster 프로젝트이다. EMA 필터는 아래 식처럼 아주 간단한 연산으로 구현한다. 프로그램 작성 관점에서 칼만 필터가 EMA 필터보다 복잡하여 미니프로그램 작성법 데모라는 취지에 상대적으로 덜 적합하여 EMA 필터 예제를 작성한다.
x_n_filt = x_n-1_filt + g * (x_n_raw - x_n-1_filt)'analysis' 카테고리의 다른 글
mat 파일을 데이터프레임으로 변환하고 feather 파일로 저장하기 (2) 2024.10.25 CAN 신호들로 실시간 연산하기 - 미니프로그램으로 yaw_rate_ws 계산 (0) 2024.10.25 CAN 트레이스 받기 - 하드웨어 설정 (0) 2024.10.25 CAN 트레이스 보기 - 바퀴 속도 (10) 2024.10.25 FFT 비주얼라이저 개선 - 미니프로그램과 ai (2) 2024.10.25