Let's Make Robots!

Pixy Out of Box

Well, it finally came in, the Pixy CMUcam5.

I'm still working with it, but wanted to make a real quick note about a few things.

This is the Getting Started section.  It is pretty straight forward, but it leaves a few things out.  For instance, where it states the API calls it doesn't make it clear the GetBlocks() function loads the Arduino array with info that can then be accessed via the pixy.block[i].info  

Here's the Pixy Arduino guide

Here's my sample code for you:

Pixy Test Code v01

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <SPI.h>
#include <Pixy.h>

Pixy pixy;

int pIndex;
int printPixy = 0;

int pHeightPlusWidth = 0;
int pHeightPlusWidthMax = 0;

int pMaxIXY = 0;


void setup() {

  Serial.begin(9600);
}

void loop() {
  //Gets data from the Pixy.
  pixy.getBlocks();
  Serial.println("Got blocks!");
  
  //This is a simple run through of the first forty objects.
  for(int i = 0; i < 40; i++){    
    
    //There is a probably a better way to see if the array contains
    //another tracked object, but this does the trick.
    if(pixy.blocks[i].width < 999){
      //Prints the object's width.
      Serial.print(pixy.blocks[i].width);
      Serial.print(" W, ");
      //Prints the object's height.
      Serial.print(pixy.blocks[i].height);
      Serial.print(" H, ");
      //Prints an objects total mass.
      printPixy = pixy.blocks[i].height * pixy.blocks[i].width;
      Serial.print(printPixy);
      Serial.println(" W*H, "+String(i));
      //Short refresh delay.
      delay(5);
      
      //This is a simple function to find the largest mass.
      //I now believe that the Pixy pre-processes this for the Arduino;
      //that is, it seems to put objects in descending order according to
      //their total mass.  Regardless, this is my fix.
      if(pHeightPlusWidth > pHeightPlusWidthMax){
        //Save the total mass for a continual check.
        pHeightPlusWidthMax = pixy.blocks[i].height + pixy.blocks[i].width;
        //Put a place holder in the index of the largest mass.
        //This is usually 0, given the Pixy pre-sorts.
        pMaxIXY = i;              
      }
    
  }
  
}
  //Uncomment if you want to print the X, Y of the largest tracked object.
 /*
  Serial.print(pixy.blocks[pMaxIXY].x);
  Serial.print("X");
  Serial.print(pixy.blocks[pMaxIXY].y);
  Serial.println("Y");
  */
}
That's about it right now.  I've only had a few hours to play with it; here's what I've get her doing
3/27/2014

I was able to polish pan/tilt tracking code a little more.  It is still very rough, but I feel it demonstrates the Pixy's responsiveness.   Eventually, I'll need to do some fancy math to,
  1. Calculate the distance from object based upon its mass.
  2. Make the servo movements based on the blob's mass, relative to its position towards the edge of field-of-view.
  3. Perhaps PID the servo movements as well.

Pixy's Pan/Tilt code




Pixy Test Code v02
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//Pan/Tilt code for Pixy CMUcam5
//C. Thomas Brittain
//Ladvien, http://letsmakerobots.com/node/40753


//Pixy Stuff (includes)
#include <Servo.h>
#include <SPI.h>
#include <Pixy.h>

//Lets setup the Pixy.
Pixy pixy;

//Variables for indexing the largest mass and printing the info.
int pIndex;
int printPixy = 0;


//Variables for tracking the largest mass.
int pHeightPlusWidth = 0;
int pHeightPlusWidthMax = 0;


//Holds the index number of the largest mass.
int pMaxIXY = 0;

Servo pixyServoX; //Attach the pan servo.
Servo pixyServoY; //Attach the tilt servo.

//Set servos to initial position.
int posX = 90; //Servo position.
int posY = 90; //Servo position.


//Servo position.
int servoPosX;
int servoPosY;

//X and Y of the largest mass.
int OOIX;  //Object of Interest X
int OOIY;  //Object of Interest Y


void setup() {

  //Begin Serial.
  Serial.begin(9600);

  //Delay to prepare the Pixy.
  delay(5000);

  //Attach servos
  pixyServoX.attach(6); //Tilt (Y)
  pixyServoY.attach(5); //Pan (X)
}

