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