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