Note that there are some explanatory texts on larger screens.

plurals
  1. POEvolutionary Algorithm for the Theo Jansen Walking Mechanism
    primarykey
    data
    text
    <p>There is a Dutch artist/engineer who created a very elaborate walking mechanism. The working principle can be seen here:</p> <p><a href="http://www.strandbeest.com/beests_leg.php" rel="nofollow">http://www.strandbeest.com/beests_leg.php</a></p> <p>The curious part is that he used a self-made Evolutionary Algorithm to calculate the ideal link lengths, which are described at the bottom of the page.</p> <p>I created a Python script to visually analyze the ground-contact part of the cycle, which must have two requisites fulfilled:</p> <ol> <li>Be as straight as possible, so as not to wobble up and down;</li> <li>Have a speed as constant as possible, so as not to drag one foot against the other;</li> </ol> <p>These two criteria would result in a "wheel like" effect, with the machine going linearly ahead without wasting kinetic energy.</p> <p>The question is:</p> <p>"Do you have any suggestion of a simple evolutionary iterative formula to optimize leg lengths (by inserting the correct mutations in the code below) so as to improve the walking path given the two criteria above?"</p> <p>EDIT: some suggestions about the "fitting rule" for genome candidates:</p> <ul> <li>Take the "lower part" (ground contact) of the cycle, given that it corresponds to one third of crank revolution (mind the lower part might have a non-horizontal slope and still be linear);</li> <li>Apply linear regression on the point positions of this "ground contact" part;</li> <li>Calculate vertical variation from the linear regression (least squares?)</li> <li>Calculate speed variation by the difference between one point and the previous one, parallel to the regression line;</li> <li>(optional) plot graphs of these "error functions", possibly allowing to select mutants visually (boooring... ;o).</li> </ul> <p>Here is my code, in Python + GTK, which gives some visual insight into the problem: (EDIT: now with parametrized magic numbers subject to mutation by <code>mut</code>'s values)</p> <pre><code># coding: utf-8 import pygtk pygtk.require('2.0') import gtk, cairo from math import * class Mechanism(): def __init__(s): pass def assemble(s, angle): # magic numbers (unmutated) mu = [38, 7.8, 15, 50, 41.5, 39.3, 61.9, 55.8, 40.1, 39.4, 36.7, 65.7, 49] # mutations mut = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # mutated mn = [mu[n]+mut[n] for n in range(13)] s.A = Point(0,0) s.B = Point(-mn[0], -mn[1]) s.C = fromPoint(s.A, mn[2], angle) s.ac = Line(s.A, s.C) s.D = linkage(s.C, mn[3], s.B, mn[4]) s.cd = Line(s.C, s.D) s.bd = Line(s.B, s.D) s.E = linkage(s.B, mn[5], s.C, mn[6]) s.be = Line(s.B, s.E) s.ce = Line(s.C, s.E) s.F = linkage(s.D, mn[7], s.B, mn[8]) s.df = Line(s.D, s.F) s.bf = Line(s.B, s.F) s.G = linkage(s.F, mn[9], s.E, mn[10]) s.fg = Line(s.F, s.G) s.eg = Line(s.E, s.G) s.H = linkage(s.G, mn[11], s.E, mn[12]) s.gh = Line(s.G, s.H) s.EH = Line(s.E, s.H) return s.H class Point: def __init__(self, x, y): self.x, self.y = float(x), float(y) def __str__(self): return "(%.2f, %.2f)" % (self.x, self.y) class Line: def __init__(self, p1, p2): self.p1, self.p2 = p1, p2 def length(self): return sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2) def fromPoint(point, distance, angle): angle = radians(angle) return Point(point.x + distance * cos(angle), point.y + distance * sin(angle)) def distance(p1, p2): return sqrt( (p1.x - p2.x)**2 + (p1.y - p2.y)**2 ) def ccw(p1, p2, px): """ Test if px is at the right side of the line p1p2 """ ax, ay, bx, by = p1.x, p1.y, p2.x, p2.y cx, cy = px.x, px.y return (bx-ax)*(cy-ay)-(by-ay)*(cx-ax) &lt; 0 def linkage(p1, l1, p2, l2): l1 = float(l1) l2 = float(l2) dx,dy = p2.x-p1.x, p2.y-p1.y d = sqrt(dx**2 + dy**2) # distance between the centers a = (l1**2 - l2**2 + d**2) / (2*d) # distance from first center to the radical line M = Point(p1.x + (dx * a/d), p1.y + (dy * a/d)) # intersection of centerline with radical line h = sqrt(l1**2 - a**2) # distance from the midline to any of the points rx,ry = -dy*(h/d), dx*(h/d) # There are two results, but only one (the correct side of the line) must be chosen R1 = Point(M.x + rx, M.y + ry) R2 = Point(M.x - rx, M.y - ry) test1 = ccw(p1, p2, R1) test2 = ccw(p1, p2, R2) if test1: return R1 else: return R2 ###############################33 mec = Mechanism() stepcurve = [mec.assemble(p) for p in xrange(360)] window=gtk.Window() panel = gtk.VBox() window.add(panel) toppanel = gtk.HBox() panel.pack_start(toppanel) class Canvas(gtk.DrawingArea): def __init__(self): gtk.DrawingArea.__init__(self) self.connect("expose_event", self.expose) def expose(self, widget, event): cr = widget.window.cairo_create() rect = self.get_allocation() w = rect.width h = rect.height cr.translate(w*0.85, h*0.3) scale = 1 cr.scale(scale, -scale) cr.set_line_width(1) def paintpoint(p): cr.arc(p.x, p.y, 1.2, 0, 2*pi) cr.set_source_rgb(1,1,1) cr.fill_preserve() cr.set_source_rgb(0,0,0) cr.stroke() def paintline(l): cr.move_to(l.p1.x, l.p1.y) cr.line_to(l.p2.x, l.p2.y) cr.stroke() for i in mec.__dict__: if mec.__dict__[i].__class__.__name__ == 'Line': paintline(mec.__dict__[i]) for i in mec.__dict__: if mec.__dict__[i].__class__.__name__ == 'Point': paintpoint(mec.__dict__[i]) cr.move_to(stepcurve[0].x, stepcurve[0].y) for p in stepcurve[1:]: cr.line_to(p.x, p.y) cr.close_path() cr.set_source_rgb(1,0,0) cr.set_line_join(cairo.LINE_JOIN_ROUND) cr.stroke() class FootPath(gtk.DrawingArea): def __init__(self): gtk.DrawingArea.__init__(self) self.connect("expose_event", self.expose) def expose(self, widget, event): cr = widget.window.cairo_create() rect = self.get_allocation() w = rect.width h = rect.height cr.save() cr.translate(w/2, h/2) scale = 20 cr.scale(scale, -scale) cr.translate(40,92) twocurves = stepcurve.extend(stepcurve) cstart = 305 cr.set_source_rgb(0,0.5,0) for p in stepcurve[cstart:cstart+121]: cr.arc(p.x, p.y, 0.1, 0, 2*pi) cr.fill() cr.move_to(stepcurve[cstart].x, stepcurve[cstart].y) for p in stepcurve[cstart+1:cstart+121]: cr.line_to(p.x, p.y) cr.set_line_join(cairo.LINE_JOIN_ROUND) cr.restore() cr.set_source_rgb(1,0,0) cr.set_line_width(1) cr.stroke() cr.save() cr.translate(w/2, h/2) scale = 20 cr.scale(scale, -scale) cr.translate(40,92) cr.move_to(stepcurve[cstart+120].x, stepcurve[cstart+120].y) for p in stepcurve[cstart+120+1:cstart+360+1]: cr.line_to(p.x, p.y) cr.restore() cr.set_source_rgb(0,0,1) cr.set_line_width(1) cr.stroke() canvas = Canvas() canvas.set_size_request(140,150) toppanel.pack_start(canvas, False, False) toppanel.pack_start(gtk.VSeparator(), False, False) footpath = FootPath() footpath.set_size_request(1000,-1) toppanel.pack_start(footpath, True, True) def changeangle(par): mec.assemble(par.get_value()-60) canvas.queue_draw() angleadjust = gtk.Adjustment(value=0, lower=0, upper=360, step_incr=1) angleScale = gtk.HScale(adjustment=angleadjust) angleScale.set_value_pos(gtk.POS_LEFT) angleScale.connect("value-changed", changeangle) panel.pack_start(angleScale, False, False) window.set_position(gtk.WIN_POS_CENTER) window.show_all() gtk.main() </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload