## DEV Community

Erick Andrés Obregón Fonseca

Posted on

# Introduction

Artificial Intelligence has become more and more important in now-days. In this case, I'll show you how to implement a Perceptron inside a microcontroller. Albeit I'm doing this for an Arduino Uno, you can follow these step to do it in any other microcontroller, like ATmega, PIC, STM, and so on.

# What is a Microcontroller (MCU)?

You can see a Microcontroller Unit or MCU as a small computer embedded in an integrated circuit (IC) chip. A Microcontroller could have one or more Central Processing Unit (CPU), a system clock, memory, peripherals (inputs/outputs), and so on. While computers can execute multiple tasks at the same time, MCUs are typically dedicated to a specific task.

# What is a Perceptron?

The Perceptron Algorithm was created in 1957 by Frank Rosenblatt. This was the forerunner of modern neural networks, our grandpa or granny.

The Perceptron is based on the mathematical abstraction of a neuron at the biological level. We've got a serial of inputs m̄ = [m0 m1 ... mD]T which are linearly combined by a vector of weights (or synaptic bonds) w̄ = [w0 w1 ... wD]T in the network function:

$\gamma (m) = \sum_{i=0}^D m_i w_i = \vec{w}^T \vec{m}$

A neuron is excited according to an activation function f(γ), which receives as input the result of the network function γ, and can correspond, for example, to the signum function:

In this case, our simple Perceptron will solve a classification problem for K=2, where K is the number of classes. The classification problem aims to assign a label (class) to an input of size n. You can view this as if someone has given you pictures of dogs, cats, rabbits, and so on. and you would have the task of assigning a class to each photo.

The vector of weights will be created with random values and will be updated if the classification was incorrect. I won't explain how this all works in-depth as it would be out of my goal, but you can read the original paper: The perceptron: a probabilistic model for information storage and organization in the brain by Frank Rosenblatt or this post written by smakosh.

# Perceptron Code

class Perceptron
{
public:
// Inputs vector
float* inputs;

// Perceptron constructor
// int ninputs - number of inputs
Perceptron(int ninputs)
{
// Default learning rate
alpha = 0.001;
// Set the number of inputs
n = ninputs + 1;

// Asking for memory
inputs = new float[n];
weights = new float[n];

// Bias
inputs[n - 1] = 1;
randomize();
}

// Random weights
void randomize()
{
for (int i = 0; i < n; i++)
{
weights[i] = (float) random(-1000, 1000) / 1000.0;
}
}

// Training function
void train(int T, float predicted)
{
float error = T - predicted;

// Update weights
for (int i = 0; i < n; i++)
{
weights[i] += alpha*error*inputs[i];
}
}

// Forward function
int feed_forward()
{
float sum = 0.0;

for (int i = 0; i < n; i++)
{
sum += inputs[i]*weights[i];
}

return activate(sum);
}

private:
// Random weights vector
float* weights;
// Inputs number
int n;
// Learning rate
float alpha;

// Activation function
int activate(float sum)
{
// Signum function
return (sum >= 0) ? 1:-1;
}
};


# Circuit Schema

In this example, we will train our model to classify a distance into near (-1) and far (1). This distance will be taken by the ultrasonic sensor HC-SR04 and we will say that any distance that is greater than 150cm will be far and for values less than or equal to 150cm we will say that it is near. To indicate this, when the distance is far we will turn on the red LED and when it is near the green LED.

# Arduino Code

Here we've got the code. We have trained our Perceptron before to predict to see the results quickly. You can also train the model by adding a button to tell it when has made a wrong prediction and needs to learn, so it can learn what is happening.

#include "perceptron.h"

// Constant values
const int ECHO_PIN = 5;
const int TRIGGER_PIN = 6;
const int GREEN_LED_PIN = 12;
const int RED_LED_PIN = 11;

// Vars
float distance;
long time;

// Our perceptron
Perceptron perceptron(1);

// Train the perceptron
void train()
{
int distance_step = 5;

// Train for 160 epochs
for (int epoch = 0; epoch < 160; epoch++)
{
for (int d = 0; d < 340; d += distance_step)
{
// Set the input in the model
perceptron.inputs[0] = d;

// Make a prediction
int predicted = perceptron.feed_forward();

if ((d <= 150 && predicted == -1) || (d > 150 && predicted == 1))
{
perceptron.train(-predicted, predicted);
}
}
}
}

void setup()
{
Serial.begin(9600);

// Set the pins
pinMode(TRIGGER_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);

// Train the model before
train();
}

void loop()
{
// Send a pulse to activate the sensor
digitalWrite(TRIGGER_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER_PIN, LOW);

// Measure response pulse
time = pulseIn(ECHO_PIN, HIGH)/2;

// Calculate the distance in cm
// d = v * t
// Speed of sound = 343m/s
// Time is microseconds
distance = float(time*0.0343);
Serial.print("Distance: ");
Serial.println(distance);

// Set the input in the model
perceptron.inputs[0] = distance;

// Classify
int predicted = perceptron.feed_forward();

Serial.print("Predicted: ");
Serial.println(predicted);

if (predicted == -1)
{
digitalWrite(GREEN_LED_PIN, LOW);
digitalWrite(RED_LED_PIN, HIGH);
}
else
{
digitalWrite(GREEN_LED_PIN, HIGH);
digitalWrite(RED_LED_PIN, LOW);
}

delay(1000);
}


# Training Results

We can see how the green LED lights up with a distance of 74.7cm, and the red LED with a distance of 244.9cm.

You can also play with the model in TinkerCAD.

# Final thoughts

The Perceptron convergence theorem states that the vector of weights converges after a finite number of iterations, even when a change in such vector involves one or more new errors in samples that were correctly classified in previous iterations. This only if the set of samples to be classified is linearly separable. In this way, a simple Perceptron is not enough for complex problems, this is where I introduce the Multilayer Perceptron, but this is a topic for another post.