view org/movement.org @ 283:23aadf376e9d

upgraded to latest jMonkeyEngine, streamlined tests
author Robert McIntyre <rlm@mit.edu>
date Wed, 15 Feb 2012 16:56:01 -0700
parents 4c07724c4f0a
children e27a91b1f997
line wrap: on
line source
1 #+title: Simulated Muscles
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: muscles for a simulated creature
5 #+keywords: simulation, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
10 * Muscles
12 Surprisingly enough, terristerial creatures only move by using torque
13 applied about their joints. There's not a single straight line of
14 force in the human body at all! (A straight line of force would
15 correspond to some sort of jet or rocket propulsion.)
17 *(next paragraph is from memory and needs to be checked!)*
19 In humans, muscles are composed of muscle fibers which can contract to
20 exert force. The muscle fibers which compose a muscle are partitioned
21 into discrete groups which are each controlled by a single alpha motor
22 neuton. A single alpha motor neuron might control as little as three
23 or as many as one thousand muscle fibers. When the alpha motor neuron
24 is engaged by the spinal cord, it activates all of the muscle fibers
25 to which it is attached. The spinal cord generally engages the alpha
26 motor neurons which control few muscle fibers before the motor neurons
27 which control many muscle fibers. This recruitment stragety allows
28 for percise movements at low strength. The collection of all motor
29 neurons that control a muscle is called the motor pool. The brain
30 essentially says "activate 30% of the motor pool" and the spinal cord
31 recruits motor neurons untill 30% are activated. Since the
32 distribution of power among motor neurons is unequal and recruitment
33 goes from weakest to strongest, the first 30% of the motor pool might
34 be 5% of the strength of the muscle.
36 My simulated muscles follow a similiar design: Each muscle is defined
37 by a 1-D array of numbers (the "motor pool"). Each entry in the array
38 represents a motor neuron which controlls a number of muscle fibers
39 equal to the value of the entry. Each muscle has a scalar strength
40 factor which determines the total force the muscle can exert when all
41 motor neurons are activated. The effector function for a muscle takes
42 a number to index into the motor pool, and then "activates" all the
43 motor neurons whose index is lower or equal to the number. Each
44 motor-neuron will apply force in proportion to its value in the array.
45 Lower values cause less force. The lower values can be put at the
46 "beginning" of the 1-D array to simulate the layout of actual human
47 muscles, which are capable of more percise movements when exerting
48 less force. Or, the motor pool can simulate more exoitic recruitment
49 strageties which do not correspond to human muscles.
51 This 1D array is defined in an image file for ease of
52 creation/visualization. Here is an example muscle profile image.
54 #+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.
55 [[../images/basic-muscle.png]]
57 * Blender Meta-data
59 In blender, each muscle is an empty node whose top level parent is
60 named "muscles", just like eyes, ears, and joints.
62 These functions define the expected meta-data for a muscle node.
64 #+name: muscle-meta-data
65 #+begin_src clojure
66 (in-ns 'cortex.movement)
68 (defvar
69 ^{:arglists '([creature])}
70 muscles
71 (sense-nodes "muscles")
72 "Return the children of the creature's \"muscles\" node.")
74 (defn muscle-profile-image
75 "Get the muscle-profile image from the node's blender meta-data."
76 [#^Node muscle]
77 (if-let [image (meta-data muscle "muscle")]
78 (load-image image)))
80 (defn muscle-strength
81 "Return the strength of this muscle, or 1 if it is not defined."
82 [#^Node muscle]
83 (if-let [strength (meta-data muscle "strength")]
84 strength 1))
86 (defn motor-pool
87 "Return a vector where each entry is the strength of the \"motor
88 neuron\" at that part in the muscle."
89 [#^Node muscle]
90 (let [profile (muscle-profile-image muscle)]
91 (vec
92 (let [width (.getWidth profile)]
93 (for [x (range width)]
94 (- 255
95 (bit-and
96 0x0000FF
97 (.getRGB profile x 0))))))))
98 #+end_src
100 Of note here is =motor-pool= which interprets the muscle-profile
101 image in a way that allows me to use gradients between white and red,
102 instead of shades of gray as I've been using for all the other
103 senses. This is purely an aesthetic touch.
105 * Creating Muscles
106 #+name: muscle-kernel
107 #+begin_src clojure
108 (in-ns 'cortex.movement)
110 (defn movement-kernel
111 "Returns a function which when called with a integer value inside a
112 running simulation will cause movement in the creature according
113 to the muscle's position and strength profile. Each function
114 returns the amount of force applied / max force."
115 [#^Node creature #^Node muscle]
116 (let [target (closest-node creature muscle)
117 axis
118 (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y)
119 strength (muscle-strength muscle)
121 pool (motor-pool muscle)
122 pool-integral (reductions + pool)
123 force-index
124 (vec (map #(float (* strength (/ % (last pool-integral))))
125 pool-integral))
126 control (.getControl target RigidBodyControl)]
127 (fn [n]
128 (let [pool-index (max 0 (min n (dec (count pool))))
129 force (force-index 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 sequence
135 of functions, each of which accept an integer value and will
136 activate their corresponding muscle."
137 [#^Node creature]
138 (for [muscle (muscles creature)]
139 (movement-kernel creature muscle)))
140 #+end_src
142 =movement-kernel= creates a function that will move the nearest
143 physical object to the muscle node. The muscle exerts a rotational
144 force dependant on it's orientation to the object in the blender
145 file. The function returned by =movement-kernel= is also a sense
146 function: it returns the percent of the total muscle strength that is
147 currently being employed. This is analogous to muscle tension in
148 humans and completes the sense of proprioception begun in the last
149 post.
151 * Visualizing Muscle Tension
152 Muscle exertion is a percent of a total, so the visulazation is just a
153 simple percent bar.
155 #+name: visualization
156 #+begin_src clojure
157 (defn movement-display-kernel
158 "Display muscle exertion data as a bar filling up with red."
159 [exertion]
160 (let [height 20
161 width 300
162 image (BufferedImage. width height
163 BufferedImage/TYPE_INT_RGB)
164 fill (min (int (* width exertion)) width)]
165 (dorun
166 (for [x (range fill)
167 y (range height)]
168 (.setRGB image x y 0xFF0000)))
169 image))
171 (defn view-movement
172 "Creates a function which accepts a list of muscle-exertion data and
173 displays each element of the list to the screen."
174 []
175 (view-sense movement-display-kernel))
176 #+end_src
178 * Adding Touch to the Worm
180 To the worm, I add two new nodes which describe a single muscle.
182 #+attr_html: width=755
183 #+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-movement
187 #+begin_src clojure
188 (defn test-worm-movement
189 ([] (test-worm-movement false))
190 ([record?]
191 (let [creature (doto (worm) (body!))
193 muscle-exertion (atom 0)
194 muscles (movement! creature)
195 muscle-display (view-movement)]
196 (.setMass
197 (.getControl (.getChild creature "worm-11") RigidBodyControl)
198 (float 0))
199 (world
200 (nodify [creature (floor)])
201 (merge standard-debug-controls
202 {"key-h"
203 (fn [_ value]
204 (if value
205 (swap! muscle-exertion (partial + 20))))
206 "key-n"
207 (fn [_ value]
208 (if value
209 (swap! muscle-exertion (fn [v] (- v 20)))))})
210 (fn [world]
211 (if record?
212 (Capture/captureVideo
213 world
214 (File. "/home/r/proj/cortex/render/worm-muscles/main-view")))
215 (light-up-everything world)
216 (enable-debug world)
217 (.setTimer world (RatchetTimer. 60))
218 (set-gravity world (Vector3f. 0 0 0))
219 (.setLocation (.getCamera world)
220 (Vector3f. -4.912815, 2.004171, 0.15710819))
221 (.setRotation (.getCamera world)
222 (Quaternion. 0.13828252, 0.65516764,
223 -0.12370994, 0.7323449)))
224 (fn [world tpf]
225 (muscle-display
226 (map #(% @muscle-exertion) muscles)
227 (if record?
228 (File. "/home/r/proj/cortex/render/worm-muscles/muscles"))))))))
229 #+end_src
231 * Video Demonstration
233 #+begin_html
234 <div class="figure">
235 <center>
236 <video controls="controls" width="550">
237 <source src="../video/worm-muscles.ogg" type="video/ogg"
238 preload="none" poster="../images/aurellem-1280x480.png" />
239 </video>
240 </center>
241 <p>The worm is now able to move. The bar in the lower right displays
242 the power output of the muscle . Each jump causes 20 more motor neurons to
243 be recruited. Notice that the power output increases non-linearly
244 with motror neuron recruitement, similiar to a human muscle.</p>
245 </div>
246 #+end_html
248 ** Making the Worm Muscles Video
249 #+name: magick7
250 #+begin_src clojure
251 (ns cortex.video.magick7
252 (:import java.io.File)
253 (:use clojure.contrib.shell-out))
255 (defn images [path]
256 (sort (rest (file-seq (File. path)))))
258 (def base "/home/r/proj/cortex/render/worm-muscles/")
260 (defn pics [file]
261 (images (str base file)))
263 (defn combine-images []
264 (let [main-view (pics "main-view")
265 muscles (pics "muscles/0")
266 targets (map
267 #(File. (str base "out/" (format "%07d.png" %)))
268 (range 0 (count main-view)))]
269 (dorun
270 (pmap
271 (comp
272 (fn [[ main-view muscles target]]
273 (println target)
274 (sh "convert"
275 main-view
276 muscles "-geometry" "+320+440" "-composite"
277 target))
278 (fn [& args] (map #(.getCanonicalPath %) args)))
279 main-view muscles targets))))
280 #+end_src
282 #+begin_src sh :results silent
283 cd ~/proj/cortex/render/worm-muscles
284 ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora worm-muscles.ogg
285 #+end_src
287 * Headers
288 #+name: muscle-header
289 #+begin_src clojure
290 (ns cortex.movement
291 "Give simulated creatures defined in special blender files the power
292 to move around in a simulated environment."
293 {:author "Robert McIntyre"}
294 (:use (cortex world util sense body))
295 (:use clojure.contrib.def)
296 (:import java.awt.image.BufferedImage)
297 (:import com.jme3.scene.Node)
298 (:import com.jme3.math.Vector3f)
299 (:import com.jme3.bullet.control.RigidBodyControl))
300 #+end_src
302 #+name: test-header
303 #+begin_src clojure
304 (ns cortex.test.movement
305 (:use (cortex world util sense body movement))
306 (:use cortex.test.body)
307 (:use clojure.contrib.def)
308 (:import java.io.File)
309 (:import java.awt.image.BufferedImage)
310 (:import com.jme3.scene.Node)
311 (:import (com.jme3.math Quaternion Vector3f))
312 (:import (com.aurellem.capture Capture RatchetTimer))
313 (:import com.jme3.bullet.control.RigidBodyControl))
314 #+end_src
316 * Source Listing
317 - [[../src/cortex/movement.clj][cortex.movement]]
318 - [[../src/cortex/test/movement.clj][cortex.test.movement]]
319 - [[../src/cortex/video/magick7.clj][cortex.video.magick7]]
320 #+html: <ul> <li> <a href="../org/movement.org">This org file</a> </li> </ul>
321 - [[http://hg.bortreb.com ][source-repository]]
323 * COMMENT code generation
324 #+begin_src clojure :tangle ../src/cortex/movement.clj
325 <<muscle-header>>
326 <<muscle-meta-data>>
327 <<muscle-kernel>>
328 <<visualization>>
329 #+end_src
331 #+begin_src clojure :tangle ../src/cortex/test/movement.clj
332 <<test-header>>
333 <<test-movement>>
334 #+end_src
336 #+begin_src clojure :tangle ../src/cortex/video/magick7.clj
337 <<magick7>>
338 #+end_src