# HG changeset patch # User Dylan Holmes # Date 1329191634 21600 # Node ID bee5145ce4630d3c9883775b4977b30077b00360 # Parent e57d8c52f12f195f9c690ad16b5212e5b0a57b49# Parent 2fdcbe8185b1a2c30fcece3697f8b629f8f3ec70 Merged Robert's proprioception with Dylan's vision. diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-creature/basic-muscle.png Binary file assets/Models/test-creature/basic-muscle.png has changed diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-creature/worm.blend Binary file assets/Models/test-creature/worm.blend has changed diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-touch/touch-cube.blend Binary file assets/Models/test-touch/touch-cube.blend has changed diff -r e57d8c52f12f -r bee5145ce463 images/basic-muscle.png Binary file images/basic-muscle.png has changed diff -r e57d8c52f12f -r bee5145ce463 images/worm-with-muscle.png Binary file images/worm-with-muscle.png has changed diff -r e57d8c52f12f -r bee5145ce463 org/hearing.org --- a/org/hearing.org Mon Feb 13 21:53:28 2012 -0600 +++ b/org/hearing.org Mon Feb 13 21:53:54 2012 -0600 @@ -950,7 +950,6 @@ #+end_src * Testing Hearing - ** Advanced Java Example I wrote a test case in Java that demonstrates the use of the Java @@ -1083,7 +1082,6 @@ sound it hears beomes fainter. This shows the 3D localization of sound in this world.

