I occasionally get asked if my self-replicating 3D printer will ever become self-aware and turn on humanity. While I can neither confirm nor deny if this is a design goal of the project, the likelihood that the printer, as designed, can undergo evolution and significantly change its function is very small. But that doesn’t have to stop us! In order to increase the accuracy of the printer, I used a custom-built genetic algorithm that simulates evolution to optimize the physical design of the motor controller.

What is Genetic Optimization

Genetic Optimization is a fascinating algorithm that allows simulated agents to discover an optimal solution in a process similar to natural selection. A population of agents is generated (in my case, 100 simulated motor controller circuits), and their fitness is measured by some method. The least fit agents are eliminated from the population while the most fit agents are allowed to “reproduce,” producing similar offspring with slight random differences. By simulating hundreds of generations, the fitness level of the population is increased (the cost is decreased) until an optimal solution (local or global) is reached.

This is how this process might look for a random selection of starting agents. Their average cost (on the vertical axis) is very high to begin with but quickly drops toward the optimal solution as more generations (on the horizontal axis) are simulated.

How is Genetic Optimization Helpful Here?

Since the motor controller cannot use microprocessors to control the motors, instead relying on electromechanical devices, the motor cannot be commanded to turn at any arbitrary speed. The current motor controller works by limiting the current through the motor by putting resistors of various values in series with the motor. Each of the 7 channels on the motor controller dedicated to each axis motor connects the input voltage to the motor through a resistor of a different value. Thus, if a certain channel is activated, the motor will spin at a speed characteristic of the resistor associated with that channel. To allow the motor to spin at more speeds, several channels can be activated simultaneously causing the motor to spin at a different, but unique speed.

Putting the other issues with this design aside, the specific resistance values are critical to achieving a functional motor controller. In the trivial case where all of the resistances are the same, there are only seven different speeds the motor can spin at, corresponding to the activation of any number of channels. A more sensible distribution (perhaps a linear one) performs better than this, but the optimal resistor assignments must be found to maximize the accuracy of the printer (and spoiler, it’s not a linear distribution). This might seem like a pretty small problem, and that is what I assumed as well until I saw the results.

The following figures show what the print would look like if the data strip was printed on the fully 3D printed 3D printer. As an aside, the reason why we can characterize the error like this without having to actually print the model is due to the fact that the positioning error is known to the Data Strip Generator. The reason for this might become clearer later, but for now, these images show what the model would look like if printed.

(Left) A Data Strip was generated which has only been optimized with gradient descent. Resistance values were set linearly. (Right) The same original file was used, but resistance values were optimized using Genetic Optimization. The difference is striking, the optimized model is nearly perfect and deviates from the expected final position by only 0.03mm.

Why Does This Work?

The values and distribution of the resistors on the motor controller are this important to the accuracy of the printer because of the way the motor controller prints angles. In any given GCode file, linear movements can take any angle relative to the axes of the machine it wants. For 3D printing, we generally only care about angles in the XY plane which simplifies the problem slightly, but not completely. Since the motor controller can only command a certain number of speeds depending on how many speed control channels are assigned to each motor, the number of angles in the XY plane that it can command is also limited.

The graphic below shows all of the combinations of speeds the X and Y motors can rotate at. A vector from the origin to any of these points shows the distance and direction that the hotend would move if the motors were commanded to turn at those speeds. Each one of these points is calculated based on the current through the motor which changes depending on which resistors/speed control channels are activated.

The distribution of speed combinations that are possible for the motor controller to command. The results of the genetic optimization are spoiled here; the graph on the right is much more evenly distributed as a result of the optimization.

Of course, not all of these speed combinations are made equal. Since the material is extruded through the nozzle at a constant rate, the surface feed rate of the printer must also remain constant for the extruded lines to be of equal width. If a speed combination from the bottom left of the plot is chosen, the surface feed rate is much slower than a pair from the top left. Therefore, only speed combinations in the yellow highlighted region are considered “valid” (as they have roughly the same surface feed rate). Thus, the points, really vectors, shown in this plot are a kind of normalized heading vector. If the motors are at a specific pair of speeds in that yellow region, we know the speed and direction of the hotend relative to the machine origin. Using this information, any linear movement can be decomposed into a normalized heading vector from that plot and a scalar multiple, the movement duration.

This plot shows the valid normalized heading vectors as actual vectors, making it easier to see which directions the printer can move in.

This is great for encoding movement: the normalized heading vector is encoded in which channels are activated or deactivated and the movement duration is encoded in how tall each row on the Data Strip is, but it comes with some problems. Principally, the printer can only move in the directions which have an associated normalized heading vector. Since there are an infinite number of possible directions in the models we want to print (in theory at least), the Data Strips are by definition an approximation of the model which they encode. To make that approximation as accurate as possible, we need to evenly distribute the heading vectors across the whole range of possible movement angles. This is where genetic optimization comes in!

The Specifics

The program starts by generating 100 Agent objects containing 7 randomly selected resistances between a minimum and maximum value (0-70 ohm). The resulting distribution of speeds that combination produces is calculated by calculating the current draw of the entire circuit for every combination of activated channels. At most, 4 channels were allowed to be activated at a time as any more than this resulted in very similar speeds due to the nature of series resistors. After this distribution is calculated, a cost score is found. In this case, a higher cost indicates a very uneven distribution and a low cost indicates a very even distribution. After these cost scores are found, the bottom 50% of the population is deleted while the top 50% get to “reproduce.”

The reproduction process is very simple. A function takes in a list of 50 Agents and for each one makes two copies with slightly modified resistance values. The resistance values can deviate at most by ±0.5 ohms per generation. This yields an output of 100 new agents and the process can be repeated again.

While this process is not as efficient as other methods and does not guarantee an optimal solution, it is very simple and converges surprisingly quickly. For most initial conditions, the simulation converges on an optimal solution within 10 or 20 seconds, and all simulations converged after 45 seconds. There was no effort made to optimize the algorithm because of this relatively short convergence time.

Conclusions

Repeated from the top of the article. (Left) A Data Strip was generated which has only been optimized with gradient descent. Resistance values were set linearly. (Right) The same original file was used, but resistance values were optimized using Genetic Optimization. The difference is striking, the optimized model is nearly perfect and deviates from the expected final position by only 0.03mm.

The results of this optimization speak for themselves. Positioning error is dramatically reduced to the point where dimensionally accurate parts can finally be printed. For the relatively complex, print-in-place gear systems of the fully 3D printed 3D printer, this increased accuracy is critical to allow it to function as expected. In addition, and of course, most importantly, I can now say that my self-replicating 3D printer has undergone evolution.

clickprintchem Avatar

Published by

Categories:

Leave a comment

Design a site like this with WordPress.com
Get started