Mercurial > cortex
view org/proprioception.org @ 380:2d0afb231081
spellcheck
author | rlm |
---|---|
date | Wed, 10 Apr 2013 16:49:05 -0400 |
parents | 4f5a5d5f1613 |
children | 5205535237fb |
line wrap: on
line source
1 #+title: The Sense of Proprioception2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: proprioception for simulated creatures5 #+keywords: simulation, jMonkeyEngine3, clojure6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org9 * Proprioception11 Close your eyes, and touch your nose with your right index finger. How12 did you do it? You could not see your hand, and neither your hand nor13 your nose could use the sense of touch to guide the path of your hand.14 There are no sound cues, and Taste and Smell certainly don't provide15 any help. You know where your hand is without your other senses16 because of Proprioception.18 Humans can sometimes loose this sense through viral infections or19 damage to the spinal cord or brain, and when they do, they loose the20 ability to control their own bodies without looking directly at the21 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 move23 by carefully watching her arms and legs. She describes proprioception24 as the "eyes of the body, the way the body sees itself".26 Proprioception in humans is mediated by [[http://en.wikipedia.org/wiki/Articular_capsule][joint capsules]], [[http://en.wikipedia.org/wiki/Muscle_spindle][muscle27 spindles]], and the [[http://en.wikipedia.org/wiki/Golgi_tendon_organ][Golgi tendon organs]]. These measure the relative28 positions of each body part by monitoring muscle strain and length.30 It's clear that this is a vital sense for fluid, graceful31 movement. It's also particularly easy to implement in jMonkeyEngine.33 My simulated proprioception calculates the relative angles of each34 joint from the rest position defined in the blender file. This35 simulates the muscle-spindles and joint capsules. I will deal with36 Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]].38 * Helper Functions40 =absolute-angle= calculates the angle between two vectors, relative to a41 third axis vector. This angle is the number of radians you have to42 move counterclockwise around the axis vector to get from the first to43 the second vector. It is not commutative like a normal dot-product44 angle is.46 #+name: helpers47 #+begin_src clojure48 (in-ns 'cortex.proprioception)50 (defn right-handed?51 "true iff the three vectors form a right handed coordinate52 system. The three vectors do not have to be normalized or53 orthogonal."54 [vec1 vec2 vec3]55 (< 0 (.dot (.cross vec1 vec2) vec3)))57 (defn absolute-angle58 "The angle between 'vec1 and 'vec2 around 'axis. In the range59 [0 (* 2 Math/PI)]."60 [vec1 vec2 axis]61 (let [angle (.angleBetween vec1 vec2)]62 (if (right-handed? vec1 vec2 axis)63 angle (- (* 2 Math/PI) angle))))64 #+end_src66 #+begin_src clojure :exports both67 (in-ns 'cortex.proprioception)68 (absolute-angle Vector3f/UNIT_X Vector3f/UNIT_Y Vector3f/UNIT_Z)69 #+end_src71 #+results:72 : 1.570796474 #+begin_src clojure :exports both75 (in-ns 'cortex.proprioception)76 (absolute-angle77 Vector3f/UNIT_X (.mult Vector3f/UNIT_Y (float -1)) Vector3f/UNIT_Z)78 #+end_src80 #+results:81 : 4.712388936673383 * Proprioception Kernel85 Given a joint, =proprioception-kernel= produces a function that86 calculates the Euler angles between the the objects the joint87 connects.89 #+name: proprioception90 #+begin_src clojure91 (defn proprioception-kernel92 "Returns a function which returns proprioceptive sensory data when93 called inside a running simulation."94 [#^Node parts #^Node joint]95 (let [[obj-a obj-b] (joint-targets parts joint)96 joint-rot (.getWorldRotation joint)97 x0 (.mult joint-rot Vector3f/UNIT_X)98 y0 (.mult joint-rot Vector3f/UNIT_Y)99 z0 (.mult joint-rot Vector3f/UNIT_Z)]100 (fn []101 (let [rot-a (.clone (.getWorldRotation obj-a))102 rot-b (.clone (.getWorldRotation obj-b))103 x (.mult rot-a x0)104 y (.mult rot-a y0)105 z (.mult rot-a z0)107 X (.mult rot-b x0)108 Y (.mult rot-b y0)109 Z (.mult rot-b z0)110 heading (Math/atan2 (.dot X z) (.dot X x))111 pitch (Math/atan2 (.dot X y) (.dot X x))113 ;; rotate x-vector back to origin114 reverse115 (doto (Quaternion.)116 (.fromAngleAxis117 (.angleBetween X x)118 (let [cross (.normalize (.cross X x))]119 (if (= 0 (.length cross)) y cross))))120 roll (absolute-angle (.mult reverse Y) y x)]121 [heading pitch roll]))))123 (defn proprioception!124 "Endow the creature with the sense of proprioception. Returns a125 sequence of functions, one for each child of the \"joints\" node in126 the creature, which each report proprioceptive information about127 that joint."128 [#^Node creature]129 ;; extract the body's joints130 (let [senses (map (partial proprioception-kernel creature)131 (joints creature))]132 (fn []133 (map #(%) senses))))134 #+end_src137 =proprioception!= maps =proprioception-kernel= across all the138 joints of the creature. It uses the same list of joints that139 =cortex.body/joints= uses.141 * Visualizing Proprioception143 Proprioception has the lowest bandwidth of all the senses so far, and144 it doesn't lend itself as readily to visual representation like145 vision, hearing, or touch. This visualization code creates a "gauge"146 to view each of the three relative angles along a circle.148 #+name: visualize149 #+begin_src clojure150 (in-ns 'cortex.proprioception)152 (defn draw-sprite [image sprite x y color ]153 (dorun154 (for [[u v] sprite]155 (.setRGB image (+ u x) (+ v y) color))))157 (defn view-angle158 "create a debug view of an angle"159 [color]160 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB)161 previous (atom [25 25])162 sprite [[0 0] [0 1]163 [0 -1] [-1 0] [1 0]]]164 (fn [angle]165 (let [angle (float angle)]166 (let [position167 [(+ 25 (int (* 20 (Math/cos angle))))168 (+ 25 (int (* -20 (Math/sin angle))))]]169 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000)170 (draw-sprite image sprite (position 0) (position 1) color)171 (reset! previous position))172 image))))174 (defn proprioception-display-kernel175 "Display proprioception angles in a BufferedImage"176 [[h p r]]177 (let [image (BufferedImage. 50 50 BufferedImage/TYPE_INT_RGB)178 previous-heading (atom [25 25])179 previous-pitch (atom [25 25])180 previous-roll (atom [25 25])182 heading-sprite [[0 0] [0 1] [0 -1] [-1 0] [1 0]]183 pitch-sprite [[0 0] [0 1] [0 -1] [-1 0] [1 0]]184 roll-sprite [[0 0] [0 1] [0 -1] [-1 0] [1 0]]185 draw-angle186 (fn [angle sprite previous color]187 (let [angle (float angle)]188 (let [position189 [(+ 25 (int (* 20 (Math/cos angle))))190 (+ 25 (int (* -20 (Math/sin angle))))]]191 (draw-sprite image sprite (@previous 0) (@previous 1) 0x000000)192 (draw-sprite image sprite (position 0) (position 1) color)193 (reset! previous position))194 image))]195 (dorun (map draw-angle196 [h p r]197 [heading-sprite pitch-sprite roll-sprite]198 [previous-heading previous-pitch previous-roll]199 [0xFF0000 0x00FF00 0xFFFFFF]))200 image))202 (defn view-proprioception203 "Creates a function which accepts a list of proprioceptive data and204 display each element of the list to the screen as an image."205 []206 (view-sense proprioception-display-kernel))207 #+end_src209 * Proprioception Test210 This test does not use the worm, but instead uses two bars, bound211 together by a point2point joint. One bar is fixed, and I control the212 other bar from the keyboard.214 #+name: test-proprioception215 #+begin_src clojure216 (in-ns 'cortex.test.proprioception)218 (defn test-proprioception219 "Testing proprioception:220 You should see two floating bars, and a display of pitch, yaw, and221 roll. The white dot measures pitch (spin around the long axis), the222 green dot measures yaw (in this case, rotation around a circle223 perpendicular to your line of view), and the red dot measures224 roll (rotation around a circle perlendicular to the the other two225 circles).227 Keys:228 r : rotate along long axis229 t : opposite direction of rotation as <r>231 f : rotate in field of view232 g : opposite direction of rotation as <f>234 v : rotate in final direction235 b : opposite direction of rotation as <v>"237 ([] (test-proprioception false))238 ([record?]239 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)240 :mass 0 :color ColorRGBA/Gray :name "hand")241 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)242 :mass 1243 :color244 (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1)245 :name "finger")246 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow247 :position (Vector3f. 0 1.2 0)248 :rotation (doto (Quaternion.)249 (.fromAngleAxis250 (/ Math/PI 2)251 (Vector3f. 0 0 1)))252 :physical? false)253 creature (nodify [hand finger joint-node])254 finger-control (.getControl finger RigidBodyControl)255 hand-control (.getControl hand RigidBodyControl)256 joint (joint-dispatch {:type :point} hand-control finger-control257 (Vector3f. 0 1.2 0)258 (Vector3f. 0 -1.2 0) nil)260 root (nodify [creature])261 prop (proprioception-kernel creature joint-node)262 prop-view (view-proprioception)]263 (.setCollisionGroup264 (.getControl hand RigidBodyControl)265 PhysicsCollisionObject/COLLISION_GROUP_NONE)266 (apply267 world268 (with-movement269 finger270 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]271 [1 1 10 10 10 10]272 [root273 standard-debug-controls274 (fn [world]275 (let [timer (RatchetTimer. 60)]276 (.setTimer world timer)277 (display-dilated-time world timer))278 (if record?279 (Capture/captureVideo280 world281 (File. "/home/r/proj/cortex/render/proprio/main-view")))282 (set-gravity world (Vector3f. 0 0 0))283 (enable-debug world)284 (light-up-everything world))285 (fn [_ _]286 (prop-view287 (list (prop))288 (if record?289 (File. "/home/r/proj/cortex/render/proprio/proprio"))))])))))290 #+end_src292 #+results: test-proprioception293 : #'cortex.test.proprioception/test-proprioception295 * Video of Proprioception297 #+begin_html298 <div class="figure">299 <center>300 <video controls="controls" width="550">301 <source src="../video/test-proprioception.ogg" type="video/ogg"302 preload="none" poster="../images/aurellem-1280x480.png" />303 </video>304 <br> <a href="http://youtu.be/JjdDmyM8b0w"> YouTube </a>305 </center>306 <p>Proprioception in a simple creature. The proprioceptive readout is307 in the upper left corner of the screen.</p>308 </div>309 #+end_html311 ** Generating the Proprioception Video312 #+name: magick6313 #+begin_src clojure314 (ns cortex.video.magick6315 (:import java.io.File)316 (:use clojure.java.shell))318 (defn images [path]319 (sort (rest (file-seq (File. path)))))321 (def base "/home/r/proj/cortex/render/proprio/")323 (defn pics [file]324 (images (str base file)))326 (defn combine-images []327 (let [main-view (pics "main-view")328 proprioception (pics "proprio/0")329 targets (map330 #(File. (str base "out/" (format "%07d.png" %)))331 (range 0 (count main-view)))]332 (dorun333 (pmap334 (comp335 (fn [[ main-view proprioception target]]336 (println target)337 (sh "convert"338 main-view339 proprioception "-geometry" "+20+20" "-composite"340 target))341 (fn [& args] (map #(.getCanonicalPath %) args)))342 main-view proprioception targets))))343 #+end_src345 #+begin_src sh :results silent346 cd ~/proj/cortex/render/proprio347 ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora \348 test-proprioception.ogg349 #+end_src351 * Headers352 #+name: proprioception-header353 #+begin_src clojure354 (ns cortex.proprioception355 "Simulate the sense of proprioception (ability to detect the356 relative positions of body parts with respect to other body parts)357 in jMonkeyEngine3. Reads specially prepared blender files to358 automatically generate proprioceptive senses."359 (:use (cortex world util sense body))360 (:import com.jme3.scene.Node)361 (:import java.awt.image.BufferedImage)362 (:import (com.jme3.math Vector3f Quaternion)))363 #+end_src365 #+name: test-proprioception-header366 #+begin_src clojure367 (ns cortex.test.proprioception368 (:import (com.aurellem.capture Capture RatchetTimer IsoTimer))369 (:use (cortex util world proprioception body))370 (:import java.io.File)371 (:import com.jme3.bullet.control.RigidBodyControl)372 (:import com.jme3.bullet.collision.PhysicsCollisionObject)373 (:import (com.jme3.math Vector3f Quaternion ColorRGBA)))374 #+end_src376 #+results: test-proprioception-header377 : com.jme3.math.ColorRGBA379 * Source Listing380 - [[../src/cortex/proprioception.clj][cortex.proprioception]]381 - [[../src/cortex/test/touch.clj][cortex.test.proprioception]]382 - [[../src/cortex/video/magick6.clj][cortex.video.magick6]]383 #+html: <ul> <li> <a href="../org/proprioception.org">This org file</a> </li> </ul>384 - [[http://hg.bortreb.com ][source-repository]]386 * Next388 Next time, I'll give the Worm the power to [[./movement.org][move on it's own]].391 * COMMENT generate source392 #+begin_src clojure :tangle ../src/cortex/proprioception.clj393 <<proprioception-header>>394 <<helpers>>395 <<proprioception>>396 <<visualize>>397 #+end_src399 #+begin_src clojure :tangle ../src/cortex/test/proprioception.clj400 <<test-proprioception-header>>401 <<test-proprioception>>402 #+end_src404 #+begin_src clojure :tangle ../src/cortex/video/magick6.clj405 <<magick6>>406 #+end_src