Here I will be explaining the Pixy camera vision sensor along with the PixyMon software, wiring the stepper motor drivers, and coding the Arduino to read the data from the Pixy and map it to run the stepper motors. I used an Arduino UNO, 2 NEMA 17 Stepper Motors, and 2 EasyDrivers by Sparkfun.
Pixy CMUCam5
Pixy is a camera vision sensor and started out as a kickstarter product. More info here.
- The Pixy camera can process upto 50 frames per second.
- Since image sensors output lots of data, the Pixy pairs a powerful dedicated processor with the image sensor and sends only useful information to the microprocessor, which makes it have plenty of processing power available for other tasks.
- Connects to Arduino, Raspberry Pi, Beaglebone and supports C/C++ and Python.
- Pixy uses a color-based filtering algorithm to detect objects. It calculates the color (hue) and saturation of each RGB pixel from the image sensor and uses these as the primary filtering parameters. Pixy’s filtering algorithm is robust when it comes to lighting and exposure changes.
First I downloaded the Pixymon software, firmware, and Arduino library. If a new firmware is available, the pixy needs to be updated. To update the firmware,
- Open the Pixymon software,
- Press and hold the white button located on the top of the Pixy while plugging in USB.
- After plugging in, let go.
- Pixy programming state detected.
- A dialog box will appear, select the firmware.hex file downloaded earlier.
- All done!!
Next step is teaching the Pixy to track an object. Pixy can learn seven color signatures, numbered 1-7. Color signature 1 is the default signature. To teach Pixy the other signatures (2-7) requires a simple button pressing sequence.
- Press and hold the white button and release when the LED turns red (first signature). Signature 1 – 7 LED color:
- Red
- Orange
- Yellow
- Green
- Cyan
- Blue
- Violet
- Bring the object (hue) in front of the camera.
- When the grid is perfect, press and release the white button once.
- The object is then locked in place.
Then hooking the Pixy with Arduino is very simple.
- Connect the 10pin to 6pin cable from the Pixy to the Arduino.
- Import the Pixy library to Arduino.
- Open example code and upload.
So for my project, I 3D modeled a ring and printed with a bright orange PLA filament. This is for the user to wear when waving their hand on top of the tank filled with ferrofluid. The Pixy will track this ring because of its color and send information to the Arduino.
Stepper Motor & EasyDriver
Stepper motors are perfect for automation or any time you need a motor to turn to a specific point, at a specific speed, in a specific direction.
The EasyDriver does something called micro stepping. It breaks down that minimum step into smaller micro steps (in this case 8 micro steps per step). Microstepping allows for smoother and more accurate control, but that means that your 200 step stepper, connected to the EasyDriver needs 1600 ( 200 * 8 ) steps to make a full rotation. More info on the EasyDriver here.
Hooking it up with the Arduino (schematic below), I used a 12V 1A power adapter to run both motors.
The Arduino code to run the stepper motors is pretty simple. It sets up pin 8 and 9 as outputs. It sets them both low to begin with. Then in the main loop, it simply toggles pin 9 high and low, waiting 1ms between toggles. I used pin 9 as the STEP control and pin 8 as the DIRECTION control to the Easy Driver.
void setup() {
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
void loop() {
digitalWrite(9, HIGH);
delay(1);
digitalWrite(9, LOW);
delay(1);
}
Then combining these two (Pixy and stepper motor) required some algorithm. Here is a working code I worked on for this project. I must say that it is not accurate, but it fairly works. You can see some of the videos on the RESULTS and TEST page.
I wrote some functions to run the motors in different directions and applied it to the data received from the Pixy.
#include <SPI.h>
#include <Pixy.h>
// This is the main Pixy object
Pixy pixy;
int dirA = 8; //x-axis
int stepA = 9; //pwm
int dirB = 7; //y-axis
int stepB = 6; //pwm
int new_posX = pixy.blocks[0].x; //default
int old_posX = new_posX;
int cur_posX = 0;
int loopX = 0;
bool change_posX = false;
int new_posY = pixy.blocks[0].y; //default
int old_posY = new_posY;
int cur_posY = 0;
int loopY = 0;
bool change_posY = false;
void setup() {
pinMode(dirA, OUTPUT);
pinMode(stepA, OUTPUT);
pinMode(dirB, OUTPUT);
pinMode(stepB, OUTPUT);
digitalWrite(dirA, LOW);
digitalWrite(stepA, LOW);
digitalWrite(dirB, LOW);
digitalWrite(stepB, LOW);
pixy.init(); //starts the Pixy
}
void loop()
{
static int i = 0;
int j;
uint16_t blocks;
// grab blocks!
blocks = pixy.getBlocks();
if (blocks) //if blocks are detected
{
i++;
if (i % 1 == 0) //this prints data every frame
{
for (j = 0; j < blocks; j++)
{
old_posX = new_posX;
new_posX = pixy.blocks[0].x;
cur_posX = new_posX – old_posX;
loopX = abs(cur_posX * 8);
old_posY = new_posY;
new_posY = pixy.blocks[0].y;
cur_posY = new_posY – old_posY;
loopY = abs(cur_posY * 15);
if (new_posX != old_posX) {
change_posX = true;
}
else if (new_posX == old_posX) {
change_posX = false;
}
else if (new_posY != old_posY) {
change_posY = true;
}
else if (new_posY == old_posY) {
change_posY = false;
}
if (change_posX == true || change_posY == true) {
if (cur_posX > 10 && cur_posY > 3 && new_posX > 0 && new_posX < 160 && new_posY > 0 && new_posY < 100)
{
runBothMotorForw(loopX, dirA, stepA, loopY, dirB, stepB); //left down
change_posX = false;
change_posY = false;
}
else if (cur_posX < -10 && cur_posY < -3 && new_posX > 160 && new_posX < 300 && new_posY > 100 && new_posY < 200)
{
runBothMotorBack(loopX, dirA, stepA, loopY, dirB, stepB); //right up
change_posX = false;
change_posY = false;
}
else if (cur_posX > 10 && new_posX > 0 && new_posX < 160 ) {
runMotorForw(loopX, dirA, stepA); //left
}
else if (cur_posX < -10 && new_posX > 160 && new_posX < 300) {
runMotorBack(loopX, dirA, stepA); //right
}
else if (cur_posY > 3 && new_posY > 0 && new_posY < 100) {
runMotorForw(loopY, dirB, stepB); //down
}
else if (cur_posY < -3 && new_posY > 100 && new_posY < 200) {
runMotorBack(loopY, dirB, stepB); //up
}
else
{
change_posX = false;
change_posY = false;
stopMotor();
}
}
else {
stopMotor();
}
}
}
}
else {
stopMotor();
}
}
void runMotorForw(int _loop, int _dir, int _step) {
for (int i = 0; i < _loop; i++) { //200 steps per revolution
digitalWrite(_dir, HIGH);
digitalWrite(_step, HIGH);
delay(1);
digitalWrite(_step, LOW);
}
}
void runMotorBack(int _loop, int _dir, int _step) {
for (int i = 0; i < _loop; i++) {
digitalWrite(_dir, LOW);
digitalWrite(_step, HIGH);
delay(1);
digitalWrite(_step, LOW);
}
}
void runBothMotorForw(int _loop1, int _dir1, int _step1, int _loop2, int _dir2, int _step2) {
for (int i = 0; i < _loop1 && i < _loop2; i++) {
digitalWrite(_dir1, HIGH);
digitalWrite(_dir2, HIGH);
digitalWrite(_step1, HIGH);
digitalWrite(_step2, HIGH);
delay(1);
digitalWrite(_step1, LOW);
digitalWrite(_step2, LOW);
}
}
void runBothMotorBack(int _loop1, int _dir1, int _step1, int _loop2, int _dir2, int _step2) {
for (int i = 0; i < _loop1 && i < _loop2; i++) {
digitalWrite(_dir1, LOW);
digitalWrite(_dir2, LOW);
digitalWrite(_step1, HIGH);
digitalWrite(_step2, HIGH);
delay(1);
digitalWrite(_step1, LOW);
digitalWrite(_step2, LOW);
}
}
void stopMotor() {
digitalWrite(dirA, LOW);
digitalWrite(stepA, LOW);
digitalWrite(dirB, LOW);
digitalWrite(stepB, LOW);
}