움직임·거리·위치 · 난이도 보통
VL53L0X 레이저 거리 센서로 mm 단위 거리를 재고 표면 색과 햇빛이 줄이는 최대 거리 확인하기
VL53L0X는 940nm 레이저가 돌아오는 시간으로 거리를 재는 I²C 센서예요(주소 0x29). 3.3V 배선, 일곱 번 재서 중앙값을 쓰는 코드, 10cm에서 잡는 오프셋 보정, 흰색·회색 표면과 햇빛에 따라 줄어드는 최대 거리, 탐구 세 가지를 정리했어요.
이 센서로 무엇을 잴 수 있나
VL53L0X는 ST마이크로일렉트로닉스(ST)가 만든 거리 센서예요. 눈에 보이지 않는 940nm 레이저 빛을 짧게 쏘고, 물체에 맞고 돌아오기까지 걸린 시간(비행 시간, ToF)으로 거리를 mm 단위로 알려 줘요. 손톱만 한 칩이라 작은 로봇, 손을 가까이 대면 반응하는 장치, 책상 위 물체까지의 거리를 재는 실험에 잘 맞아요.
데이터시트는 최대 2m까지 잴 수 있다고 적지만, 그것은 조건이 좋을 때의 이야기예요. 실내(적외선 없음)에서 흰 표면(반사율 88%)은 보통 200cm 넘게(장거리 설정), 최소 120cm이고, 회색 표면(17%)은 보통 80cm, 최소 70cm예요. 흐린 날 바깥처럼 적외선이 섞이면 흰 표면도 보통 80cm로 줄어요. 그래서 이 센서로는 "표면 색과 빛 환경이 최대 거리를 얼마나 바꾸나" 자체를 탐구할 수 있어요.
원리
빛은 1m를 약 3.3ns(나노초)에 가요. 1m 앞의 물체라면 왕복에 약 6.7ns가 걸리는데, 피코의 시계로는 잴 수 없을 만큼 짧은 시간이에요. VL53L0X 안의 SPAD(빛 알갱이 하나도 감지하는 다이오드) 배열과 계산 회로가 이 시간을 대신 재고, 결과를 거리로 바꿔 I²C로 넘겨줘요.
센서가 보는 범위(시야각, FOV)는 25°예요. 시야의 지름은 거리 × 2 × tan(12.5°), 곧 거리의 약 0.44배라서 30cm에서는 지름 약 13cm, 1m에서는 지름 약 44cm 원이에요. 데이터시트의 거리 표도 과녁이 이 시야를 다 채운 조건에서 잰 값이에요. A4 종이(폭 21cm)를 1m 앞에 두면 시야를 다 채우지 못해 뒤쪽 벽이 함께 보여요.
정확도는 표준편차로 적혀 있어요. 기본 시간(33ms)으로 실내에서 흰 표면 120cm일 때 4%, 회색 표면 70cm일 때 7%예요. 재는 시간을 늘리면 좋아져서, 고정밀 설정(200ms)은 1.2m에서 ±3% 안이에요. 데이터시트는 오프셋을 10cm에서 보정하라고 권하고, 보정 뒤에도 전압이나 온도가 바뀌면 오프셋이 보통 ±10mm 움직인다고 적어요.
I²C 주소는 데이터시트 표기로 0x52인데, 이것은 읽기·쓰기 비트를 붙인 8비트 표기예요. MicroPython의 i2c.scan()은 7비트 주소인 0x29로 보여 줘요. 데이터시트는 주소를 프로그램으로 바꿀 수 있다고 적어요.
준비물
- 라즈베리파이 피코와 USB 케이블
- VL53L0X 모듈(VIN·GND·SCL·SDA·GPIO1·XSHUT), 암-수 점퍼선
- 줄자나 1m 자, 센서를 고정할 받침
- 과녁: 흰 도화지, 검은 도화지, 알루미늄 호일, 투명 파일(모두 같은 크기의 판에 붙여요)
배선
브레드보드 따라 꽂기 순서 (9단계)
- 피코를 브레드보드 가운데 홈에 걸쳐 꽂아요. USB 단자가 왼쪽을 보게 하고, 1번 핀이 f2, 40번 핀이 e2에 들어가요. 다리가 홈 양쪽에 모두 들어갔는지 옆에서 봐요.
- 주황 선: 피코 36번 핀(3V3(OUT)) 옆 칸 a6 → 위쪽 + 줄(3.3V). 이제 이 줄 전체가 3.3V예요.
- 검은 선: 피코 38번 핀(GND) 옆 칸 a4 → 위쪽 − 줄(GND). 이제 이 줄 전체가 GND예요.
- 레이저 거리, ToF VL53L0X 꽂기: 다리 VIN · GND · SDA · SCL를 위쪽 a24 · a25 · a26 · a27에 차례로 꽂아요. 다리가 브레드보드에 바로 안 들어가면 암-수 점퍼선으로 같은 칸에 이어요.
- 주황 선(3.3V 전원): 레이저 거리, ToF VIN 줄 b24 → 위쪽 + 줄(3.3V)
- 검은 선(GND): 레이저 거리, ToF GND 줄 b25 → 위쪽 − 줄(GND)
- 파란 선(신호): 레이저 거리, ToF SDA 줄 b26 → 피코 6번 핀(GP4) 옆 칸 j7
- 파란 선(신호): 레이저 거리, ToF SCL 줄 b27 → 피코 7번 핀(GP5) 옆 칸 j8
- 마지막 확인: ① 위쪽 + 줄(3.3V)과 아래쪽 + 줄(5V)이 서로 이어지지 않았나요? ② 모든 부품의 GND가 − 줄에 이어졌나요? ③ 5V 신호 다리는 저항을 거쳐 피코에 이어졌나요? ④ 긴 브레드보드는 + − 줄이 가운데에서 끊긴 것도 있어요(빨간·파란 선이 끊겨 있으면 짧은 선으로 이어요). ⑤ 그림과 한 칸씩 비교한 뒤 USB를 꽂아요.
| 모듈 핀 | 피코 핀 | 비고 |
|---|---|---|
| VIN | 3V3 (36번) | 칩 동작 전압 2.6~3.5V |
| GND | GND (38번) | 피코와 접지를 함께 써요 |
| SDA | GP4 (6번) | I2C0 SDA |
| SCL | GP5 (7번) | I2C0 SCL |
| GPIO1 · XSHUT | 연결 안 함 | 센서를 여러 개 쓸 때 XSHUT을 써요 |
센서 창에 보호 필름이 붙어 있다면 떼고 써요(시나브로 PICO 안내). 데이터시트는 센서 앞에 덮개 유리를 두면 그 유리에서 되돌아오는 빛(크로스토크)을 보정해야 한다고 적어요. 처음에는 아크릴 창 없이 맨 센서로 재요.
XSHUT은 LOW일 때 센서를 끄는 핀이에요. 비워 둔 채 스캔에 0x29가 보이지 않는다면 XSHUT을 3V3에 이어 봐요. 같은 센서를 여러 개 쓸 때는 XSHUT으로 하나씩 켜 가며 주소를 바꿔요.
코드
from machine import Pin, I2C
import time
from vl53l0x import VL53L0X # kevinmcaleer/vl53l0x (BSD-3-Clause)
i2c = I2C(0, sda=Pin(4), scl=Pin(5))
print("I2C:", [hex(a) for a in i2c.scan()]) # 0x29가 보여야 해요
tof = VL53L0X(i2c)
OFFSET = 0 # 10cm 보정으로 구한 값(mm). 처음에는 0
def median_mm(n=7):
vals = []
for i in range(n):
d = tof.ping() # 한 번 재고 멈춤, mm
if 0 < d < 8000: # 8190·8191 같은 값은 측정 실패 표시
vals.append(d)
time.sleep_ms(40)
if not vals:
return -1, 0
vals.sort()
return vals[len(vals) // 2] - OFFSET, len(vals)
while True:
mm, ok = median_mm()
print(f"DATA: dist_mm={mm}, ok={ok}")
time.sleep(0.3)
- 라이브러리는 kevinmcaleer/vl53l0x의
vl53l0x.py(BSD-3-Clause)예요. 시나브로 PICO는 실행할 때 이 파일(커밋 고정)을 피코의/lib에 알아서 넣어 줘요. ping()은 측정을 한 번 시작하고 결과를 읽은 뒤 멈춰요. 돌려주는 값은 mm예요.- 측정이 실패하면 8190이나 8191 같은 큰 값이 와요. ST 커뮤니티 답변은 이것을 측정 실패 표시라고 설명해요. 코드는 8000보다 큰 값을 버리고, 일곱 번 중 성공한 값의 중앙값을 써요.
ok는 일곱 번 중 성공 횟수예요. 이 숫자가 줄면 과녁이 멀거나, 어둡거나, 빛을 되돌려 보내지 않는다는 신호예요.
값 해석과 보정
오프셋은 직접 재요. 라이브러리 README에는 지은이가 시험했을 때 약 50mm 차이가 나서 결과에서 50을 뺐다는 메모가 있어요. 내 센서가 같다는 보장은 없어요. 흰 판을 센서 앞면에서 정확히 100mm에 세우고 dist_mm를 20줄 기록해 평균을 내요. 평균 − 100이 내 센서의 OFFSET이에요. 데이터시트가 오프셋 보정을 10cm에서 권하는 것과 같은 거리예요.
기울기도 확인해요. 오프셋을 넣은 뒤 200mm, 500mm, 1000mm에서도 재 봐요. 거리가 멀수록 차이가 커진다면 실제 거리 = a × 센서 거리 + b 꼴의 보정식을 구해요(센서 값의 신뢰도).
과녁이 시야를 채우는지 계산해요. 거리 × 0.44보다 큰 과녁을 써야 해요. 50cm라면 지름 22cm보다 넓은 판이 필요해요. 작은 물체를 재야 한다면 거리를 줄여요.
표면이 결과를 바꿔요. 데이터시트 표처럼 어두운 표면은 최대 거리가 짧아져요. 거울이나 호일처럼 반짝이는 면은 빛을 한쪽으로 튕겨 내서 기울어진 정도에 따라 측정이 실패하기도 해요. 투명한 판은 빛이 통과해 뒤쪽 물체까지의 거리가 나올 수 있어요.
자주 하는 실수
- 보호 필름을 붙인 채 쓰기. 필름에서 되돌아오는 빛 때문에 거리가 짧게 나오거나 측정이 실패해요.
- README의 −50mm를 그대로 빼기. 센서마다 달라요. 100mm에서 직접 구해요.
- 작은 과녁을 멀리 두기. 시야를 다 채우지 못해 뒤의 벽이 섞여요.
- 8190을 거리로 그래프에 넣기. 측정 실패 표시예요. 코드처럼 걸러요.
- 창가의 햇빛 아래에서 실내 결과와 섞기. 적외선이 섞이면 최대 거리와 정확도가 함께 떨어져요. 장소를 따로 적어요.
이 센서로 잴 수 있는 탐구
1. 거리에 따라 오차가 커질까
- 질문: 실제 거리가 멀어질수록 센서 거리와 자로 잰 거리의 차이가 커질까?
- 바꾸는 것: 실제 거리 100mm부터 1000mm까지 100mm 간격
- 재는 것: dist_mm(mm), 실제와의 차이(mm), ok(7번 중 성공 횟수)
- 방법: 센서와 흰 판을 받침에 고정하고 판이 시야를 채우게 크기를 맞춰요. 거리마다 10줄 평균을 내고 3번 반복해 보정식을 구해요.
2. 표면에 따라 잴 수 있는 최대 거리가 달라질까
- 질문: 흰 도화지, 검은 도화지, 알루미늄 호일, 투명 파일은 각각 몇 mm까지 잴 수 있을까?
- 바꾸는 것: 과녁 표면 4가지(같은 크기의 판)
- 재는 것: ok가 4 이상으로 나온 가장 먼 거리(mm)
- 방법: 커튼을 친 교실에서 300mm부터 50mm씩 멀리 옮기며 재요. 판은 센서와 마주 보게 세우고, 표면마다 3번 반복해요.
3. 햇빛은 얼마나 방해할까
- 질문: 같은 흰 판을 500mm에서 잴 때, 커튼 친 교실과 햇빛 드는 창가에서 값이 흔들리는 폭은 얼마나 다를까?
- 바꾸는 것: 장소(커튼 친 교실, 햇빛 드는 창가, 그늘진 바깥)
- 재는 것: dist_mm 50줄의 표준편차(mm), ok의 평균(회)
- 방법: 받침과 판을 그대로 옮겨 같은 거리로 맞춰요. 장소마다 3번 기록하고 시각과 날씨도 적어요.
안전
- VL53L0X는 데이터시트상 IEC 60825-1:2014 기준 Class 1 레이저 기기예요. 정상적으로 쓸 때 눈에 안전한 등급이지만, 일부러 센서 창을 가까이 들여다보지 않아요.
- 로봇에 달아 움직일 때는 느린 속도로 먼저 시험하고, 책상 끝에서는 손으로 잡을 준비를 해요.
- 이 장치는 교육용이에요. 주차 보조나 넘어짐 감지처럼 사람의 안전이 걸린 곳에 쓰지 않아요.
- 전원은 3V3에만 잇고, 배선은 USB를 뽑은 채로 바꿔요.
더 알아보기
- 데이터시트: ST "VL53L0X World's smallest Time-of-Flight ranging and gesture detection sensor" DocID029104 Rev 2(940nm, Class 1, 2.6~3.5V, 주소 0x52, 시야각 25°, 최대 거리·정확도·측정 시간·오프셋 표)
- 라이브러리: kevinmcaleer/vl53l0x(BSD-3-Clause)의
vl53l0x.py와 README(ping(), 50mm 메모), 예제tof_test.py - 참고: ST 커뮤니티 "Why vl53l0x returns 8191 all the time"(8190·8191은 측정 실패 표시)
- 기초 글: I²C 장치 여러 개 연결하기, 센서 값의 신뢰도, 잴 수 있는 탐구 주제 만들기, 피코 핀 지도
- 함께 읽기: HC-SR04 초음파 거리 센서