# ๐Ÿค– CartPole-v1 // ํ”ผ์ง€์ปฌ AI & ์šฐ์ฃผ ํ–‰์„ฑ Sim-to-Real ๋ฒค์น˜๋งˆํฌ [![Language: English](https://img.shields.io/badge/Language-English-blue)](README.md) [![Language: ํ•œ๊ตญ์–ด](https://img.shields.io/badge/Language-ํ•œ๊ตญ์–ด-green)](README_KR.md) [![Hugging Face Spaces](https://img.shields.io/badge/๐Ÿค—%20Hugging%20Face-๋ผ์ด๋ธŒ%20์ŠคํŽ˜์ด์Šค%20๋ฐ๋ชจ-purple)](https://huggingface.co/spaces/hwihwalab/cartpole-v1-ppo) [![Hugging Face Model Hub](https://img.shields.io/badge/๐Ÿค—%20Hugging%20Face-๋ชจ๋ธ%20ํ—ˆ๋ธŒ-orange)](https://huggingface.co/hwihwalab/cartpole-v1-ppo) [![Gymnasium](https://img.shields.io/badge/Gymnasium-CartPole--v1-000000?logo=openaigym)](https://gymnasium.farama.org/environments/classic_control/cart_pole/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-EE4C2C?logo=pytorch&logoColor=white)](https://pytorch.org/) [![Stable-Baselines3](https://img.shields.io/badge/Stable--Baselines3-v2.0+-blue)](https://stable-baselines3.readthedocs.io/) [![Benchmark](https://img.shields.io/badge/Benchmark-1%2C800%20Episodes%20%7C%20100%25%20Solved-brightgreen)](#-์‹ค์ธก-1800ํšŒ-์ „์ˆ˜-๋ฒค์น˜๋งˆํฌ-์‹คํ—˜-๊ฒฐ๊ณผ) [![GitHub](https://img.shields.io/badge/GitHub-cartpole--v1--ppo-181717?logo=github)](https://github.com/Hwihwa-Lab/cartpole-v1-ppo) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hwihwa-Lab/cartpole-v1-ppo/blob/main/LICENSE) > **"์ง€๊ตฌ์—์„œ ํ•™์Šต๋œ ๊ฐ•ํ™”ํ•™์Šต AI๋Š” ์™ธ๊ณ„ ํ–‰์„ฑ์˜ ์ค‘๋ ฅ ๋ณ€ํ™” ์†์—์„œ๋„ ์‚ด์•„๋‚จ์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€?"** > ์‹ฌ์ธต ์‹ ๊ฒฝ๋ง ๊ฐ•ํ™”ํ•™์Šต **PPO(Proximal Policy Optimization)**์™€ ์ „ํ†ต ํ˜„๋Œ€ ์ œ์–ด๊ณตํ•™์˜ ์ •์ ์ธ **์ตœ์  LQR(Linear Quadratic Regulator)**์„ 4๊ฐœ ์šฐ์ฃผ ํ–‰์„ฑ ์ค‘๋ ฅ ๋ฐ ๊ทนํ•œ ์™ธ๋ž€ ํ™˜๊ฒฝ์—์„œ ๋น„๊ต ๋ถ„์„ํ•œ ์‹ค์ธก ํ”ผ์ง€์ปฌ AI ๋ฒค์น˜๋งˆํฌ ์Šค์œ„ํŠธ์ž…๋‹ˆ๋‹ค. > *[ ๐ŸŒ English Documentation ](README.md) | [ ๐Ÿ‡ฐ๐Ÿ‡ท ํ•œ๊ตญ์–ด ๋งค๋‰ด์–ผ ](README_KR.md) | [ ๐ŸŽฎ ์‹ค์‹œ๊ฐ„ ์›น ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๋ผ์ด๋ธŒ ๋ฐ๋ชจ ](https://huggingface.co/spaces/hwihwalab/cartpole-v1-ppo)* > [!TIP] > ๐ŸŽฎ **๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฌด์„ค์น˜ ์ฆ‰์‹œ ์ฒดํ—˜**: [๐Ÿ‘‰ Hugging Face Spaces ๋ผ์ด๋ธŒ ๋ฐ๋ชจ ์‹คํ–‰](https://huggingface.co/spaces/hwihwalab/cartpole-v1-ppo) > ๐Ÿ“ฆ **๊ณต์‹ ๋ชจ๋ธ ํ—ˆ๋ธŒ**: [๐Ÿค— hwihwalab/cartpole-v1-ppo](https://huggingface.co/hwihwalab/cartpole-v1-ppo) | ๐Ÿ™ **GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ**: [Hwihwa-Lab/cartpole-v1-ppo](https://github.com/Hwihwa-Lab/cartpole-v1-ppo) --- ## ๐ŸŽฎ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ ์ฒดํ—˜๊ด€ (Hugging Face Spaces) ๐Ÿ‘‰ **[๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹ค์‹œ๊ฐ„ ๋ฌผ๋ฆฌ AI ์—ฐ๊ตฌ์†Œ ์‹คํ–‰ํ•˜๊ธฐ](https://huggingface.co/spaces/hwihwalab/cartpole-v1-ppo)** * ๐Ÿ–ฑ๏ธ **๋งˆ์šฐ์Šค/ํ„ฐ์น˜ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์™ธ๋ž€ (Troll the AI)**: ์บ”๋ฒ„์Šค๋ฅผ ๋งˆ์šฐ์Šค๋กœ ๊ธ๊ฑฐ๋‚˜ ๋‹น๊ฒจ์„œ ์‹ค์‹œ๊ฐ„ ์ถฉ๊ฒฉ(`โšก ยฑXX.X N`)์„ ๊ฐ€ํ•˜๊ณ  AI๊ฐ€ ์˜ค๋š์ด์ฒ˜๋Ÿผ ์ค‘์‹ฌ์„ ์žก๋Š” ๋ชจ์Šต์„ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. * ๐Ÿช **์šฐ์ฃผ ํ–‰์„ฑ ์ค‘๋ ฅ Sim-to-Real ์ „์ด**: ๋‹ฌ($1.62\,\text{m/s}^2$), ํ™”์„ฑ($3.72\,\text{m/s}^2$), ์ง€๊ตฌ($9.81\,\text{m/s}^2$), ๋ชฉ์„ฑ($24.79\,\text{m/s}^2$)์„ ์›ํด๋ฆญ์œผ๋กœ ๋„˜๋‚˜๋“ค๋ฉฐ ๋ฌผ๋ฆฌ์  ๋ฐ˜์‘ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•ฉ๋‹ˆ๋‹ค. * ๐ŸŒ€ **์‹ค์‹œ๊ฐ„ ์œ„์ƒ ํ‰๋ฉด๋„ ($\theta$ vs $\dot{\theta}$)**: ํ˜ผ๋ˆ์˜ ์™ธ๋ž€ ์†์—์„œ $(0, 0)$ ํ‰ํ˜•์ ์œผ๋กœ ์ˆ˜๋ ดํ•˜๋Š” ๋‚˜์„  ๊ถค์ (Attractor)์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. * โšก **6๋‹จ๊ณ„ ์‚ฌ์ด๋ฒ„๋„คํ‹ฑ ๋ฐฐ์† ๋ฐํฌ**: $0.25\times$ ์Šฌ๋กœ์šฐ ๋ชจ์…˜๋ถ€ํ„ฐ $5.0\times\text{ Turbo}$, $10.0\times\text{ Max}$๊นŒ์ง€ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ### โŒจ๏ธ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์กฐ์ž‘ ๋ฐ ๋‹จ์ถ•ํ‚ค ๋งคํ•‘ ๊ฐ€์ด๋“œ | ์กฐ์ž‘ ๋ฐฉ์‹ / ๋‹จ์ถ•ํ‚ค | ์ œ์–ด ๋™์ž‘ | ์ƒ์„ธ ์„ค๋ช… | | :--- | :--- | :--- | | **`[ ๋งˆ์šฐ์Šค ๋“œ๋ž˜๊ทธ / ํด๋ฆญ ]`** | **์™ธ๋ž€ ์ถฉ๊ฒฉ ์ฃผ์ž…** | ์บ”๋ฒ„์Šค์—์„œ ๋“œ๋ž˜๊ทธํ•˜์—ฌ ์กฐ์ค€์„ ์„ ๊ธ‹๊ณ  $\pm 5\text{N} \sim \pm 30\text{N}$์˜ ๋ฌผ๋ฆฌ ์ถฉ๊ฒฉ ์ธ๊ฐ€ | | **`[ Space ]`** | **์‹œ์ž‘ / ์ผ์‹œ์ •์ง€** | 60FPS ์‹ค์‹œ๊ฐ„ ๋ฌผ๋ฆฌ ๋™์—ญํ•™ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ฐ€๋™ ๋ฐ ์ •์ง€ | | **`[ R ]`** | **์ดˆ๊ธฐํ™” (Reset)** | ์—ญ์ง„์ž ์‹œ์Šคํ…œ์„ ํ‘œ์ค€ ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ์ฆ‰์‹œ ๋ฆฌ์…‹ | | **`[ M ]`** | **์ œ์–ด๊ธฐ ๋ณ€๊ฒฝ** | `TRAINED PPO` โž” `LQR` โž” `UNDERCOOKED` โž” `MANUAL` ์ˆœํ™˜ ์ „ํ™˜ | | **`[ โ—€ / โ–ถ ]`** | **์ˆ˜๋™ ์กฐ์ž‘ (Teleop)** | ์ธ๊ฐ„ ์šด์˜์ž ํ‚ค๋ณด๋“œ ์ž…๋ ฅ์œผ๋กœ ์นดํŠธ ์ขŒ/์šฐ ์ง์ ‘ ์ด๋™ | | **`[ F ]`** | **๋žœ๋ค ์ถฉ๊ฒฉ** | $\pm 10\text{N}$์˜ ๋ฌด์ž‘์œ„ ์ˆœ๊ฐ„ ์™ธ๋ž€ ์ฃผ์ž… | --- ## ๐Ÿ—๏ธ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜ ๋‹ค์ด์–ด๊ทธ๋žจ ```mermaid flowchart TB subgraph Client_Layer ["๐Ÿค– Physical AI & Robotics Dynamics Suite (One-Screen Golden Ratio)"] UI_Left["Left: ์ œ์–ด๊ธฐ ์•„๋ ˆ๋‚˜ (PPO vs LQR), 4-DOF ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ & ๋ฐฐ์† ๋“œ๋กญ๋‹ค์šด"] UI_Center["Center: 60FPS ์บ”๋ฒ„์Šค, ๋งˆ์šฐ์Šค ์™ธ๋ž€ ๋ฒกํ„ฐ & ์œ„์ƒ ํ‰๋ฉด ์–ดํŠธ๋ž™ํ„ฐ"] UI_Right["Right: Sim-to-Real ํ–‰์„ฑ ํŠœ๋„ˆ (L, M, g) & ์‹ค์‹œ๊ฐ„ Chart.js"] end subgraph Core_Engine ["โšก Pure JS ๋ฌผ๋ฆฌ & ์ œ์–ด ๋Ÿฐํƒ€์ž„ (cartpole_sim.js)"] Physics["๊ฐ€๋ณ€ ๋ฌผ๋ฆฌ ํ•ด์„๊ธฐ (Euler ์ ๋ถ„ ๊ธฐ๋ฐ˜ ๋™์  L, M, g ํ•ด์„)"] LQR_Ctrl["๊ณ ์ „ ์ตœ์  LQR ์ œ์–ด๊ธฐ (Riccati ๊ฒŒ์ธ ํ–‰๋ ฌ u = -K*x)"] PPO_Ctrl["์ˆœ๋ฐฉํ–ฅ MLP ์ •์ฑ… (Tanh x 2 -> Softmax ํ™•๋ฅ  ๊ฒฐ์ •)"] PhasePlot["์œ„์ƒ ํ‰๋ฉด ์—”์ง„ (ฮธ vs ฮธฬ‡ ๊ถค์  ๋‚˜์„  ์ˆ˜๋ ด ์–ดํŠธ๋ž™ํ„ฐ)"] WeightsJSON["์ถ”์ถœ๋œ ์‹ ๊ฒฝ๋ง ๊ฐ€์ค‘์น˜ (cartpole_weights.json)"] end subgraph Python_Backend ["๐Ÿ ํŒŒ์ด์ฌ ํ•™์Šต & ๋ฒค์น˜๋งˆํฌ ์ธํ”„๋ผ"] Trainer["PPO ์ •์ฑ… ํŠธ๋ ˆ์ด๋„ˆ (train.py @ 25,000 steps)"] Benchmark["์ž๋™ํ™” 1,800ํšŒ ์ „์ˆ˜ ๋ฒค์น˜๋งˆํฌ ์—”์ง„ (benchmark_experiments.py)"] LocalServer["๋ฌด์˜์กด์„ฑ ๊ฒฝ๋Ÿ‰ ๋กœ์ปฌ ์„œ๋ฒ„ (run.py @ Port 8000)"] TestSuite["์ž๋™ํ™” ํ…Œ์ŠคํŠธ ํ•˜๋„ค์Šค (test_app.py - 6๊ฐœ ๊ฒ€์ฆ ์ผ€์ด์Šค)"] end subgraph Hub_Distribution ["๐ŸŒ ํ—ˆ๊น…ํŽ˜์ด์Šค ํ†ตํ•ฉ ๋ฐฐํฌ (deploy_to_hf.py)"] Spaces["HF Spaces (Static SDK ๋ฌด์ง€์—ฐ ์›น ๋ฒค์น˜๋งˆํฌ)"] Models["HF Model Hub (๊ฐ€์ค‘์น˜, ๋ฒค์น˜๋งˆํฌ JSON, ๋ชจ๋ธ ์นด๋“œ)"] end WeightsJSON --> PPO_Ctrl Physics --> UI_Center PPO_Ctrl --> UI_Left LQR_Ctrl --> UI_Left PhasePlot --> UI_Center Physics --> UI_Right Trainer --> WeightsJSON Benchmark --> Models LocalServer --> Client_Layer Client_Layer --> Spaces Trainer --> Models ``` --- ## ๐Ÿ“Š ์‹ค์ธก ๋ฒค์น˜๋งˆํฌ ์‹คํ—˜ ๋ฐ์ดํ„ฐ (์ด 1,800ํšŒ ๋ฌผ๋ฆฌ ์—ํ”ผ์†Œ๋“œ) ๋ณธ ๋ฐ์ดํ„ฐ๋Š” ์ž๋™ํ™” ๋ฒค์น˜๋งˆํฌ ์—”์ง„(`benchmark_experiments.py`)์„ ํ†ตํ•ด ์ด **1,800ํšŒ์˜ ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์—ํ”ผ์†Œ๋“œ**๋ฅผ ์ „์ˆ˜ ์ธก์ •ํ•˜์—ฌ ์ง‘๊ณ„๋œ 100% ์‹ค์ธก ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ### ๐Ÿช 1. ํ–‰์„ฑ๋ณ„ ์ œ๋กœ์ƒท(Zero-Shot) ์ค‘๋ ฅ ์ „์ด ๋ฒค์น˜๋งˆํฌ (ํ‘œ์ค€ ๋ฌด์™ธ๋ž€ ํ™˜๊ฒฝ) | ์ œ์–ด๊ธฐ (Controller) | ๐ŸŒ™ ๋‹ฌ (1.62 m/sยฒ) | ๐Ÿ”ด ํ™”์„ฑ (3.72 m/sยฒ) | ๐ŸŒ ์ง€๊ตฌ (9.81 m/sยฒ) | ๐Ÿช ๋ชฉ์„ฑ (24.79 m/sยฒ) | ํ‰๊ท  ๊ฐ๋„ ์˜ค์ฐจ | | :--- | :---: | :---: | :---: | :---: | :---: | | **Trained PPO (20K)** | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | 0.26ยฐ (์ง€๊ตฌ) / 0.53ยฐ (๋ชฉ์„ฑ) | | **Optimal LQR (Riccati)** | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | 0.18ยฐ (์ง€๊ตฌ) / 0.42ยฐ (๋ชฉ์„ฑ) | | **Undercooked PPO (2K)** | 21.7 (0%) | 21.7 (0%) | 19.3 (0%) | 18.6 (0%) | N/A (ํ•™์Šต ๋ฏธ์™„๋ฃŒ ์กฐ๊ธฐ ์ถ”๋ฝ) | ### ๐ŸŒช๏ธ 2. ํ™˜๊ฒฝ ์ŠคํŠธ๋ ˆ์Šค & ๊ฐ•์ธ์„ฑ ๋ฒค์น˜๋งˆํฌ (์ง€๊ตฌ ์ค‘๋ ฅ 9.81 m/sยฒ) | ์ œ์–ด๊ธฐ (Controller) | ํ‘œ์ค€ ๋ฌด์™ธ๋ž€ (Clean) | ์ง€์† ํ’์•• (+2.2N) | ์„ผ์„œ ๋…ธ์ด์ฆˆ (ฯƒ=0.05) | ๋ณตํ•ฉ ์ŠคํŠธ๋ ˆ์Šค ํ™˜๊ฒฝ | | :--- | :---: | :---: | :---: | :---: | | **Trained PPO (20K)** | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | | **Optimal LQR (Riccati)** | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | **500.0** (100%) | | **Undercooked PPO (2K)** | 19.3 (0%) | 16.5 (0%) | 20.5 (0%) | 14.2 (0%) | --- ## ๐Ÿ”ฌ ์ฃผ์š” ์—ฐ๊ตฌ ๊ฒฐ๋ก  ๋ฐ ์ด๋ก ์  ๊ณ ์ฐฐ (Key Scientific Findings) 1. **๋น„์„ ํ˜• ์‹ ๊ฒฝ๋ง ์ •์ฑ…์˜ ์ œ๋กœ์ƒท ๊ฐ•์ธ์„ฑ**: - ์ง€๊ตฌ์—์„œ๋งŒ ํ•™์Šต๋œ PPO ์—์ด์ „ํŠธ๋Š” $0.17g$ (๋‹ฌ)๋ถ€ํ„ฐ $2.53g$ (๋ชฉ์„ฑ)๊นŒ์ง€ ๊ทน๋‹จ์ ์ธ ์ค‘๋ ฅ ๋ณ€ํ™” ์†์—์„œ๋„ ์ถ”๊ฐ€ ์žฌํ•™์Šต ์—†์ด 100% ์ƒ์กด์œจ(500์Šคํ… ๋งŒ์  ์™„์ฃผ)์„ ์œ ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. - ๊ณ ์ค‘๋ ฅ(๋ชฉ์„ฑ: $24.79\,\text{m/s}^2$) ํ™˜๊ฒฝ์—์„œ๋Š” ๋น ๋ฅธ ์Šค์œ„์นญ ์ฃผํŒŒ์ˆ˜๋ฅผ ํ†ตํ•ด ๊ฐ๋„ ์˜ค์ฐจ๋ฅผ $|\theta| \le 0.53^\circ$ ์ด๋‚ด๋กœ ์–ต์ œํ–ˆ์Šต๋‹ˆ๋‹ค. 2. **์ˆ˜ํ•™์  ์ตœ์  ์ œ์–ด(LQR) vs ๋”ฅ๋Ÿฌ๋‹ ๊ฐ•ํ™”ํ•™์Šต(PPO)**: - ์ •๋ฐ€ํ•œ ์„ ํ˜• ์•ˆ์ •์„ฑ ์˜์—ญ์—์„œ๋Š” LQR์ด ๋” ์ข์€ ๊ฐ๋„ ๋ฐ๋“œ๋ฐด๋“œ($|\theta| \approx 0.18^\circ$)๋ฅผ ์œ ์ง€ํ–ˆ์œผ๋‚˜, ๋น„๋Œ€์นญ ์ง€์† ํ’์•• ์™ธ๋ž€์—์„œ๋Š” PPO๊ฐ€ ๋น„๋Œ€์นญ ๋“€ํ‹ฐ๋น„ ์กฐ์ ˆ์„ ํ†ตํ•ด ๋›ฐ์–ด๋‚œ ์ ์‘์„ฑ์„ ์ž…์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. 3. **์œ„์ƒ ๊ณต๊ฐ„ ์ˆ˜๋ ด์„ฑ(Attractor Convergence)**: - ์‹ค์‹œ๊ฐ„ ์œ„์ƒ ํ‰๋ฉด๋„ ๋ถ„์„ ๊ฒฐ๊ณผ, LQR๊ณผ PPO ๋ชจ๋‘ ์™ธ๋ž€ ์ดํ›„ $(0, 0)$ ํ‰ํ˜•์ ์œผ๋กœ ์ ๊ทผ์  ๋‚˜์„  ์ˆ˜๋ ด(Asymptotic Spiral Convergence)์„ ์™„๋ฃŒํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. --- ## ๐ŸŽฌ ๋™์—ญํ•™ ๋ฌผ๋ฆฌ ๊ฑฐ๋™ ์ƒ์„ธ ๋ถ„์„ (์‹ค์ œ ์นดํŠธํด์ด ์–ด๋–ป๊ฒŒ ์›€์ง์˜€๋Š”๊ฐ€?) 1,800ํšŒ ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ์—ฐ์† ์ƒํƒœ ๊ณต๊ฐ„($x, \dot{x}, \theta, \dot{\theta}$) ๊ถค์  ๋กœ๊ทธ ๋ถ„์„ ๊ฒฐ๊ณผ, ๊ฐ ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋…ํŠนํ•œ ๋ฌผ๋ฆฌ์  ๊ฑฐ๋™ ํŒจํ„ด์ด ๊ด€์ธก๋˜์—ˆ์Šต๋‹ˆ๋‹ค: 1. **๐ŸŒ ์ง€๊ตฌ ํ‘œ์ค€ ํ™˜๊ฒฝ ($9.81\,\text{m/s}^2$ ยท ๋Œ€์นญํ˜• ์ดˆ๋ฏธ์„ธ ์ง„๋™ ์ œ์–ด)**: - **์นดํŠธ ์ด๋™ ๋ฐ˜๊ฒฝ**: ๋ ˆ์ผ ์ค‘์•™ ๊ธฐ์ค€ $|x| \le 0.12\,\text{m}$ ์ด๋‚ด์— ์™„๋ฒฝํžˆ ๊ฐ‡ํ˜€ ๋จธ๋ฌด๋ฆ…๋‹ˆ๋‹ค. - **์•ก์ถ”์—์ดํ„ฐ ๊ฑฐ๋™**: $+10\,\text{N}$๊ณผ $-10\,\text{N}$์˜ ํž˜์„ $\approx 14.2\,\text{Hz}$์˜ ์ฃผํŒŒ์ˆ˜๋กœ ๋น ๋ฅด๊ฒŒ ์ „ํ™˜ํ•˜๋ฉฐ, ์ขŒ์šฐ ๋Œ€์นญ ๋“€ํ‹ฐ๋น„($50.0\%\,\text{L} / 50.0\%\,\text{R}$)๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. - **๋ง‰๋Œ€ ์ž์„ธ**: ๋ˆˆ์— ๋„๋Š” ํ”๋“ค๋ฆผ ์—†์ด $|\theta| \le 0.26^\circ$์˜ ์—„๊ฒฉํ•œ ์ง๋ฆฝ ๋ถˆ๊ฐ๋Œ€(Deadband)๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค. 2. **๐ŸŒ™ ๋‹ฌ๋‚˜๋ผ ์ €์ค‘๋ ฅ ($1.62\,\text{m/s}^2$ ยท ๋‘ฅ์‹ค๋‘ฅ์‹ค ์˜ค๋ฒ„์ŠˆํŒ… ํŒŒ๋„ํƒ€๊ธฐ)**: - **์นดํŠธ ์ด๋™ ๋ฐ˜๊ฒฝ**: ์นดํŠธ๊ฐ€ ๋ ˆ์ผ ์ขŒ์šฐ ๋„“์€ ์˜์—ญ($|x| \approx 0.45\,\text{m} \sim 0.82\,\text{m}$)์„ ์„œํ•‘ํ•˜๋“ฏ ์˜ค๊ฐ‘๋‹ˆ๋‹ค. - **๋™์—ญํ•™ ์›์ธ**: ์ค‘๋ ฅ์ด ์•ฝํ•ด ๋ง‰๋Œ€์˜ ์ž์—ฐ ๋‚™ํ•˜ ๋ณต์› ํ† ํฌ๊ฐ€ ์ž‘๊ธฐ ๋•Œ๋ฌธ์—, $\pm 10\,\text{N}$์˜ ์ด์‚ฐ ์ถฉ๊ฒฉ๋ ฅ์ด ๋ง‰๋Œ€์— ๊ธด ์ฃผ๊ธฐ(Low-frequency)์˜ ๊ฐ์šด๋™๋Ÿ‰์„ ์œ ๋ฐœํ•˜์—ฌ ์™„๋งŒํ•œ ์‚ฌ์ธํŒŒ ํ˜•ํƒœ๋กœ ์Šค์œ™ํ•˜๋ฉฐ ์•ˆ์ •ํ™”๋ฉ๋‹ˆ๋‹ค. 3. **๐Ÿช ๋ชฉ์„ฑ ์ดˆ๊ณ ์ค‘๋ ฅ ($24.79\,\text{m/s}^2$ ยท ์ดˆ๊ณ ์ฃผํŒŒ ํŒŒ๋ฅด๋ฅด ๋–จ๋ฆผ)**: - **์•ก์ถ”์—์ดํ„ฐ ๊ฑฐ๋™**: ์Šค์œ„์นญ ์ฃผํŒŒ์ˆ˜๊ฐ€ $>22.5\,\text{Hz}$ ์ด์ƒ์œผ๋กœ ๊ธ‰์ƒ์Šนํ•ฉ๋‹ˆ๋‹ค. - **๋™์—ญํ•™ ์›์ธ**: ์ค‘๋ ฅ ํ† ํฌ($\tau_g = m g l \sin\theta$)๊ฐ€ $2.53$๋ฐฐ ๊ฐ•๋ ฅํ•ด์ ธ ๋ง‰๋Œ€๊ฐ€ ์กฐ๊ธˆ๋งŒ ๊ธฐ์šธ์–ด์ ธ๋„ ๋ถ•๊ดด ์†๋„๊ฐ€ ํญ๋ฐœ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๋ฏ€๋กœ, PPO ์‹ ๊ฒฝ๋ง์ด ์ดˆ๊ธด๋ฐ• ๊ณ ์ฃผํŒŒ ํŽ„์Šค๋ฅผ ์—ฐ์† ์ฃผ์ž…ํ•˜์—ฌ ์“ฐ๋Ÿฌ์ง์„ ๋ฐฉ์–ดํ•ฉ๋‹ˆ๋‹ค. 4. **๐Ÿ’จ ์ธก๋ฉด ์ง€์† ํ’์•• ์™ธ๋ž€ ($+2.2\,\text{N}$ ยท ๋น„๋Œ€์นญ ๋ฆฐ ์นด์šดํ„ฐ ์Šคํ‹ฐ์–ด)**: - **๋“€ํ‹ฐ๋น„ ๋น„๋Œ€์นญ ์ „ํ™˜**: PPO ์ •์ฑ…์ด ์Šค์Šค๋กœ ์ขŒ์ธก ํž˜ ๋น„์œจ์„ $64.8\%\,\text{L} / 35.2\%\,\text{R}$๋กœ ๋น„๋Œ€์นญ ํŽธํ–ฅ์‹œํ‚ต๋‹ˆ๋‹ค. - **๋ฌผ๋ฆฌ์  ์ž์„ธ**: ์นดํŠธ๋ฅผ $x \approx -0.18\,\text{m}$ ๋ฐ”๋žŒ ๋ถ€๋Š” ๋ฐ˜๋Œ€ํŽธ์— ๊ณ ์ •์‹œํ‚ค๊ณ , ๋ง‰๋Œ€๋ฅผ ๋ฐ”๋žŒ ๋ฐฉํ–ฅ์œผ๋กœ ์‚ด์ง ๊ธฐ์šธ์—ฌ ํ’์••๊ณผ ์ค‘๋ ฅ์˜ ํ† ํฌ ํ‰ํ˜•์„ ์™„๋ฒฝํžˆ ๋งž์ถฅ๋‹ˆ๋‹ค. 5. **โšก ์™ธ๋ž€ ์ถฉ๊ฒฉ ๋ณต์› ๊ธฐ๋™ (2๋‹จ๊ณ„ ์บ์นญ & ์„ผํ„ฐ๋ง ๊ธฐ๋™)**: - **1๋‹จ๊ณ„ (Catching)**: $+15\,\text{N}$ ์ถฉ๊ฒฉ ์ธ๊ฐ€ ์‹œ, ์นดํŠธ๊ฐ€ ์ถฉ๊ฒฉ ๋ฐฉํ–ฅ์œผ๋กœ ๊ธ‰๊ฐ€์†ํ•˜์—ฌ ๊ธฐ์šธ์–ด์ง€๋Š” ๋ง‰๋Œ€์˜ ์งˆ๋Ÿ‰ ์ค‘์‹ฌ ๋ฐ”๋กœ ๋ฐ‘์œผ๋กœ ๋ฐ›์นจ์ ์„ ์‹ ์†ํžˆ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค. - **2๋‹จ๊ณ„ (Settling)**: ๊ฐ์†๋„ $\dot{\theta} \rightarrow 0$ ์ˆ˜๋ ด ํ›„, 2D ์œ„์ƒ ํ‰๋ฉด ๋‚˜์„  ๊ถค์ ์„ ๋”ฐ๋ผ ์นดํŠธ๋ฅผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ ˆ์ผ ์›์ ($x = 0.0\,\text{m}$)์œผ๋กœ ๊ฒฌ์ธ ๋ณต๊ท€์‹œํ‚ต๋‹ˆ๋‹ค. --- ## ๐Ÿ“‚ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒŒ์ผ ๊ตฌ์„ฑ ๋ฐ ๋‹จ์ผ ์ฑ…์ž„ ๋ช…์„ธ | ํŒŒ์ผ ๊ฒฝ๋กœ | ๋‹จ์ผ ์ฑ…์ž„ (Single Responsibility) | | :--- | :--- | | `models/cartpole_ppo.zip` | ํ•™์Šต ์™„๋ฃŒ๋œ ๊ณต์‹ PyTorch / Stable-Baselines3 PPO ์ •์ฑ… ๊ฐ€์ค‘์น˜ ์•„์นด์ด๋ธŒ | | `cartpole_weights.json` | ๋ธŒ๋ผ์šฐ์ € ๋‚ด 60FPS ์ˆœ์ˆ˜ JS ์‹ค์‹œ๊ฐ„ ์ถ”๋ก ์šฉ PPO MLP ์‹ ๊ฒฝ๋ง ๊ฐ€์ค‘์น˜ `[Linear(4,64) โž” Linear(64,64) โž” Linear(64,2)]` | | `replay.mp4` | ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ชจ๋ธ ํŽ˜์ด์ง€ ์ „์šฉ 1:1 ๊ณ ํ™”์งˆ(720ร—720) ๋น„๋””์˜ค ํ”„๋ฆฌ๋ทฐ ์˜์ƒ | | `index.html` | ๋ฌด์Šคํฌ๋กค ํ™ฉ๊ธˆ๋ถ„ํ•  Cybernetic Bento ๊ด€์ œ ๋ ˆ์ด์•„์›ƒ | | `style.css` | ๋„ค์˜ค ๋‹คํฌ๋ชจ๋“œ ๊ธ€๋ž˜์Šค๋ชจํ”ผ์ฆ˜, ๋ฐ˜์‘ํ˜• ๊ฒŒ์ด์ง€ ๋ฐ ํ–…ํ‹ฑ ์ปจํŠธ๋กค ์Šคํƒ€์ผ ์‹œ์Šคํ…œ | | `cartpole_sim.js` | 60FPS ๊ฐ€๋ณ€ ๋ฌผ๋ฆฌ ํ•ด์„, PPO/LQR ์ œ์–ด๊ธฐ, ๋งˆ์šฐ์Šค ์™ธ๋ž€ ๋ฐ ์œ„์ƒ ํ‰๋ฉด๋„ ๋ Œ๋”๋Ÿฌ | | `train.py` | 25,000 ์Šคํ… PPO ํ•™์Šต๊ธฐ ๋ฐ ์›น ๋ธŒ๋ผ์šฐ์ €์šฉ JSON ๊ฐ€์ค‘์น˜ ์ถ”์ถœ๊ธฐ | | `benchmark_experiments.py` | 4๋Œ€ ํ–‰์„ฑ & 3๋Œ€ ์™ธ๋ž€ 1,800ํšŒ ์ „์ˆ˜ ๋ฒค์น˜๋งˆํฌ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ | | `benchmark_results.json` | 4๋Œ€ ํ–‰์„ฑ ๋ฐ 3๋Œ€ ์™ธ๋ž€ ์กฐ๊ฑด์— ๋Œ€ํ•œ 1,800ํšŒ ์ „์ˆ˜ ํ‰๊ฐ€ ์ •๋Ÿ‰ ๋ฐ์ดํ„ฐ | | `generate_trajectory_dataset.py` | 77,821 ์Šคํ…์˜ ๊ณ ๋นˆ๋„ ๋ฌผ๋ฆฌ ๊ถค์ (Parquet/JSONL) ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ๊ธฐ | | `run.py` / `run_desktop.py` | ๋ธŒ๋ผ์šฐ์ € ๋‹จ๋… ์•ฑ ๋ชจ๋“œ๋ฅผ ์—ด์–ด์ฃผ๋Š” ๋ฌด์˜์กด์„ฑ ๊ฒฝ๋Ÿ‰ ๋กœ์ปฌ ์„œ๋ฒ„ ๋ฐ ๋ฐ์Šคํฌํ†ฑ ๋Ÿฐ์ฒ˜ | | `deploy_to_hf.py` | ํ—ˆ๊น…ํŽ˜์ด์Šค Models, Spaces, Datasets ์›ํด๋ฆญ 3์ค‘ ๋™์‹œ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ | | `LICENSE` | ๊ณต์‹ MIT ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด์„ ์Šค | --- ## โšก ๋น ๋ฅธ ์‹คํ–‰ ๋ฐ ์žฌํ˜„ ๊ฐ€์ด๋“œ ### 1. ์ „์šฉ ๋…๋ฆฝํ˜• ๋ฐ์Šคํฌํ†ฑ ์•ฑ ์‹คํ–‰ ```powershell python run.py ``` ### 2. ์ž๋™ํ™” 1,800ํšŒ ์ „์ˆ˜ ๋ฒค์น˜๋งˆํฌ ์žฌ์‹คํ–‰ ```powershell python benchmark_experiments.py ``` ### 3. ์‹œ์Šคํ…œ ๋ฌด๊ฒฐ์„ฑ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ์‹คํ–‰ ```powershell python test_app.py ``` --- ## ๐ŸŒ ํœ˜ํ™” ๋กœ๋ณดํ‹ฑ์Šค ์ƒํƒœ๊ณ„ ๋กœ๋“œ๋งต ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ํœ˜ํ™” ๋žฉ ํ”ผ์ง€์ปฌ AI & ๋กœ๋ณดํ‹ฑ์Šค ์‹œ๋ฆฌ์ฆˆ์˜ ๊ธฐ์ดˆ 1๋‹จ๊ณ„์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค: 1. **CartPole-v1 PPO** ยท 1D ๊ณ ์ „ ์ œ์–ด ์—ญ์ง„์ž ๊ท ํ˜• ์ œ์–ด & Sim-to-Real ๋ฒค์น˜๋งˆํฌ 2. **LunarLander-v3 D3QN** ยท 2D ๋‹ฌ ์ฐฉ๋ฅ™์„  ๋ณตํ•ฉ ์ถ”์ง„์ฒด ์ œ์–ด & ๋ฒกํ„ฐ ๋™์—ญํ•™ 3. **LeRobot Push-T** ยท 2D ํ…”๋ ˆ์˜คํผ๋ ˆ์ด์…˜ & Diffusion ๋ชจ๋ฐฉ ํ•™์Šต 4. **LeRobot ALOHA Sim** ยท ์–‘ํŒ” ๋กœ๋ด‡ ์ •๋ฐ€ ๋งค๋‹ˆํ“ฐ๋ ˆ์ด์…˜ & ์•ก์ถ”์—์ดํ„ฐ ์–ด๋ ˆ์ด 5. **MicroDuck 14-DOF** ยท 3D ์ด์กฑ๋ณดํ–‰ ๋””์ง€ํ„ธ ํŠธ์œˆ ์‹ค์‹œ๊ฐ„ ์กฐ์ข…์„ --- ## ๐Ÿ“„ ๋ผ์ด์„ ์Šค (License) ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” MIT License๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ [LICENSE](https://github.com/Hwihwa-Lab/cartpole-v1-ppo/blob/main/LICENSE) ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”. --- *Trained and deployed with [CartPole Physical AI Lab](https://huggingface.co/spaces/hwihwalab/cartpole-v1-ppo) by **HWIHWA LAB**.*