Mercurial > cortex
comparison org/proprioception.org @ 259:7cac5ef852e3
proprioception rough draft complete.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 14 Feb 2012 02:28:25 -0700 |
parents | f4b67005b702 |
children | c39b8b29a79e |
comparison
equal
deleted
inserted
replaced
258:f4b67005b702 | 259:7cac5ef852e3 |
---|---|
35 simulates the muscle-spindles and joint capsules. I will deal with | 35 simulates the muscle-spindles and joint capsules. I will deal with |
36 Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]]. | 36 Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]]. |
37 | 37 |
38 * Helper Functions | 38 * Helper Functions |
39 | 39 |
40 =(absolute-angle)= calculates the angle between two vectors, relative to a | |
41 third axis vector. This angle is the number of radians you have to | |
42 move counterclockwise around the axis vector to get from the first to | |
43 the second vector. It is not commutative like a normal dot-product | |
44 angle is. | |
45 | |
40 #+name: helpers | 46 #+name: helpers |
41 #+begin_src clojure | 47 #+begin_src clojure |
42 (in-ns 'cortex.proprioception) | 48 (in-ns 'cortex.proprioception) |
43 | 49 |
44 (defn right-handed? | 50 (defn right-handed? |
47 orthogonal." | 53 orthogonal." |
48 [vec1 vec2 vec3] | 54 [vec1 vec2 vec3] |
49 (< 0 (.dot (.cross vec1 vec2) vec3))) | 55 (< 0 (.dot (.cross vec1 vec2) vec3))) |
50 | 56 |
51 (defn absolute-angle | 57 (defn absolute-angle |
52 "The angle between 'vec1 and 'vec2. Positive if the angle to get | 58 "The angle between 'vec1 and 'vec2 around 'axis. In the range |
53 from 'vec1 to 'vec2 is counterclockwise around 'axis, and negative | 59 [0 (* 2 Math/PI)]." |
54 otherwise." | |
55 [vec1 vec2 axis] | 60 [vec1 vec2 axis] |
56 (let [angle (.angleBetween vec1 vec2)] | 61 (let [angle (.angleBetween vec1 vec2)] |
57 (if (right-handed? vec1 vec2 axis) | 62 (if (right-handed? vec1 vec2 axis) |
58 angle (- (* 2 Math/PI) angle)))) | 63 angle (- (* 2 Math/PI) angle)))) |
59 #+end_src | 64 #+end_src |
60 | 65 |
66 #+begin_src clojure :exports both | |
67 (in-ns 'cortex.proprioception) | |
68 (absolute-angle Vector3f/UNIT_X Vector3f/UNIT_Y Vector3f/UNIT_Z) | |
69 #+end_src | |
70 | |
71 #+results: | |
72 : 1.5707964 | |
73 | |
74 #+begin_src clojure :exports both | |
75 (in-ns 'cortex.proprioception) | |
76 (absolute-angle | |
77 Vector3f/UNIT_X (.mult Vector3f/UNIT_Y (float -1)) Vector3f/UNIT_Z) | |
78 #+end_src | |
79 | |
80 #+results: | |
81 : 4.7123889366733 | |
82 | |
61 * Proprioception Kernel | 83 * Proprioception Kernel |
84 | |
85 Given a joint, =(proprioception-kernel)= produces a function that | |
86 calculates the euler angles between the the objects the joint | |
87 connects. | |
62 | 88 |
63 #+name: proprioception | 89 #+name: proprioception |
64 #+begin_src clojure | 90 #+begin_src clojure |
65 (defn proprioception-kernel | 91 (defn proprioception-kernel |
66 "Returns a function which returns proprioceptive sensory data when | 92 "Returns a function which returns proprioceptive sensory data when |
69 (let [[obj-a obj-b] (joint-targets parts joint) | 95 (let [[obj-a obj-b] (joint-targets parts joint) |
70 joint-rot (.getWorldRotation joint) | 96 joint-rot (.getWorldRotation joint) |
71 x0 (.mult joint-rot Vector3f/UNIT_X) | 97 x0 (.mult joint-rot Vector3f/UNIT_X) |
72 y0 (.mult joint-rot Vector3f/UNIT_Y) | 98 y0 (.mult joint-rot Vector3f/UNIT_Y) |
73 z0 (.mult joint-rot Vector3f/UNIT_Z)] | 99 z0 (.mult joint-rot Vector3f/UNIT_Z)] |
74 (println-repl "x:" x0) | |
75 (println-repl "y:" y0) | |
76 (println-repl "z:" z0) | |
77 (println-repl "init-a:" (.getWorldRotation obj-a)) | |
78 (println-repl "init-b:" (.getWorldRotation obj-b)) | |
79 | |
80 (fn [] | 100 (fn [] |
81 (let [rot-a (.clone (.getWorldRotation obj-a)) | 101 (let [rot-a (.clone (.getWorldRotation obj-a)) |
82 rot-b (.clone (.getWorldRotation obj-b)) | 102 rot-b (.clone (.getWorldRotation obj-b)) |
83 x (.mult rot-a x0) | 103 x (.mult rot-a x0) |
84 y (.mult rot-a y0) | 104 y (.mult rot-a y0) |
111 (joints creature))] | 131 (joints creature))] |
112 (fn [] | 132 (fn [] |
113 (map #(%) senses)))) | 133 (map #(%) senses)))) |
114 #+end_src | 134 #+end_src |
115 | 135 |
136 | |
137 =(proprioception!)= maps =(proprioception-kernel)= across all the | |
138 joints of the creature. It uses the same list of joints that | |
139 =(cortex.body/joints)= uses. | |
140 | |
116 * Visualizing Proprioception | 141 * Visualizing Proprioception |
142 | |
143 Proprioception has the lowest bandwidth of all the senses so far, and | |
144 it doesn't lend itself as readily to visual representation like | |
145 vision, hearing, or touch. This visualization code creates a "guage" | |
146 to view each of the three relative angles along a circle. | |
117 | 147 |
118 #+name: visualize | 148 #+name: visualize |
119 #+begin_src clojure | 149 #+begin_src clojure |
120 (in-ns 'cortex.proprioception) | 150 (in-ns 'cortex.proprioception) |
121 | 151 |
138 (+ 25 (int (* -20 (Math/sin angle))))]] | 168 (+ 25 (int (* -20 (Math/sin angle))))]] |
139 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000) | 169 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000) |
140 (draw-sprite image sprite (position 0) (position 1) color) | 170 (draw-sprite image sprite (position 0) (position 1) color) |
141 (reset! previous position)) | 171 (reset! previous position)) |
142 image)))) | 172 image)))) |
143 | |
144 | 173 |
145 (defn proprioception-display-kernel | 174 (defn proprioception-display-kernel |
146 "Display proprioception angles in a BufferedImage" | 175 "Display proprioception angles in a BufferedImage" |
147 [[h p r]] | 176 [[h p r]] |
148 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) | 177 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) |
175 display each element of the list to the screen as an image." | 204 display each element of the list to the screen as an image." |
176 [] | 205 [] |
177 (view-sense proprioception-display-kernel)) | 206 (view-sense proprioception-display-kernel)) |
178 #+end_src | 207 #+end_src |
179 | 208 |
180 * Demonstration of Proprioception | 209 * Proprioception Test |
181 | 210 This test does not use the worm, but instead uses two bars, bound |
182 #+name: test-body | 211 together by a point2point joint. One bar is fixed, and I control the |
183 #+begin_src clojure | 212 other bar from the keyboard. |
213 | |
214 #+name: test-proprioception | |
215 #+begin_src clojure | |
216 (in-ns 'cortex.test.proprioception) | |
217 | |
184 (defn test-proprioception | 218 (defn test-proprioception |
185 "Testing proprioception: | 219 "Testing proprioception: |
186 You should see two foating bars, and a printout of pitch, yaw, and | 220 You should see two foating bars, and a printout of pitch, yaw, and |
187 roll. Pressing key-r/key-t should move the blue bar up and down and | 221 roll. Pressing key-r/key-t should move the blue bar up and down and |
188 change only the value of pitch. key-f/key-g moves it side to side | 222 change only the value of pitch. key-f/key-g moves it side to side |
189 and changes yaw. key-v/key-b will spin the blue segment clockwise | 223 and changes yaw. key-v/key-b will spin the blue segment clockwise |
190 and counterclockwise, and only affect roll." | 224 and counterclockwise, and only affect roll." |
191 [] | 225 ([] (test-proprioception false)) |
192 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) | 226 ([record?] |
193 :mass 0 :color ColorRGBA/Green :name "hand") | 227 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) |
194 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) | 228 :mass 0 :color ColorRGBA/Gray :name "hand") |
195 :mass 1 :color ColorRGBA/Red :name "finger") | 229 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) |
196 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow | 230 :mass 1 |
197 :position (Vector3f. 0 1.2 0) | 231 :color |
198 :rotation (doto (Quaternion.) | 232 (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1) |
199 (.fromAngleAxis | 233 :name "finger") |
200 (/ Math/PI 2) | 234 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow |
201 (Vector3f. 0 0 1))) | 235 :position (Vector3f. 0 1.2 0) |
202 :physical? false) | 236 :rotation (doto (Quaternion.) |
203 joint (join-at-point hand finger (Vector3f. 0 1.2 0 )) | 237 (.fromAngleAxis |
204 creature (nodify [hand finger joint-node]) | 238 (/ Math/PI 2) |
205 finger-control (.getControl finger RigidBodyControl) | 239 (Vector3f. 0 0 1))) |
206 hand-control (.getControl hand RigidBodyControl)] | 240 :physical? false) |
207 | 241 creature (nodify [hand finger joint-node]) |
208 | 242 finger-control (.getControl finger RigidBodyControl) |
209 (let | 243 hand-control (.getControl hand RigidBodyControl) |
210 ;; ******************************************* | 244 joint (joint-dispatch {:type :point} hand-control finger-control |
211 | 245 (Vector3f. 0 1.2 0) |
212 [floor (box 10 10 10 :position (Vector3f. 0 -15 0) | 246 (Vector3f. 0 -1.2 0) nil) |
213 :mass 0 :color ColorRGBA/Gray) | 247 |
214 | 248 root (nodify [creature]) |
215 root (nodify [creature floor]) | 249 prop (proprioception-kernel creature joint-node) |
216 prop (joint-proprioception creature joint-node) | 250 prop-view (view-proprioception)] |
217 prop-view (proprioception-debug-window) | 251 (.setCollisionGroup |
218 | 252 (.getControl hand RigidBodyControl) |
219 controls | 253 PhysicsCollisionObject/COLLISION_GROUP_NONE) |
220 (merge standard-debug-controls | 254 (apply |
221 {"key-o" | 255 world |
222 (fn [_ _] (.setEnabled finger-control true)) | 256 (with-movement |
223 "key-p" | 257 finger |
224 (fn [_ _] (.setEnabled finger-control false)) | 258 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] |
225 "key-k" | 259 [1 1 10 10 10 10] |
226 (fn [_ _] (.setEnabled hand-control true)) | 260 [root |
227 "key-l" | 261 standard-debug-controls |
228 (fn [_ _] (.setEnabled hand-control false)) | 262 (fn [world] |
229 "key-i" | 263 (if record? |
230 (fn [world _] (set-gravity world (Vector3f. 0 0 0))) | 264 (Capture/captureVideo |
231 "key-period" | 265 world |
232 (fn [world _] | 266 (File. "/home/r/proj/cortex/render/proprio/main-view"))) |
233 (.setEnabled finger-control false) | 267 (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) |
234 (.setEnabled hand-control false) | 268 (set-gravity world (Vector3f. 0 0 0)) |
235 (.rotate creature (doto (Quaternion.) | 269 (enable-debug world) |
236 (.fromAngleAxis | 270 (light-up-everything world)) |
237 (float (/ Math/PI 15)) | 271 (fn [_ _] |
238 (Vector3f. 0 0 -1)))) | 272 (prop-view |
239 | 273 (list (prop)) |
240 (.setEnabled finger-control true) | 274 (if record? |
241 (.setEnabled hand-control true) | 275 (File. "/home/r/proj/cortex/render/proprio/proprio"))))]))))) |
242 (set-gravity world (Vector3f. 0 0 0)) | 276 #+end_src |
243 ) | 277 |
244 | 278 #+results: test-proprioception |
245 | 279 : #'cortex.test.proprioception/test-proprioception |
246 } | 280 |
247 ) | 281 * Video of Proprioception |
248 | 282 |
249 ] | 283 #+begin_html |
250 (comment | 284 <div class="figure"> |
251 (.setCollisionGroup | 285 <center> |
252 (.getControl hand RigidBodyControl) | 286 <video controls="controls" width="550"> |
253 PhysicsCollisionObject/COLLISION_GROUP_NONE) | 287 <source src="../video/test-proprioception.ogg" type="video/ogg" |
254 ) | 288 preload="none" poster="../images/aurellem-1280x480.png" /> |
255 (apply | 289 </video> |
256 world | 290 </center> |
257 (with-movement | 291 <p>Proprioception in a simple creature. The proprioceptive readout is |
258 hand | 292 in the upper left corner of the screen.</p> |
259 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"] | 293 </div> |
260 [10 10 10 10 1 1] | 294 #+end_html |
261 (with-movement | 295 |
262 finger | 296 ** Generating the Proprioception Video |
263 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] | 297 #+name: magick6 |
264 [1 1 10 10 10 10] | 298 #+begin_src clojure |
265 [root | 299 (ns cortex.video.magick6 |
266 controls | 300 (:import java.io.File) |
267 (fn [world] | 301 (:use clojure.contrib.shell-out)) |
268 (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) | 302 |
269 (set-gravity world (Vector3f. 0 0 0)) | 303 (defn images [path] |
270 (light-up-everything world)) | 304 (sort (rest (file-seq (File. path))))) |
271 (fn [_ _] (prop-view (list (prop))))])))))) | 305 |
306 (def base "/home/r/proj/cortex/render/proprio/") | |
307 | |
308 (defn pics [file] | |
309 (images (str base file))) | |
310 | |
311 (defn combine-images [] | |
312 (let [main-view (pics "main-view") | |
313 proprioception (pics "proprio/0") | |
314 targets (map | |
315 #(File. (str base "out/" (format "%07d.png" %))) | |
316 (range 0 (count main-view)))] | |
317 (dorun | |
318 (pmap | |
319 (comp | |
320 (fn [[ main-view proprioception target]] | |
321 (println target) | |
322 (sh "convert" | |
323 main-view | |
324 proprioception "-geometry" "+20+20" "-composite" | |
325 target)) | |
326 (fn [& args] (map #(.getCanonicalPath %) args))) | |
327 main-view proprioception targets)))) | |
328 #+end_src | |
329 | |
330 #+begin_src sh :results silent | |
331 cd ~/proj/cortex/render/proprio | |
332 ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora \ | |
333 test-proprioception.ogg | |
272 #+end_src | 334 #+end_src |
273 | 335 |
274 * Headers | 336 * Headers |
275 #+name: proprioception-header | 337 #+name: proprioception-header |
276 #+begin_src clojure | 338 #+begin_src clojure |
284 (:import com.jme3.scene.Node) | 346 (:import com.jme3.scene.Node) |
285 (:import java.awt.image.BufferedImage) | 347 (:import java.awt.image.BufferedImage) |
286 (:import (com.jme3.math Vector3f Quaternion))) | 348 (:import (com.jme3.math Vector3f Quaternion))) |
287 #+end_src | 349 #+end_src |
288 | 350 |
351 #+name: test-proprioception-header | |
352 #+begin_src clojure | |
353 (ns cortex.test.proprioception | |
354 (:import (com.aurellem.capture Capture RatchetTimer)) | |
355 (:use (cortex util world proprioception body)) | |
356 (:import java.io.File)) | |
357 (cortex.import/mega-import-jme3) | |
358 #+end_src | |
359 | |
360 * Source Listing | |
361 - [[../src/cortex/proprioception.clj][cortex.proprioception]] | |
362 - [[../src/cortex/test/touch.clj][cortex.test.proprioception]] | |
363 - [[../src/cortex/video/magick6.clj][cortex.video.magick6]] | |
364 #+html: <ul> <li> <a href="../org/proprioception.org">This org file</a> </li> </ul> | |
365 - [[http://hg.bortreb.com ][source-repository]] | |
366 | |
367 * Next | |
368 | |
369 Next time, I'll give the Worm the power to [[./movement.org][move on it's own]]. | |
370 | |
289 | 371 |
290 * COMMENT generate source | 372 * COMMENT generate source |
291 #+begin_src clojure :tangle ../src/cortex/proprioception.clj | 373 #+begin_src clojure :tangle ../src/cortex/proprioception.clj |
292 <<proprioception-header>> | 374 <<proprioception-header>> |
293 <<helpers>> | 375 <<helpers>> |
294 <<proprioception>> | 376 <<proprioception>> |
295 <<visualize>> | 377 <<visualize>> |
296 #+end_src | 378 #+end_src |
379 | |
380 #+begin_src clojure :tangle ../src/cortex/test/proprioception.clj | |
381 <<test-proprioception-header>> | |
382 <<test-proprioception>> | |
383 #+end_src | |
384 | |
385 #+begin_src clojure :tangle ../src/cortex/video/magick6.clj | |
386 <<magick6>> | |
387 #+end_src |