Mercurial > cortex
diff 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 |
line wrap: on
line diff
1.1 --- a/org/proprioception.org Mon Feb 13 21:53:28 2012 -0600 1.2 +++ b/org/proprioception.org Mon Feb 13 21:53:54 2012 -0600 1.3 @@ -6,36 +6,89 @@ 1.4 #+SETUPFILE: ../../aurellem/org/setup.org 1.5 #+INCLUDE: ../../aurellem/org/level-0.org 1.6 1.7 -#+name: proprioception 1.8 +* Proprioception 1.9 + 1.10 +Close your eyes, and touch your nose with your right index finger. How 1.11 +did you do it? You could not see your hand, and neither your hand nor 1.12 +your nose could use the sense of touch to guide the path of your hand. 1.13 +There are no sound cues, and Taste and Smell certainly don't provide 1.14 +any help. You know where your hand is without your other senses 1.15 +because of Proprioception. 1.16 + 1.17 +Humans can sometimes loose this sense through viral infections or 1.18 +damage to the spinal cord or brain, and when they do, they loose the 1.19 +ability to control their own bodies without looking directly at the 1.20 +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]], 1.21 +a woman named Christina looses this sense and has to learn how to move 1.22 +by carefully watching her arms and legs. She describes proprioception 1.23 +as the "eyes of the body, the way the body sees itself". 1.24 + 1.25 +Proprioception in humans is mediated by [[http://en.wikipedia.org/wiki/Articular_capsule][joint capsules]], [[http://en.wikipedia.org/wiki/Muscle_spindle][muscle 1.26 +spindles]], and the [[http://en.wikipedia.org/wiki/Golgi_tendon_organ][Golgi tendon organs]]. These measure the relative 1.27 +positions of each pody part by monitoring muscle strain and length. 1.28 + 1.29 +It's clear that this is a vital sense for fulid, graceful 1.30 +movement. It's also particurally easy to implement in jMonkeyEngine. 1.31 + 1.32 +My simulated proprioception calculates the relative angles of each 1.33 +joint from the rest position defined in the blender file. This 1.34 +simulates the muscle-spindles and joint capsules. I will deal with 1.35 +Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]]. 1.36 + 1.37 +* Helper Functions 1.38 + 1.39 +=(absolute-angle)= calculates the angle between two vectors, relative to a 1.40 +third axis vector. This angle is the number of radians you have to 1.41 +move counterclockwise around the axis vector to get from the first to 1.42 +the second vector. It is not commutative like a normal dot-product 1.43 +angle is. 1.44 + 1.45 +#+name: helpers 1.46 #+begin_src clojure 1.47 -(ns cortex.proprioception 1.48 - "Simulate the sense of proprioception (ability to detect the 1.49 - relative positions of body parts with repsect to other body parts) 1.50 - in jMonkeyEngine3. Reads specially prepared blender files to 1.51 - automatically generate proprioceptive senses." 1.52 - (:use (cortex world util sense body)) 1.53 - (:use clojure.contrib.def) 1.54 - (:import com.jme3.scene.Node) 1.55 - (:import java.awt.image.BufferedImage) 1.56 - (:import (com.jme3.math Vector3f Quaternion))) 1.57 +(in-ns 'cortex.proprioception) 1.58 1.59 (defn right-handed? 1.60 "true iff the three vectors form a right handed coordinate 1.61 - system. The three vectors do not have to be normalized or 1.62 - orthogonal." 1.63 + system. The three vectors do not have to be normalized or 1.64 + orthogonal." 1.65 [vec1 vec2 vec3] 1.66 (< 0 (.dot (.cross vec1 vec2) vec3))) 1.67 1.68 (defn absolute-angle 1.69 - "The angle between 'vec1 and 'vec2. Positive if the angle to get 1.70 - from 'vec1 to 'vec2 is counterclockwise around 'axis, and negative 1.71 - otherwise." 1.72 + "The angle between 'vec1 and 'vec2 around 'axis. In the range 1.73 + [0 (* 2 Math/PI)]." 1.74 [vec1 vec2 axis] 1.75 (let [angle (.angleBetween vec1 vec2)] 1.76 (if (right-handed? vec1 vec2 axis) 1.77 angle (- (* 2 Math/PI) angle)))) 1.78 +#+end_src 1.79 1.80 -(defn proprioception-fn 1.81 +#+begin_src clojure :exports both 1.82 +(in-ns 'cortex.proprioception) 1.83 +(absolute-angle Vector3f/UNIT_X Vector3f/UNIT_Y Vector3f/UNIT_Z) 1.84 +#+end_src 1.85 + 1.86 +#+results: 1.87 +: 1.5707964 1.88 + 1.89 +#+begin_src clojure :exports both 1.90 +(in-ns 'cortex.proprioception) 1.91 +(absolute-angle 1.92 + Vector3f/UNIT_X (.mult Vector3f/UNIT_Y (float -1)) Vector3f/UNIT_Z) 1.93 +#+end_src 1.94 + 1.95 +#+results: 1.96 +: 4.7123889366733 1.97 + 1.98 +* Proprioception Kernel 1.99 + 1.100 +Given a joint, =(proprioception-kernel)= produces a function that 1.101 +calculates the euler angles between the the objects the joint 1.102 +connects. 1.103 + 1.104 +#+name: proprioception 1.105 +#+begin_src clojure 1.106 +(defn proprioception-kernel 1.107 "Returns a function which returns proprioceptive sensory data when 1.108 called inside a running simulation." 1.109 [#^Node parts #^Node joint] 1.110 @@ -44,12 +97,6 @@ 1.111 x0 (.mult joint-rot Vector3f/UNIT_X) 1.112 y0 (.mult joint-rot Vector3f/UNIT_Y) 1.113 z0 (.mult joint-rot Vector3f/UNIT_Z)] 1.114 - (println-repl "x:" x0) 1.115 - (println-repl "y:" y0) 1.116 - (println-repl "z:" z0) 1.117 - (println-repl "init-a:" (.getWorldRotation obj-a)) 1.118 - (println-repl "init-b:" (.getWorldRotation obj-b)) 1.119 - 1.120 (fn [] 1.121 (let [rot-a (.clone (.getWorldRotation obj-a)) 1.122 rot-b (.clone (.getWorldRotation obj-b)) 1.123 @@ -80,18 +127,33 @@ 1.124 that joint." 1.125 [#^Node creature] 1.126 ;; extract the body's joints 1.127 - (let [senses (map (partial proprioception-fn creature) 1.128 + (let [senses (map (partial proprioception-kernel creature) 1.129 (joints creature))] 1.130 (fn [] 1.131 (map #(%) senses)))) 1.132 +#+end_src 1.133 1.134 1.135 +=(proprioception!)= maps =(proprioception-kernel)= across all the 1.136 +joints of the creature. It uses the same list of joints that 1.137 +=(cortex.body/joints)= uses. 1.138 + 1.139 +* Visualizing Proprioception 1.140 + 1.141 +Proprioception has the lowest bandwidth of all the senses so far, and 1.142 +it doesn't lend itself as readily to visual representation like 1.143 +vision, hearing, or touch. This visualization code creates a "guage" 1.144 +to view each of the three relative angles along a circle. 1.145 + 1.146 +#+name: visualize 1.147 +#+begin_src clojure 1.148 +(in-ns 'cortex.proprioception) 1.149 + 1.150 (defn draw-sprite [image sprite x y color ] 1.151 (dorun 1.152 (for [[u v] sprite] 1.153 (.setRGB image (+ u x) (+ v y) color)))) 1.154 1.155 - 1.156 (defn view-angle 1.157 "create a debug view of an angle" 1.158 [color] 1.159 @@ -109,7 +171,6 @@ 1.160 (reset! previous position)) 1.161 image)))) 1.162 1.163 - 1.164 (defn proprioception-display-kernel 1.165 "Display proprioception angles in a BufferedImage" 1.166 [[h p r]] 1.167 @@ -143,14 +204,16 @@ 1.168 display each element of the list to the screen as an image." 1.169 [] 1.170 (view-sense proprioception-display-kernel)) 1.171 - 1.172 - 1.173 - 1.174 #+end_src 1.175 1.176 -#+name: test-body 1.177 +* Proprioception Test 1.178 +This test does not use the worm, but instead uses two bars, bound 1.179 +together by a point2point joint. One bar is fixed, and I control the 1.180 +other bar from the keyboard. 1.181 + 1.182 +#+name: test-proprioception 1.183 #+begin_src clojure 1.184 - 1.185 +(in-ns 'cortex.test.proprioception) 1.186 1.187 (defn test-proprioception 1.188 "Testing proprioception: 1.189 @@ -159,92 +222,166 @@ 1.190 change only the value of pitch. key-f/key-g moves it side to side 1.191 and changes yaw. key-v/key-b will spin the blue segment clockwise 1.192 and counterclockwise, and only affect roll." 1.193 - [] 1.194 - (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) 1.195 - :mass 0 :color ColorRGBA/Green :name "hand") 1.196 - finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) 1.197 - :mass 1 :color ColorRGBA/Red :name "finger") 1.198 - joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow 1.199 - :position (Vector3f. 0 1.2 0) 1.200 - :rotation (doto (Quaternion.) 1.201 - (.fromAngleAxis 1.202 - (/ Math/PI 2) 1.203 - (Vector3f. 0 0 1))) 1.204 - :physical? false) 1.205 - joint (join-at-point hand finger (Vector3f. 0 1.2 0 )) 1.206 - creature (nodify [hand finger joint-node]) 1.207 - finger-control (.getControl finger RigidBodyControl) 1.208 - hand-control (.getControl hand RigidBodyControl)] 1.209 - 1.210 + ([] (test-proprioception false)) 1.211 + ([record?] 1.212 + (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) 1.213 + :mass 0 :color ColorRGBA/Gray :name "hand") 1.214 + finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) 1.215 + :mass 1 1.216 + :color 1.217 + (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1) 1.218 + :name "finger") 1.219 + joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow 1.220 + :position (Vector3f. 0 1.2 0) 1.221 + :rotation (doto (Quaternion.) 1.222 + (.fromAngleAxis 1.223 + (/ Math/PI 2) 1.224 + (Vector3f. 0 0 1))) 1.225 + :physical? false) 1.226 + creature (nodify [hand finger joint-node]) 1.227 + finger-control (.getControl finger RigidBodyControl) 1.228 + hand-control (.getControl hand RigidBodyControl) 1.229 + joint (joint-dispatch {:type :point} hand-control finger-control 1.230 + (Vector3f. 0 1.2 0) 1.231 + (Vector3f. 0 -1.2 0) nil) 1.232 1.233 - (let 1.234 - ;; ******************************************* 1.235 - 1.236 - [floor (box 10 10 10 :position (Vector3f. 0 -15 0) 1.237 - :mass 0 :color ColorRGBA/Gray) 1.238 - 1.239 - root (nodify [creature floor]) 1.240 - prop (joint-proprioception creature joint-node) 1.241 - prop-view (proprioception-debug-window) 1.242 - 1.243 - controls 1.244 - (merge standard-debug-controls 1.245 - {"key-o" 1.246 - (fn [_ _] (.setEnabled finger-control true)) 1.247 - "key-p" 1.248 - (fn [_ _] (.setEnabled finger-control false)) 1.249 - "key-k" 1.250 - (fn [_ _] (.setEnabled hand-control true)) 1.251 - "key-l" 1.252 - (fn [_ _] (.setEnabled hand-control false)) 1.253 - "key-i" 1.254 - (fn [world _] (set-gravity world (Vector3f. 0 0 0))) 1.255 - "key-period" 1.256 - (fn [world _] 1.257 - (.setEnabled finger-control false) 1.258 - (.setEnabled hand-control false) 1.259 - (.rotate creature (doto (Quaternion.) 1.260 - (.fromAngleAxis 1.261 - (float (/ Math/PI 15)) 1.262 - (Vector3f. 0 0 -1)))) 1.263 - 1.264 - (.setEnabled finger-control true) 1.265 - (.setEnabled hand-control true) 1.266 - (set-gravity world (Vector3f. 0 0 0)) 1.267 - ) 1.268 - 1.269 - 1.270 - } 1.271 - ) 1.272 + root (nodify [creature]) 1.273 + prop (proprioception-kernel creature joint-node) 1.274 + prop-view (view-proprioception)] 1.275 + (.setCollisionGroup 1.276 + (.getControl hand RigidBodyControl) 1.277 + PhysicsCollisionObject/COLLISION_GROUP_NONE) 1.278 + (apply 1.279 + world 1.280 + (with-movement 1.281 + finger 1.282 + ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] 1.283 + [1 1 10 10 10 10] 1.284 + [root 1.285 + standard-debug-controls 1.286 + (fn [world] 1.287 + (if record? 1.288 + (Capture/captureVideo 1.289 + world 1.290 + (File. "/home/r/proj/cortex/render/proprio/main-view"))) 1.291 + (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) 1.292 + (set-gravity world (Vector3f. 0 0 0)) 1.293 + (enable-debug world) 1.294 + (light-up-everything world)) 1.295 + (fn [_ _] 1.296 + (prop-view 1.297 + (list (prop)) 1.298 + (if record? 1.299 + (File. "/home/r/proj/cortex/render/proprio/proprio"))))]))))) 1.300 +#+end_src 1.301 1.302 - ] 1.303 - (comment 1.304 - (.setCollisionGroup 1.305 - (.getControl hand RigidBodyControl) 1.306 - PhysicsCollisionObject/COLLISION_GROUP_NONE) 1.307 - ) 1.308 - (apply 1.309 - world 1.310 - (with-movement 1.311 - hand 1.312 - ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"] 1.313 - [10 10 10 10 1 1] 1.314 - (with-movement 1.315 - finger 1.316 - ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] 1.317 - [1 1 10 10 10 10] 1.318 - [root 1.319 - controls 1.320 - (fn [world] 1.321 - (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) 1.322 - (set-gravity world (Vector3f. 0 0 0)) 1.323 - (light-up-everything world)) 1.324 - (fn [_ _] (prop-view (list (prop))))])))))) 1.325 +#+results: test-proprioception 1.326 +: #'cortex.test.proprioception/test-proprioception 1.327 1.328 +* Video of Proprioception 1.329 + 1.330 +#+begin_html 1.331 +<div class="figure"> 1.332 +<center> 1.333 +<video controls="controls" width="550"> 1.334 + <source src="../video/test-proprioception.ogg" type="video/ogg" 1.335 + preload="none" poster="../images/aurellem-1280x480.png" /> 1.336 +</video> 1.337 +</center> 1.338 +<p>Proprioception in a simple creature. The proprioceptive readout is 1.339 + in the upper left corner of the screen.</p> 1.340 +</div> 1.341 +#+end_html 1.342 + 1.343 +** Generating the Proprioception Video 1.344 +#+name: magick6 1.345 +#+begin_src clojure 1.346 +(ns cortex.video.magick6 1.347 + (:import java.io.File) 1.348 + (:use clojure.contrib.shell-out)) 1.349 + 1.350 +(defn images [path] 1.351 + (sort (rest (file-seq (File. path))))) 1.352 + 1.353 +(def base "/home/r/proj/cortex/render/proprio/") 1.354 + 1.355 +(defn pics [file] 1.356 + (images (str base file))) 1.357 + 1.358 +(defn combine-images [] 1.359 + (let [main-view (pics "main-view") 1.360 + proprioception (pics "proprio/0") 1.361 + targets (map 1.362 + #(File. (str base "out/" (format "%07d.png" %))) 1.363 + (range 0 (count main-view)))] 1.364 + (dorun 1.365 + (pmap 1.366 + (comp 1.367 + (fn [[ main-view proprioception target]] 1.368 + (println target) 1.369 + (sh "convert" 1.370 + main-view 1.371 + proprioception "-geometry" "+20+20" "-composite" 1.372 + target)) 1.373 + (fn [& args] (map #(.getCanonicalPath %) args))) 1.374 + main-view proprioception targets)))) 1.375 #+end_src 1.376 1.377 +#+begin_src sh :results silent 1.378 +cd ~/proj/cortex/render/proprio 1.379 +ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora \ 1.380 + test-proprioception.ogg 1.381 +#+end_src 1.382 + 1.383 +* Headers 1.384 +#+name: proprioception-header 1.385 +#+begin_src clojure 1.386 +(ns cortex.proprioception 1.387 + "Simulate the sense of proprioception (ability to detect the 1.388 + relative positions of body parts with repsect to other body parts) 1.389 + in jMonkeyEngine3. Reads specially prepared blender files to 1.390 + automatically generate proprioceptive senses." 1.391 + (:use (cortex world util sense body)) 1.392 + (:use clojure.contrib.def) 1.393 + (:import com.jme3.scene.Node) 1.394 + (:import java.awt.image.BufferedImage) 1.395 + (:import (com.jme3.math Vector3f Quaternion))) 1.396 +#+end_src 1.397 + 1.398 +#+name: test-proprioception-header 1.399 +#+begin_src clojure 1.400 +(ns cortex.test.proprioception 1.401 +(:import (com.aurellem.capture Capture RatchetTimer)) 1.402 +(:use (cortex util world proprioception body)) 1.403 +(:import java.io.File)) 1.404 +(cortex.import/mega-import-jme3) 1.405 +#+end_src 1.406 + 1.407 +* Source Listing 1.408 + - [[../src/cortex/proprioception.clj][cortex.proprioception]] 1.409 + - [[../src/cortex/test/touch.clj][cortex.test.proprioception]] 1.410 + - [[../src/cortex/video/magick6.clj][cortex.video.magick6]] 1.411 +#+html: <ul> <li> <a href="../org/proprioception.org">This org file</a> </li> </ul> 1.412 + - [[http://hg.bortreb.com ][source-repository]] 1.413 + 1.414 +* Next 1.415 + 1.416 +Next time, I'll give the Worm the power to [[./movement.org][move on it's own]]. 1.417 + 1.418 1.419 * COMMENT generate source 1.420 #+begin_src clojure :tangle ../src/cortex/proprioception.clj 1.421 +<<proprioception-header>> 1.422 +<<helpers>> 1.423 <<proprioception>> 1.424 +<<visualize>> 1.425 #+end_src 1.426 + 1.427 +#+begin_src clojure :tangle ../src/cortex/test/proprioception.clj 1.428 +<<test-proprioception-header>> 1.429 +<<test-proprioception>> 1.430 +#+end_src 1.431 + 1.432 +#+begin_src clojure :tangle ../src/cortex/video/magick6.clj 1.433 +<<magick6>> 1.434 +#+end_src