- #+end_html *** Creating the Ear Video @@ -1169,7 +1167,6 @@ #+html: - [[http://hg.bortreb.com ][source-repository]] - * Next The worm can see and hear, but it can't feel the world or itself. Next post, I'll give the worm a [[./touch.org][sense of touch]]. diff -r e57d8c52f12f -r bee5145ce463 org/ideas.org --- a/org/ideas.org Mon Feb 13 21:53:28 2012 -0600 +++ b/org/ideas.org Mon Feb 13 21:53:54 2012 -0600 @@ -100,6 +100,7 @@ - [ ] show sensor maps in HUD display? -- 4 days - [ ] show sensor maps in AWT display? -- 2 days - [ ] upgrade to clojure 1.3, replace all defvars with new def + - [ ] common video creation code. diff -r e57d8c52f12f -r bee5145ce463 org/movement.org --- a/org/movement.org Mon Feb 13 21:53:28 2012 -0600 +++ b/org/movement.org Mon Feb 13 21:53:54 2012 -0600 @@ -1,4 +1,4 @@ -#+title: Movement! +#+title: Simulated Muscles #+author: Robert McIntyre #+email: rlm@mit.edu #+description: muscles for a simulated creature @@ -7,48 +7,61 @@ #+INCLUDE: ../../aurellem/org/level-0.org +* Muscles + Surprisingly enough, terristerial creatures only move by using torque applied about their joints. There's not a single straight line of force in the human body at all! (A straight line of force would -correspond to some sort of jet or rocket propulseion.) +correspond to some sort of jet or rocket propulsion.) +*(next paragraph is from memory and needs to be checked!)* -Here's how motor-control/ proprioception will work: Each muscle is -defined by a 1-D array of numbers (the "motor pool") each of which -represent muscle fibers. A muscle also has a scalar :strength factor -which determines how strong the muscle as a whole is. The effector -function for a muscle takes a number < (count motor-pool) and that -number is said to "activate" all the muscle fibers whose index is -lower than the number. Each fiber will apply force in proportion to -its value in the array. Lower values cause less force. The lower -values can be put at the "beginning" of the 1-D array to simulate the -layout of actual human muscles, which are capable of more percise -movements when exerting less force. +In humans, muscles are composed of millions of sarcomeres, which can +contract to exert force. A single motor neuron might control 100-1,000 +sarcomeres. When the motor neuron is engaged by the brain, it +activates all of the sarcomeres to which it is attached. Some motor +neurons command many sarcomeres, and some command only a few. The +spinal cord generally engages the motor neurons which control few +sarcomeres before the motor neurons which control many sarcomeres. +This recruitment stragety allows for percise movements at low +strength. The collection of all motor neurons that control a muscle is +called the motor pool. The brain essentially says "activate 30% of the +motor pool" and the spinal cord recruits motor neurons untill 30% are +activated. Since the distribution of power among motor neurons is +unequal and recruitment goes from weakest to strongest, 30% of the +motor pool might be 5% of the strength of the muscle. -#+name: movement +My simulated muscles follow a similiar design: Each muscle is defined +by a 1-D array of numbers (the "motor pool"). Each number represents a +motor neuron which controlls a number of sarcomeres equal to the +number. A muscle also has a scalar :strength factor which determines +the total force the muscle can exert when all motor neurons are +activated. The effector function for a muscle takes a number to index +into the motor pool, and that number "activates" all the motor neurons +whose index is lower or equal to the number. Each motor-neuron will +apply force in proportion to its value in the array. Lower values +cause less force. The lower values can be put at the "beginning" of +the 1-D array to simulate the layout of actual human muscles, which +are capable of more percise movements when exerting less force. Or, +the motor pool can simulate more exoitic recruitment strageties which +do not correspond to human muscles. + +This 1D array is defined in an image file for ease of +creation/visualization. Here is an example muscle profile image. + +#+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. +[[../images/basic-muscle.png]] + +* Blender Meta-data + +In blender, each muscle is an empty node whose top level parent is +named "muscles", just like eyes, ears, and joints. + +These functions define the expected meta-data for a muscle node. + +#+name: muscle-meta-data #+begin_src clojure -(ns cortex.movement - "Give simulated creatures defined in special blender files the power - to move around in a simulated environment." - {:author "Robert McIntyre"} - (:use (cortex world util sense body)) - (:use clojure.contrib.def) - (:import java.awt.image.BufferedImage) - (:import com.jme3.scene.Node) - (:import com.jme3.math.Vector3f) - (:import com.jme3.bullet.control.RigidBodyControl)) - -(defn muscle-profile - "Return a vector where each entry is the strength of the \"motor - pool\" at that part in the muscle." - [#^BufferedImage profile] - (vec - (let [width (.getWidth profile)] - (for [x (range width)] - (- 255 - (bit-and - 0x0000FF - (.getRGB profile x 0))))))) +(in-ns 'cortex.movement) (defvar ^{:arglists '([creature])} @@ -56,30 +69,64 @@ (sense-nodes "muscles") "Return the children of the creature's \"muscles\" node.") -(defn movement-fn +(defn muscle-profile-image + "Get the muscle-profile image from the node's blender meta-data." + [#^Node muscle] + (if-let [image (meta-data muscle "muscle")] + (load-image image))) + +(defn muscle-strength + "Return the strength of this muscle, or 1 if it is not defined." + [#^Node muscle] + (if-let [strength (meta-data muscle "strength")] + strength 1)) + +(defn motor-pool + "Return a vector where each entry is the strength of the \"motor + neuron\" at that part in the muscle." + [#^Node muscle] + (let [profile (muscle-profile-image muscle)] + (vec + (let [width (.getWidth profile)] + (for [x (range width)] + (- 255 + (bit-and + 0x0000FF + (.getRGB profile x 0)))))))) +#+end_src + +Of note here is =(motor-pool)= which interprets the muscle-profile +image in a way that allows me to use gradients between white and red, +instead of shades of gray as I've been using for all the other +senses. This is purely an aesthetic touch. + +* Creating Muscles +#+name: muscle-kernel +#+begin_src clojure +(in-ns 'cortex.movement) + +(defn movement-kernel "Returns a function which when called with a integer value inside a running simulation will cause movement in the creature according to the muscle's position and strength profile. Each function returns the amount of force applied / max force." - [#^Node parts #^Node muscle] - (let [target (closest-node parts muscle) + [#^Node creature #^Node muscle] + (let [target (closest-node creature muscle) axis (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y) - strength (meta-data muscle "strength") - image-name (read-string (meta-data muscle "muscle")) - image (load-image image-name) - fibers (muscle-profile image) - fiber-integral (reductions + fibers) + strength (muscle-strength muscle) + + pool (motor-pool muscle) + pool-integral (reductions + pool) force-index - (vec (map #(float (* strength (/ % (last fiber-integral)))) - fiber-integral)) + (vec (map #(float (* strength (/ % (last pool-integral)))) + pool-integral)) control (.getControl target RigidBodyControl)] (fn [n] - (let [pool-index (max 0 (min n (dec (count fibers)))) + (let [pool-index (max 0 (min n (dec (count pool)))) force (force-index pool-index)] (.applyTorque control (.mult axis force)) (float (/ force strength)))))) - (defn movement! "Endow the creature with the power of movement. Returns a sequence @@ -87,8 +134,24 @@ activate their corresponding muscle." [#^Node creature] (for [muscle (muscles creature)] - (movement-fn creature muscle))) + (movement-kernel creature muscle))) +#+end_src +=(movement-kernel)= creates a function that will move the nearest +physical object to the muscle node. The muscle exerts a rotational +force dependant on it's orientation to the object in the blender +file. The function returned by =(movement-kernel)= is also a sense +function: it returns the percent of the total muscle strength that is +currently being employed. This is analogous to muscle tension in +humans and completes the sense of proprioception begun in the last +post. + +* Visualizing Muscle Tension +Muscle exertion is a percent of a total, so the visulazation is just a +simple percent bar. + +#+name: visualization +#+begin_src clojure (defn movement-display-kernel "Display muscle exertion data as a bar filling up with red." [exertion] @@ -108,14 +171,166 @@ displays each element of the list to the screen." [] (view-sense movement-display-kernel)) - #+end_src +* Adding Touch to the Worm +#+begin_src clojure +(defn test-movement + ([] (test-movement false)) + ([record?] + (let [creature (doto (worm) (body!)) + muscle-exertion (atom 0) + muscles (movement! creature) + muscle-display (view-movement)] + (.setMass + (.getControl (.getChild creature "worm-11") RigidBodyControl) + (float 0)) + (world + (nodify [creature (floor)]) + (merge standard-debug-controls + {"key-h" + (fn [_ value] + (if value + (swap! muscle-exertion (partial + 20)))) + "key-n" + (fn [_ value] + (if value + (swap! muscle-exertion (fn [v] (- v 20)))))}) + (fn [world] + (if record? + (Capture/captureVideo + world + (File. "/home/r/proj/cortex/render/worm-muscles/main-view"))) + (light-up-everything world) + (enable-debug world) + (.setTimer world (RatchetTimer. 60)) + (set-gravity world (Vector3f. 0 0 0)) + (.setLocation (.getCamera world) + (Vector3f. -4.912815, 2.004171, 0.15710819)) + (.setRotation (.getCamera world) + (Quaternion. 0.13828252, 0.65516764, + -0.12370994, 0.7323449)) + (comment + (com.aurellem.capture.Capture/captureVideo + world (file-str "/home/r/proj/ai-videos/hand")))) + (fn [world tpf] + (muscle-display + (map #(% @muscle-exertion) muscles) + (if record? + (File. "/home/r/proj/cortex/render/worm-muscles/muscles")))))))) +#+end_src + +* Video Demonstration + +#+begin_html +
+
+ +
+

