Mercurial > cortex
view org/movement.org @ 413:54ef2e06c3ef
gave main worm a consistent muscle order.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Wed, 19 Mar 2014 14:24:13 -0400 |
parents | ff0d8955711e |
children | 5205535237fb |
line wrap: on
line source
1 #+title: Simulated Muscles2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: muscles for a simulated creature5 #+keywords: simulation, jMonkeyEngine3, clojure6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org10 * Muscles12 Surprisingly enough, terrestrial creatures only move by using torque13 applied about their joints. There's not a single straight line of14 force in the human body at all! (A straight line of force would15 correspond to some sort of jet or rocket propulsion.)17 In humans, muscles are composed of muscle fibers which can contract to18 exert force. The muscle fibers which compose a muscle are partitioned19 into discrete groups which are each controlled by a single alpha motor20 neuron. A single alpha motor neuron might control as little as three21 or as many as one thousand muscle fibers. When the alpha motor neuron22 is engaged by the spinal cord, it activates all of the muscle fibers23 to which it is attached. The spinal cord generally engages the alpha24 motor neurons which control few muscle fibers before the motor neurons25 which control many muscle fibers. This recruitment strategy allows26 for precise movements at low strength. The collection of all motor27 neurons that control a muscle is called the motor pool. The brain28 essentially says "activate 30% of the motor pool" and the spinal cord29 recruits motor neurons until 30% are activated. Since the30 distribution of power among motor neurons is unequal and recruitment31 goes from weakest to strongest, the first 30% of the motor pool might32 be 5% of the strength of the muscle.34 My simulated muscles follow a similar design: Each muscle is defined35 by a 1-D array of numbers (the "motor pool"). Each entry in the array36 represents a motor neuron which controls a number of muscle fibers37 equal to the value of the entry. Each muscle has a scalar strength38 factor which determines the total force the muscle can exert when all39 motor neurons are activated. The effector function for a muscle takes40 a number to index into the motor pool, and then "activates" all the41 motor neurons whose index is lower or equal to the number. Each42 motor-neuron will apply force in proportion to its value in the array.43 Lower values cause less force. The lower values can be put at the44 "beginning" of the 1-D array to simulate the layout of actual human45 muscles, which are capable of more precise movements when exerting46 less force. Or, the motor pool can simulate more exotic recruitment47 strategies which do not correspond to human muscles.49 This 1D array is defined in an image file for ease of50 creation/visualization. Here is an example muscle profile image.52 #+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.53 [[../images/basic-muscle.png]]55 * Blender Meta-data57 In blender, each muscle is an empty node whose top level parent is58 named "muscles", just like eyes, ears, and joints.60 These functions define the expected meta-data for a muscle node.62 #+name: muscle-meta-data63 #+begin_src clojure64 (in-ns 'cortex.movement)66 (def67 ^{:doc "Return the children of the creature's \"muscles\" node."68 :arglists '([creature])}69 muscles70 (sense-nodes "muscles"))73 (defn muscle-profile-image74 "Get the muscle-profile image from the node's blender meta-data."75 [#^Node muscle]76 (if-let [image (meta-data muscle "muscle")]77 (load-image image)))79 (defn muscle-strength80 "Return the strength of this muscle, or 1 if it is not defined."81 [#^Node muscle]82 (if-let [strength (meta-data muscle "strength")]83 strength 1))85 (defn motor-pool86 "Return a vector where each entry is the strength of the \"motor87 neuron\" at that part in the muscle."88 [#^Node muscle]89 (let [profile (muscle-profile-image muscle)]90 (vec91 (let [width (.getWidth profile)]92 (for [x (range width)]93 (- 25594 (bit-and95 0x0000FF96 (.getRGB profile x 0))))))))97 #+end_src99 Of note here is =motor-pool= which interprets the muscle-profile100 image in a way that allows me to use gradients between white and red,101 instead of shades of gray as I've been using for all the other102 senses. This is purely an aesthetic touch.104 * Creating Muscles105 #+name: muscle-kernel106 #+begin_src clojure107 (in-ns 'cortex.movement)109 (defn movement-kernel110 "Returns a function which when called with a integer value inside a111 running simulation will cause movement in the creature according112 to the muscle's position and strength profile. Each function113 returns the amount of force applied / max force."114 [#^Node creature #^Node muscle]115 (let [target (closest-node creature muscle)116 axis117 (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y)118 strength (muscle-strength muscle)120 pool (motor-pool muscle)121 pool-integral (reductions + pool)122 forces123 (vec (map #(float (* strength (/ % (last pool-integral))))124 pool-integral))125 control (.getControl target RigidBodyControl)]126 ;;(println-repl (.getName target) axis)127 (fn [n]128 (let [pool-index (max 0 (min n (dec (count pool))))129 force (forces pool-index)]130 (.applyTorque control (.mult axis force))131 (float (/ force strength))))))133 (defn movement!134 "Endow the creature with the power of movement. Returns a sequence135 of functions, each of which accept an integer value and will136 activate their corresponding muscle."137 [#^Node creature]138 (for [muscle (muscles creature)]139 (movement-kernel creature muscle)))140 #+end_src142 =movement-kernel= creates a function that will move the nearest143 physical object to the muscle node. The muscle exerts a rotational144 force dependent on it's orientation to the object in the blender145 file. The function returned by =movement-kernel= is also a sense146 function: it returns the percent of the total muscle strength that is147 currently being employed. This is analogous to muscle tension in148 humans and completes the sense of proprioception begun in the last149 post.151 * Visualizing Muscle Tension152 Muscle exertion is a percent of a total, so the visualization is just a153 simple percent bar.155 #+name: visualization156 #+begin_src clojure157 (defn movement-display-kernel158 "Display muscle exertion data as a bar filling up with red."159 [exertion]160 (let [height 20161 width 300162 image (BufferedImage. width height163 BufferedImage/TYPE_INT_RGB)164 fill (min (int (* width exertion)) width)]165 (dorun166 (for [x (range fill)167 y (range height)]168 (.setRGB image x y 0xFF0000)))169 image))171 (defn view-movement172 "Creates a function which accepts a list of muscle-exertion data and173 displays each element of the list to the screen."174 []175 (view-sense movement-display-kernel))176 #+end_src178 * Adding Muscles to the Worm180 To the worm, I add two new nodes which describe a single muscle.182 #+attr_html: width=755183 #+caption: The node highlighted in orange is the parent node of all muscles in the worm. The arrow highlighted in yellow represents the creature's single muscle, which moves the top segment. The other nodes which are not highlighted are joints, eyes, and ears.184 [[../images/worm-with-muscle.png]]186 #+name: test-movement187 #+begin_src clojure188 (in-ns 'cortex.test.movement)190 (defn test-worm-movement191 "Testing movement:192 You should see the worm suspended in mid air and a display on the193 right which shows the current relative power being exerted by the194 muscle. As you increase muscle strength, the bar should fill with195 red, and the worm's upper segment should move.197 Keys:198 h : increase muscle exertion199 n : decrease muscle exertion"200 ([] (test-worm-movement false))201 ([record?]202 (let [creature (doto (worm) (body!))204 muscle-exertion (atom 0)205 muscles (movement! creature)206 muscle-display (view-movement)]207 (.setMass208 (.getControl (.getChild creature "worm-11") RigidBodyControl)209 (float 0))210 (world211 (nodify [creature (floor)])212 (merge standard-debug-controls213 {"key-h"214 (fn [_ value]215 (if value216 (swap! muscle-exertion (partial + 20))))217 "key-n"218 (fn [_ value]219 (if value220 (swap! muscle-exertion (fn [v] (- v 20)))))})221 (fn [world]223 (let [timer (RatchetTimer. 60)]224 (.setTimer world timer)225 (display-dilated-time world timer))226 (if record?227 (Capture/captureVideo228 world229 (File. "/home/r/proj/cortex/render/worm-muscles/main-view")))230 (light-up-everything world)231 (enable-debug world)232 (set-gravity world (Vector3f. 0 0 0))233 (.setLocation (.getCamera world)234 (Vector3f. -4.912815, 2.004171, 0.15710819))235 (.setRotation (.getCamera world)236 (Quaternion. 0.13828252, 0.65516764,237 -0.12370994, 0.7323449)))238 (fn [world tpf]239 (muscle-display240 (map #(% @muscle-exertion) muscles)241 (if record?242 (File. "/home/r/proj/cortex/render/worm-muscles/muscles"))))))))243 #+end_src245 #+results: test-movement246 : #'cortex.test.movement/test-worm-movement248 * Video Demonstration250 #+begin_html251 <div class="figure">252 <center>253 <video controls="controls" width="550">254 <source src="../video/worm-muscles.ogg" type="video/ogg"255 preload="none" poster="../images/aurellem-1280x480.png" />256 </video>257 <br> <a href="http://youtu.be/8Rp4jEGMDWU"> YouTube </a>258 </center>259 <p>The worm is now able to move. The bar in the lower right displays260 the power output of the muscle . Each jump causes 20 more motor neurons to261 be recruited. Notice that the power output increases non-linearly262 with motor neuron recruitment, similar to a human muscle.</p>263 </div>264 #+end_html266 ** Making the Worm Muscles Video267 #+name: magick7268 #+begin_src clojure269 (ns cortex.video.magick7270 (:import java.io.File)271 (:use clojure.java.shell))273 (defn images [path]274 (sort (rest (file-seq (File. path)))))276 (def base "/home/r/proj/cortex/render/worm-muscles/")278 (defn pics [file]279 (images (str base file)))281 (defn combine-images []282 (let [main-view (pics "main-view")283 muscles (pics "muscles/0")284 targets (map285 #(File. (str base "out/" (format "%07d.png" %)))286 (range 0 (count main-view)))]287 (dorun288 (pmap289 (comp290 (fn [[ main-view muscles target]]291 (println target)292 (sh "convert"293 main-view294 muscles "-geometry" "+320+440" "-composite"295 target))296 (fn [& args] (map #(.getCanonicalPath %) args)))297 main-view muscles targets))))298 #+end_src300 #+begin_src sh :results silent301 cd ~/proj/cortex/render/worm-muscles302 ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora worm-muscles.ogg303 #+end_src305 * Headers306 #+name: muscle-header307 #+begin_src clojure308 (ns cortex.movement309 "Give simulated creatures defined in special blender files the power310 to move around in a simulated environment."311 {:author "Robert McIntyre"}312 (:use (cortex world util sense body))313 (:import java.awt.image.BufferedImage)314 (:import com.jme3.scene.Node)315 (:import com.jme3.math.Vector3f)316 (:import com.jme3.bullet.control.RigidBodyControl))317 #+end_src319 #+name: test-header320 #+begin_src clojure321 (ns cortex.test.movement322 (:use (cortex world util sense body movement))323 (:use cortex.test.body)324 (:import java.io.File)325 (:import java.awt.image.BufferedImage)326 (:import com.jme3.scene.Node)327 (:import (com.jme3.math Quaternion Vector3f))328 (:import (com.aurellem.capture Capture RatchetTimer IsoTimer))329 (:import com.jme3.bullet.control.RigidBodyControl))330 #+end_src332 #+results: test-header333 : com.jme3.bullet.control.RigidBodyControl335 * Source Listing336 - [[../src/cortex/movement.clj][cortex.movement]]337 - [[../src/cortex/test/movement.clj][cortex.test.movement]]338 - [[../src/cortex/video/magick7.clj][cortex.video.magick7]]339 #+html: <ul> <li> <a href="../org/movement.org">This org file</a> </li> </ul>340 - [[http://hg.bortreb.com ][source-repository]]342 * COMMENT code generation343 #+begin_src clojure :tangle ../src/cortex/movement.clj344 <<muscle-header>>345 <<muscle-meta-data>>346 <<muscle-kernel>>347 <<visualization>>348 #+end_src350 #+begin_src clojure :tangle ../src/cortex/test/movement.clj351 <<test-header>>352 <<test-movement>>353 #+end_src355 #+begin_src clojure :tangle ../src/cortex/video/magick7.clj356 <<magick7>>357 #+end_src