comparison thesis/cortex.org @ 475:3ec428e096e5

most of the way to getting touch integrated.
author Robert McIntyre <rlm@mit.edu>
date Fri, 28 Mar 2014 21:48:53 -0400
parents 57c7d5aec8d5
children 5a15611fbb9f
comparison
equal deleted inserted replaced
474:57c7d5aec8d5 475:3ec428e096e5
25 #+caption: 25 #+caption:
26 #+caption: 26 #+caption:
27 #+caption: 27 #+caption:
28 #+name: name 28 #+name: name
29 #+begin_listing clojure 29 #+begin_listing clojure
30 #+BEGIN_SRC clojure
31 #+END_SRC
30 #+end_listing 32 #+end_listing
31 33
32 #+caption: 34 #+caption:
33 #+caption: 35 #+caption:
34 #+caption: 36 #+caption:
1584 simulate touch, I use jMonkeyEngine's physics system to execute 1586 simulate touch, I use jMonkeyEngine's physics system to execute
1585 many small collision detections, one for each feeler. The placement 1587 many small collision detections, one for each feeler. The placement
1586 of the feelers is determined by a UV-mapped image which shows where 1588 of the feelers is determined by a UV-mapped image which shows where
1587 each feeler should be on the 3D surface of the body. 1589 each feeler should be on the 3D surface of the body.
1588 1590
1589 *** Defining Touch Meta-Data in Blender 1591 *** COMMENT Defining Touch Meta-Data in Blender
1590 1592
1591 Each geometry can have a single UV map which describes the 1593 Each geometry can have a single UV map which describes the
1592 position of the feelers which will constitute its sense of touch. 1594 position of the feelers which will constitute its sense of touch.
1593 This image path is stored under the ``touch'' key. The image itself 1595 This image path is stored under the ``touch'' key. The image itself
1594 is black and white, with black meaning a feeler length of 0 (no 1596 is black and white, with black meaning a feeler length of 0 (no
1595 feeler is present) and white meaning a feeler length of =scale=, 1597 feeler is present) and white meaning a feeler length of =scale=,
1596 which is a float stored under the key "scale". 1598 which is a float stored under the key "scale".
1597 1599
1598 #+name: meta-data 1600 #+caption: Touch does not use empty nodes, to store metadata,
1599 #+begin_src clojure 1601 #+caption: because the metadata of each solid part of a
1602 #+caption: creature's body is sufficient.
1603 #+name: touch-meta-data
1604 #+begin_listing clojure
1600 (defn tactile-sensor-profile 1605 (defn tactile-sensor-profile
1601 "Return the touch-sensor distribution image in BufferedImage format, 1606 "Return the touch-sensor distribution image in BufferedImage format,
1602 or nil if it does not exist." 1607 or nil if it does not exist."
1603 [#^Geometry obj] 1608 [#^Geometry obj]
1604 (if-let [image-path (meta-data obj "touch")] 1609 (if-let [image-path (meta-data obj "touch")]
1608 "Return the length of each feeler. Default scale is 0.01 1613 "Return the length of each feeler. Default scale is 0.01
1609 jMonkeyEngine units." 1614 jMonkeyEngine units."
1610 [#^Geometry obj] 1615 [#^Geometry obj]
1611 (if-let [scale (meta-data obj "scale")] 1616 (if-let [scale (meta-data obj "scale")]
1612 scale 0.1)) 1617 scale 0.1))
1613 #+end_src 1618 #+end_listing
1614 1619
1615 Here is an example of a UV-map which specifies the position of touch 1620 Here is an example of a UV-map which specifies the position of
1616 sensors along the surface of the upper segment of the worm. 1621 touch sensors along the surface of the upper segment of a fingertip.
1617 1622
1618 #+attr_html: width=755 1623
1619 #+caption: This is the tactile-sensor-profile for the upper segment of the worm. It defines regions of high touch sensitivity (where there are many white pixels) and regions of low sensitivity (where white pixels are sparse). 1624 #+caption: This is the tactile-sensor-profile for the upper segment
1625 #+caption: of a fingertip. It defines regions of high touch sensitivity
1626 #+caption: (where there are many white pixels) and regions of low
1627 #+caption: sensitivity (where white pixels are sparse).
1628 #+name: fimgertip-UV
1629 #+ATTR_LaTeX: :width 10cm
1620 [[../images/finger-UV.png]] 1630 [[../images/finger-UV.png]]
1621 1631
1622 *** Implementation Summary 1632 *** COMMENT Implementation Summary
1623 1633
1624 To simulate touch there are three conceptual steps. For each solid 1634 To simulate touch there are three conceptual steps. For each solid
1625 object in the creature, you first have to get UV image and scale 1635 object in the creature, you first have to get UV image and scale
1626 parameter which define the position and length of the feelers. 1636 parameter which define the position and length of the feelers.
1627 Then, you use the triangles which comprise the mesh and the UV 1637 Then, you use the triangles which comprise the mesh and the UV
1634 Extracting the meta-data has already been described. The third 1644 Extracting the meta-data has already been described. The third
1635 step, physics collision detection, is handled in =touch-kernel=. 1645 step, physics collision detection, is handled in =touch-kernel=.
1636 Translating the positions and orientations of the feelers from the 1646 Translating the positions and orientations of the feelers from the
1637 UV-map to world-space is itself a three-step process. 1647 UV-map to world-space is itself a three-step process.
1638 1648
1639 - Find the triangles which make up the mesh in pixel-space and in 1649 - Find the triangles which make up the mesh in pixel-space and in
1640 world-space. =triangles= =pixel-triangles=. 1650 world-space. (=triangles= =pixel-triangles=).
1641 1651
1642 - Find the coordinates of each feeler in world-space. These are the 1652 - Find the coordinates of each feeler in world-space. These are
1643 origins of the feelers. =feeler-origins=. 1653 the origins of the feelers. (=feeler-origins=).
1644 1654
1645 - Calculate the normals of the triangles in world space, and add 1655 - Calculate the normals of the triangles in world space, and add
1646 them to each of the origins of the feelers. These are the 1656 them to each of the origins of the feelers. These are the
1647 normalized coordinates of the tips of the feelers. =feeler-tips=. 1657 normalized coordinates of the tips of the feelers.
1648 1658 (=feeler-tips=).
1649 *** Triangle Math 1659
1650 1660 *** COMMENT Triangle Math
1651 The rigid objects which make up a creature have an underlying 1661
1652 =Geometry=, which is a =Mesh= plus a =Material= and other important 1662 The rigid objects which make up a creature have an underlying
1653 data involved with displaying the object. 1663 =Geometry=, which is a =Mesh= plus a =Material= and other
1654 1664 important data involved with displaying the object.
1655 A =Mesh= is composed of =Triangles=, and each =Triangle= has three 1665
1656 vertices which have coordinates in world space and UV space. 1666 A =Mesh= is composed of =Triangles=, and each =Triangle= has three
1657 1667 vertices which have coordinates in world space and UV space.
1658 Here, =triangles= gets all the world-space triangles which comprise a 1668
1659 mesh, while =pixel-triangles= gets those same triangles expressed in 1669 Here, =triangles= gets all the world-space triangles which
1660 pixel coordinates (which are UV coordinates scaled to fit the height 1670 comprise a mesh, while =pixel-triangles= gets those same triangles
1661 and width of the UV image). 1671 expressed in pixel coordinates (which are UV coordinates scaled to
1662 1672 fit the height and width of the UV image).
1663 #+name: triangles-2 1673
1664 #+begin_src clojure 1674 #+caption: Programs to extract triangles from a geometry and get
1665 (in-ns 'cortex.touch) 1675 #+caption: their verticies in both world and UV-coordinates.
1676 #+name: get-triangles
1677 #+begin_listing clojure
1666 (defn triangle 1678 (defn triangle
1667 "Get the triangle specified by triangle-index from the mesh." 1679 "Get the triangle specified by triangle-index from the mesh."
1668 [#^Geometry geo triangle-index] 1680 [#^Geometry geo triangle-index]
1669 (triangle-seq 1681 (triangle-seq
1670 (let [scratch (Triangle.)] 1682 (let [scratch (Triangle.)]
1682 [#^Mesh mesh triangle-index] 1694 [#^Mesh mesh triangle-index]
1683 (let [indices (int-array 3)] 1695 (let [indices (int-array 3)]
1684 (.getTriangle mesh triangle-index indices) 1696 (.getTriangle mesh triangle-index indices)
1685 (vec indices))) 1697 (vec indices)))
1686 1698
1687 (defn vertex-UV-coord 1699 (defn vertex-UV-coord
1688 "Get the UV-coordinates of the vertex named by vertex-index" 1700 "Get the UV-coordinates of the vertex named by vertex-index"
1689 [#^Mesh mesh vertex-index] 1701 [#^Mesh mesh vertex-index]
1690 (let [UV-buffer 1702 (let [UV-buffer
1691 (.getData 1703 (.getData
1692 (.getBuffer 1704 (.getBuffer
1709 [#^Geometry geo image] 1721 [#^Geometry geo image]
1710 (let [height (.getHeight image) 1722 (let [height (.getHeight image)
1711 width (.getWidth image)] 1723 width (.getWidth image)]
1712 (map (partial pixel-triangle geo image) 1724 (map (partial pixel-triangle geo image)
1713 (range (.getTriangleCount (.getMesh geo)))))) 1725 (range (.getTriangleCount (.getMesh geo))))))
1714 #+end_src 1726 #+end_listing
1715 1727
1716 *** The Affine Transform from one Triangle to Another 1728 *** The Affine Transform from one Triangle to Another
1717 1729
1718 =pixel-triangles= gives us the mesh triangles expressed in pixel 1730 =pixel-triangles= gives us the mesh triangles expressed in pixel
1719 coordinates and =triangles= gives us the mesh triangles expressed in 1731 coordinates and =triangles= gives us the mesh triangles expressed
1720 world coordinates. The tactile-sensor-profile gives the position of 1732 in world coordinates. The tactile-sensor-profile gives the
1721 each feeler in pixel-space. In order to convert pixel-space 1733 position of each feeler in pixel-space. In order to convert
1722 coordinates into world-space coordinates we need something that takes 1734 pixel-space coordinates into world-space coordinates we need
1723 coordinates on the surface of one triangle and gives the corresponding 1735 something that takes coordinates on the surface of one triangle
1724 coordinates on the surface of another triangle. 1736 and gives the corresponding coordinates on the surface of another
1725 1737 triangle.
1726 Triangles are [[http://mathworld.wolfram.com/AffineTransformation.html ][affine]], which means any triangle can be transformed into 1738
1727 any other by a combination of translation, scaling, and 1739 Triangles are [[http://mathworld.wolfram.com/AffineTransformation.html ][affine]], which means any triangle can be transformed
1728 rotation. The affine transformation from one triangle to another 1740 into any other by a combination of translation, scaling, and
1729 is readily computable if the triangle is expressed in terms of a $4x4$ 1741 rotation. The affine transformation from one triangle to another
1730 matrix. 1742 is readily computable if the triangle is expressed in terms of a
1731 1743 $4x4$ matrix.
1732 \begin{bmatrix} 1744
1733 x_1 & x_2 & x_3 & n_x \\ 1745 \begin{bmatrix}
1734 y_1 & y_2 & y_3 & n_y \\ 1746 x_1 & x_2 & x_3 & n_x \\
1735 z_1 & z_2 & z_3 & n_z \\ 1747 y_1 & y_2 & y_3 & n_y \\
1736 1 & 1 & 1 & 1 1748 z_1 & z_2 & z_3 & n_z \\
1737 \end{bmatrix} 1749 1 & 1 & 1 & 1
1738 1750 \end{bmatrix}
1739 Here, the first three columns of the matrix are the vertices of the 1751
1740 triangle. The last column is the right-handed unit normal of the 1752 Here, the first three columns of the matrix are the vertices of
1741 triangle. 1753 the triangle. The last column is the right-handed unit normal of
1742 1754 the triangle.
1743 With two triangles $T_{1}$ and $T_{2}$ each expressed as a matrix like 1755
1744 above, the affine transform from $T_{1}$ to $T_{2}$ is 1756 With two triangles $T_{1}$ and $T_{2}$ each expressed as a matrix
1745 1757 like above, the affine transform from $T_{1}$ to $T_{2}$ is
1746 $T_{2}T_{1}^{-1}$ 1758
1747 1759 $T_{2}T_{1}^{-1}$
1748 The clojure code below recapitulates the formulas above, using 1760
1749 jMonkeyEngine's =Matrix4f= objects, which can describe any affine 1761 The clojure code below recapitulates the formulas above, using
1750 transformation. 1762 jMonkeyEngine's =Matrix4f= objects, which can describe any affine
1751 1763 transformation.
1752 #+name: triangles-3 1764
1753 #+begin_src clojure 1765 #+caption: Program to interpert triangles as affine transforms.
1754 (in-ns 'cortex.touch) 1766 #+name: triangle-affine
1755 1767 #+begin_listing clojure
1768 #+BEGIN_SRC clojure
1756 (defn triangle->matrix4f 1769 (defn triangle->matrix4f
1757 "Converts the triangle into a 4x4 matrix: The first three columns 1770 "Converts the triangle into a 4x4 matrix: The first three columns
1758 contain the vertices of the triangle; the last contains the unit 1771 contain the vertices of the triangle; the last contains the unit
1759 normal of the triangle. The bottom row is filled with 1s." 1772 normal of the triangle. The bottom row is filled with 1s."
1760 [#^Triangle t] 1773 [#^Triangle t]
1775 triangle." 1788 triangle."
1776 [#^Triangle tri-1 #^Triangle tri-2] 1789 [#^Triangle tri-1 #^Triangle tri-2]
1777 (.mult 1790 (.mult
1778 (triangle->matrix4f tri-2) 1791 (triangle->matrix4f tri-2)
1779 (.invert (triangle->matrix4f tri-1)))) 1792 (.invert (triangle->matrix4f tri-1))))
1780 #+end_src 1793 #+END_SRC
1781 1794 #+end_listing
1782 *** Triangle Boundaries 1795
1796 *** COMMENT Triangle Boundaries
1783 1797
1784 For efficiency's sake I will divide the tactile-profile image into 1798 For efficiency's sake I will divide the tactile-profile image into
1785 small squares which inscribe each pixel-triangle, then extract the 1799 small squares which inscribe each pixel-triangle, then extract the
1786 points which lie inside the triangle and map them to 3D-space using 1800 points which lie inside the triangle and map them to 3D-space using
1787 =triangle-transform= above. To do this I need a function, 1801 =triangle-transform= above. To do this I need a function,
1789 triangle. 1803 triangle.
1790 1804
1791 =inside-triangle?= determines whether a point is inside a triangle 1805 =inside-triangle?= determines whether a point is inside a triangle
1792 in 2D pixel-space. 1806 in 2D pixel-space.
1793 1807
1794 #+name: triangles-4 1808 #+caption: Program to efficiently determine point includion
1795 #+begin_src clojure 1809 #+caption: in a triangle.
1810 #+name: in-triangle
1811 #+begin_listing clojure
1812 #+BEGIN_SRC clojure
1796 (defn convex-bounds 1813 (defn convex-bounds
1797 "Returns the smallest square containing the given vertices, as a 1814 "Returns the smallest square containing the given vertices, as a
1798 vector of integers [left top width height]." 1815 vector of integers [left top width height]."
1799 [verts] 1816 [verts]
1800 (let [xs (map first verts) 1817 (let [xs (map first verts)
1822 (let [[vert-1 vert-2 vert-3] [(.get1 tri) (.get2 tri) (.get3 tri)]] 1839 (let [[vert-1 vert-2 vert-3] [(.get1 tri) (.get2 tri) (.get3 tri)]]
1823 (and 1840 (and
1824 (same-side? vert-1 vert-2 vert-3 p) 1841 (same-side? vert-1 vert-2 vert-3 p)
1825 (same-side? vert-2 vert-3 vert-1 p) 1842 (same-side? vert-2 vert-3 vert-1 p)
1826 (same-side? vert-3 vert-1 vert-2 p)))) 1843 (same-side? vert-3 vert-1 vert-2 p))))
1827 #+end_src 1844 #+END_SRC
1828 1845 #+end_listing
1829 *** Feeler Coordinates 1846
1830 1847 *** COMMENT Feeler Coordinates
1831 The triangle-related functions above make short work of calculating 1848
1832 the positions and orientations of each feeler in world-space. 1849 The triangle-related functions above make short work of
1833 1850 calculating the positions and orientations of each feeler in
1834 #+name: sensors 1851 world-space.
1835 #+begin_src clojure 1852
1836 (in-ns 'cortex.touch) 1853 #+caption: Program to get the coordinates of ``feelers '' in
1837 1854 #+caption: both world and UV-coordinates.
1855 #+name: feeler-coordinates
1856 #+begin_listing clojure
1857 #+BEGIN_SRC clojure
1838 (defn feeler-pixel-coords 1858 (defn feeler-pixel-coords
1839 "Returns the coordinates of the feelers in pixel space in lists, one 1859 "Returns the coordinates of the feelers in pixel space in lists, one
1840 list for each triangle, ordered in the same way as (triangles) and 1860 list for each triangle, ordered in the same way as (triangles) and
1841 (pixel-triangles)." 1861 (pixel-triangles)."
1842 [#^Geometry geo image] 1862 [#^Geometry geo image]
1860 (pixel-triangles geo image) 1880 (pixel-triangles geo image)
1861 (triangles geo))] 1881 (triangles geo))]
1862 (map (fn [transform coords] 1882 (map (fn [transform coords]
1863 (map #(.mult transform (->vector3f %)) coords)) 1883 (map #(.mult transform (->vector3f %)) coords))
1864 transforms (feeler-pixel-coords geo image)))) 1884 transforms (feeler-pixel-coords geo image))))
1865 1885 #+END_SRC
1886 #+end_listing
1887
1888 #+caption: Program to get the position of the base and tip of
1889 #+caption: each ``feeler''
1890 #+name: feeler-tips
1891 #+begin_listing clojure
1892 #+BEGIN_SRC clojure
1866 (defn feeler-origins 1893 (defn feeler-origins
1867 "The world space coordinates of the root of each feeler." 1894 "The world space coordinates of the root of each feeler."
1868 [#^Geometry geo image] 1895 [#^Geometry geo image]
1869 (reduce concat (feeler-world-coords geo image))) 1896 (reduce concat (feeler-world-coords geo image)))
1870 1897
1882 (mapcat (fn [origins normal] 1909 (mapcat (fn [origins normal]
1883 (map #(.add % normal) origins)) 1910 (map #(.add % normal) origins))
1884 world-coords normals))) 1911 world-coords normals)))
1885 1912
1886 (defn touch-topology 1913 (defn touch-topology
1887 "touch-topology? is not a function."
1888 [#^Geometry geo image] 1914 [#^Geometry geo image]
1889 (collapse (reduce concat (feeler-pixel-coords geo image)))) 1915 (collapse (reduce concat (feeler-pixel-coords geo image))))
1890 #+end_src 1916 #+END_SRC
1891 *** Simulated Touch 1917 #+end_listing
1892 1918
1893 =touch-kernel= generates functions to be called from within a 1919 *** COMMENT Simulated Touch
1894 simulation that perform the necessary physics collisions to collect 1920
1895 tactile data, and =touch!= recursively applies it to every node in 1921 Now that the functions to construct feelers are complete,
1896 the creature. 1922 =touch-kernel= generates functions to be called from within a
1897 1923 simulation that perform the necessary physics collisions to
1898 #+name: kernel 1924 collect tactile data, and =touch!= recursively applies it to every
1899 #+begin_src clojure 1925 node in the creature.
1900 (in-ns 'cortex.touch) 1926
1901 1927 #+caption: Efficient program to transform a ray from
1928 #+caption: one position to another.
1929 #+name: set-ray
1930 #+begin_listing clojure
1931 #+BEGIN_SRC clojure
1902 (defn set-ray [#^Ray ray #^Matrix4f transform 1932 (defn set-ray [#^Ray ray #^Matrix4f transform
1903 #^Vector3f origin #^Vector3f tip] 1933 #^Vector3f origin #^Vector3f tip]
1904 ;; Doing everything locally reduces garbage collection by enough to 1934 ;; Doing everything locally reduces garbage collection by enough to
1905 ;; be worth it. 1935 ;; be worth it.
1906 (.mult transform origin (.getOrigin ray)) 1936 (.mult transform origin (.getOrigin ray))
1907 (.mult transform tip (.getDirection ray)) 1937 (.mult transform tip (.getDirection ray))
1908 (.subtractLocal (.getDirection ray) (.getOrigin ray)) 1938 (.subtractLocal (.getDirection ray) (.getOrigin ray))
1909 (.normalizeLocal (.getDirection ray))) 1939 (.normalizeLocal (.getDirection ray)))
1910 1940 #+END_SRC
1911 (import com.jme3.math.FastMath) 1941 #+end_listing
1912 1942
1943 #+caption: This is the core of touch in =CORTEX= each feeler
1944 #+caption: follows the object it is bound to, reporting any
1945 #+caption: collisions that may happen.
1946 #+name: touch-kernel
1947 #+begin_listing clojure
1948 #+BEGIN_SRC clojure
1913 (defn touch-kernel 1949 (defn touch-kernel
1914 "Constructs a function which will return tactile sensory data from 1950 "Constructs a function which will return tactile sensory data from
1915 'geo when called from inside a running simulation" 1951 'geo when called from inside a running simulation"
1916 [#^Geometry geo] 1952 [#^Geometry geo]
1917 (if-let 1953 (if-let
1920 ray-reference-tips (feeler-tips geo profile) 1956 ray-reference-tips (feeler-tips geo profile)
1921 ray-length (tactile-scale geo) 1957 ray-length (tactile-scale geo)
1922 current-rays (map (fn [_] (Ray.)) ray-reference-origins) 1958 current-rays (map (fn [_] (Ray.)) ray-reference-origins)
1923 topology (touch-topology geo profile) 1959 topology (touch-topology geo profile)
1924 correction (float (* ray-length -0.2))] 1960 correction (float (* ray-length -0.2))]
1925
1926 ;; slight tolerance for very close collisions. 1961 ;; slight tolerance for very close collisions.
1927 (dorun 1962 (dorun
1928 (map (fn [origin tip] 1963 (map (fn [origin tip]
1929 (.addLocal origin (.mult (.subtract tip origin) 1964 (.addLocal origin (.mult (.subtract tip origin)
1930 correction))) 1965 correction)))
1958 (if (> response limit) (float 0.0) 1993 (if (> response limit) (float 0.0)
1959 (+ response correction))) 1994 (+ response correction)))
1960 (float 0.0) 1995 (float 0.0)
1961 limit))) 1996 limit)))
1962 limit]))))))))))) 1997 limit])))))))))))
1963 1998 #+END_SRC
1999 #+end_listing
2000
2001 Armed with the =touch!= function, =CORTEX= becomes capable of
2002 giving creatures a sense of touch. A simple test is to create a
2003 cube that is outfitted with a uniform distrubition of touch
2004 sensors. It can feel the ground and any balls that it touches.
2005
2006 #+caption: =CORTEX= interface for creating touch in a simulated
2007 #+caption: creature.
2008 #+name: touch
2009 #+begin_listing clojure
2010 #+BEGIN_SRC clojure
1964 (defn touch! 2011 (defn touch!
1965 "Endow the creature with the sense of touch. Returns a sequence of 2012 "Endow the creature with the sense of touch. Returns a sequence of
1966 functions, one for each body part with a tactile-sensor-profile, 2013 functions, one for each body part with a tactile-sensor-profile,
1967 each of which when called returns sensory data for that body part." 2014 each of which when called returns sensory data for that body part."
1968 [#^Node creature] 2015 [#^Node creature]
1969 (filter 2016 (filter
1970 (comp not nil?) 2017 (comp not nil?)
1971 (map touch-kernel 2018 (map touch-kernel
1972 (filter #(isa? (class %) Geometry) 2019 (filter #(isa? (class %) Geometry)
1973 (node-seq creature))))) 2020 (node-seq creature)))))
1974 #+end_src 2021 #+END_SRC
1975 2022 #+end_listing
1976 2023
1977 Armed with the =touch!= function, =CORTEX= becomes capable of giving 2024 The tactile-sensor-profile image for the touch cube is a simple
1978 creatures a sense of touch. A simple test is to create a cube that is 2025 cross with a unifom distribution of touch sensors:
1979 outfitted with a uniform distrubition of touch sensors. It can feel 2026
1980 the ground and any balls that it touches. 2027 #+caption: The touch profile for the touch-cube. Each pure white
1981 2028 #+caption: pixel defines a touch sensitive feeler.
1982 # insert touch cube image; UV map 2029 #+name: touch-cube-uv-map
1983 # insert video 2030 #+ATTR_LaTeX: :width 10cm
2031 [[./images/touch-profile.png]]
2032
2033 #+caption: The touch cube reacts to canonballs. The black, red,
2034 #+caption: and white cross on the right is a visual display of
2035 #+caption: the creature's touch. White means that it is feeling
2036 #+caption: something strongly, black is not feeling anything,
2037 #+caption: and gray is in-between. The cube can feel both the
2038 #+caption: floor and the ball. Notice that when the ball causes
2039 #+caption: the cube to tip, that the bottom face can still feel
2040 #+caption: part of the ground.
2041 #+name: touch-cube-uv-map
2042 #+ATTR_LaTeX: :width 15cm
2043 [[./images/touch-cube.png]]
1984 2044
1985 ** Proprioception is the sense that makes everything ``real'' 2045 ** Proprioception is the sense that makes everything ``real''
1986 2046
1987 ** Muscles are both effectors and sensors 2047 ** Muscles are both effectors and sensors
1988 2048