The worm is now able to move. The bar in the lower right displays + the power output of the muscle . Each jump causes 20 more motor neurons to + be recruited. Notice that the power output increases non-linearly + with motror neuron recruitement, similiar to a human muscle.

+
+#+end_html + + +** Making the Worm Muscles Video +#+name: magick7 +#+begin_src clojure +(ns cortex.video.magick7 + (:import java.io.File) + (:use clojure.contrib.shell-out)) + +(defn images [path] + (sort (rest (file-seq (File. path))))) + +(def base "/home/r/proj/cortex/render/worm-muscles/") + +(defn pics [file] + (images (str base file))) + +(defn combine-images [] + (let [main-view (pics "main-view") + muscles (pics "muscles/0") + targets (map + #(File. (str base "out/" (format "%07d.png" %))) + (range 0 (count main-view)))] + (dorun + (pmap + (comp + (fn [[ main-view muscles target]] + (println target) + (sh "convert" + main-view + muscles "-geometry" "+320+440" "-composite" + target)) + (fn [& args] (map #(.getCanonicalPath %) args))) + main-view muscles targets)))) +#+end_src + +#+begin_src sh :results silent +cd ~/proj/cortex/render/worm-muscles +ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora worm-muscles.ogg +#+end_src + +* Headers +#+name: muscle-header +#+begin_src clojure +(ns cortex.movement + "Give simulated creatures defined in special blender files the power + to move around in a simulated environment." + {:author "Robert McIntyre"} + (:use (cortex world util sense body)) + (:use clojure.contrib.def) + (:import java.awt.image.BufferedImage) + (:import com.jme3.scene.Node) + (:import com.jme3.math.Vector3f) + (:import com.jme3.bullet.control.RigidBodyControl)) +#+end_src + +#+name: test-header +#+begin_src clojure +(ns cortex.test.movement + (:use (cortex world util sense body movement)) + (:use cortex.test.body) + (:use clojure.contrib.def) + (:import java.io.File) + (:import java.awt.image.BufferedImage) + (:import com.jme3.scene.Node) + (:import com.jme3.math.Vector3f) + (:import (com.aurellem.capture Capture RatchetTimer)) + (:import com.jme3.bullet.control.RigidBodyControl)) + +(cortex.import/mega-import-jme3) +#+end_src + +* Source Listing + - [[../src/cortex/movement.clj][cortex.movement]] + - [[../src/cortex/test/movement.clj][cortex.test.movement]] + - [[../src/cortex/video/magick7.clj][cortex.video.magick7]] +#+html: + - [[http://hg.bortreb.com ][source-repository]] * COMMENT code generation #+begin_src clojure :tangle ../src/cortex/movement.clj -<> +<> +<> +<> +<> #+end_src + +#+begin_src clojure :tangle ../src/cortex/test/movement.clj +<> +<> +#+end_src + +#+begin_src clojure :tangle ../src/cortex/video/magick7.clj +<> +#+end_src diff -r e57d8c52f12f -r bee5145ce463 org/proprioception.org --- a/org/proprioception.org Mon Feb 13 21:53:28 2012 -0600 +++ b/org/proprioception.org Mon Feb 13 21:53:54 2012 -0600 @@ -6,36 +6,89 @@ #+SETUPFILE: ../../aurellem/org/setup.org #+INCLUDE: ../../aurellem/org/level-0.org -#+name: proprioception +* Proprioception + +Close your eyes, and touch your nose with your right index finger. How +did you do it? You could not see your hand, and neither your hand nor +your nose could use the sense of touch to guide the path of your hand. +There are no sound cues, and Taste and Smell certainly don't provide +any help. You know where your hand is without your other senses +because of Proprioception. + +Humans can sometimes loose this sense through viral infections or +damage to the spinal cord or brain, and when they do, they loose the +ability to control their own bodies without looking directly at the +parts they want to move. In [[http://en.wikipedia.org/wiki/The_Man_Who_Mistook_His_Wife_for_a_Hat][The Man Who Mistook His Wife for a Hat]], +a woman named Christina looses this sense and has to learn how to move +by carefully watching her arms and legs. She describes proprioception +as the "eyes of the body, the way the body sees itself". + +Proprioception in humans is mediated by [[http://en.wikipedia.org/wiki/Articular_capsule][joint capsules]], [[http://en.wikipedia.org/wiki/Muscle_spindle][muscle +spindles]], and the [[http://en.wikipedia.org/wiki/Golgi_tendon_organ][Golgi tendon organs]]. These measure the relative +positions of each pody part by monitoring muscle strain and length. + +It's clear that this is a vital sense for fulid, graceful +movement. It's also particurally easy to implement in jMonkeyEngine. + +My simulated proprioception calculates the relative angles of each +joint from the rest position defined in the blender file. This +simulates the muscle-spindles and joint capsules. I will deal with +Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]]. + +* Helper Functions + +=(absolute-angle)= calculates the angle between two vectors, relative to a +third axis vector. This angle is the number of radians you have to +move counterclockwise around the axis vector to get from the first to +the second vector. It is not commutative like a normal dot-product +angle is. + +#+name: helpers #+begin_src clojure -(ns cortex.proprioception - "Simulate the sense of proprioception (ability to detect the - relative positions of body parts with repsect to other body parts) - in jMonkeyEngine3. Reads specially prepared blender files to - automatically generate proprioceptive senses." - (:use (cortex world util sense body)) - (:use clojure.contrib.def) - (:import com.jme3.scene.Node) - (:import java.awt.image.BufferedImage) - (:import (com.jme3.math Vector3f Quaternion))) +(in-ns 'cortex.proprioception) (defn right-handed? "true iff the three vectors form a right handed coordinate - system. The three vectors do not have to be normalized or - orthogonal." + system. The three vectors do not have to be normalized or + orthogonal." [vec1 vec2 vec3] (< 0 (.dot (.cross vec1 vec2) vec3))) (defn absolute-angle - "The angle between 'vec1 and 'vec2. Positive if the angle to get - from 'vec1 to 'vec2 is counterclockwise around 'axis, and negative - otherwise." + "The angle between 'vec1 and 'vec2 around 'axis. In the range + [0 (* 2 Math/PI)]." [vec1 vec2 axis] (let [angle (.angleBetween vec1 vec2)] (if (right-handed? vec1 vec2 axis) angle (- (* 2 Math/PI) angle)))) +#+end_src -(defn proprioception-fn +#+begin_src clojure :exports both +(in-ns 'cortex.proprioception) +(absolute-angle Vector3f/UNIT_X Vector3f/UNIT_Y Vector3f/UNIT_Z) +#+end_src + +#+results: +: 1.5707964 + +#+begin_src clojure :exports both +(in-ns 'cortex.proprioception) +(absolute-angle + Vector3f/UNIT_X (.mult Vector3f/UNIT_Y (float -1)) Vector3f/UNIT_Z) +#+end_src + +#+results: +: 4.7123889366733 + +* Proprioception Kernel + +Given a joint, =(proprioception-kernel)= produces a function that +calculates the euler angles between the the objects the joint +connects. + +#+name: proprioception +#+begin_src clojure +(defn proprioception-kernel "Returns a function which returns proprioceptive sensory data when called inside a running simulation." [#^Node parts #^Node joint] @@ -44,12 +97,6 @@ x0 (.mult joint-rot Vector3f/UNIT_X) y0 (.mult joint-rot Vector3f/UNIT_Y) z0 (.mult joint-rot Vector3f/UNIT_Z)] - (println-repl "x:" x0) - (println-repl "y:" y0) - (println-repl "z:" z0) - (println-repl "init-a:" (.getWorldRotation obj-a)) - (println-repl "init-b:" (.getWorldRotation obj-b)) - (fn [] (let [rot-a (.clone (.getWorldRotation obj-a)) rot-b (.clone (.getWorldRotation obj-b)) @@ -80,18 +127,33 @@ that joint." [#^Node creature] ;; extract the body's joints - (let [senses (map (partial proprioception-fn creature) + (let [senses (map (partial proprioception-kernel creature) (joints creature))] (fn [] (map #(%) senses)))) +#+end_src +=(proprioception!)= maps =(proprioception-kernel)= across all the +joints of the creature. It uses the same list of joints that +=(cortex.body/joints)= uses. + +* Visualizing Proprioception + +Proprioception has the lowest bandwidth of all the senses so far, and +it doesn't lend itself as readily to visual representation like +vision, hearing, or touch. This visualization code creates a "guage" +to view each of the three relative angles along a circle. + +#+name: visualize +#+begin_src clojure +(in-ns 'cortex.proprioception) + (defn draw-sprite [image sprite x y color ] (dorun (for [[u v] sprite] (.setRGB image (+ u x) (+ v y) color)))) - (defn view-angle "create a debug view of an angle" [color] @@ -109,7 +171,6 @@ (reset! previous position)) image)))) - (defn proprioception-display-kernel "Display proprioception angles in a BufferedImage" [[h p r]] @@ -143,14 +204,16 @@ display each element of the list to the screen as an image." [] (view-sense proprioception-display-kernel)) - - - #+end_src -#+name: test-body +* Proprioception Test +This test does not use the worm, but instead uses two bars, bound +together by a point2point joint. One bar is fixed, and I control the +other bar from the keyboard. + +#+name: test-proprioception #+begin_src clojure - +(in-ns 'cortex.test.proprioception) (defn test-proprioception "Testing proprioception: @@ -159,92 +222,166 @@ change only the value of pitch. key-f/key-g moves it side to side and changes yaw. key-v/key-b will spin the blue segment clockwise and counterclockwise, and only affect roll." - [] - (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) - :mass 0 :color ColorRGBA/Green :name "hand") - finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) - :mass 1 :color ColorRGBA/Red :name "finger") - joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow - :position (Vector3f. 0 1.2 0) - :rotation (doto (Quaternion.) - (.fromAngleAxis - (/ Math/PI 2) - (Vector3f. 0 0 1))) - :physical? false) - joint (join-at-point hand finger (Vector3f. 0 1.2 0 )) - creature (nodify [hand finger joint-node]) - finger-control (.getControl finger RigidBodyControl) - hand-control (.getControl hand RigidBodyControl)] - + ([] (test-proprioception false)) + ([record?] + (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0) + :mass 0 :color ColorRGBA/Gray :name "hand") + finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0) + :mass 1 + :color + (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1) + :name "finger") + joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow + :position (Vector3f. 0 1.2 0) + :rotation (doto (Quaternion.) + (.fromAngleAxis + (/ Math/PI 2) + (Vector3f. 0 0 1))) + :physical? false) + creature (nodify [hand finger joint-node]) + finger-control (.getControl finger RigidBodyControl) + hand-control (.getControl hand RigidBodyControl) + joint (joint-dispatch {:type :point} hand-control finger-control + (Vector3f. 0 1.2 0) + (Vector3f. 0 -1.2 0) nil) - (let - ;; ******************************************* - - [floor (box 10 10 10 :position (Vector3f. 0 -15 0) - :mass 0 :color ColorRGBA/Gray) - - root (nodify [creature floor]) - prop (joint-proprioception creature joint-node) - prop-view (proprioception-debug-window) - - controls - (merge standard-debug-controls - {"key-o" - (fn [_ _] (.setEnabled finger-control true)) - "key-p" - (fn [_ _] (.setEnabled finger-control false)) - "key-k" - (fn [_ _] (.setEnabled hand-control true)) - "key-l" - (fn [_ _] (.setEnabled hand-control false)) - "key-i" - (fn [world _] (set-gravity world (Vector3f. 0 0 0))) - "key-period" - (fn [world _] - (.setEnabled finger-control false) - (.setEnabled hand-control false) - (.rotate creature (doto (Quaternion.) - (.fromAngleAxis - (float (/ Math/PI 15)) - (Vector3f. 0 0 -1)))) - - (.setEnabled finger-control true) - (.setEnabled hand-control true) - (set-gravity world (Vector3f. 0 0 0)) - ) - - - } - ) + root (nodify [creature]) + prop (proprioception-kernel creature joint-node) + prop-view (view-proprioception)] + (.setCollisionGroup + (.getControl hand RigidBodyControl) + PhysicsCollisionObject/COLLISION_GROUP_NONE) + (apply + world + (with-movement + finger + ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] + [1 1 10 10 10 10] + [root + standard-debug-controls + (fn [world] + (if record? + (Capture/captureVideo + world + (File. "/home/r/proj/cortex/render/proprio/main-view"))) + (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) + (set-gravity world (Vector3f. 0 0 0)) + (enable-debug world) + (light-up-everything world)) + (fn [_ _] + (prop-view + (list (prop)) + (if record? + (File. "/home/r/proj/cortex/render/proprio/proprio"))))]))))) +#+end_src - ] - (comment - (.setCollisionGroup - (.getControl hand RigidBodyControl) - PhysicsCollisionObject/COLLISION_GROUP_NONE) - ) - (apply - world - (with-movement - hand - ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"] - [10 10 10 10 1 1] - (with-movement - finger - ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"] - [1 1 10 10 10 10] - [root - controls - (fn [world] - (.setTimer world (com.aurellem.capture.RatchetTimer. 60)) - (set-gravity world (Vector3f. 0 0 0)) - (light-up-everything world)) - (fn [_ _] (prop-view (list (prop))))])))))) +#+results: test-proprioception +: #'cortex.test.proprioception/test-proprioception +* Video of Proprioception + +#+begin_html +
+
+ +
+

