
Pixy cmucam 5
September of last year I backed the Pixy CMUcam5 project on kickstarter. Back then the people behind the project (Charmed labs) promised a November delivery date. With over 2000 people backing the project things turned out a bit differently. I only received my Pixy last week. I also had the bad luck that the package was stuck at the Belgian customs, and I had to pay another 25€ import tax and VAT to receive my Pixy 🙁
The Pixy is a fast vision sensor that you can quickly “teach” to find objects. To do that the CMUcam 5 uses an Omnivision OV9715 image sensor and a NXP LPC4330, 204 MHz, dual core micro controller. The image sensor is fitted with a replaceable M12 lens. It comes with firmware and software to track objects based on color signatures. Pixy uses a hue-based color filtering algorithm to detect objects. Pixy remembers up to 7 different color signatures, so you can teach it to track 7 different objects with unique colors. It does this at a frame rate of 50 Hz. That’s pretty fast.

Pixy cmucam 5
Pixy comes with a cable to connect it to the ICSP connector of an Arduino. In the box there was also a small bag with some screws and brackets. All the connectors are on the back of the board. There is a mini USB connector to hook it up to your PC. A 2 pin connector to power it, a 6 pin connector to hook up 2 servos. The most interesting connector for me is the 10 pin connector. This connector provides I2C, SPI and serial connections. So you can hook it up to whatever micro controller you want, the pins are 5V tolerant. I couldn’t find the pin out of the 10 pin connector on the Pixy wiki so I carefully did some testing to find out the pin out.

