Mercurial > cortex
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 |