Proprioception in a simple creature. The proprioceptive readout is + in the upper left corner of the screen.

+
+#+end_html + +** Generating the Proprioception Video +#+name: magick6 +#+begin_src clojure +(ns cortex.video.magick6 + (:import java.io.File) + (:use clojure.contrib.shell-out)) + +(defn images [path] + (sort (rest (file-seq (File. path))))) + +(def base "/home/r/proj/cortex/render/proprio/") + +(defn pics [file] + (images (str base file))) + +(defn combine-images [] + (let [main-view (pics "main-view") + proprioception (pics "proprio/0") + targets (map + #(File. (str base "out/" (format "%07d.png" %))) + (range 0 (count main-view)))] + (dorun + (pmap + (comp + (fn [[ main-view proprioception target]] + (println target) + (sh "convert" + main-view + proprioception "-geometry" "+20+20" "-composite" + target)) + (fn [& args] (map #(.getCanonicalPath %) args))) + main-view proprioception targets)))) #+end_src +#+begin_src sh :results silent +cd ~/proj/cortex/render/proprio +ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora \ + test-proprioception.ogg +#+end_src + +* Headers +#+name: proprioception-header +#+begin_src clojure +(ns cortex.proprioception + "Simulate the sense of proprioception (ability to detect the + relative positions of body parts with repsect to other body parts) + in jMonkeyEngine3. Reads specially prepared blender files to + automatically generate proprioceptive senses." + (:use (cortex world util sense body)) + (:use clojure.contrib.def) + (:import com.jme3.scene.Node) + (:import java.awt.image.BufferedImage) + (:import (com.jme3.math Vector3f Quaternion))) +#+end_src + +#+name: test-proprioception-header +#+begin_src clojure +(ns cortex.test.proprioception +(:import (com.aurellem.capture Capture RatchetTimer)) +(:use (cortex util world proprioception body)) +(:import java.io.File)) +(cortex.import/mega-import-jme3) +#+end_src + +* Source Listing + - [[../src/cortex/proprioception.clj][cortex.proprioception]] + - [[../src/cortex/test/touch.clj][cortex.test.proprioception]] + - [[../src/cortex/video/magick6.clj][cortex.video.magick6]] +#+html: + - [[http://hg.bortreb.com ][source-repository]] + +* Next + +Next time, I'll give the Worm the power to [[./movement.org][move on it's own]]. + * COMMENT generate source #+begin_src clojure :tangle ../src/cortex/proprioception.clj +<> +<> <> +<> #+end_src + +#+begin_src clojure :tangle ../src/cortex/test/proprioception.clj +<> +<> +#+end_src + +#+begin_src clojure :tangle ../src/cortex/video/magick6.clj +<> +#+end_src diff -r e57d8c52f12f -r bee5145ce463 org/touch.org --- a/org/touch.org Mon Feb 13 21:53:28 2012 -0600 +++ b/org/touch.org Mon Feb 13 21:53:54 2012 -0600 @@ -122,7 +122,7 @@ #+end_src It is convienent to treat a =Triangle= as a vector of vectors, and a -=Vector2f= and =Vector3f= as vectors of floats. (->vector3f) and +=Vector2f= or =Vector3f= as vectors of floats. (->vector3f) and (->triangle) undo the operations of =(vector3f-seq)= and =(triangle-seq)=. If these classes implemented =Iterable= then =(seq)= would work on them automitacally. @@ -497,6 +497,7 @@ I'll use a new creature --- a simple cube which has touch sensors evenly distributed along each of its sides. +#+name: test-touch-0 #+begin_src clojure (in-ns 'cortex.test.touch) @@ -504,10 +505,7 @@ (load-blender-model "Models/test-touch/touch-cube.blend")) #+end_src -#+begin_html -
-#+end_html - +** The Touch Cube #+begin_html
@@ -527,12 +525,10 @@ #+caption: The distribution of feelers along the touch-cube. The colors of the faces are irrelevant; only the white pixels specify feelers. [[../images/touch-profile.png]] +#+name: test-touch-1 #+begin_src clojure (in-ns 'cortex.test.touch) -(import com.aurellem.capture.Capture) -(import java.io.File) - (defn test-basic-touch ([] (test-basic-touch false)) ([record?] @@ -576,6 +572,7 @@ #+end_html ** Generating the Basic Touch Video +#+name: magick4 #+begin_src clojure (ns cortex.video.magick4 (:import java.io.File) @@ -615,31 +612,88 @@ background main-view touch targets)))) #+end_src +#+begin_src sh :results silent +cd /home/r/proj/cortex/render/touch-cube/ +ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora basic-touch.ogg +#+end_src * Adding Touch to the Worm -#+name: test-touch +#+name: test-touch-2 #+begin_src clojure -(ns cortex.test.touch - (:use (cortex world util sense body touch)) - (:use cortex.test.body)) +(in-ns 'cortex.test.touch) -(cortex.import/mega-import-jme3) +(defn test-touch + ([] (test-touch false)) + ([record?] + (let [the-worm (doto (worm) (body!)) + touch (touch! the-worm) + touch-display (view-touch)] + (world + (nodify [the-worm (floor)]) + standard-debug-controls + + (fn [world] + (if record? + (Capture/captureVideo + world + (File. "/home/r/proj/cortex/render/worm-touch/main-view/"))) + (speed-up world) + (light-up-everything world)) -(defn test-touch [] - (let [the-worm (doto (worm) (body!)) - touch (touch! the-worm) - touch-display (view-touch)] - (world (nodify [the-worm (floor)]) - standard-debug-controls - - (fn [world] - (speed-up world) - (light-up-everything world)) + (fn [world tpf] + (touch-display + (map #(% (.getRootNode world)) touch) + (if record? + (File. "/home/r/proj/cortex/render/worm-touch/touch/")))))))) +#+end_src - (fn [world tpf] - (touch-display - (map #(% (.getRootNode world)) touch)))))) +** Worm Touch Demonstration +#+begin_html +
+
+ +
+

