addinedu

encoder 로 속도구하기

엔코더 Position 데이터로부터 속도 계산. 미분을 통한 각속도 계산과 Low-pass filter를 적용한 노이즈 제거 방법 설명.

Machine Learning
Python

Encoder (엔코더)

엔코더(Encoder)는 기계의 회전이나 직선 운동과 같은 물리적 움직임을 전기 신호로 변환하여 위치, 속도, 방향 정보를 알려주는 센서로, 모터 제어, 로봇 공학 등 자동화 분야에서 필수적인 피드백 장치이며, AI 분야에서는 데이터를 압축하거나 특징을 추출하는 모듈을 의미하기도 한다. 이는 '코드로 바꾼다(Encode)'는 의미에서 유래하여, 아날로그 신호를 디지털 펄스 신호로 변환하는 역할을 합니다.

In [5]:
from matplotlib.legend_handler import update_from_first_child
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 샘플링 시간: 엔코더에서 Position 값을 읽는 시간 간격 (0.0001초 = 10kHz)
ts = 0.0001

# 엔코더 데이터 URL
url = "https://raw.githubusercontent.com/PinkWink/for_ROS2_study/refs/heads/main/%EA%B3%B5%EC%A7%9C%EC%88%A0/filter%20tutorials/exVelocityCalcRawData.csv"
raw_data = pd.read_csv(url)
raw_data = raw_data.drop(raw_data.columns[0], axis=1)

# Position 값의 의미:
# - 엔코더에서 측정된 누적된 각도(radian)를 나타낸다
# - 측정 시작 시점부터 현재까지 총 회전한 각도를 radian 단위로 누적한 값
# - 예: Position = 2π rad → 1바퀴 회전, Position = 4π rad → 2바퀴 회전
# - Position 값의 변화율(미분)을 계산하면 각속도(angular velocity)를 구할 수 있다
# - 속도 = d(Position)/dt = (Position[n] - Position[n-1]) / ts
# - 여기서 ts는 샘플링 시간(0.0001초)이다

raw_data.head()
time Position
0 0.0001 165.879903
1 0.0002 165.879903
2 0.0003 165.879903
3 0.0004 165.879903
4 0.0005 165.879903
Plain text view
     time    Position
0  0.0001  165.879903
1  0.0002  165.879903
2  0.0003  165.879903
3  0.0004  165.879903
4  0.0005  165.879903

그래프로 데이터를 그려보기

In [2]:
plt.figure(figsize=(12,5))
plt.plot(raw_data.time, raw_data.Position)
plt.title("Position Raw Data", size=15)
plt.xlabel("time (s)", size=15)
plt.ylabel("rad", size=15)
plt.axis([4, 25, 0, 250])

plt.grid(True)
Notebook output
In [19]:
vel_diff = np.zeros(len(raw_data.time))

for i in np.arange(1, len(raw_data.time)):
    vel_diff[i] = (raw_data.Position[i] - raw_data.Position[i-1]) / ts

raw_data['vel_diff'] = vel_diff
In [20]:
plt.figure(figsize=(12,5))
plt.plot(raw_data.time, raw_data.vel_diff, raw_data.time, raw_data.Position, "c")
plt.xlabel("time (s)", size=15)
plt.ylabel("rad or rad/s", size=15)
plt.title("Position and Velocity", size=15)
plt.axis([4, 25, -270, 240])
plt.grid(True)
Notebook output

low-pass filter 를 적용

In [21]:
from signal_processor import SignalProcessor

sp = SignalProcessor(window_size=100, alpha=0.0099)

lp = []

for val in raw_data.vel_diff:
    lp.append(sp.low_pass_filter(val))

raw_data['vel_LPF'] = lp
In [22]:
plt.figure(figsize=(12,5))
plt.plot(raw_data.time, raw_data.vel_diff, raw_data.time, raw_data.vel_LPF, "r")
plt.xlabel("time (s)", size=15)
plt.ylabel("rad/s", size=15)
plt.title("Velocity LPF and Raw Data", size=15)
plt.legend(["velocity diff", "velocity LPF"])
plt.axis([4, 25, -270, 240])
plt.grid(True)
Notebook output

필터를 적용한 속도와 위치를 함께 그려보기

In [26]:
plt.figure(figsize=(12,5))
plt.plot(raw_data.time, raw_data.vel_LPF, raw_data.time, raw_data.Position, "c")
plt.xlabel("time (s)", size=15)
plt.ylabel("rad or rad/s", size=15)
plt.title("Position and Velocity LPF", size=15)
plt.axis([4, 25, -150, 240])
plt.legend(["velocity LPF", "Position"])
plt.grid(True)
Notebook output

12.4 ~ 13.5 초 사이 구간을 확대

In [24]:
plt.figure(figsize=(12, 5))
plt.plot(
    raw_data.time, raw_data.vel_diff, 'r',
    raw_data.time, raw_data.vel_LPF, 'c',
    raw_data.time, raw_data.Position, 'k'
)
plt.xlabel('time (s)', size=15)
plt.ylabel('rad or rad/sec', size=15)
plt.title('Calculating Velocity using simple LPF', size=15)
plt.legend(['vel_diff', 'vel_LPF', 'Position'])
plt.axis([12.4, 13.5, -150, 150])
plt.grid(True)
Notebook output

12.8 ~ 12.9초 구간 확대

plt.figure(figsize=(12, 5))
plt.plot(
    raw_data.time, raw_data.vel_diff, 'r',
    raw_data.time, raw_data.vel_LPF, 'c',
    raw_data.time, raw_data.Position, 'k'
)
plt.xlabel('time (s)', size=15)
plt.ylabel('rad or rad/sec', size=15)
plt.title('Calculating Velocity using simple LPF', size=15)
plt.legend(['vel_diff', 'vel_LPF', 'Position'])
plt.axis([12.8, 12.9, -50, 50])
plt.grid(True)
Notebook output

확대해 보니 vel_diff 의 밀도가 갈 수록 희미해졌다. PWM 주파수가 엔코더 샘플링(10kHz: ts = 0.0001초)보다 낮으면, 한 PWM 주기 동안 여러 샘플이 수집되어 펄스형 패턴이 관찰된다.

즉, 해당 구간에서 속도가 변한 이유는 엔코더 샘플링 주파수가 PWM 주파수보다 컸기에 vel_diff 가 펄스로 관측되었고, 펄스의 밀도의 변화는 low-pass filter 덕분에 속도를 부드럽게 그릴 수 있었다.