/* ArdMind - Steve Grand-based Neuron Reinforcement Test This sketch is a test of how neurons, based on the neural networks from the "Creatures" series of artifical life games, can control a servo in the real world. I give full credit to Steve Grand, the creator of the "Creatures" franchise and the neural networks within said franchise. Connect a micro servo to the Arduino as follows: Red servo wire- 5.5V pin Black/brown servo wire- Ground (GND) pin Yellow servo wire- pin 9 That's it! Arduino Uno Specifications: Sketch uses 2182 bytes (6%) of program storage space. Maximum is 32256 bytes. Global variables use 50 bytes (2%) of dynamic memory, leaving 1998 bytes for local variables. Maximum is 2048 bytes. */ #include byte cellLayer(byte cellNum, byte thres, byte leakRate, byte restState, byte inp1, byte inp2 = 0, byte inp3 = 0, byte inp4 = 0, byte inp5 = 0) { // Initialize the cell (neuron) and the input arrays byte cells[cellNum]; byte inps[5] = {inp1, inp2, inp3, inp4, inp5}; // Now iterate over both arrays for (byte c = 0; c < cells[cellNum]; c++) { for (byte i = 0; i < inps[5]; i++) { // Set the current neuron's value to the present input value cells[c] += inps[i]; // If the cell's value rises above the threshold, reduce the cell's value by the same threshold if (cells[c] > thres) { cells[c] -= thres; } // Otherwise, if the neuron is above the "resting state," then the neuron will "leak" else if (cells[c] > restState) { cells[c] -= leakRate; } } } // Output the cell array return cells[cellNum]; } byte dendLayer(byte dendNum, byte ltGain, byte stLeak, byte cellInps, byte reinforce = 0) { // Initialize the dendrites, cell and the short-term and long-term weight arrays (you'll see what the weights do soon) byte dends[dendNum]; byte cells[cellInps]; byte stw[dendNum]; byte ltw[dendNum]; // Iterate over the dendrite, cell input, and weight arrays for (byte d = 0; d <= dends[dendNum]; d++) { for (byte c = 0; c <= cells[cellInps]; c++) { for (byte sw = 0; sw <= stw[dendNum]; sw++) { // Calculate the current dendrite value dends[d] += cells[c] * (stw[sw] / 255); for (byte lw = 0; lw <= ltw[dendNum]; lw++) { // Now we compute the short-term weights' values. The 'ltw' variable increases towards the higher-but-decreasing // value of the short-term weight. When the faster short-term weight reaches the same value as the slower long-term weight, // they both stop, making their shared value the dendrite's final weight // Start decreasing the short-term weight stw[sw] -= stLeak; // Start increasing the long-term weight ltw[lw] += ltGain; // Calculate the current short-term weight value stw[sw] = ltw[lw] + (cells[c] / 255) * reinforce; // If 'stw' and 'ltw' share the same value, then make that value the final weight if (stw[sw] == ltw[lw]) { stw[sw] = ltw[lw]; } } } } } return dends[dendNum]; } // Set up the servo Servo testServo; void setup() { // Set the servo to pin 9 testServo.attach(9); // Put the servo into the starting position testServo.write(80); } void loop() { // Set up the cell and dendrite variables byte inpCells = 9; byte outDends = 9; byte thres = 105; byte cellLeak = 1; byte cellRest = 5; byte dendGain = 2; byte dendLeak = 5; // Now start processing! byte inpLayer = cellLayer(inpCells, thres, cellLeak, cellRest, analogRead(0), analogRead(3), analogRead(5)); byte outLayer = dendLayer(outDends, dendGain, dendLeak, inpLayer, analogRead(9)); // Make sure the output value doesn't go outside the servo's movement range if (outLayer > 158) { outLayer = 158; } else if (outLayer < 2) { outLayer = 2; } // Finally, the servo "chooses" when to move based on the neuron/dendrite layer's output! for (byte s = 2; s <= outLayer; s++) { // Use the output values to determine which direction the servo moves in if (outLayer < 80) { testServo.write(-s); } else { testServo.write(s); } } }