void loop() {

  //Get the first set of data from the Pixy.  
  pixy.getBlocks();  
  Serial.println("Got blocks!");
  
  //Run through the first 40 indexes
  for(int i = 0; i < 40; i++){    
   //Or until the widith of objects is unreasonable (garabage data).
    if(pixy.blocks[i].width < 999){
      //Rough estimate of the OOI's mass.
      printPixy = pixy.blocks[i].height + pixy.blocks[i].width;
      //Compare the mass to the last largest mass found.
      if(pHeightPlusWidth > pHeightPlusWidthMax){
        //If this is the new largest mass, load it into the Max variable.
        pHeightPlusWidthMax = pixy.blocks[i].height + pixy.blocks[i].width;
        //Also, load the largest mass' index number.
        pMaxIXY = i;        
      }
   }
}
  //Print the OOI info.
  Serial.print(pixy.blocks[pMaxIXY].x);
  Serial.print(" X, ");
  Serial.print(pixy.blocks[pMaxIXY].y);
  Serial.println(" Y ");

  //Load the largest mass X and Y into the object of interest variables.  
  OOIX = pixy.blocks[pMaxIXY].x;
  OOIY = pixy.blocks[pMaxIXY].y;
  
  //Open the servo write function.
  ServoWrite();
  
  //Set the largest mass index back to 0.
  pMaxIXY=0;  
}


void ServoWrite(){

//X
  if(OOIX > 280){
    if(servoPosX>7){
      servoPosX=servoPosX-7;
      pixyServoX.write(servoPosX);    
      delay(10);
    }
  }
  if(OOIX < 40){
    if(servoPosX<173)
    {
      servoPosX=servoPosX+7;
      pixyServoX.write(servoPosX);
      delay(10);    
    }
  }
  
  if(OOIX > 220){
    if(servoPosX>4){
      servoPosX=servoPosX-3;
      pixyServoX.write(servoPosX);    
      delay(1);
    }
  }
  if(OOIX < 80){
    if(servoPosX<177)
    {
      servoPosX=servoPosX+3;
      pixyServoX.write(servoPosX);
      delay(1);    
    }
  }
  
//Y
  if(OOIY < 60){
    if(servoPosY>0)
    {
      servoPosY=servoPosY-2;
      pixyServoY.write(servoPosY);
      delay(5);    
    }
  }
  if(OOIY > 170){
    if(servoPosY<91)
    {
      servoPosY=servoPosY+2;
      pixyServoY.write(servoPosY);
      delay(5);    
    }
  }  
}
3/29/14
I worked with the pan code a little more.  The following version takes accounts for the space between 0 and x, then (x+width) and 320.  I refer to these as the LeftMargin and RightMargin.  It then maps the these measures against servo steps, adjustable with variable ServoMaxJump.  This allows the pan-n-tilt setup to respond appropriately to the same object at different distances.  

 

Also, I've included center limits, xLowCenterLimit and xHighCenterLimit so the pan servo is not jumping around when the object is directly infront of the Pixy.  

 

Lastly, I added PanLowLimit and PanHighLimit in case someone doesn't want to use their full range of the pan servo.

 

3/30/14

Alright, I "finished" the pan-n-tilt code for Arduino.

Pixy Test Code v08
//Pan/Tilt code for Pixy CMUcam5
//C. Thomas Brittain
//Ladvien, http://letsmakerobots.com/node/40753


//Pixy Stuff (includes)
#include <Servo.h>
#include <SPI.h>
#include <Pixy.h>

//Lets setup the Pixy.
Pixy pixy;

//Variables for indexing the largest mass and printing the info.
int pIndex;
int printPixy = 0;


//Variables for tracking the largest mass.
int pHeightPlusWidth = 0;
int pHeightPlusWidthMax = 0;


//Holds the index number of the largest mass.
int pMaxIXY = 0;

Servo pixyServoX; //Attach the pan servo.
Servo pixyServoY; //Attach the tilt servo.

//Set servos to initial position.
int posX = 90; //Servo position.
int posY = 90; //Servo position.


//Servo position.
int servoPosX = 90;
int servoPosY = 90;

//These are the limit settings for the Servo's movement range.
//Given, 90 is middle.
int PanLowLimit = 1; 
int PanHighLimit = 180; 
int TiltLowLimit = 50; 
int TiltHighLimit = 95;

//Adjust to control how many servo positions are changed at once.
int servoMaxJumpX = 7;
int servoMaxJumpY = 3;

//Centered margin limits.
int xLowCenterLimit = 80;
int xHighCenterLimit = 240;
int yLowCenterLimit = 40;
int yHighCenterLimit = 160;

int servoJumpDelay = 20;

//X and Y of the largest mass.
int OOIX1;  //Object of Interest X1
int OOIY1;  //Object of Interest Y1
int OOIX2;  //Object of Interest X1
int OOIY2;  //Object of Interest Y1


//For calculating servo positions.
int RightMargin; int LeftMargin; int BottomMargin; int TopMargin;

void setup() {

  //Begin Serial.
  Serial.begin(9600);

  //Attach servos
  pixyServoX.attach(6); //Tilt (Y)
  pixyServoY.attach(5); //Pan (X)
  pixyServoX.write(servoPosX);
  pixyServoY.write(servoPosY);
  
  //Delay to prepare the Pixy.
  delay(2000);

}

