diff org/movement.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 2fdcbe8185b1
children d487348c461c
line wrap: on
line diff
     1.1 --- a/org/movement.org	Mon Feb 13 21:53:28 2012 -0600
     1.2 +++ b/org/movement.org	Mon Feb 13 21:53:54 2012 -0600
     1.3 @@ -1,4 +1,4 @@
     1.4 -#+title: Movement!
     1.5 +#+title: Simulated Muscles
     1.6  #+author: Robert McIntyre
     1.7  #+email: rlm@mit.edu
     1.8  #+description: muscles for a simulated creature
     1.9 @@ -7,48 +7,61 @@
    1.10  #+INCLUDE: ../../aurellem/org/level-0.org
    1.11  
    1.12  
    1.13 +* Muscles
    1.14 +
    1.15  Surprisingly enough, terristerial creatures only move by using torque
    1.16  applied about their joints.  There's not a single straight line of
    1.17  force in the human body at all!  (A straight line of force would
    1.18 -correspond to some sort of jet or rocket propulseion.)
    1.19 +correspond to some sort of jet or rocket propulsion.)
    1.20  
    1.21 +*(next paragraph is from memory and needs to be checked!)*
    1.22  
    1.23 -Here's how motor-control/ proprioception will work: Each muscle is
    1.24 -defined by a 1-D array of numbers (the "motor pool") each of which
    1.25 -represent muscle fibers. A muscle also has a scalar :strength factor
    1.26 -which determines how strong the muscle as a whole is.  The effector
    1.27 -function for a muscle takes a number < (count motor-pool) and that
    1.28 -number is said to "activate" all the muscle fibers whose index is
    1.29 -lower than the number.  Each fiber will apply force in proportion to
    1.30 -its value in the array.  Lower values cause less force.  The lower
    1.31 -values can be put at the "beginning" of the 1-D array to simulate the
    1.32 -layout of actual human muscles, which are capable of more percise
    1.33 -movements when exerting less force.
    1.34 +In humans, muscles are composed of millions of sarcomeres, which can
    1.35 +contract to exert force. A single motor neuron might control 100-1,000
    1.36 +sarcomeres. When the motor neuron is engaged by the brain, it
    1.37 +activates all of the sarcomeres to which it is attached. Some motor
    1.38 +neurons command many sarcomeres, and some command only a few. The
    1.39 +spinal cord generally engages the motor neurons which control few
    1.40 +sarcomeres before the motor neurons which control many sarcomeres.
    1.41 +This recruitment stragety allows for percise movements at low
    1.42 +strength. The collection of all motor neurons that control a muscle is
    1.43 +called the motor pool. The brain essentially says "activate 30% of the
    1.44 +motor pool" and the spinal cord recruits motor neurons untill 30% are
    1.45 +activated. Since the distribution of power among motor neurons is
    1.46 +unequal and recruitment goes from weakest to strongest, 30% of the
    1.47 +motor pool might be 5% of the strength of the muscle.
    1.48  
    1.49 -#+name: movement
    1.50 +My simulated muscles follow a similiar design: Each muscle is defined
    1.51 +by a 1-D array of numbers (the "motor pool"). Each number represents a
    1.52 +motor neuron which controlls a number of sarcomeres equal to the
    1.53 +number. A muscle also has a scalar :strength factor which determines
    1.54 +the total force the muscle can exert when all motor neurons are
    1.55 +activated.  The effector function for a muscle takes a number to index
    1.56 +into the motor pool, and that number "activates" all the motor neurons
    1.57 +whose index is lower or equal to the number.  Each motor-neuron will
    1.58 +apply force in proportion to its value in the array.  Lower values
    1.59 +cause less force.  The lower values can be put at the "beginning" of
    1.60 +the 1-D array to simulate the layout of actual human muscles, which
    1.61 +are capable of more percise movements when exerting less force. Or,
    1.62 +the motor pool can simulate more exoitic recruitment strageties which
    1.63 +do not correspond to human muscles. 
    1.64 +
    1.65 +This 1D array is defined in an image file for ease of
    1.66 +creation/visualization. Here is an example muscle profile image.
    1.67 +
    1.68 +#+caption: A muscle profile image that describes the strengths of each motor neuron in a muscle. White is weakest and dark red is strongest. This particular pattern has weaker motor neurons at the beginning, just like human muscle.
    1.69 +[[../images/basic-muscle.png]]
    1.70 +
    1.71 +* Blender Meta-data
    1.72 +
    1.73 +In blender, each muscle is an empty node whose top level parent is
    1.74 +named "muscles", just like eyes, ears, and joints.
    1.75 +
    1.76 +These functions define the expected meta-data for a muscle node.
    1.77 +
    1.78 +#+name: muscle-meta-data
    1.79  #+begin_src clojure 
    1.80 -(ns cortex.movement
    1.81 -  "Give simulated creatures defined in special blender files the power
    1.82 -   to move around in a simulated environment."
    1.83 -  {:author "Robert McIntyre"}
    1.84 -  (:use (cortex world util sense body))
    1.85 -  (:use clojure.contrib.def)
    1.86 -  (:import java.awt.image.BufferedImage)
    1.87 -  (:import com.jme3.scene.Node)
    1.88 -  (:import com.jme3.math.Vector3f)
    1.89 -  (:import com.jme3.bullet.control.RigidBodyControl))
    1.90 -
    1.91 -(defn muscle-profile
    1.92 -  "Return a vector where each entry is the strength of the \"motor
    1.93 -  pool\" at that part in the muscle."
    1.94 -  [#^BufferedImage profile]
    1.95 -  (vec
    1.96 -   (let [width (.getWidth profile)]
    1.97 -     (for [x (range width)]
    1.98 -       (- 255
    1.99 -          (bit-and
   1.100 -           0x0000FF
   1.101 -           (.getRGB profile x 0)))))))
   1.102 +(in-ns 'cortex.movement)
   1.103  
   1.104  (defvar 
   1.105    ^{:arglists '([creature])}
   1.106 @@ -56,30 +69,64 @@
   1.107    (sense-nodes "muscles")
   1.108    "Return the children of the creature's \"muscles\" node.")
   1.109  
   1.110 -(defn movement-fn
   1.111 +(defn muscle-profile-image
   1.112 +  "Get the muscle-profile image from the node's blender meta-data."
   1.113 +  [#^Node muscle]
   1.114 +  (if-let [image (meta-data muscle "muscle")]
   1.115 +    (load-image image)))
   1.116 +
   1.117 +(defn muscle-strength
   1.118 +  "Return the strength of this muscle, or 1 if it is not defined."
   1.119 +  [#^Node muscle]
   1.120 +  (if-let [strength (meta-data muscle "strength")]
   1.121 +    strength 1))
   1.122 +
   1.123 +(defn motor-pool
   1.124 +  "Return a vector where each entry is the strength of the \"motor
   1.125 +   neuron\" at that part in the muscle."
   1.126 +  [#^Node muscle]
   1.127 +  (let [profile (muscle-profile-image muscle)]
   1.128 +    (vec
   1.129 +     (let [width (.getWidth profile)]
   1.130 +       (for [x (range width)]
   1.131 +       (- 255
   1.132 +          (bit-and
   1.133 +           0x0000FF
   1.134 +           (.getRGB profile x 0))))))))
   1.135 +#+end_src
   1.136 +
   1.137 +Of note here is =(motor-pool)= which interprets the muscle-profile
   1.138 +image in a way that allows me to use gradients between white and red,
   1.139 +instead of shades of gray as I've been using for all the other
   1.140 +senses. This is purely an aesthetic touch.
   1.141 +
   1.142 +* Creating Muscles
   1.143 +#+name: muscle-kernel
   1.144 +#+begin_src clojure
   1.145 +(in-ns 'cortex.movement)
   1.146 +
   1.147 +(defn movement-kernel
   1.148    "Returns a function which when called with a integer value inside a
   1.149     running simulation will cause movement in the creature according
   1.150     to the muscle's position and strength profile. Each function
   1.151     returns the amount of force applied / max force."
   1.152 -  [#^Node parts #^Node muscle]
   1.153 -  (let [target (closest-node parts muscle)
   1.154 +  [#^Node creature #^Node muscle]
   1.155 +  (let [target (closest-node creature muscle)
   1.156          axis
   1.157          (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y)
   1.158 -        strength (meta-data muscle "strength")
   1.159 -        image-name (read-string (meta-data muscle "muscle"))
   1.160 -        image (load-image image-name)
   1.161 -        fibers (muscle-profile image)
   1.162 -        fiber-integral (reductions + fibers)
   1.163 +        strength (muscle-strength muscle)
   1.164 +        
   1.165 +        pool (motor-pool muscle)
   1.166 +        pool-integral (reductions + pool)
   1.167          force-index
   1.168 -        (vec (map  #(float (* strength (/ % (last fiber-integral))))
   1.169 -              fiber-integral))
   1.170 +        (vec (map  #(float (* strength (/ % (last pool-integral))))
   1.171 +              pool-integral))
   1.172          control (.getControl target RigidBodyControl)]
   1.173      (fn [n]
   1.174 -      (let [pool-index (max 0 (min n (dec (count fibers))))
   1.175 +      (let [pool-index (max 0 (min n (dec (count pool))))
   1.176              force (force-index pool-index)]
   1.177          (.applyTorque control (.mult axis force))
   1.178          (float (/ force strength))))))
   1.179 -        
   1.180  
   1.181  (defn movement!
   1.182    "Endow the creature with the power of movement. Returns a sequence
   1.183 @@ -87,8 +134,24 @@
   1.184     activate their corresponding muscle."
   1.185    [#^Node creature]
   1.186      (for [muscle (muscles creature)]
   1.187 -      (movement-fn creature muscle)))
   1.188 +      (movement-kernel creature muscle)))
   1.189 +#+end_src
   1.190  
   1.191 +=(movement-kernel)= creates a function that will move the nearest
   1.192 +physical object to the muscle node. The muscle exerts a rotational
   1.193 +force dependant on it's orientation to the object in the blender
   1.194 +file. The function returned by =(movement-kernel)= is also a sense
   1.195 +function: it returns the percent of the total muscle strength that is
   1.196 +currently being employed. This is analogous to muscle tension in
   1.197 +humans and completes the sense of proprioception begun in the last
   1.198 +post. 
   1.199 +
   1.200 +* Visualizing Muscle Tension
   1.201 +Muscle exertion is a percent of a total, so the visulazation is just a
   1.202 +simple percent bar.
   1.203 +
   1.204 +#+name: visualization
   1.205 +#+begin_src clojure
   1.206  (defn movement-display-kernel
   1.207    "Display muscle exertion data as a bar filling up with red."
   1.208    [exertion]
   1.209 @@ -108,14 +171,166 @@
   1.210    displays each element of the list to the screen."
   1.211    []
   1.212    (view-sense movement-display-kernel))
   1.213 -
   1.214  #+end_src
   1.215  
   1.216 +* Adding Touch to the Worm
   1.217  
   1.218 +#+begin_src clojure
   1.219 +(defn test-movement
   1.220 +  ([] (test-movement false))
   1.221 +  ([record?]
   1.222 +     (let [creature (doto (worm) (body!))
   1.223  
   1.224 +           muscle-exertion (atom 0)
   1.225 +           muscles (movement! creature)
   1.226 +           muscle-display (view-movement)]
   1.227 +       (.setMass
   1.228 +        (.getControl (.getChild creature "worm-11") RigidBodyControl)
   1.229 +        (float 0))
   1.230 +       (world
   1.231 +        (nodify [creature (floor)])
   1.232 +        (merge standard-debug-controls
   1.233 +               {"key-h"
   1.234 +                (fn [_ value]
   1.235 +                  (if value
   1.236 +                    (swap!  muscle-exertion (partial + 20))))
   1.237 +                "key-n"
   1.238 +                (fn [_ value]
   1.239 +                  (if value
   1.240 +                    (swap! muscle-exertion (fn [v] (- v 20)))))})
   1.241 +        (fn [world]
   1.242 +          (if record?
   1.243 +            (Capture/captureVideo
   1.244 +             world
   1.245 +             (File. "/home/r/proj/cortex/render/worm-muscles/main-view")))
   1.246 +          (light-up-everything world)
   1.247 +          (enable-debug world)
   1.248 +          (.setTimer world (RatchetTimer. 60))
   1.249 +          (set-gravity world (Vector3f. 0 0 0))
   1.250 +          (.setLocation (.getCamera world)
   1.251 +                        (Vector3f. -4.912815, 2.004171, 0.15710819))
   1.252 +          (.setRotation (.getCamera world)
   1.253 +                        (Quaternion. 0.13828252, 0.65516764, 
   1.254 +                                     -0.12370994, 0.7323449))
   1.255  
   1.256 +          (comment 
   1.257 +            (com.aurellem.capture.Capture/captureVideo
   1.258 +             world (file-str "/home/r/proj/ai-videos/hand"))))
   1.259 +        (fn [world tpf]
   1.260 +          (muscle-display
   1.261 +           (map #(% @muscle-exertion) muscles)
   1.262 +           (if record?
   1.263 +             (File. "/home/r/proj/cortex/render/worm-muscles/muscles"))))))))
   1.264 +#+end_src
   1.265 +
   1.266 +* Video Demonstration 
   1.267 +
   1.268 +#+begin_html
   1.269 +<div class="figure">
   1.270 +<center>
   1.271 +<video controls="controls" width="550">
   1.272 +  <source src="../video/worm-muscles.ogg" type="video/ogg"
   1.273 +	  preload="none" poster="../images/aurellem-1280x480.png" />
   1.274 +</video>
   1.275 +</center>
   1.276 +<p>The worm is now able to move. The bar in the lower right displays
   1.277 +  the power output of the muscle . Each jump causes 20 more motor neurons to
   1.278 +  be recruited.  Notice that the power output increases non-linearly
   1.279 +  with motror neuron recruitement, similiar to a human muscle.</p>
   1.280 +</div>
   1.281 +#+end_html
   1.282 +
   1.283 +
   1.284 +** Making the Worm Muscles Video
   1.285 +#+name: magick7
   1.286 +#+begin_src clojure
   1.287 +(ns cortex.video.magick7
   1.288 +  (:import java.io.File)
   1.289 +  (:use clojure.contrib.shell-out))
   1.290 +
   1.291 +(defn images [path]
   1.292 +  (sort (rest (file-seq (File. path)))))
   1.293 +
   1.294 +(def base "/home/r/proj/cortex/render/worm-muscles/")
   1.295 +
   1.296 +(defn pics [file]
   1.297 +  (images (str base file)))
   1.298 +
   1.299 +(defn combine-images []
   1.300 +  (let [main-view (pics "main-view")
   1.301 +        muscles (pics "muscles/0")
   1.302 +        targets (map
   1.303 +                 #(File. (str base "out/" (format "%07d.png" %)))
   1.304 +                 (range 0 (count main-view)))]
   1.305 +    (dorun
   1.306 +     (pmap
   1.307 +      (comp
   1.308 +       (fn [[ main-view muscles target]]
   1.309 +         (println target)
   1.310 +         (sh "convert"
   1.311 +             main-view 
   1.312 +             muscles "-geometry" "+320+440" "-composite"
   1.313 +             target))
   1.314 +       (fn [& args] (map #(.getCanonicalPath %) args)))
   1.315 +       main-view muscles targets))))
   1.316 +#+end_src
   1.317 +
   1.318 +#+begin_src sh :results silent
   1.319 +cd ~/proj/cortex/render/worm-muscles
   1.320 +ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora worm-muscles.ogg
   1.321 +#+end_src
   1.322 +
   1.323 +* Headers
   1.324 +#+name: muscle-header
   1.325 +#+begin_src clojure
   1.326 +(ns cortex.movement
   1.327 +  "Give simulated creatures defined in special blender files the power
   1.328 +   to move around in a simulated environment."
   1.329 +  {:author "Robert McIntyre"}
   1.330 +  (:use (cortex world util sense body))
   1.331 +  (:use clojure.contrib.def)
   1.332 +  (:import java.awt.image.BufferedImage)
   1.333 +  (:import com.jme3.scene.Node)
   1.334 +  (:import com.jme3.math.Vector3f)
   1.335 +  (:import com.jme3.bullet.control.RigidBodyControl))
   1.336 +#+end_src
   1.337 +
   1.338 +#+name: test-header
   1.339 +#+begin_src clojure
   1.340 +(ns cortex.test.movement
   1.341 +  (:use (cortex world util sense body movement))
   1.342 +  (:use cortex.test.body)
   1.343 +  (:use clojure.contrib.def)
   1.344 +  (:import java.io.File)
   1.345 +  (:import java.awt.image.BufferedImage)
   1.346 +  (:import com.jme3.scene.Node)
   1.347 +  (:import com.jme3.math.Vector3f)
   1.348 +  (:import (com.aurellem.capture Capture RatchetTimer))
   1.349 +  (:import com.jme3.bullet.control.RigidBodyControl))
   1.350 +
   1.351 +(cortex.import/mega-import-jme3)
   1.352 +#+end_src
   1.353 +
   1.354 +* Source Listing
   1.355 +  - [[../src/cortex/movement.clj][cortex.movement]]
   1.356 +  - [[../src/cortex/test/movement.clj][cortex.test.movement]]
   1.357 +  - [[../src/cortex/video/magick7.clj][cortex.video.magick7]]
   1.358 +#+html: <ul> <li> <a href="../org/movement.org">This org file</a> </li> </ul>
   1.359 +  - [[http://hg.bortreb.com ][source-repository]]
   1.360  
   1.361  * COMMENT code generation
   1.362  #+begin_src clojure :tangle ../src/cortex/movement.clj
   1.363 -<<movement>>
   1.364 +<<muscle-header>>
   1.365 +<<muscle-meta-data>>
   1.366 +<<muscle-kernel>>
   1.367 +<<visualization>>
   1.368  #+end_src
   1.369 +
   1.370 +#+begin_src clojure :tangle ../src/cortex/test/movement.clj
   1.371 +<<test-header>>
   1.372 +<<test-movement>>
   1.373 +#+end_src
   1.374 +
   1.375 +#+begin_src clojure :tangle ../src/cortex/video/magick7.clj
   1.376 +<<magick7>>
   1.377 +#+end_src