|
11 | 11 | :tags [:physics :machine-learning :optimization :ppo :control]}}} |
12 | 12 |
|
13 | 13 | (ns ppo.main |
14 | | - (:require [clojure.math :refer (PI cos sin)] |
| 14 | + (:require [clojure.math :refer (PI cos sin to-radians)] |
| 15 | + [clojure.core.async :as async] |
| 16 | + [quil.core :as q] |
| 17 | + [quil.middleware :as m] |
15 | 18 | [libpython-clj2.require :refer (require-python)])) |
16 | 19 |
|
17 | 20 | (require-python '[torch :as torch]) |
|
203 | 206 | max-speed (:max-speed config) |
204 | 207 | velocity (- (rand (* 2.0 max-speed)) max-speed)] |
205 | 208 | (->Pendulum config (setup angle velocity)))) |
| 209 | + |
| 210 | +;; ### Visualisation |
| 211 | +;; |
| 212 | +;; The following method is used to draw the pendulum and visualise the motor control input. |
| 213 | +(defn draw-state [{:keys [angle]} {:keys [control]}] |
| 214 | + (let [origin-x (/ (q/width) 2) |
| 215 | + origin-y (/ (q/height) 2) |
| 216 | + length (* 0.5 (q/height) (:length config)) |
| 217 | + pendulum-x (+ origin-x (* length (sin angle))) |
| 218 | + pendulum-y (- origin-y (* length (cos angle))) |
| 219 | + size (* 0.05 (q/height)) |
| 220 | + arc-radius (* (abs control) 0.2 (q/height)) |
| 221 | + positive (pos? control) |
| 222 | + tip-angle (if positive 225 -45)] |
| 223 | + (q/frame-rate frame-rate) |
| 224 | + (q/background 255) |
| 225 | + (q/stroke-weight 5) |
| 226 | + (q/stroke 0) |
| 227 | + (q/fill 175) |
| 228 | + (q/line origin-x origin-y pendulum-x pendulum-y) |
| 229 | + (q/stroke-weight 1) |
| 230 | + (q/ellipse pendulum-x pendulum-y size size) |
| 231 | + (q/no-fill) |
| 232 | + (q/arc origin-x origin-y (* 2 arc-radius) (* 2 arc-radius) (to-radians -45) (to-radians 225)) |
| 233 | + (q/with-translation [(+ origin-x (* (cos (to-radians tip-angle)) arc-radius)) (+ origin-y (* (sin (to-radians tip-angle)) arc-radius))] |
| 234 | + (q/with-rotation [(to-radians (if positive 225 -45))] |
| 235 | + (q/triangle 0 (if positive 10 -10) -5 0 5 0))) |
| 236 | + (when (:save config) |
| 237 | + (q/save-frame "frame-####.png")))) |
| 238 | + |
| 239 | +;; ### Animation |
| 240 | +;; |
| 241 | +;; The following method animates the pendulum and facilitates mouse control. |
| 242 | +(defn run [] |
| 243 | + (let [done-chan (async/chan) |
| 244 | + last-action (atom {:control 0.0})] |
| 245 | + (q/sketch |
| 246 | + :title "Inverted Pendulum with Mouse Control" |
| 247 | + :size [854 480] |
| 248 | + :setup #(setup PI 0.0) |
| 249 | + :update (fn [state] |
| 250 | + (let [action {:control (min 1.0 (max -1.0 (- 1.0 (/ (q/mouse-x) (/ (q/width) 2.0)))))} |
| 251 | + state (update-state state action config)] |
| 252 | + (when (done? state config) (async/close! done-chan)) |
| 253 | + (reset! last-action action) |
| 254 | + state)) |
| 255 | + :draw #(draw-state % @last-action) |
| 256 | + :middleware [m/fun-mode] |
| 257 | + :on-close (fn [& _] (async/close! done-chan))) |
| 258 | + (async/<!! done-chan)) |
| 259 | + (System/exit 0)) |
0 commit comments