Pixy io connector pinout
Pin 1 = SPI MISO, UART RX, GPIO0 Pin 2 = 5V (in or out)
Pin 3 = SPI SCK, DAC out, GPIO1 Pin 4 = SPI MOSI, UART TX, GPIO2
Pin 5 = I2C SCL Pin 6 = GND
Pin 7 = SPI SS, ADC in, GPIO3 Pin 8 = GND
Pin 9 = I2C SDA Pin 10 = GND
Pixy comes with software to configure and set it up, Pixymon. You can download it from the CMUcam website. To install it on my Gentoo GNU/Linux workstation I had to modify the install script. I changed the “qmake-qt4” to qmake. The linux section in the buildpixymon.sh file should look like this:
if [[ “$platform” == ‘linux’ ]]; then
qmake pixymon.pro
make -w
cd ../../..
mkdir bin
cp src/host/pixymon/PixyMon bin
strip bin/PixyMon
cp src/host/pixymon/pixyflash.bin.hdr bin
fi
I installed the software as a regular user and fired it up without further problems. I then connected the Pixy to my PC with a mini USB cable.
There are 2 ways of teaching Pixy an object. You can just press the button on the Pixy and hold an object in front of it. Or you can teach it an object through the Pixymon software. In my experience it’s more accurate when using Pixymon. It’s also much handier when you want to teach it multiple objects. The software is very simple to use. In minutes I had the Pixy tracking on object. I was pretty amazed by the simplicity of the process.
Pixy has a connector to hook up 2 servos, as you can also buy a pan/tilt kit with it. I already have several cheap pan/tilt kits so I didn’t order one. The Pixymon software lets you set up the servos.
I wanted to learn how to process the data from the Pixy and decided not to use the servo outputs of the Pixy but let an Arduino control the servos. I used I2C to let the Pixy talk to an ATmega1284P. The Pixy outputs data in the form of one array per detected object. Each array contains the x and y position of the center of the object and the width and height of the object. This makes it very straightforward to process the data.
Some time ago I made a small robot with a WiiCamera sensor. The data it outputs is quite similar to the Pixy. So I dusted of my little robot and replaced the WiiCamera with the Pixy. I made some quick code changes, the result you can see in the following video:
The robot uses an ATmega1284 micro controller with an Arduino bootloader. The motors are controlled by an L293D H-bridge. The motors are fitted with encoders. This is the code I used for the video:
// http://wwww.bajdi.com // Robot with 2 geared DC motors controlled by L293D, µCU = ATmega1284P-PU // Pixy tracking object, communicates via I2C with µCU // Pixy mounted on pan/tilt kit, 2 SG90 servos // Robot will try to follow the object // Pixy data: // block 1 X = 0 object detected on the left // block 1 X = 319 object detected on the right // block 1 Y = 0 object detected top // block 1 Y = 199 object detected bottom // block 1 width = 1 to 320 // block 1 height = 1 to 200 #include <Servo.h> #include <Wire.h> #include <PixyI2C.h> PixyI2C pixy; uint16_t blocks; unsigned int objectSize; unsigned long pixyTime; Servo panServo; Servo tiltServo; const int panServoLeft = 2100; const int panServoCenter = 1450; const int panServoRight = 750; int panServoPos; const int tiltServoUp = 2000; const int tiltServoCenter = 1300; const int tiltServoDown = 900; int tiltServoPos; const int EN1 = 3; //pwm, enable motor 1 = pin 1 of L293D const int direction1 = 13; //direcion motor 1 = pin 2 of L293D const int EN2 = 14; //pwm, enable motor 2 = pin 9 of L293D const int direction2 = 15; //direction motor 2 = pin 15 of L293D byte speedLeft = 100; byte speedRight = 100; const int lEncoder = 10; // interrupt 0 const int rEncoder = 11; // interrupt 1 const int motorTime = 100; unsigned long encoderTime; volatile unsigned long lpulse = 121; // width of left and right encoder pulses in uS volatile unsigned long rpulse = 121; // width of left and right encoder pulses in uS volatile unsigned long ltime; // remembers time of left encoders last state change in uS volatile unsigned long rtime; // remembers time of right encoders last state change in uS void setup() { Serial.begin(57600); pinMode(EN1, OUTPUT); pinMode(direction1, OUTPUT); pinMode(EN2, OUTPUT); pinMode(direction2, OUTPUT); analogWrite(EN1, 0); analogWrite(EN2, 0); digitalWrite(lEncoder,1); // enable pullup resistor for left encoder sensor digitalWrite(rEncoder,1); attachInterrupt(0,Lencoder,CHANGE); // trigger left encoder ISR on state change attachInterrupt(1,Rencoder,CHANGE); // trigger right encoder ISR on state change delay(50); panServo.attach(22); panServoPos = panServoCenter; panServo.writeMicroseconds(panServoPos); tiltServo.attach(23); tiltServoPos = tiltServoCenter; tiltServo.writeMicroseconds(tiltServoPos); delay(500); } void loop() { if (millis() - encoderTime >= 5) { encoderTime = millis(); int lastPulseL = millis() - ltime; // Check how much time has passed since last pulse int lastPulseR = millis() - rtime; if (lastPulseL > motorTime && speedLeft < 170) // If the pulse is longer then the time we want speed up motor { speedLeft = speedLeft+2; } if (lastPulseL < motorTime && speedLeft > 0) // // If the pulse is shorter then the time we want slow down motor { speedLeft = speedLeft-2; } if (lastPulseR > motorTime && speedRight < 170) { speedRight = speedRight+2; } if (lastPulseR < motorTime && speedRight > 0) { speedRight = speedRight-2; } } if (millis() - pixyTime >= 20) { pixyTime = millis(); blocks = pixy.getBlocks(); if (blocks) // object detected { objectSize = pixy.blocks[0].width * pixy.blocks[0].height; if(objectSize > 400) { if (pixy.blocks[0].x > 150 && panServoPos > panServoRight) { panServoPos -= 10; } if (pixy.blocks[0].x < 170 && panServoPos < panServoLeft) { panServoPos += 10; } if (pixy.blocks[0].y > 90 && tiltServoPos > tiltServoDown) { tiltServoPos -= 10; } if (pixy.blocks[0].y < 110 && tiltServoPos < tiltServoUp) { tiltServoPos += 10; } if (objectSize < 20000) // object far from robot { if(panServoPos < panServoCenter+200 && panServoPos > panServoCenter-200) { forward(); } if (panServoPos >= panServoCenter+200) { left(); } if (panServoPos <= panServoCenter-200) { right(); } } if (objectSize >= 20000) // object close to robot { if(panServoPos < panServoCenter+200 && panServoPos > panServoCenter-200) { backward(); } if (panServoPos >= panServoCenter+200) { left(); } if (panServoPos <= panServoCenter-200) { right(); } } } else { centerServo(); stop(); } } else { centerServo(); stop(); } panServo.writeMicroseconds(panServoPos); tiltServo.writeMicroseconds(tiltServoPos); Serial.print("pixy.blocks[0].x = "); Serial.println(pixy.blocks[0].x); Serial.print("pixy.blocks[0].y = "); Serial.println(pixy.blocks[0].y); Serial.print("pixy.blocks[0].width = "); Serial.println(pixy.blocks[0].width); Serial.print("pixy.blocks[0].height = "); Serial.println(pixy.blocks[0].height); Serial.print("objectSize = "); Serial.println(objectSize); } } void forward() { digitalWrite(direction1, HIGH); digitalWrite(direction2, HIGH); analogWrite(EN1, speedLeft); analogWrite(EN2, speedRight); } void stop() { digitalWrite(direction1, LOW); digitalWrite(direction2, LOW); analogWrite(EN1, 0); analogWrite(EN2, 0); } void backward() { digitalWrite(direction1, LOW); digitalWrite(direction2, LOW); analogWrite(EN1, speedLeft); analogWrite(EN2, speedRight); } void left() { digitalWrite(direction1, LOW); digitalWrite(direction2, HIGH); analogWrite(EN1, speedLeft); analogWrite(EN2, speedRight); } void right() { digitalWrite(direction1, HIGH); digitalWrite(direction2, LOW); analogWrite(EN1, speedLeft); analogWrite(EN2, speedRight); } //======================================================= ISR for left encoder ======================================================= void Lencoder() { lpulse=millis()-ltime; // time between last state change and this state change ltime=millis(); // update ltime with time of most recent state change //lcount++; } //======================================================= ISR for right encoder ====================================================== void Rencoder() { rpulse=millis()-rtime; // time between last state change and this state change rtime=millis(); // update ltime with time of most recent state change //rcount++; } void centerServo() { if (panServoPos > panServoCenter) // center pan servo { panServoPos -= 10; } if (panServoPos < panServoCenter) // center pan servo { panServoPos += 10; } if (tiltServoPos > tiltServoCenter) // center tilt servo { tiltServoPos -= 10; } if (tiltServoPos < tiltServoCenter) // center tilt servo { tiltServoPos += 10; } }
The Pixy is a fantastic and super easy to use vision sensor. If you’re a bit handy you can have a robot tracking an object in no time. At the moment the firmware only detects objects based on color. The makers of the Pixy are working on new firmware to track objects using color codes. That should make it possible to track more advanced objects. I did have some trouble with the camera, sometimes I could not get it to track an object. Luckily the Pixymon software lets you see what the camera is seeing. It was mainly caused by the bad lighting in my room, there was not enough color saturation in the object I wanted to track. You need a well lit space to make it a bit easier for the camera.
Hi Bajdi, very useful post. Have you been able to pair your pixy 5 with an arduino due board? If you have could you please give me some help with it. I keep getting errors.
Thanks loads.
There is a new library out that supports the Arduino Due: http://cmucam.org/projects/cmucam5/files
No, haven’t used it with a Due. The current Arduino library only supports AVR micro controllers. You’ll need to find a better programmer then me to change the library so it works with the Due…
Hello sir, I am glad I found your site, I have a pixy too but i did not know what else to with it beside pan and till that`s the only thing they show to do on their website. I want to ask you about your code, do you load this into arduino or to pixy board, and where do you connect the motors and servos, can you post a schematic with the wiring, I have one o those arduino cars like yours with H bridge.thank you so much.
The code runs on the Arduino. The Pixy talks to the Arduino over I2C. Sorry I don’t draw schematics of simple things like this…
hello
sorry to bother you again
I would like to know which is the pin out because my arduino uno does not have pin 14 or 15
const int EN1 = 3; //pwm, enable motor 1 = pin 1 of L293D
const int direction1 = 13; //direcion motor 1 = pin 2 of L293D
const int EN2 = 14; //pwm, enable motor 2 = pin 9 of L293D
const int direction2 = 15; //direction motor 2 = pin 15 of L293D
thank you.
I used an ATmega1284P in the video. Just change the pins to suit your Arduino, EN1 and 2 must be pwm pins. You will also need to change the encoder pins.
thank you so much for your help
where did you bought you encoder motors, my arduino car came whit the encoder wheels but no sensor,
I used a Dagu encoder kit and fitted it to the yellow motors. http://www.bajdi.com/adding-encoders-to-those-cheap-yellow-motors/
I`m waiting for encoders, thank you. I hope it will work, I will let you know.
hello again
just to let you know that my robot did not work,I can hear the servos trying to start, I change pins like for servos used 9 and 6 pwm
encoders to 10 and 11
EN1 3 EN2 5 pwm
derection 8 and 4
to my settings are
arduino car from ebay
arduino uno. microcontroller
L298n dual motor driver from ebay this is the link
http://www.ebay.com/itm/L298N-Dual-Stepper-Motor-Driver-Controller-Board-Module-5V-For-Arduino-Smart-Car-/181085286324?pt=LH_DefaultDomain_0&hash=item2a29862fb4
can you please help.
thank you
Encoders must use interrupt pins … Please read the interrupt documentation on the Arduino website. Don’t expect your servos to work with the code above. Have you changed the left/right/center, up/down/center to suit your servos, pan/tilt kit? Also how are you powering the servos? Do NOT power them from the Arduino, the voltage regulator can not provide enough current for 2 servos…
Hello
I just want to let you know that i will post a video soon about my build, got it to follow objects and run motors,i`m working on slowing motor down they running to fast. Bad guys broke in to my house and still my laptop that`s why it took long to build robot, thank you for the code. change microcontroller to arduino mega because it has more pins.
hello, how to get pixy library into arduino programmer?
Uh, just install the Arduino IDE and pixy library. Then you can upload the example sketches to your Arduino.
Hello and good work. I try to use your sketch on my own robot and i have a question about the line “const int motorTime = 100;” why 100? I also supose that “lpuse” and “rpulse” variables are not necessary since they do not appear in the main loop. So I can delete these variables in ISR, right?
The constant motorTime sets the speed of the motors (it’s the time between encoder pulses). The variables lpulse and rpulse are used in the interrupts (Lencoder and Rencoder). I suggest you do a bit of reading about interrupts to understand the code 🙂
In fact i did.Ok for ltime and rtime but lpulse and rpulse are set in the interrupts but are used nowhere else so what are they for. Do not take it for a critique just want to be sure to understand.
Oops 🙂 I used the lpulse and rpulse values for debugging. As you can see in this code: http://www.bajdi.com/adding-encoders-to-those-cheap-yellow-motors/
Ok thanks. And congratulations for your website very useful.
hello, i want to ask your favor. i have been exploring this pixy to be a sensor camera for my project which is multiple lines tracking robot. what is your opinion for pixy to detecting the line. is it the pixy can detect other than object? please i hope you can give some suggestion or opinion for this kind of problem what i supposed to do
I’m not sure if Pixy is the right “sensor” for your project. Maybe better if you ask your question on the Pixy forum. http://www.cmucam.org/projects/cmucam5/boards
hey i have a similar project, i am supposed to design and prototype a colour tracking robot with obstacle avoidance.. i would love to see your circuit for that robot.
I’m learning Pixy for a school project. This post has been super informative. Thanks!
I have two questions.
1. Can you give a short explanation of the code lines 78-101? I’m not sure what the pulse is, and why you’re checking it.
2. If there were two objects of the same color, the pixy has a hard time understanding what to do. I assume with the current code, the bot wouldn’t know where to go. Is it possible, using the pixy, to tell the bot to go toward the left most one or toward right most one?
The code between lines 78 and 101 controls the speed of the motors. My motors are fitted with encoders, by measuring the time between the encoder pulses I can determine the speed of the motors and make then turn faster/slower.
As for your second question, of course you could program something like that.
Hello, can this code work with arduino mega 2560?
Sure, just change the pin numbers.
admin,,, may I use AVR ATmega 32 to control this sensor?
thaks before..
Sure but you will need to write your own library or convert the Arduino library so it works with the ATmega32.
will any pan and tilt servo mechanism work?
Should work if it uses hobby rc servos.
Sir,i have pixy and arduino mega and L293d motor driver and assembled two servo in a bot..do i have to alter anything in the above code?do i have to buy encoder?if so what is the specification for that encoder?do i have to power the two servo separately?
Please tell me in detail
Thank You
Is possible to control power window motor using relay modules,pixy camera and ultrasonic sensor? Please help me in order to solve this problem.Thank you.
Everything is possible!
Hi your work is amazing. I am trying to do almost the same project. So i am wondering if you can show me or send me the schematic of circuit. Sorry for my english.And by the way i am using l293b motor driver, would it be a problem?
Sorry I don’t make schematics of such simple circuits. All the pin definitions are in the code, that should help you …
DO you know how to work this without using arduino but using a regular atmgega chip and atmel studio?
An Arduino uses a regular ATmega micro controller … It just has the Arduino bootloader. Better ask your question on the pixy forum.