cartpole-v1-ppo / README_KR.md
Hwihwa_Lab
chore: untrack and remove Korean jupyter notebook from public HF and GitHub repos
7115a00
|
Raw History Blame Contribute Delete
14.7 kB
# ๐Ÿค– 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**.*