Skip to content

Commit e84ff84

Browse files
New examples added
1 parent 24490d5 commit e84ff84

File tree

6 files changed

+322
-24
lines changed

6 files changed

+322
-24
lines changed

examples/analogIn/analogIn.ino

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
/*
2-
* An example where a single neuron is driven by analog input (acting like the
3-
* pre-synaptic current coming into the neuron's axon.
2+
* Neurons - analogIn
3+
*
4+
* This example shows how to drive a single neuron independent of a network, here
5+
* only the Neuron class is used) through an incoming analog input voltage.
6+
*
7+
* All other example involving networks use neurons driven by 'spiking' sensors.
8+
* These sensors cause digital pin changes at the neurons' assigned input pins and
9+
* are detected using pin change interrupts.
10+
*
411
*/
512

6-
713
#include <Neurons.h>
814

9-
int neuronOutputPin = 9;
10-
int neuronInputPin = A1;
15+
int dt = 100; //in milliseconds
16+
int neuronOutputPin = 9; //you can visualize the neuron's response by connecting an LED to this pin
17+
int neuronInputPin = A1; //this is where the input will come in from
1118
Neuron testNeuron(neuronInputPin, neuronOutputPin);
1219

1320
void setup(){
@@ -17,10 +24,11 @@ void setup(){
1724
}
1825

1926
void loop(){
27+
testNeuron.inputCurrent = analogRead(testNeuron.inputPin)/3; //You can change the scaling here from 1/3 to something else for a different neuron sensitivity
2028

21-
testNeuron.inputCurrent = analogRead(testNeuron.inputPin)/3;
22-
23-
testNeuron.calculateMembranePotential((float)testNeuron.dt);
29+
testNeuron.calculateMembranePotential((float)dt);
2430
testNeuron.LED_Output(testNeuron.outputPin);
25-
delay(testNeuron.dt);
31+
delay(dt);
2632
}
33+
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Neurons - connectivityMatrix
3+
*
4+
* This is a four neuron network. Two sensory, two motor.
5+
*
6+
* It implements a kind of filter over the previous twoNeuronNetwork. Whereas in that
7+
* example, the two sensory neurons acted directly like motor neurons, here, the sensory
8+
* neurons aren't used directly. The connectivity matrix implements a kind of linear
9+
* combination of the sensory neuron spikes, which drive motor neurons.
10+
*
11+
* The motor neurons' outputs aren't converted to motor commands in this example, for the sake
12+
* of clarity. But, the 'Visualize' function (in 'void loop()') allows you to get an idea of
13+
* the spiking behaviour by interfacing LEDs to the default output pins.
14+
*
15+
*/
16+
17+
18+
#include <Neurons.h>
19+
#include <Network.h>
20+
21+
22+
//Network variables
23+
int sensoryNeuronIDs[2] = {0,1};
24+
int connectivity[16] = {0 , 0 , 100,-800, // Note that the matrix is actually one-dimensional, it's expanded here for clarity.
25+
0 , 0 ,-800, 100, // The rows correspond to the spiking neuron and the columns the neurons on the reciving end.
26+
0 , 0 , 0 , 0 , // You could, in principle, implement a fully connected network with this matrix.
27+
0 , 0 , 0 , 0};
28+
29+
30+
Network testNetwork(4,connectivity); // The constructor takes the connectivity matrix as an input too, if you have one
31+
32+
33+
//Interrupt variables
34+
static volatile bool pins[2] = {0,1};
35+
36+
37+
void setup(){
38+
Serial.begin(9600);
39+
testNetwork.dt = 5;
40+
testNetwork.setSensoryNeurons(sensoryNeuronIDs, 2);
41+
}
42+
43+
void loop() {
44+
testNetwork.updateNeurons();
45+
testNetwork.issueSpikes();
46+
47+
testNetwork.Visualize();
48+
delay(testNetwork.dt);
49+
}
50+
51+
52+
ISR(PCINT1_vect){
53+
if(digitalRead(A0)!=pins[0]){
54+
testNetwork.Neurons[0].inputCurrent += 2000;
55+
pins[0] = digitalRead(A0);
56+
}
57+
if(digitalRead(A1)!=pins[1]){
58+
testNetwork.Neurons[1].inputCurrent += 2000;
59+
pins[1] = digitalRead(A1);
60+
}
61+
}
62+
63+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Neurons - singleNeuronNetwork
3+
*
4+
* This example illustrates the use of a network to drive motor outputs. This particular
5+
* network contains only one neuron, and serves as an example to make the transition from single
6+
* neurons to connected networks easier.
7+
*
8+
* The one neuron is both sensory and motor. It takes input from the default input pin for
9+
* neuron 0 (A0, set for Pin Change Interrupt); it sends output spikes from the default output
10+
* pin for neuron 0 (pin 7). For a complete list of these default pins, see lines 11(default
11+
* output) and 12 (default input) of Network.cpp.
12+
*
13+
*/
14+
15+
#include <Neurons.h>
16+
#include <Network.h>
17+
18+
19+
//Network variables
20+
Network testNetwork(1); // Network constructor.
21+
int sensoryNeuronIDs[1] = {0}; // The array should contain the indexes of the sensory neurons. Useful later, when we 'setSensoryNeurons'.
22+
23+
24+
//Interrupt variables
25+
/* Sensory input is best taken in asynchronously (all other neurons' membrane
26+
* potentials and spikes are updated synchronously).
27+
* Since the sensory neurons expect inputs at the PinChange Interrupt Pins, we
28+
* need the following boolean array to check the state of the sensory pins.
29+
*/
30+
static volatile bool pins[1] = {0};
31+
32+
33+
//Output Control Variables
34+
/* It helps to specify the indices of the neurons whose spikes are supposed to cause motor
35+
* output. For example, the zeroeth neuron (the only neuron in this network), is
36+
* supposed to cause the robot to move forward:
37+
* The boolean 'commandSent' variable is required to ensure that the command is
38+
* sent only once per spike.
39+
*/
40+
int forwardNeuron = 0;
41+
volatile boolean commandSent=1;
42+
43+
44+
void setup(){
45+
Serial.begin(9600);
46+
47+
//Network stuff
48+
testNetwork.dt = 5; //Update rate of the network (in milliseconds)
49+
testNetwork.setSensoryNeurons(sensoryNeuronIDs, 1); // Using the array from above to know which pins are sensory
50+
51+
/* Motor output will be sent from thes pins. In this case, the pins were
52+
* connected to an L293D motor driver IC, and the pins and values are set
53+
* accordingly. You may need to change this according to your setup.
54+
*/
55+
//Left wheel
56+
pinMode(8,OUTPUT); digitalWrite(8,LOW);
57+
pinMode(9,OUTPUT); digitalWrite(9,LOW);
58+
//Right Wheel
59+
pinMode(10,OUTPUT); digitalWrite(10,HIGH);
60+
pinMode(11,OUTPUT); digitalWrite(11,HIGH);
61+
}
62+
63+
void loop() {
64+
/*
65+
* Every iteration of the loop indicates one time-step. Within each time-step,
66+
* a number of tasks occur, each with it's own dedicated code block in the code
67+
* below:
68+
*
69+
* 1. The neurons' membrane potentials are updated, and if spikes occur
70+
* then they are relayed throughout the network.
71+
* 2. The requisite instructions are issued, according to the spiking of the motor
72+
* neurons.
73+
* 3. The outputs of the neurons are sent though the output pins, from where they can
74+
* be visualized on LEDs. This is an optional step.
75+
*
76+
* The neuron's input pins are continuously monitored in the background, triggering the interrupt
77+
* routine whenever a change occurs on any one of them.
78+
*
79+
*/
80+
81+
82+
/* This code block is where the synchronous update of the neurons' membrane potentials
83+
* and spikes takes place. This part of the code remains pretty much constant.
84+
*/
85+
testNetwork.updateNeurons();
86+
testNetwork.issueSpikes();
87+
88+
/* This block ensures that a command sent as a response to a motor neuron spike issued in the previous
89+
* time-step doesn't persist into the next time-step.
90+
* The last if-statement is is where the commands are actually sent, according to the spiking
91+
* of the motor neurons.
92+
*/
93+
if(commandSent){
94+
digitalWrite(8,LOW);
95+
digitalWrite(9,LOW);
96+
digitalWrite(10,LOW);
97+
digitalWrite(11,LOW);
98+
commandSent=0;
99+
}
100+
if(testNetwork.Neurons[forwardNeuron].spike) moveForward();
101+
102+
/* The 'Visualize' function is used to send the required outputs to the default output pins
103+
* of the neurons (these default pins are assigned according to the neuron index, see line
104+
* 11 of Network.cpp)
105+
*/
106+
testNetwork.Visualize();
107+
108+
/* This delay is actually very important. It allows for the sensory neurons to 'accumulate'
109+
* input current from their input pins before the next calculation of the nest time-step commence.
110+
*/
111+
delay(testNetwork.dt);
112+
}
113+
114+
115+
/* This interrupt routine relays asynchronous spikes coming in at the analogPins to the sensoryNeurons.
116+
* Notice the input pin is A0 - the default for neuron index 0. This is another part of the code that
117+
* remains pretty much constant. The same block repeats with more neurons.
118+
*/
119+
ISR(PCINT1_vect){
120+
if(digitalRead(A0)!=pins[0]){
121+
testNetwork.Neurons[0].inputCurrent += 2000;
122+
pins[0] = digitalRead(A0);
123+
}
124+
}
125+
126+
/* This function issues the instructions required by the associated motor neuron.
127+
* In this case, that would be to make the robot turn right.
128+
*/
129+
void moveForward(){
130+
digitalWrite(8,LOW);
131+
digitalWrite(9,HIGH);
132+
digitalWrite(10,LOW);
133+
digitalWrite(11,HIGH);
134+
commandSent=1;
135+
}
136+
137+
138+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Neurons - twoNeuronNetwork
3+
*
4+
* This example uses two sensory-motor Neurons.
5+
*
6+
* One sound sensor is mounted on the left side of the robot, the other on the right.
7+
* The two neurons are interfaced with the sound sensors, and also act as motor neurons.
8+
* When the right sensor spikes, the robot turns right. When the left one spikes the
9+
* robot turns left.
10+
*
11+
* Though significantly different in terms of functionality, the code is almost the same
12+
* as in the single neuron case. Code blocks that are different from the singleNeuronNetwork
13+
* example are marked with a '//!!//'.
14+
*
15+
*/
16+
17+
#include <Neurons.h>
18+
#include <Network.h>
19+
20+
//!!//
21+
//Network variables
22+
int sensoryNeuronIDs[2] = {0,1};
23+
Network testNetwork(2);
24+
25+
//!!//
26+
//Interrupt variables
27+
static volatile bool pins[2] = {0,1};
28+
29+
//!!//
30+
//Output Control Variables
31+
int leftTurnNeuron = 0, rightTurnNeuron = 1;
32+
volatile boolean commandSent=1;
33+
34+
void setup(){
35+
Serial.begin(9600);
36+
37+
//!!//
38+
//Network stuff
39+
testNetwork.dt = 5;
40+
testNetwork.setSensoryNeurons(sensoryNeuronIDs, 2);
41+
42+
//Left wheel
43+
pinMode(8,OUTPUT); digitalWrite(8,LOW);
44+
pinMode(9,OUTPUT); digitalWrite(9,LOW);
45+
//Right Wheel
46+
pinMode(10,OUTPUT); digitalWrite(10,HIGH);
47+
pinMode(11,OUTPUT); digitalWrite(11,HIGH);
48+
}
49+
50+
void loop() {
51+
testNetwork.updateNeurons();
52+
testNetwork.issueSpikes();
53+
54+
//!!//
55+
if(commandSent){
56+
digitalWrite(8,LOW);
57+
digitalWrite(9,LOW);
58+
digitalWrite(10,LOW);
59+
digitalWrite(11,LOW);
60+
commandSent=0;
61+
}
62+
if(testNetwork.Neurons[rightTurnNeuron].spike) turnRight();
63+
if(testNetwork.Neurons[leftTurnNeuron].spike) turnLeft();
64+
65+
testNetwork.Visualize();
66+
delay(testNetwork.dt);
67+
}
68+
69+
70+
//!!//
71+
ISR(PCINT1_vect){
72+
if(digitalRead(A0)!=pins[0]){
73+
testNetwork.Neurons[0].inputCurrent += 2000;
74+
pins[0] = digitalRead(A0);
75+
}
76+
if(digitalRead(A1)!=pins[1]){
77+
testNetwork.Neurons[1].inputCurrent += 2000;
78+
pins[1] = digitalRead(A1);
79+
}
80+
}
81+
82+
void turnRight(){
83+
digitalWrite(8,LOW);
84+
digitalWrite(9,HIGH);
85+
digitalWrite(10,LOW);
86+
digitalWrite(11,HIGH);
87+
commandSent=1;
88+
}
89+
90+
//!!//
91+
void turnLeft(){
92+
digitalWrite(8,HIGH);
93+
digitalWrite(9,LOW);
94+
digitalWrite(10,HIGH);
95+
digitalWrite(11,LOW);
96+
commandSent=1;
97+
}
98+
99+
100+

src/Network.cpp

+2-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#define MAX_NEURONS 6
1010

11-
const int Network::defaultOutPins[MAX_NEURONS] = {7, 4, 2, 1, 0, 6};
11+
const int Network::defaultOutPins[MAX_NEURONS] = {7, 6, 5, 4, 3, 2};
1212
const int Network::defaultInputPins[MAX_NEURONS] = {A0, A1, A2, A3, A4, A5};
1313
int Network::dt;
1414
/*CONSTRUCTORS*/
@@ -106,14 +106,8 @@ void Network::setSensoryNeurons(int* sensoryNeuronIDs, int sensoryNeuronCount_){
106106
};
107107

108108
void Network::updateNeurons(){
109-
static int presentTime = micros();
110-
111109
for(int i=0; i<neuronCount; i++){
112-
presentTime = micros();
113-
Neurons[i].calculateMembranePotential(dt);//((float)((presentTime-initTimes[i])/1000));
114-
//Serial.println((float)((presentTime-initTimes[i])/1000));
115-
//Serial.println(Neurons[i].membranePotential); Serial.println();
116-
//times[i] = presentTime;
110+
Neurons[i].calculateMembranePotential(dt);
117111
}
118112

119113
};

src/Neurons.h

+2-7
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,9 @@ class Neuron{
1212
float Vspike;
1313

1414
public:
15-
/*
16-
* Only sensory neurons have an inputPin.
17-
* Every neuron toggles it's outputPin when it spikes.
18-
* The outputPin is connectected to the spikePin.
19-
* spikePins trigger pinChangeInterrupts that cause the downstream neurons to increment their currents.
20-
*/
2115
int inputPin, outputPin;
22-
float inputCurrent, membranePotential;
16+
float membranePotential;
17+
long int inputCurrent;
2318
boolean spike;
2419

2520
Neuron();

0 commit comments

Comments
 (0)