The worm responds to touch.

+
+#+end_html + + +** Generating the Worm Touch Video +#+name: magick5 +#+begin_src clojure +(ns cortex.video.magick5 + (:import java.io.File) + (:use clojure.contrib.shell-out)) + +(defn images [path] + (sort (rest (file-seq (File. path))))) + +(def base "/home/r/proj/cortex/render/worm-touch/") + +(defn pics [file] + (images (str base file))) + +(defn combine-images [] + (let [main-view (pics "main-view") + touch (pics "touch/0") + targets (map + #(File. (str base "out/" (format "%07d.png" %))) + (range 0 (count main-view)))] + (dorun + (pmap + (comp + (fn [[ main-view touch target]] + (println target) + (sh "convert" + main-view + touch "-geometry" "+0+0" "-composite" + target)) + (fn [& args] (map #(.getCanonicalPath %) args))) + main-view touch targets)))) #+end_src * Headers @@ -661,9 +715,44 @@ (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f))) #+end_src +#+name: test-touch-header +#+begin_src clojure +(ns cortex.test.touch + (:use (cortex world util sense body touch)) + (:use cortex.test.body) + (:import com.aurellem.capture.Capture) + (:import java.io.File) + (:import (com.jme3.math Vector3f ColorRGBA))) +#+end_src + * Source Listing + - [[../src/cortex/touch.clj][cortex.touch]] + - [[../src/cortex/test/touch.clj][cortex.test.touch]] + - [[../src/cortex/video/magick4.clj][cortex.video.magick4]] + - [[../src/cortex/video/magick5.clj][cortex.video.magick5]] +#+html: + - [[http://hg.bortreb.com ][source-repository]] + * Next +So far I've implemented simulated Vision, Hearing, and Touch, the most +obvious and promiment senses that humans have. Smell and Taste shall +remain unimplemented for now. This accounts for the "five senses" that +feature so prominently in our lives. But humans have far more than the +five main senses. There are internal chemical senses, pain (which is +*not* the same as touch), heat sensitivity, and our sense of balance, +among others. One extra sense is so important that I must implement it +to have a hope of making creatures that can gracefully control their +own bodies. It is Proprioception, which is the sense of the location +of each body part in relation to the other body parts. +Close your eyes, and touch your nose with your right index finger. How +did you do it? You could not see your hand, and neither your hand nor +your nose could use the sense of touch to guide the path of your hand. +There are no sound cues, and Taste and Smell certainly don't provide +any help. You know where your hand is without your other senses +because of Proprioception. + +Onward to [[./proprioception.org][proprioception]]! * COMMENT Code Generation #+begin_src clojure :tangle ../src/cortex/touch.clj @@ -678,11 +767,22 @@ <> #+end_src +#+begin_src clojure :tangle ../src/cortex/test/touch.clj +<> +<> +<> +<> +#+end_src -#+begin_src clojure :tangle ../src/cortex/test/touch.clj -<> +#+begin_src clojure :tangle ../src/cortex/video/magick4.clj +<> #+end_src +#+begin_src clojure :tangle ../src/cortex/video/magick5.clj +<> +#+end_src + +