Other articles

  1. Clojure and the Mandelbrot set on LEGO Mindstorms EV3

    The LeJOS operating system for LEGO Mindstorms EV3 is, as I said before, a small Linux with an embedded JVM.


    Running Clojure:

    To run Clojure on the EV3 I had to download the Clojure runtime, unzip it and throw away everything but the actual jar-file clojure-1.8.0.jar.

    Then the jar-file needs to be transfered to the EV3 (via scp for example) and can be executed via ssh with jrun -jar clojure-1.8.0.jar (I already have an alias in my ~/.ssh/config for the EV3). After about two minutes we have a Clojure repl that we can use to answer the important questions of our time.

    Takes about two minutes to start the repl

    Actually coding something:

    The first thing we have to notice is, that the leJOS menu stays visible when we opened the clojure repl. This is a problem insofar, that when we use the Clojure code to interact with (for example) the display, both the leJOS menu and our application want to use the display and their interactions will overlap in unpredictable ways (for me both images were "fighting", but the system might as well crash).

    So I created a shell script that kills the existing java process and opens it again once our command ran.

    #!/bin/sh
    killall java
    jrun # ... we will look at that later
    ~/lejos/bin/startmenu &
    

    Now we want to actually write Clojure code where we calculate members of the Mandelbrot set. The Mandelbrot set is the set of all numbers \(c\) for which \(z_n\) with \(z_0 = 0\) and \(z_{n+1} = z_{n}^2 + c\) does not diverge (when \(n\) goes to infinity). This is calculated in the complex plane. The numbers for which this doesn't diverge are usually drawn in black while the diverging numbers remain white.

    I looked for a Java-based library for the complex numbers and found one with Apache. This library was insofar underwhelming, that taking a complex number to the power of an arbitrary Integer doesn't work as one expects. The library always uses the following equivalence: \(y^x = exp(x \times log(y))\) which is fine in general but doesn't work if \(y\) is zero, which is the base case but also no problem with positive integer powers. Took me an hour because that is not at all well documented (for the integer-case). So the first thing is, to write our own pow function: (defn pow [y, x] (if (.equals y Complex/ZERO) y (.pow y x)))

    Now we can define both \(z_n\) and whether any \(z_n\) diverges:

    (defn zn [n, c] ( if (== n 0) Complex/ZERO (.add c (pow (zn (- n 1) c) 2.0 ) ) ))
    (defn diverges [x] (or (.isNaN x) (.isInfinite x) (< 2.0 (.abs x))))
    

    And the idea is, to set the whole display to black and evaluate for every black pixel \(z_1\) and then for every remaining black pixel \(z_2\). Once a pixel was set to white, we no longer need to evaluate it, because we already know it diverges.

    So here's the full code for mb.clj:

    (import [org.apache.commons.math3.complex Complex])
    (import [lejos.hardware.lcd LCD])
    (import [lejos.utility Delay])
    
    (defn pow [y, x] (if (.equals y Complex/ZERO) y (.pow y x) ))
    (defn zn [n, c] ( if (== n 0) Complex/ZERO (.add c (pow (zn (- n 1) c) 2.0 ) ) ))
    (defn diverges [x] (or (.isNaN x) (.isInfinite x) (< 2.0 (.abs x))))
    
    (defn scale_x [x] ( - (/ (* 2.5 x) LCD/SCREEN_WIDTH ) 2.0 ))
    (defn scale_y [y] ( - (/ (* 2.0 y) LCD/SCREEN_HEIGHT) 1.0 ))
    
    (doseq [x (range  LCD/SCREEN_WIDTH )
            y (range  LCD/SCREEN_HEIGHT)] (LCD/setPixel x y 1))
    
    (doseq [rep (range 1 1000)]
      (doseq [x (range  LCD/SCREEN_WIDTH )
              y (range  LCD/SCREEN_HEIGHT)]
              (
                  if (== 1 (LCD/getPixel x y))
                  (if (diverges (zn rep (Complex. (scale_x x) (scale_y y)))) (LCD/setPixel x y 0))
              )
      )
    )
    

    We don't need a delay at the end because this will take long enough.

    But now we have to think about running this. Basically we need to include every jar we use into the java classpath and then run the clojure main with our script as the parameter. For org.apache.commons.math3 we need the commons-math3-3.6.1.jar from Apache and for the lejos namespace we need the ev3classes.jar from leJOS (which is not included on the system because it is usually compiled into the finished jar).

    Once this is all done we can basically run our application with jrun -cp "../ev3classes.jar:./commons-math3-3.6.1.jar:../clojure-1.8.0.jar" clojure.main mb.clj.

    After a few hours, it will look like this

    I am pretty sure it is possible to compile the whole jar with the correct main using eclipse/ant and whatnot. But that's the first successful experiment in this direction. Here's a timelapse of the program in action.


  2. Working and Working on LEGO Mindstorms EV3

    Presentation Space at a Networking Event for Educators

    Since October I am working at the Leipzig University of Applied Science as a researcher and project manager in a robotics project. The project is called "Roberta" and is aimed at young people, specifically girls and young women. With consumer-grade robotics sets, like LEGO Mindstorms, we try to get them excited about robotics, computer science and STEM in general. What we're doing is working with educators to get this project to schools.

    I am doing project management but I am also doing research and testing the limits of what we can do with those robots. Not from an engineering (or software engineering) perspective. I am not throwing C-code at the machines.


    "programming"

    The EV3 Bricks (the brain of the LEGO Mindstorms EV3) are running a small Linux. Using the original proprietary LEGO Mindstorms programming software is a bit lacking. You use the graphical interface to create a flow-graph that describes the actions the robot should take. Sadly, this language doesn't support variable scoping (meaning that every variable is global) and only support non-recursive functions. Indirect recursion is prohibited as well (no tricks!). That's no fun.

    Alternatively there are multiple (two) alternative Linux-based operating systems. I have yet to try the other one, but the first one is basically a Linux with an embedded JavaVM. This is usually used to, well, program Java. I've tried that and it's boring because Java is boring. But having the JVM is, even with 300Mhz and 64MB RAM, pretty powerful.

    I've tried to run Jython on the brick but there wasn't enough memory to do it. Using a USB Stick formatted as swap I was able to get it to run, but it took almost an hour to even start the repl. Running any actual code took prohibitively long.

    16GB of additional very slow main memory ought to be enough for everybody

    But Clojure also runs on the JVM and this worked quite nicely. The next article will be about me, running Clojure on a LEGO Mindstorms Ev3.


  3. Solving Tatham's Puzzles - Signpost (backtracking)

    I've started writing solvers for games of Simon Tatham's Portable Puzzle Collection. Those Problems can usually be reduced to some NP-complete problem. This is quite a fun exercise.

    Unsolved Signpost Puzzle

    Unsolved Signpost Puzzle

    We will be continuing with the puzzle "Signpost", which is a variant of Hamiltonian paths in a directed graph where, for some fields or nodes, the position within the path is given.

    The goal of the puzzle is to connect the Node "1" to some node, which is then node "2" in the direction the arrow points to. All numbers need to be connected to their successor until all nodes have a single unique number from "1" to (in this example) "25".

    We will be solving this problem in two different ways, both with the help of the programming language Python. But first of all we'll need to convert a puzzle into a machine readable format. Luckily, Signpost has an easy and accessible format. The problem instance from the image is the instance 5x5:1cceefcfggeeccghcac3e12hch10ah25a. It starts off with the size of the problem and then a list of letters that show the directions of the arrow where a is north or 12 o'clock and all other letters go clockwise with h being north-west. A letter can be prefixed by a number which is the hint for that node. That node's number is fixed. The cells or nodes are defined line by line from the top left to the bottom right.

    Problems don't need to start with 1 or end with the last number. The start and the end of the path can be anywhere but puzzles generated with the ends at opposite corner look nicer.

    First we define what our data looks like. We implement a simple form of directed graph that we define as a set of nodes and their successors. From that we derive following definition:

    class Node(object):
        hint = None
        following = frozenset()
    
        def __init__(self, direction):
            self.direction = direction
    

    We don't want to use any of that code in a library or something something so we don't do anything fancy here and we use the simplest class definition that comes to mind.


    Railroad diagram of regex

    ^(?P<size_x>\d+)x(?P<size_y>\d+)$

    Railroad diagram of regex

    (?P<defn>\d*[a-h])

    Now we go on to parsing the problem. The parsing function should get the problem as a string and return a 3-tuple containing width and height of the problem as well as a mapping of positions within the grid to ``Node`` instances.

    Onto actually parsing. We can split at the colon and then regular expressions to the rescue. The size definition has the form ^(?P<size_x>\d+)x(?P<size_y>\d+)$ while a single node definition has the form (?P<defn>\d*[a-h]). As we can see, the digits are optional but we need to greedily accept them. All non-overlapping occurences in order will be our cell definitions.

    Read the rest of this article.


Page 1 / 2 »

links

social

Theme based on notmyidea