|
|
민선생의 전자공방 ・ 2026. 6. 25. 13:46
| 브레드보드 활용 IO 10편 |
| 🤖 아두이노 R4로 세우는 인버티드 펜듈럼 — 하드웨어 구성 & 9편 프로젝트 로드맵 — |
| LM가이드 · NEMA17 · DRV8825 · MPU6050 · PID 이중루프 · 스윙업 제어 아두이노 프로젝트 · 민선생의 전자공방 |
■ 프롤로그
안녕하세요, 민선생입니다. 지금까지 브레드보드 활용 IO 인터페이스 시리즈를 통해 I2C OLED, RC 서보모터, 초음파 센서, 온습도 센서, MPU-6050, 스텝모터까지 다양한 센서와 액추에이터를 하나씩 경험해 왔습니다.
이번 10편부터는 그동안 배운 모든 기술을 하나로 집대성하는 종합 프로젝트를 시작합니다. 바로 제어공학의 'Hello World'라 불리는 인버티드 펜듈럼(Inverted Pendulum, 역진자) 프로젝트입니다.
| 📌 이전 편 참조 |
| 이 프로젝트는 지금까지 배운 내용이 모두 활용됩니다. |
| • 8편 MPU-6050 자이로·가속도 센서 → 펜듈럼 각도 감지의 핵심 센서 |
| • 9편 스텝모터 + DRV8825 드라이버 → 카트를 움직이는 액추에이터 |
| • 1편 I2C 통신 + LCD → 상태 모니터링 디스플레이 |
| • 공통 Non-blocking 타이머 구조 → 탈조 없는 실시간 제어의 핵심 |
| ✨ 30년 만의 재도전 — MC68000에서 Arduino R4로 |
| 사실 저는 1990년대에 MC68000 프로세서와 DC 서보모터, 쇠봉 하드웨어로 |
| 인버티드 펜듈럼 제어를 석사 논문 주제로 삼았습니다.(사실 제가 선택한것은 아니고 교수님 주제로 정해 주셨습니다^^) |
| 주신 펜듈럼 장치를 세우려고 정말 몇번이나 회로를 다시 만들고 만들고, 다시 설계하고 하고 했는데, 남들은 별거 아니라고 해도 당시 정말 지식이나 경험이 부족했던 저는 자존심이 상해 이걸 세우기 전까지는 논문을 쓸 수 없었습니다. 해도 해도 안되기에 이건 DC 서보모터 드라이버의 문제다 생각이 들어. 제조회사를 알아내 점검해봐야 겠다 생각했습니다. DC 서보 모터 드라이버하고, 혹시 몰라 장착되어 있던 일제 서보 모터를 떼어 들고 서보 모터 드라이버 생산 업체가 있던 인천남동공단 회사까지 가방에 메고 대전에서 고속 버스를 타고 갔습니다. 드라이버가 고장난것 같다 수리 해줄 수 있는지 문의하고 점검받고 싶어서^ ㅎㅎ 당시 인천 남동 공단에 있던 서보 드라이버 생산업체 담당자 분이 딱했는지 친절하게 점검해 주셨는데, 드라이버가 아닌 장착되 있던 일제 모터가 고장이 났다 했습니다. 선생 봉급에 1994년 당시 60만원(일제로 구매하려면 더 비쌌습니다.) DC 서보모터 90W 로터리 엔코더 내장형이라 비쌌습니다. 지금도 이정도 급 사려면 50~60만원 이상은 주어야 구매 가능합니다.^ 거의 한달 월급 주고 국산 모터를 새로사서 교체하고, 다시 테스트... 회로도 다시 몇번을 납땜해 만들고 했는데 아무리 해도 서질 않았습니다. 암만 봐도 모터 토크는 부족했습니다. 교수님이 주신 장치는 공작기계용 볼스크류에 몇 kg이나 되는 쇠봉을 단 무거운 시스템이었습니다. 이송 속도는 느렸지만 움직이기 시작하면 정말 무서울 정도였습니다. 교수님이 해볼만 하니 주셨을 텐데... 그때는 왜 그렇게 어려운 장치를 제게 주셨는지 이해하지 못했습니다. 나중에 알고 보니, 그 장치를 성공시킨 사람이 제가 처음이었다고 합니다. 당시에는 몰랐지만, 이전 실험에서 모터가 과부하로 고장 난 상태였던 것 같습니다. 정말 마지막, 지푸라기라도 집는 심정으로 뱅뱅(Bang-Bang) 제어를 해보았고, 우와~~~~ 드디어 펜듈럼을 세우는데 성공했습니다. 뱅뱅제어 정말 이름이 좋지.. 그냥 좌우로 최대힘으로 움직이는 것입니다. 파닥파닥 움직여 쇠막대를 세웠습니다. 1994년 9월경 저녁 8시반 즘 실습실에서..... 아이들 전자기기 기능사 실기 지도를 하면서 나는 버벅대며 어셈블리로 코딩하다. 마지막이다 싶어 그동안 시도 했던 PID를 포기하고 정말 단순한 뱅뱅제어를 했보았는데....... 그제서야 막대가 직립되었습다. 아무튼 펜듈럼 막대는 세웠고 다만 당시 PID가 아닌 뱅뱅제어로 성공한것에 헛 웃음도나고 부끄럽기도 했지만 교수님도 웃으며 수고했다 하시곤, 논문 쓰라 하시기에 번개처럼 한달만에 논문을 써서 석사는 했습니다. 남들보다 6개월 더 걸렸지만요. ^^; |
| 30여년이 지난 지금, Arduino R4 + 3D 프린팅 + 카본봉으로 |
| 다시한번 그때의 아쉬움을 이중루프 PID 제어 인버티드 펜듈럼으로 완성해 보고자 합니다. |
| 솔직히 자신은 없습니다^^ 나이들어 가만있으면 중간은 가는데, 이런거 하다 안되면 본전도 못찿을건 세상살아 잘 아는데, 도전안하는게 맞긴한데...... ^^; 그래도 피지컬 코딩을 시작하는 분들에게 6개월의 시행착오는 덜어 줄 수는 있겠다 싶어 다시한번 해보려 합니다.^^ |
공작용 볼스크류와 DC 서보모터, 쇠봉과 쇠막대로 구성된 인버티드펜듈럼, 1994
당시에는 스마트폰도 디지털카메라도 흔하지 않던 시절이라 별도의 사진은 남아 있지 않습니다.
위 사진은 석사 논문에 실린 사진을 캡처한 것입니다. 화질이 다소 좋지 않은 점 양해 부탁드립니다.
펜듈럼 제러를 위한 MC68000 어셈블 프로그램 일부
1. 인버티드 펜듈럼이란?
인버티드 펜듈럼은 카트(이동하는 받침대) 위에 세운 막대를 쓰러지지 않도록 유지하는 장치입니다. 마치 손가락 위에 막대를 세우고 균형을 잡는 것처럼, 카트를 좌우로 이동시켜 막대가 넘어지려는 방향을 상쇄합니다. 휴머노이드 로봇이 서서 자세를 유지하는 가장 기본적이며 중요한 제어시스템입니다. 로봇을 추라 생각해 보세요 어떻게 서있도록 제어해야 하는지!! ^
인버티드 펜듈럼 설계 스케치 — 전체 구성 한눈에 보기
| 🎯 이 프로젝트를 선택한 이유 |
| 왜 인버티드 펜듈럼인가? |
| ① 제어공학의 교과서적 예제 — PID 제어, 상태공간 제어, 칼만 필터가 모두 적용됨 |
| ② 센서(MPU-6050) + 액추에이터(스텝모터)의 실시간 융합 제어 경험 |
| ③ Non-blocking 타이밍 설계의 중요성을 몸으로 익히는 최고의 실습 |
| ④ 완성 시 로봇 공학 진입을 위한 탄탄한 기반 확보 |
2. 하드웨어 구성
2-1. 전체 구성 사진
인버티드 펜듈럼 — 전체 구성 한눈에 보기
정면 모습 (카트가 중앙, 카본봉 펜듈럼이 하사점으로 늘어진 상태), 이 상태에서 좌우로 스윙(좌우로 운동하며 운동에너지로 직립까지 하도록)하며 직립하게 하는것이 목표입니다.
인버티드펜듈럼 하드웨어 구성
1. LM 가이드와 스텝핑 모터 지지대(양쪽)
STL 파일 여기의 스텝모터와 LM가이드 부분을 그대로 사용하였으니 참조 바랍니다.
바디는 "별도로 설계하지 않고 이전 Arduino Project 6편에서 제작했던 CNC 드로잉 머신의 X축 LM 가이드와 스텝모터 부분을 그대로 활용했고, 펜듈럼 막대 지지대 부분만 설계했고 펜듈럼 막대는 화분에 지지대로 사용했던 카본막대를 사용 했습니다. 덕분에 하드웨어 제작 시간을 크게 줄일 수 있었습니다."
Arduino GRBL CNC 드로잉 머신 만들기— G코드로 배우는 현대 기계제어의 세계 : Arduino Project 6편
아두이노 CNC 드로잉 머신을 직접 만들어보며 GRBL 설정과 G코드의 원리를 정리한 실습형 프로젝트...
blog.naver.com
펜듈럼 바디로 XY 플로터의 X축 부품과 스텝모터 부분 그대로 사용하였으니 다운로드해 사용하셔요.
기타 펜듈럼 고정부는 3D 프린터로 설계한 파일을 공유하니 필요하시면 자유로이 활용하셔도 좋습니다. 펜듈럼 막대는 구매한것이 아니라 제가 제 사무실에 화분 지지대로 쓰던 카본막대를 사용했으니 주위에서 비슷한 (낙시대 아니면, 가늘고 긴 나무 막대도 구해 사용하시면 될듯 합니다. ) 막대를 구해 사용하시면 됩니다.
2. I2C Character LCD 20*4 고정 구조물
본 프로젝트의 구조물 일부는 Thingiverse 사용자 farmerde의 작품을 활용하였습니다.
해당 파일은 크리에이티브 커먼즈 저작자표시-비영리 (CC BY-NC) 라이선스로 배포됩니다.
This thing was created by Thingiverse user farmerde, and is licensed under Creative Commons - Attribution - Non-Commercial
thingiverse에 가셔서 "Character LCD 20x4 Holder" 로 검색하시면 찾으실 수 있습니다.
LCD 20x4 Holder by Prosto_Maksks
PLA,0,230%
www.thingiverse.com
Thingiverse에 있는 Character LCD 3D 자료
I2C Character LCD 20*4 지지대 Thingiverse STL 파일 목록
3. LM 가이드위에 붙이는 기둥과 펜듈럼 고정 지지대
첨부파일
막대 받침-3-2 너트 내장.STEP
첨부파일
센서 와셔.STEP
첨부파일
센서 케이스.STEP
펜듈럼 지지부와 MPU6050 케이스
2-2. 구성 부품 목록
| 구분 | 부품 | 규격·설정 | 역할 |
| 제어기 | Arduino UNO R4 WiFi | ARM Cortex-M4 48MHz | 메인 제어 · Non-blocking 스케줄러 |
| 스텝모터 | NEMA 17 | 1.8°/step · 200 step/rev | 카트 구동 |
| 모터드라이버 | DRV8825 | 1/8 마이크로스텝 M0=H · M1=H · M2=L | 스텝 펄스 증폭 및 전류 제어 |
| 각도센서 | MPU-6050 | I2C 0x68 · Roll 값 사용 | 펜듈럼 각도·각속도 측정 |
| 디스플레이 | I2C LCD 20×4 | I2C 0x27 | 초기화·호밍 상태 표시 |
| 안전장치 | 리밋스위치 ×2 | 병렬 연결 → D3 평시 LOW · 터치 HIGH | 호밍 끝점 감지 + 비상정지 |
| 기구부 | LM 가이드 | 전체 420mm | 카트 직선 이동 레일 |
| 벨트 | GT2 타이밍 벨트 | 20T × 2mm 풀리 200 step = 40mm | 모터 → 카트 동력 전달 |
| 펜듈럼 막대 | 카본 낚싯대 | 820mm · 42g | 가볍고 긴 막대 → 제어 여유 확보 |
| 전원 | 12V DC | 아두이노 + 드라이버 공유 | 모터 토크 충분히 확보 |
✅ 부품 선정 핵심 이유 (★아두이노 R4 선택의 이유)
• Arduino UNO R4 WiFi 선택의 결정적 이유 (핵심 두뇌): 기존 아두이노 R3(ATmega328, 8비트, 16MHz)는 복잡한 소수점 연산을 소프트웨어로 흉내 내기 때문에 연산 속도가 매우 느립니다. 반면 R4는 32비트 ARM Cortex-M4(48MHz) 코어를 탑재하고 있으며, 특히 하드웨어적으로 소수점 연산을 가속하는 **FPU(부동소수점 연산 장치)**를 내장하고 있습니다. 덕분에 atan2 각도 계산, 칼만 필터, PID 제어의 복잡한 실수 연산을 수 마이크로초(μs) 이내에 처리하여 스텝모터의 초고속 제어(2,000Hz 이상)를 가능하게 해줍니다.
• 카본봉 (820mm / 42g): 길고 가벼울수록 쓰러지는 주파수가 낮아져 제어 반응 시간에 여유가 생깁니다. (무거운 쇠봉을 썼던 30년 전의 실패를 반면교사 삼아 선택!)
• DRV8825 1/8 마이크로스텝: 1 step = 0.025mm의 고분해능으로 덜컹거림 없는 미세 위치 제어가 가능합니다.
• LM 가이드: 마찰력이 낮고 직선 정밀도가 뛰어나 모터의 부하를 최소화하고 제어 안정성을 극대화합니다.
2-3. 제어부 배선
Inverted Pendulum(역진자) 브레드보드 회로 배선도(배선도 툴 Wokwi)
실제 제어부 — Arduino R4 · DRV8825 드라이버 · 브레드보드 배선
핀 배선 정리
| 아두이노 핀 | 연결 부품 | 신호·기능 | 비고 |
| D3 | 리밋스위치 ×2 | Homing 끝점 + 비상정지 | INPUT_PULLUP · 디바운싱 20ms |
| D4 | DRV8825 DIR | 모터 방향 제어 | CW=HIGH / CCW=LOW |
| D5 | DRV8825 STEP | 스텝 펄스 출력 | micros() Non-blocking |
| D6 | DRV8825 EN | 드라이버 Enable | Active LOW · 정지 시 HIGH |
| A4 (SDA) | MPU-6050 / LCD | I2C 데이터 | 스윙업 후 LCD 갱신 차단 |
| A5 (SCL) | MPU-6050 / LCD | I2C 클럭 | MPU-6050 전용화 |
| DRV8825 마이크로스텝 설정 |
| ⚠️ DRV8825 마이크로스텝 핀 설정 (반드시 확인!) |
| 1/8 마이크로스텝 : M0=HIGH, M1=HIGH, M2=LOW ← 모터의 부드러운 동작을 위해 펜듈럼 제어에 1/8을 사용 |
| 분해능 : 200 step/rev × 8 = 1,600 step/rev → 1 step = 0.025mm |
| STEP 최소 펄스폭 : HIGH ≥ 1.9μs, LOW ≥ 1.9μs → 총 ≥ 4μs 보장 필수 |
| → 9편 DRV8825 탈조 방지 편 참조 |
2-4. 카트 & MPU-6050 장착
▲ 카트 상세 — MPU-6050이 펜듈럼 회전축에 직접 장착된 모습
MPU-6050 센서는 펜듈럼 막대의 회전축에 직접 장착되어 있습니다. 막대가 기울면 센서도 함께 기울기 때문에, Roll 값(AY, AZ 기반 atan2)으로 펜듈럼의 절대 각도를 정확하게 측정할 수 있습니다.
| 각도 정의 (이 프로젝트 기준) |
| • 하사점 (막대가 아래로 늘어진 상태) = 0° |
| • 상사점 (막대가 위로 똑바로 선 상태) = ±180° |
| • 오른쪽 기울임 → + 방향, 왼쪽 기울임 → - 방향 |
| 전원을 켜면 자동으로 하사점(0°) 영점 캘리브레이션을 수행합니다. |
| → 8편 MPU-6050 Roll·Pitch 각도 측정 편 참조 |
3. I2C 자원 관리 전략 — 탈조의 진짜 원인
이 프로젝트에서 가장 중요한 설계 원칙 중 하나가 I2C 자원 관리입니다. MPU-6050과 LCD가 모두 같은 I2C 버스(A4/A5)를 공유하기 때문입니다.
| 왜 탈조가 일어났는가 |
| ❌ 펜듈럼을 세우기 위해 지난 방학때 조금 시도 했었지만 모터 탈조로 직립에 실패 했었습니다. 과거 실패 원인 — 탈조의 진짜 범인 |
| 이전에 같은 하드웨어로 제어를 시도했을 때 스텝모터가 '틱틱' 탈조하는 현상이 심각했습니다. |
| 이제와 원인을 분석한 결과, lcd.clear()와 lcd.print()가 주범이었습니다. |
| lcd.clear() 실행 시 : I2C 통신 점유 → CPU 15~25ms 동안 완전 멈춤 |
| 스텝모터는 500μs 마다 펄스가 필요 → 25ms 동안 50개의 펄스가 사라짐 |
| → 모터 회전 관성을 이기지 못하고 '틱!' 하고 헛돌기 시작 |
| 9편 Non-blocking 스텝모터 제어 편에서 이 문제를 이미 다루었습니다. |
| I2C 자원 분리 정책 |
| ✅ 이 프로젝트의 I2C 자원 분리 전략 |
| 구간 ① 초기화·호밍 단계 : LCD + MPU-6050 공유 사용 (상태 표시 허용) |
| 구간 ② 스윙업 진입 이후 : LCD 갱신 완전 차단 → MPU-6050 I2C 독점 |
| 결과 : 스텝모터 펄스 타이밍이 LCD 통신에 전혀 방해받지 않음 |
| → 탈조 없는 강력한 스윙업 토크 확보 가능 |
4. Non-blocking 스케줄러 아키텍처
이 프로젝트 전체를 관통하는 핵심 설계 원칙은 Non-blocking 구조입니다. delay()를 완전히 제거하고, micros()와 millis()로 모든 작업의 타이밍을 독립적으로 관리합니다.
| 📌 이전 편 참조 |
| 9편에서 배운 Non-blocking 4중 타이머 구조가 이 프로젝트의 뼈대가 됩니다. |
| 핵심 원리 : 각 기능이 서로를 기다리지 않고 독립적으로 실행되도록 설계 |
| Non-blocking 스케줄러 뼈대 (전체 프로젝트 공통) |
| void loop() { |
| // [0순위] 리밋스위치 확인 — 항상 최우선 (비상정지) |
| if (checkLimit()) { emergencyStop(); return; } |
| unsigned long us = micros(); |
| unsigned long ms = millis(); |
| // [1순위] 스텝 펄스 생성 (500~1000μs 주기) ← 탈조 방지 핵심 |
| if (us - lastMotorUs >= stepInterval) { |
| toggleStepPin(); |
| lastMotorUs = us; |
| } |
| // [2순위] MPU-6050 폴링 + 각도필터 + PID 연산 (5ms 주기) |
| if (ms - lastImuMs >= 5) { |
| readMPU_polling(); // Wire.requestFrom() 폴링 방식 |
| updateFilter(); // 상보필터 or 칼만필터 |
| computeControl(); // 스윙업 or PID 제어 |
| lastImuMs = ms; |
| } |
| // [3순위] PC Telemetry 전송 (20ms 주기, 115200bps) |
| if (ms - lastSerialMs >= 20) { |
| sendTelemetry(); // '$angle,pos,pid,mode\n' |
| lastSerialMs = ms; |
| } |
| // ★ loop() 내 delay() / lcd.print() 완전 금지 |
| } |
5. 프로젝트 추진 계획 — 9편 연재 로드맵
이 프로젝트는 총 9편의 블로그 포스팅으로 완성해보고자 합니다. 각 단계는 독립적으로 검증하고 단계별로 진행하겠습니다. 이전 단계가 완벽히 동작하는 것을 확인한 후 다음 단계 코드를 작성하려 합니다.
| 편 | 단계 | 핵심 기술 | 달성 목표 | 적용 기술·이론 |
| 10편 | — | 하드웨어 구성 | 전체 시스템 이해 추진 계획 수립 | LM가이드·벨트·GT2풀리 1/8 마이크로스텝 원리 |
| 11편 | 1-A | MPU-6050 조정 | 360° 각도 출력 확인 영점 캘리브레이션 | atan2 각도 변환 자이로 드리프트 보정 |
| 12편 | 1-B | 상보 필터 | 부드러운 각도 출력 ±180° 불연속 처리 | 상보필터(α=0.98) Angle Wrapping |
| 13편 | 2 | 오토 호밍 | LM가이드 자동 원점 정중앙 위치 결정 | 리밋스위치 활용 Back-off 알고리즘 |
| 14편 | 3-A | 스윙업 기초 | ±150° 도달 확인 탈조 없는 구동 | 위상반전 스윙업 에너지 주입 원리 |
| 15편 | 3-B | 에너지 스윙업 | ±170° 도달 후 PID 자동 전환 | Furuta 에너지 펌핑 모드 전환 타이밍 |
| 16편 | 4-A | 각도 PID | 직립 10초 유지 각도 단독 제어 | PID 이론·튜닝 순서 D-term Kick 방지 |
| 17편 | 4-B | 이중루프 PID | 30초+ 직립 중앙 위치 복귀 | Cascade 이중루프 외부·내부 루프 설계 |
| 18편 | 5 | PC 모니터링 | 실시간 Telemetry 완성 리뷰 | Serial 프로토콜 Python 시각화 |
6. 단계별 적용 기술 & 이론적 배경
| 단계 1-A | MPU-6050 캘리브레이션 & 원시 각도 출력 |
핵심 기술
• MPU-6050의 인터럽트 INT 핀 미사용 → Wire.requestFrom() 폴링 방식으로 데이터 읽기
• setup()에서 500회 자이로 샘플 평균 → 정적 드리프트 오프셋 제거
• 가속도 각도 영점 캘리브레이션 → angleOffset 저장 → 하사점 정확히 0°
• 각도 = atan2(AY, AZ) × (180/π) — 8편에서 배운 Roll 각도 계산 동일
이론적 배경
MPU-6050은 가속도계(중력 방향 측정)와 자이로스코프(각속도 측정)를 내장한 IMU 센서입니다. 가속도계만으로는 진동 노이즈가 크고, 자이로만으로는 시간이 지나면 오차가 누적(드리프트)됩니다. 두 센서를 융합하는 필터가 다음 단계의 핵심입니다.
| 📌 이전 편 참조 |
| 8편 — MPU-6050 Roll·Pitch 자세 감지 → atan2 각도 계산, I2C 레지스터 읽기 방법 |
| 9편 — Non-blocking 타이머 구조 → 폴링 방식으로 MPU 읽기의 기초 |
| 단계 1-B | 상보 필터 + Angle Wrapping |
핵심 기술
• 상보필터 : angle = α·(angle + gyro·dt) + (1-α)·accelAngle (α=0.98)
• dt = micros() 실측 → 고정 dt 사용 시 타이밍 오차 누적 발생
• Angle Wrapping : +180° → -179° 불연속 점프 방지 코드
이론적 배경
| 상보 필터 원리 |
| 자이로 : 빠른 반응 · 단기 정확 → dt가 짧을수록 정확 (고주파 통과) |
| 가속도 : 장기 안정 · 진동에 취약 → 평균값이 정확 (저주파 통과) |
| α = 0.98 : '자이로 98% + 가속도 2%'로 융합 |
| 결과 : 빠른 회전에서도 드리프트 없이 정확한 각도 출력 |
| Angle Wrapping 이유 |
| atan2 함수 결과는 -180°~+180° 범위 |
| 막대가 상사점(±180°)을 미세하게 지나면 +179° ↔ -179°로 순간 점프 |
| → PID 오차가 갑자기 358°로 계산 → 모터 급발진 → 기구 파손 |
| → while 루프로 ±180° 범위 내로 정규화하여 완전 방지 |
| 단계 2 | 오토 호밍 & 중앙 정렬 |
핵심 기술
• CW 이동 → 리밋 터치(D3=HIGH) → Back-off 100 step → leftPos=0 확정
• CCW 이동 → 반대쪽 리밋 터치 → Back-off → maxPos 확정
• 중앙 = maxPos/2 위치로 이동 → 호밍 완료
• SW 디바운싱 20ms → 노이즈에 의한 오감지 방지
이론적 배경
3D 프린터, CNC 기계에서 사용하는 오토 호밍(Auto-Homing) 알고리즘과 동일한 원리입니다. 기계가 스스로 이동 범위를 측정하고 정중앙을 찾아갑니다. 리밋스위치 2개를 D3 하나에 병렬 연결했으므로 좌우 구분 없이 방향을 코드로 고정하는 방식을 사용합니다.
| 단계 3-A / 3-B | 스윙업 제어 (위상반전 → 에너지 기반) |
핵심 기술
• 3-A : 각도·각속도 부호 동시 비교 → 카트 이동 방향 결정 (그네 에너지 주입)
• 3-B : Furuta 에너지 펌핑 제어 → E_current와 E_target 비교
• 전환 조건 : |θ| ≥ 170° AND |θ̇| < 임계값 → PID 모드로 전환
• 전환 시 I-term 리셋 + D-term Kick 방지 (1사이클 D항 무효화)
📖 Furuta란?
일본 도쿄공과대학교의 후루타 키요시(Furuta Kiyoshi)교수가 1992년에 발표한 에너지 기반 스윙업 제어 이론에서 따온 이름입니다. 역진자를 수동 조작 없이 스스로 세우는 이 방법은 현재 전 세계 제어공학 교재의 표준 스윙업 알고리즘으로 자리잡고 있습니다.
💡 보충 설명 : Furuta 에너지 펌핑 제어란?
역진자 제어에서 가장 직관적이고 강력한 스윙업 방법이 바로 **에너지 기반 제어(Energy-based Control)**입니다. 우리는 왜 각도만 제어하지 않고 굳이 '에너지'를 따지는 걸까요?
1. 왜 에너지인가?
막대가 아래로 늘어진 상태(하사점)에서 상사점(직립)으로 한 번에 쳐올리기는 불가능합니다. 너무 세게 밀면 뱅글뱅글 돌아버리고, 살살 밀면 다시 내려옵니다.
이때 "펜듈럼이 가지고 있는 총 에너지"를 목표값과 비교하며 조절하면 이 문제를 우아하게 해결할 수 있습니다.
펜듈럼의 총 에너지(E)는 운동 에너지와 위치 에너지의 합입니다.
E_total = ½ × m × L² × θ̇² + m × g × L × (1 - cosθ) ↑ 운동 에너지 ↑ 위치 에너지
| 기호 | 의미 | 이 프로젝트에서 |
| m | 펜듈럼 막대 질량 | 카본 낚싯대 42g |
| L | 회전축~무게중심 거리 | 약 410mm (820mm의 절반) |
| θ̇ | 각속도 (theta dot) | MPU-6050 자이로값 (°/s) |
| θ | 현재 각도 (theta) | MPU-6050 가속도 계산값 |
| g | 중력가속도 | 9.8 m/s² |
E_current (현재 에너지) : 현재 θ와 θ̇로 계산한 값 E_target (목표 에너지) : 상사점에서 속도=0일 때 → E_target = 2mgL
2. 펌핑 제어 로직
제어기는 매 5ms마다 E_diff = E_current - E_target 을 계산합니다.
제어 출력 u = k × (E_current - E_target) × θ̇ × cosθ
| 상황 | 조건 | 제어기 동작 |
| 에너지 부족 | E_current < E_target | 카트를 펜듈럼 흔들림과 같은 방향으로 가속 → 에너지 주입 |
| 에너지 충분 | E_current ≈ E_target | 펌핑 중단 → PID 직립 제어로 자동 전환 |
| 에너지 과잉 | E_current > E_target | 카트를 반대 방향으로 이동 → 에너지 억제 |
그네 타기와 완전히 같은 원리입니다. 그네가 앞으로 나갈 때 발을 앞으로 뻗고 (같은 방향 가속), 뒤로 올 때 발을 당기면 (같은 방향 가속) 점점 높이 올라갑니다. 카트가 바로 이 역할을 합니다.
3. 전체 동작 흐름
전원 ON ↓ ① 초기화 & 호밍 : 카트를 중앙으로 이동 ↓ ② 스윙업 시작 : E_current < E_target → 카트 좌우 왕복으로 에너지 주입 → 진폭 0° → ±45° → ±90° → ±135° → ±170° ↓ ③ 전환 조건 감지 : |θ| ≥ 170° AND |θ̇| < 임계값 (상사점 근처 + 각속도 급감 = 꼭대기에서 멈추는 찰나) ↓ ④ PID 직립 제어 : I-term 리셋 후 이중루프 PID로 자동 전환 → 상사점(±180°) 유지 + 카트 중앙 복귀
4. 이 프로젝트에서의 구현 계획
에너지 제어는 두 단계로 나누어 구현합니다.
14편 (단계 3-A) : 단순 위상 반전 스윙업 각도·각속도 부호만 보고 방향 결정 (원리 이해용) 15편 (단계 3-B) : Furuta 에너지 펌핑 완성판 u = k × (E_current - E_target) × θ̇ × cosθ 위 수식을 실제 아두이노 코드로 구현 시도
이론적 배경
| 스윙업의 물리 원리 — 그네와 완전히 같은 원리 |
| 막대가 아래에서 올라올 때 카트를 같은 방향으로 가속 → 운동에너지 주입 |
| 막대가 내려올 때 카트를 반대 방향으로 이동 → 에너지 손실 최소화 |
| 반복할수록 진폭이 점점 커져 결국 상사점(±180°)에 도달 |
| 에너지 펌핑 수식 (3-B 단계) |
| E = ½·J·θ̇² + m·g·L·(cosθ - 1) (현재 에너지) |
| E_up = 2·m·g·L (상사점 목표 에너지) |
| u = k·(E - E_up)·θ̇·cosθ |
| 단계 4-A / 4-B | PID 직립 제어 + 이중루프 |
핵심 기술
• 4-A : 칼만 필터 각도 기준 내부 루프 PID (5ms 주기) — 각도만 제어
• 4-B : 이중루프 — 외부루프(위치, 20ms) → 각도 목표값 보정 → 내부루프(각도, 5ms)
• 최단 거리 오차 보정 : ±180° 경계 PID 오차 계산 시 필수
이론적 배경
| 이중루프(Cascade) PID 구조 |
| 문제 : 각도만 제어하면 카트가 서서히 한쪽으로 흘러 결국 리밋스위치 충돌 |
| 해결 : 외부 루프가 위치 오차를 계산 → 각도 목표값을 미세하게 기울여 |
| 카트가 자연스럽게 중앙으로 복귀하도록 유도 |
| 각도 목표 = 180° + (Kp_pos × 위치오차 + Kd_pos × 카트속도) |
| 보정 범위 : ±5° 제한 (급변 방지) |
| 칼만 필터 (4단계에서 적용) |
| 직립 제어에서는 미세한 각도 드리프트가 PID를 불안정하게 만듦 |
| 칼만 필터 : 자이로 바이어스까지 추정 → 수식적으로 최적화된 각도 출력 |
| 단계 5 | PC Telemetry & 실시간 모니터링 |
• Serial 115,200bps + 20ms 주기 전송 → '$angle,pos,pid,mode\n' 패킷
• Python + matplotlib 실시간 그래프 → PID 게인 튜닝에 직접 활용
• Arduino Serial Plotter로 즉시 확인 가능 (코딩 불필요)
7. 마무리
이번 10편에서는 인버티드 펜듈럼의 전체 하드웨어 구성과 9편에 걸친 프로젝트 로드맵을 정리해 보았습니다. 지금까지 브레드보드 IO 시리즈에서 배운 MPU-6050 각도 측정(8편), 스텝모터 Non-blocking 제어(9편), I2C 통신(1편) 등의 기술은 이 프로젝트에서 구현하기 위한 기본 배경 지식입니다.
계획은 거창하게 세웠지만 잘 될지는 모르겠습니다. 사정에 따라 조금 늦어 질 수도 있고 변경될 수도 있지만 꾸준히 블로그에 시행착오까지도 공유하려 합니다.
ㅎㅎ
미리 변명해보자면
사실 성공에서는 기쁨을... 실패에서는 배움을... 얻는 법아니겠습니까? ^^
30년 전 MC68000과 어셈블리어, DC 서보모터로 쇠봉 펜듈럼을 1년 넘게 걸려 정말 힘들게 세웠던 경험을 바탕으로, 이번엔 단계별 꼼꼼히 기록하고 검증하며 블로그에 공유하며 이중루프 PID 직립 제어까지 천천히 구현해 나가겠습니다.
자!!! 그 첫걸음 이제 시작합니다. ^^
▶ 다음 편(11편) : 단계 1-A 코드 전체 공개 & 상세 설명
① MPU-6050 자이로 오프셋 캘리브레이션 (500회 평균)
② 가속도 각도 영점 캘리브레이션 → 하사점에서 정확히 0.00° 출력
③ 시리얼 플로터에서 accelAngle 360° 연속 출력 (튐 없게)
④ 리밋스위치 비상정지 동작 확인
▶ 이후 12편 : 단계 1-B 상보필터 + Angle Wrapping
민선생의 전자공방 · 브레드보드 활용 IO 인터페이스 10편 · 2026
태그#아두이노#아두이노R4#인버티드펜듈럼#역진자#MPU6050#스텝모터#DRV8825#NEMA17#PID제어#상보필터#제어공학#로봇공학#메이커#아두이노프로젝트#넌블로킹#스윙업#자이로센서#LM가이드 태그수정
