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