void loop() {

  //Get the first set of data from the Pixy.  
  pixy.getBlocks();  
  //Serial.println("Got blocks!");
  
  //Run through the first 40 indexes
  for(int i = 0; i < 40; i++){    
   //Or until the widith of objects is unreasonable (garabage data).
    if(pixy.blocks[i].width < 999){
      //Rough estimate of the OOI's mass.
      printPixy = pixy.blocks[i].height + pixy.blocks[i].width;
      //Compare the mass to the last largest mass found.
      if(pHeightPlusWidth > pHeightPlusWidthMax){
        //If this is the new largest mass, load it into the Max variable.
        pHeightPlusWidthMax = pixy.blocks[i].height + pixy.blocks[i].width;
        //Also, load the largest mass' index number.
        pMaxIXY = i;        
      }
   }
}
/*
  //Print the OOI info.
  Serial.print(pixy.blocks[pMaxIXY].x);
  Serial.print(" X, ");
  Serial.print(pixy.blocks[pMaxIXY].y);
  Serial.println(" Y ");
*/
  //Load the largest mass X and Y into the object of interest variables.  
  OOIX1 = pixy.blocks[pMaxIXY].x;
  OOIY1 = pixy.blocks[pMaxIXY].y;
  
  //Open the servo write function.
  ServoWrite();
  
  //Set the largest mass index back to 0.
  pMaxIXY=0;  
}


void ServoWrite(){
  
  RightMargin = 320 - (OOIX1 + (pixy.blocks[pMaxIXY].width/2));  //This assumes the Pixy has a field of view 320 pixels wide.
  LeftMargin = OOIX1; //Object of interest's X is the same as the left margin marker.
  BottomMargin = 240 - (OOIY1 + (pixy.blocks[pMaxIXY].height/2)); //This assumes the Pixy has a field of view 240 pixels high.
  TopMargin = OOIY1; //The object of interest's Y is the same as the top margin marker.
  
  
  //First, let's check to see if the OOI is centered enough.
  if (OOIX1 > xLowCenterLimit && OOIX1 < xHighCenterLimit){
      delay(servoJumpDelay);
  }
  else { //It's not centered, let's move servos and center it.
        //First, see which is greater left or right margin.
        if(RightMargin >= LeftMargin){
          if(servoPosX < PanHighLimit){ 
            int moveX;
            moveX = RightMargin;
            moveX = map(moveX, 0, RightMargin, 0, servoMaxJumpX);
            servoPosX = servoPosX + moveX;
            pixyServoX.write(servoPosX);
            delay(servoJumpDelay);
            }
        }
      else{  
       //Left Margin was greater, so lets move stage-right. 
       if(servoPosX > PanLowLimit){ 
            //Right Margin was greater, so lets move stage-left.
            int moveX;
            moveX = LeftMargin;
            moveX = map(moveX, 0, LeftMargin, 0, servoMaxJumpX);
            servoPosX = servoPosX - moveX;
            pixyServoX.write(servoPosX);
            delay(servoJumpDelay);
          }
      }
  }


  //First, let's check to see if the OOI is centered enough.
  if (OOIY1 > yLowCenterLimit && OOIY1 < yHighCenterLimit){
      delay(servoJumpDelay);
  }
  else { //It's not centered, let's move servos and center it.
        //First, see which is greater bottom or top margin.
        if(BottomMargin >= TopMargin){
          if(servoPosY > TiltLowLimit){ 
            int moveY;
            moveY = BottomMargin;
            moveY = map(moveY, 0, BottomMargin, 0, servoMaxJumpY);
            servoPosY = servoPosY - moveY;
            pixyServoY.write(servoPosY);
            delay(servoJumpDelay);
            }
        }
      else{  
       //Top Margin was greater, so lets move stage-up. 
       if(servoPosY < TiltHighLimit){ 
            //Topt Margin was greater, so lets move stage-up.
            int moveY;
            moveY = TopMargin;
            moveY = map(moveY, 0, TopMargin, 0, servoMaxJumpY);
            servoPosY = servoPosY + moveY;
            pixyServoY.write(servoPosY);
            delay(servoJumpDelay);
          }
      }
  }

} //End ServoWrite()

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

awesome stuff!!

what would be a good way to search for an object, then start to track it once found?

Bajdi has done more, he'd be better to ask. http://www.bajdi.com/pixy-cmucam5/

I dont know why my comment posted twice, so I am deleting it and replacing it with this

Thanks for posting this.

My Pixy just arrived but I don't have time to play with it yet.

I'm looking forward to see what you do with yours.

No problem.  I think you'll like it.  

I've tried to find communication setup information (should be capable of SPI, UART, I2C), but the all I've located are the Arduino libraries for each.  I've been through the header files, I don't think it'd be real hard to reverse-design them to be used on the Propeller board.

So what's the verdict? How smart is it in the real world? Can it discern flesh tones? Does it have enough resolution to make it useful off the desktop?

Worth it.  Video and more info at breakfast.