Mercurial > cortex
comparison org/proprioception.org @ 266:bee5145ce463
Merged Robert's proprioception with Dylan's vision.
author | Dylan Holmes <ocsenave@gmail.com> |
---|---|
date | Mon, 13 Feb 2012 21:53:54 -0600 |
parents | 7cac5ef852e3 |
children | c39b8b29a79e |
comparison
equal
deleted
inserted
replaced
265:e57d8c52f12f | 266:bee5145ce463 |
---|---|
4 #+description: proprioception for simulated creatures | 4 #+description: proprioception for simulated creatures |
5 #+keywords: simulation, jMonkeyEngine3, clojure | 5 #+keywords: simulation, jMonkeyEngine3, clojure |
6 #+SETUPFILE: ../../aurellem/org/setup.org | 6 #+SETUPFILE: ../../aurellem/org/setup.org |
7 #+INCLUDE: ../../aurellem/org/level-0.org | 7 #+INCLUDE: ../../aurellem/org/level-0.org |
8 | 8 |
9 #+name: proprioception | 9 * Proprioception |
10 #+begin_src clojure | 10 |
11 (ns cortex.proprioception | 11 Close your eyes, and touch your nose with your right index finger. How |
12 "Simulate the sense of proprioception (ability to detect the | 12 did you do it? You could not see your hand, and neither your hand nor |
13 relative positions of body parts with repsect to other body parts) | 13 your nose could use the sense of touch to guide the path of your hand. |
14 in jMonkeyEngine3. Reads specially prepared blender files to | 14 There are no sound cues, and Taste and Smell certainly don't provide |
15 automatically generate proprioceptive senses." | 15 any help. You know where your hand is without your other senses |
16 (:use (cortex world util sense body)) | 16 because of Proprioception. |
17 (:use clojure.contrib.def) | 17 |
18 (:import com.jme3.scene.Node) | 18 Humans can sometimes loose this sense through viral infections or |
19 (:import java.awt.image.BufferedImage) | 19 damage to the spinal cord or brain, and when they do, they loose the |
20 (:import (com.jme3.math Vector3f Quaternion))) | 20 ability to control their own bodies without looking directly at the |
21 parts they want to move. In [[http://en.wikipedia.org/wiki/The_Man_Who_Mistook_His_Wife_for_a_Hat][The Man Who Mistook His Wife for a Hat]], | |
22 a woman named Christina looses this sense and has to learn how to move | |
23 by carefully watching her arms and legs. She describes proprioception | |
24 as the "eyes of the body, the way the body sees itself". | |
25 | |
26 Proprioception in humans is mediated by [[http://en.wikipedia.org/wiki/Articular_capsule][joint capsules]], [[http://en.wikipedia.org/wiki/Muscle_spindle][muscle | |
27 spindles]], and the [[http://en.wikipedia.org/wiki/Golgi_tendon_organ][Golgi tendon organs]]. These measure the relative | |
28 positions of each pody part by monitoring muscle strain and length. | |
29 | |
30 It's clear that this is a vital sense for fulid, graceful | |
31 movement. It's also particurally easy to implement in jMonkeyEngine. | |
32 | |
33 My simulated proprioception calculates the relative angles of each | |
34 joint from the rest position defined in the blender file. This | |
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]]. | |
37 | |
38 * Helper Functions | |
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 | |
46 #+name: helpers | |
47 #+begin_src clojure | |
48 (in-ns 'cortex.proprioception) | |
21 | 49 |
22 (defn right-handed? | 50 (defn right-handed? |
23 "true iff the three vectors form a right handed coordinate | 51 "true iff the three vectors form a right handed coordinate |
24 system. The three vectors do not have to be normalized or | 52 system. The three vectors do not have to be normalized or |
25 orthogonal." | 53 orthogonal." |
26 [vec1 vec2 vec3] | 54 [vec1 vec2 vec3] |
27 (< 0 (.dot (.cross vec1 vec2) vec3))) | 55 (< 0 (.dot (.cross vec1 vec2) vec3))) |
28 | 56 |
29 (defn absolute-angle | 57 (defn absolute-angle |
30 "The angle between 'vec1 and 'vec2. Positive if the angle to get | 58 "The angle between 'vec1 and 'vec2 around 'axis. In the range |
31 from 'vec1 to 'vec2 is counterclockwise around 'axis, and negative | 59 [0 (* 2 Math/PI)]." |
32 otherwise." | |
33 [vec1 vec2 axis] | 60 [vec1 vec2 axis] |
34 (let [angle (.angleBetween vec1 vec2)] | 61 (let [angle (.angleBetween vec1 vec2)] |
35 (if (right-handed? vec1 vec2 axis) | 62 (if (right-handed? vec1 vec2 axis) |
36 angle (- (* 2 Math/PI) angle)))) | 63 angle (- (* 2 Math/PI) angle)))) |
37 | 64 #+end_src |
38 (defn proprioception-fn | 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 | |
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. | |
88 | |
89 #+name: proprioception | |
90 #+begin_src clojure | |
91 (defn proprioception-kernel | |
39 "Returns a function which returns proprioceptive sensory data when | 92 "Returns a function which returns proprioceptive sensory data when |
40 called inside a running simulation." | 93 called inside a running simulation." |
41 [#^Node parts #^Node joint] | 94 [#^Node parts #^Node joint] |
42 (let [[obj-a obj-b] (joint-targets parts joint) | 95 (let [[obj-a obj-b] (joint-targets parts joint) |
43 joint-rot (.getWorldRotation joint) | 96 joint-rot (.getWorldRotation joint) |
44 x0 (.mult joint-rot Vector3f/UNIT_X) | 97 x0 (.mult joint-rot Vector3f/UNIT_X) |
45 y0 (.mult joint-rot Vector3f/UNIT_Y) | 98 y0 (.mult joint-rot Vector3f/UNIT_Y) |
46 z0 (.mult joint-rot Vector3f/UNIT_Z)] | 99 z0 (.mult joint-rot Vector3f/UNIT_Z)] |
47 (println-repl "x:" x0) | |
48 (println-repl "y:" y0) | |
49 (println-repl "z:" z0) | |
50 (println-repl "init-a:" (.getWorldRotation obj-a)) | |
51 (println-repl "init-b:" (.getWorldRotation obj-b)) | |
52 | |
53 (fn [] | 100 (fn [] |
54 (let [rot-a (.clone (.getWorldRotation obj-a)) | 101 (let [rot-a (.clone (.getWorldRotation obj-a)) |
55 rot-b (.clone (.getWorldRotation obj-b)) | 102 rot-b (.clone (.getWorldRotation obj-b)) |
56 x (.mult rot-a x0) | 103 x (.mult rot-a x0) |
57 y (.mult rot-a y0) | 104 y (.mult rot-a y0) |
78 sequence of functions, one for each child of the \"joints\" node in | 125 sequence of functions, one for each child of the \"joints\" node in |
79 the creature, which each report proprioceptive information about | 126 the creature, which each report proprioceptive information about |
80 that joint." | 127 that joint." |
81 [#^Node creature] | 128 [#^Node creature] |
82 ;; extract the body's joints | 129 ;; extract the body's joints |
83 (let [senses (map (partial proprioception-fn creature) | 130 (let [senses (map (partial proprioception-kernel creature) |
84 (joints creature))] | 131 (joints creature))] |
85 (fn [] | 132 (fn [] |
86 (map #(%) senses)))) | 133 (map #(%) senses)))) |
87 | 134 #+end_src |
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 | |
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. | |
147 | |
148 #+name: visualize | |
149 #+begin_src clojure | |
150 (in-ns 'cortex.proprioception) | |
88 | 151 |
89 (defn draw-sprite [image sprite x y color ] | 152 (defn draw-sprite [image sprite x y color ] |
90 (dorun | 153 (dorun |
91 (for [[u v] sprite] | 154 (for [[u v] sprite] |
92 (.setRGB image (+ u x) (+ v y) color)))) | 155 (.setRGB image (+ u x) (+ v y) color)))) |
93 | |
94 | 156 |
95 (defn view-angle | 157 (defn view-angle |
96 "create a debug view of an angle" | 158 "create a debug view of an angle" |
97 [color] | 159 [color] |
98 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) | 160 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) |
106 (+ 25 (int (* -20 (Math/sin angle))))]] | 168 (+ 25 (int (* -20 (Math/sin angle))))]] |
107 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000) | 169 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000) |
108 (draw-sprite image sprite (position 0) (position 1) color) | 170 (draw-sprite image sprite (position 0) (position 1) color) |
109 (reset! previous position)) | 171 (reset! previous position)) |
110 image)))) | 172 image)))) |
111 | |
112 | 173 |
113 (defn proprioception-display-kernel | 174 (defn proprioception-display-kernel |
114 "Display proprioception angles in a BufferedImage" | 175 "Display proprioception angles in a BufferedImage" |
115 [[h p r]] | 176 [[h p r]] |
116 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) | 177 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB) |
141 (defn view-proprioception | 202 (defn view-proprioception |
142 "Creates a function which accepts a list of proprioceptive data and | 203 "Creates a function which accepts a list of proprioceptive data and |
143 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." |
144 [] | 205 [] |
145 (view-sense proprioception-display-kernel)) | 206 (view-sense proprioception-display-kernel)) |
146 | 207 #+end_src |
147 | 208 |
148 | 209 * Proprioception Test |
149 #+end_src | 210 This test does not use the worm, but instead uses two bars, bound |
150 | 211 together by a point2point joint. One bar is fixed, and I control the |
151 #+name: test-body | 212 other bar from the keyboard. |
152 #+begin_src clojure | 213 |
153 | 214 #+name: test-proprioception |
215 #+begin_src clojure | |
216 (in-ns 'cortex.test.proprioception) | |
154 | 217 |
155 (defn test-proprioception | 218 (defn test-proprioception |
156 "Testing proprioception: | 219 "Testing proprioception: |
157 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 |
158 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 |
159 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 |
160 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 |
161 and counterclockwise, and only affect roll." | 224 and counterclockwise, and only affect roll." |
162 [] | 225 ([] (test-proprioception false)) |
163 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) | 226 ([record?] |
164 :mass 0 :color ColorRGBA/Green :name "hand") | 227 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) |
165 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) | 228 :mass 0 :color ColorRGBA/Gray :name "hand") |
166 :mass 1 :color ColorRGBA/Red :name "finger") | 229 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) |
167 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow | 230 :mass 1 |
168 :position (Vector3f. 0 1.2 0) | 231 :color |
169 :rotation (doto (Quaternion.) | 232 (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1) |
170 (.fromAngleAxis | 233 :name "finger") |
171 (/ Math/PI 2) | 234 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow |
172 (Vector3f. 0 0 1))) | 235 :position (Vector3f. 0 1.2 0) |
173 :physical? false) | 236 :rotation (doto (Quaternion.) |
174 joint (join-at-point hand finger (Vector3f. 0 1.2 0 )) | 237 (.fromAngleAxis |
175 creature (nodify [hand finger joint-node]) | 238 (/ Math/PI 2) |
176 finger-control (.getControl finger RigidBodyControl) | 239 (Vector3f. 0 0 1))) |
177 hand-control (.getControl hand RigidBodyControl)] | 240 :physical? false) |
178 | 241 creature (nodify [hand finger joint-node]) |
179 | 242 finger-control (.getControl finger RigidBodyControl) |
180 (let | 243 hand-control (.getControl hand RigidBodyControl) |
181 ;; ******************************************* | 244 joint (joint-dispatch {:type :point} hand-control finger-control |
182 | 245 (Vector3f. 0 1.2 0) |
183 [floor (box 10 10 10 :position (Vector3f. 0 -15 0) | 246 (Vector3f. 0 -1.2 0) nil) |
184 :mass 0 :color ColorRGBA/Gray) | 247 |
185 | 248 root (nodify [creature]) |
186 root (nodify [creature floor]) | 249 prop (proprioception-kernel creature joint-node) |
187 prop (joint-proprioception creature joint-node) | 250 prop-view (view-proprioception)] |
188 prop-view (proprioception-debug-window) | 251 (.setCollisionGroup |
189 | 252 (.getControl hand RigidBodyControl) |
190 controls | 253 PhysicsCollisionObject/COLLISION_GROUP_NONE) |
191 (merge standard-debug-controls | 254 (apply |
192 {"key-o" | 255 world |
193 (fn [_ _] (.setEnabled finger-control true)) | 256 (with-movement |
194 "key-p" | 257 finger |
195 (fn [_ _] (.setEnabled finger-control false)) | 258 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] |
196 "key-k" | 259 [1 1 10 10 10 10] |
197 (fn [_ _] (.setEnabled hand-control true)) | 260 [root |
198 "key-l" | 261 standard-debug-controls |
199 (fn [_ _] (.setEnabled hand-control false)) | 262 (fn [world] |
200 "key-i" | 263 (if record? |
201 (fn [world _] (set-gravity world (Vector3f. 0 0 0))) | 264 (Capture/captureVideo |
202 "key-period" | 265 world |
203 (fn [world _] | 266 (File. "/home/r/proj/cortex/render/proprio/main-view"))) |
204 (.setEnabled finger-control false) | 267 (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) |
205 (.setEnabled hand-control false) | 268 (set-gravity world (Vector3f. 0 0 0)) |
206 (.rotate creature (doto (Quaternion.) | 269 (enable-debug world) |
207 (.fromAngleAxis | 270 (light-up-everything world)) |
208 (float (/ Math/PI 15)) | 271 (fn [_ _] |
209 (Vector3f. 0 0 -1)))) | 272 (prop-view |
210 | 273 (list (prop)) |
211 (.setEnabled finger-control true) | 274 (if record? |
212 (.setEnabled hand-control true) | 275 (File. "/home/r/proj/cortex/render/proprio/proprio"))))]))))) |
213 (set-gravity world (Vector3f. 0 0 0)) | 276 #+end_src |
214 ) | 277 |
215 | 278 #+results: test-proprioception |
216 | 279 : #'cortex.test.proprioception/test-proprioception |
217 } | 280 |
218 ) | 281 * Video of Proprioception |
219 | 282 |
220 ] | 283 #+begin_html |
221 (comment | 284 <div class="figure"> |
222 (.setCollisionGroup | 285 <center> |
223 (.getControl hand RigidBodyControl) | 286 <video controls="controls" width="550"> |
224 PhysicsCollisionObject/COLLISION_GROUP_NONE) | 287 <source src="../video/test-proprioception.ogg" type="video/ogg" |
225 ) | 288 preload="none" poster="../images/aurellem-1280x480.png" /> |
226 (apply | 289 </video> |
227 world | 290 </center> |
228 (with-movement | 291 <p>Proprioception in a simple creature. The proprioceptive readout is |
229 hand | 292 in the upper left corner of the screen.</p> |
230 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"] | 293 </div> |
231 [10 10 10 10 1 1] | 294 #+end_html |
232 (with-movement | 295 |
233 finger | 296 ** Generating the Proprioception Video |
234 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] | 297 #+name: magick6 |
235 [1 1 10 10 10 10] | 298 #+begin_src clojure |
236 [root | 299 (ns cortex.video.magick6 |
237 controls | 300 (:import java.io.File) |
238 (fn [world] | 301 (:use clojure.contrib.shell-out)) |
239 (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) | 302 |
240 (set-gravity world (Vector3f. 0 0 0)) | 303 (defn images [path] |
241 (light-up-everything world)) | 304 (sort (rest (file-seq (File. path))))) |
242 (fn [_ _] (prop-view (list (prop))))])))))) | 305 |
243 | 306 (def base "/home/r/proj/cortex/render/proprio/") |
244 #+end_src | 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 | |
334 #+end_src | |
335 | |
336 * Headers | |
337 #+name: proprioception-header | |
338 #+begin_src clojure | |
339 (ns cortex.proprioception | |
340 "Simulate the sense of proprioception (ability to detect the | |
341 relative positions of body parts with repsect to other body parts) | |
342 in jMonkeyEngine3. Reads specially prepared blender files to | |
343 automatically generate proprioceptive senses." | |
344 (:use (cortex world util sense body)) | |
345 (:use clojure.contrib.def) | |
346 (:import com.jme3.scene.Node) | |
347 (:import java.awt.image.BufferedImage) | |
348 (:import (com.jme3.math Vector3f Quaternion))) | |
349 #+end_src | |
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]]. | |
245 | 370 |
246 | 371 |
247 * COMMENT generate source | 372 * COMMENT generate source |
248 #+begin_src clojure :tangle ../src/cortex/proprioception.clj | 373 #+begin_src clojure :tangle ../src/cortex/proprioception.clj |
374 <<proprioception-header>> | |
375 <<helpers>> | |
249 <<proprioception>> | 376 <<proprioception>> |
250 #+end_src | 377 <<visualize>> |
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 |