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)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)
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)필터를 적용한 속도와 위치를 함께 그려보기
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)
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)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)확대해 보니 vel_diff 의 밀도가 갈 수록 희미해졌다. PWM 주파수가 엔코더 샘플링(10kHz: ts = 0.0001초)보다 낮으면, 한 PWM 주기 동안 여러 샘플이 수집되어 펄스형 패턴이 관찰된다.
즉, 해당 구간에서 속도가 변한 이유는 엔코더 샘플링 주파수가 PWM 주파수보다 컸기에 vel_diff 가 펄스로 관측되었고, 펄스의 밀도의 변화는 low-pass filter 덕분에 속도를 부드럽게 그릴 수 있었다.