물체 운동 속도 측정기
포토 인터럽터 2개를 일정 거리로 놓고 물체가 첫 번째와 두 번째 빛길을 가리는 시간을 재서 평균 속도를 계산합니다.
- 사용 센서
- 포토 인터럽터 2개, 부저 또는 2색 LED
- 측정값
- 통과 시간, 평균 속도, 반복 실험 편차
코드로 연결하기
조이스틱과 서보모터를 연결하고, 센서 데이터를 과학 실험으로 확장하는 방법을 실습합니다.
센서 입력과 모터 출력을 함께 사용해 작은 조종 시스템을 만들어 봅니다.
응용 구성: 조이스틱 아날로그 입력 + 360도 무한회전 서보 2개 PWM 제어
360도 무한회전 서보는 각도를 정하는 모터가 아니라 속도와 방향을 정하는 모터입니다. 조이스틱이 가운데에 있으면 두 서보가 멈추고, 레버를 앞으로/뒤로 밀면 회전 방향이 바뀌며, 많이 기울일수록 더 빠르게 회전합니다. X축은 좌우 회전, Y축은 전진/후진 속도로 섞어 작은 2바퀴 로봇처럼 움직입니다.
from machine import ADC, Pin, PWM
import time
# 360도 무한회전 서보 2개
left_servo = PWM(Pin(13), freq=50) # D13 S핀
right_servo = PWM(Pin(14), freq=50) # D14 S핀
# 조이스틱: VCC는 5V, VRx/VRy는 저항 분압 후 ESP32 ADC로 입력
joy_x = ADC(Pin(34)) # VRx
joy_y = ADC(Pin(35)) # VRy
joy_sw = Pin(32, Pin.IN, Pin.PULL_UP)
for adc in (joy_x, joy_y):
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)
# 서보마다 정지값이 조금 다릅니다. 중립에서 움직이면 75~78 사이에서 조정하세요.
LEFT_STOP = 77
RIGHT_STOP = 77
MIN_DUTY = 40
MAX_DUTY = 115
# 조이스틱 중립 근처 흔들림을 무시하는 범위입니다.
RAW_DEAD_ZONE = 180
CENTER_SAMPLES = 30
def clamp(value, low=-100, high=100):
return max(low, min(high, value))
def read_center(adc, samples=CENTER_SAMPLES):
total = 0
for _ in range(samples):
total += adc.read()
time.sleep_ms(10)
return total // samples
def axis_to_speed(raw, center):
"""
조이스틱 ADC 값을 -100~100 속도값으로 변환합니다.
중립이면 0, 한쪽으로 밀면 양수, 반대쪽으로 밀면 음수가 됩니다.
"""
delta = raw - center
if abs(delta) < RAW_DEAD_ZONE:
return 0
if delta > 0:
span = max(1, 4095 - center)
else:
span = max(1, center)
speed = int(delta / span * 100)
return clamp(speed)
def set_servo(servo, speed, stop_duty):
"""
speed: -100(역방향 최대) ~ 0(정지) ~ 100(정방향 최대)
"""
speed = clamp(speed)
if speed == 0:
duty = stop_duty
elif speed > 0:
duty = int(stop_duty + (MAX_DUTY - stop_duty) * speed / 100)
else:
duty = int(stop_duty - (stop_duty - MIN_DUTY) * abs(speed) / 100)
servo.duty(duty)
def stop_all():
set_servo(left_servo, 0, LEFT_STOP)
set_servo(right_servo, 0, RIGHT_STOP)
print("조이스틱을 가운데에 놓고 기다리세요.")
center_x = read_center(joy_x)
center_y = read_center(joy_y)
print("중립값:", "x =", center_x, "y =", center_y)
stop_all()
time.sleep(1)
try:
while True:
turn = axis_to_speed(joy_x.read(), center_x)
drive = -axis_to_speed(joy_y.read(), center_y) # 방향이 반대면 -를 제거하세요.
# 버튼을 누르면 즉시 정지
if joy_sw.value() == 0:
stop_all()
time.sleep(0.1)
continue
# 중립이면 drive=0, turn=0이 되어 두 서보가 멈춥니다.
# 레버를 많이 기울일수록 speed 절댓값이 커져 더 빠르게 회전합니다.
left_speed = clamp(drive + turn)
right_speed = clamp(drive - turn)
# 오른쪽 서보가 왼쪽과 마주 보는 방향으로 붙어 있으면 부호를 반대로 줍니다.
set_servo(left_servo, left_speed, LEFT_STOP)
set_servo(right_servo, -right_speed, RIGHT_STOP)
print("turn:", turn, "drive:", drive, "left:", left_speed, "right:", right_speed)
time.sleep(0.05)
except KeyboardInterrupt:
pass
finally:
stop_all()
left_servo.deinit()
right_servo.deinit()
현재 센서 목록만으로 만들 수 있는 간단한 측정 장치 예시입니다. 값은 CSV로 저장하거나 시리얼 플로터로 관찰하면 탐구 보고서 자료로 바로 쓸 수 있습니다.
포토 인터럽터 2개를 일정 거리로 놓고 물체가 첫 번째와 두 번째 빛길을 가리는 시간을 재서 평균 속도를 계산합니다.
진자가 중앙을 지날 때 포토 인터럽터가 신호를 만들도록 설치하고, 연속 통과 시간 차이로 주기를 구합니다.
LED 또는 레이저를 용액 뒤에서 비추고 조도 센서로 통과한 빛을 읽어 탁도 변화나 색 변화 속도를 비교합니다.
반응 전후 온도 변화를 일정 간격으로 기록해 냉각 곡선이나 반응열 차이를 비교합니다.
빛의 세기와 온습도를 함께 기록하고, LED 색이나 조도 조건을 바꿔 식물 실험 환경을 비교합니다.
발효 용기 주변 온습도를 기록하고, 투명 튜브를 지나는 기포를 포토 인터럽터로 세어 발효 속도를 비교합니다.
LED가 켜진 순간부터 터치 센서나 버튼을 누르기까지의 시간을 재서 시각 자극 반응 시간을 비교합니다.
온습도와 조도를 기록하면서 팬이나 조명 조건을 바꿔 물의 증발 또는 표본 건조 속도 차이를 비교합니다.