Mindstorms 3rd Party ROBOTC Drivers RobotC
[Home] [Download] [Submit a bug/suggestion] [ROBOTC Forums] [Blog] [Support this project]

NXTCAM-driver.h

Go to the documentation of this file.
00001 /*!@addtogroup mindsensors
00002  * @{
00003  * @defgroup nxtcam NXTCam Vision System
00004  * NXTCam Vision System
00005  * @{
00006  */
00007 
00008 /*
00009  * $Id: NXTCAM-driver.h 56 2011-05-30 19:47:12Z xander $
00010  */
00011 
00012 #ifndef __NXTCAM_H__
00013 #define __NXTCAM_H__
00014 /** \file NXTCAM-driver.h
00015  * \brief Mindsensors NXTCam driver
00016  *
00017  * NXTCAM-driver.h provides an API for the Mindsensors NXTCam.  This version is an extensive rewrite of
00018  * Gordon Wyeth's driver. The blob information is no longer kept in seperate array,
00019  * but in an array of structs.
00020  *
00021  * Changelog:
00022  * - 1.0: Partial rewrite of original driver, using structs instead of arrays to hold blob info
00023  * - 1.1: Further rewrite to use common.h functions to further align with standard driver framework
00024  * - 1.2: Added NXTCAMgetCenter() to calculate the center of a single blob
00025  * - 1.3: Added NXTCAMinitTL() to enable line tracking mode<br>
00026  *        Fixed bug in NXTCAMinit() that did not configure object tracking properly<br>
00027  *        Added extra wait times after each issued command in init functions
00028  * - 1.4: Removed printDebugLine from driver
00029  * - 1.5: Added ability to specify I2C address with optional argument.  Defaults to 0x02 when not specified.
00030  *
00031  * License: You may use this code as you wish, provided you give credit where it's due.
00032  *
00033  * THIS CODE WILL ONLY WORK WITH ROBOTC VERSION 2.00 AND HIGHER.
00034  * \author Xander Soldaat
00035  * \author Gordon Wyeth
00036  * \date 03 Dec 2010
00037  * \version 1.5
00038  * \example NXTCAM-test1.c
00039  */
00040 
00041 #pragma systemFile
00042 
00043 #ifndef __COMMON_H__
00044 #include "common.h"
00045 #endif
00046 
00047 #define MAX_BLOBS         8     /*!< Maximum number of blobs returned by the NXTCam */
00048 #define NXTCAM_I2C_ADDR   0x02  /*!< I2C address used by the NXTCam */
00049 #define NXTCAM_CMD_REG    0x41  /*!< Register used for issuing commands */
00050 #define NXTCAM_COUNT_REG  0x42  /*!< Register used to hold number of blobs detected */
00051 #define NXTCAM_DATA_REG   0x43  /*!< Register containing data pertaining to blobs */
00052 
00053 #define SIDE_CENTER(X1, X2) ((X1 + X2) / 2)  /*!< Returns the center of a side */
00054 
00055 /*! Blob struct, contains all the data for a blob. */
00056 typedef struct {
00057   int x1;       /*!< left */
00058   int y1;       /*!< top */
00059   int x2;       /*!< right */
00060   int y2;       /*!< bottom */
00061   int colour;   /*!< Blob colour */
00062   int size;     /*!< Blob size */
00063 } blob;
00064 
00065 /*! Array of blob as a typedef, this is a work around for RobotC's inability to pass an array to a function */
00066 typedef blob blob_array[MAX_BLOBS];
00067 
00068 tByteArray NXTCAM_I2CRequest;    /*!< Array to hold I2C command data */
00069 tByteArray NXTCAM_I2CReply;      /*!< Array to hold I2C reply data */
00070 
00071 // "public" functions
00072 bool NXTCAMinit(tSensors link, ubyte address = NXTCAM_I2C_ADDR);
00073 bool NXTCAMinitTL(tSensors link, ubyte address = NXTCAM_I2C_ADDR);
00074 int NXTCAMgetBlobs(tSensors link, blob_array &blobs, bool mergeBlobs, ubyte address = NXTCAM_I2C_ADDR);
00075 int NXTCAMgetBlobs(tSensors link, blob_array &blobs, ubyte address = NXTCAM_I2C_ADDR);
00076 
00077 // internal functions, used by the above
00078 bool _camera_cmd(tSensors link, byte cmd, ubyte address);
00079 int _mergeBlobs(int blob1, int blob2, int nblobs, blob_array &blobs);
00080 int _merge(int nblobs, blob_array &blobs);
00081 void _sortBlobs(int nblobs, blob_array &blobs);
00082 void NXTCAMgetAverageCenter(blob_array &blobs, int nblobs, int colourindex, int &x, int &y);
00083 void NXTCAMgetCenter(blob_array &blobs, int index, int &x, int &y);
00084 
00085 /*! boolean to signal if there are still blobs that might qualify for merging */
00086 bool still_merging;
00087 
00088 /**
00089  * This function sends a command to the camera over I2C.
00090  *
00091  * Note: this is an internal function and should not be called directly.
00092  * @param link the sensor port number
00093  * @param cmd the command to be sent
00094  * @param address the I2C address to use, optional, defaults to 0x02
00095  * @return true if no error occured, false if it did
00096  */
00097 bool _camera_cmd(tSensors link, byte cmd, ubyte address) {
00098   NXTCAM_I2CRequest[0] = 3;                 // Message size
00099   NXTCAM_I2CRequest[1] = address;           // I2C Address
00100   NXTCAM_I2CRequest[2] = NXTCAM_CMD_REG;    // Register used for issuing commands
00101   NXTCAM_I2CRequest[3] = cmd;               // Command to be executed
00102 
00103   return writeI2C(link, NXTCAM_I2CRequest, 0);
00104 }
00105 
00106 /**
00107  * This function initialises camera ready to find blobs and sort them according to size.
00108  * @param link the sensor port number
00109  * @param address the I2C address to use, optional, defaults to 0x02
00110  * @return true if no error occured, false if it did
00111  */
00112 bool NXTCAMinit(tSensors link, ubyte address) {
00113   if (!_camera_cmd(link, 'D', address)) // Stop object tracking
00114     return false;
00115   wait1Msec(500);
00116 
00117   if (!_camera_cmd(link, 'A', address)) // Sort by size
00118     return false;
00119   wait1Msec(500);
00120 
00121   if (!_camera_cmd(link,'B', address))  // Set object tracking mode
00122     return false;
00123   wait1Msec(500);
00124 
00125   if (!_camera_cmd(link,'E', address))  // Start object tracking
00126     return false;
00127   wait1Msec(500);
00128 
00129   return true;
00130 }
00131 
00132 
00133 /**
00134  * This function initialises camera ready to track lines.
00135  * @param link the sensor port number
00136  * @param address the I2C address to use, optional, defaults to 0x02
00137  * @return true if no error occured, false if it did
00138  */
00139 bool NXTCAMinitTL(tSensors link, ubyte address) {
00140   if (!_camera_cmd(link, 'D', address)) // Stop object tracking
00141     return false;
00142   wait1Msec(500);
00143 
00144   if (!_camera_cmd(link, 'X', address)) // Do not sort objects
00145     return false;
00146   wait1Msec(500);
00147 
00148   if (!_camera_cmd(link,'L', address))  // Set tracking line mode
00149     return false;
00150   wait1Msec(500);
00151 
00152   if (!_camera_cmd(link,'E', address))  // Start tracking
00153     return false;
00154 
00155   wait1Msec(500);
00156   return true;
00157 }
00158 
00159 
00160 /**
00161  * This function fetches the blob data from the camera and merges the colliding ones.
00162  * @param link the sensor port number
00163  * @param blobs the array of blobs
00164  * @param mergeBlobs whether or not to merge the colliding blobs
00165  * @param address the I2C address to use, optional, defaults to 0x02
00166  * @return the number of blobs detected, -1 if an error occurred
00167  */
00168 int NXTCAMgetBlobs(tSensors link, blob_array &blobs, bool mergeBlobs, ubyte address) {
00169   int _nblobs = NXTCAMgetBlobs(link, blobs, address);
00170   if (mergeBlobs == true)
00171     return _merge(_nblobs, blobs);
00172   return _nblobs;
00173 }
00174 
00175 /**
00176  * This function fetches the blob data from the camera.
00177  * @param link the sensor port number
00178  * @param blobs the array of blobs
00179  * @param address the I2C address to use, optional, defaults to 0x02
00180  * @return the number of blobs detected, -1 if an error occurred
00181  */
00182 int NXTCAMgetBlobs(tSensors link, blob_array &blobs, ubyte address) {
00183   int _nblobs = 0;
00184 
00185   // clear the array used for the blobs
00186   memset(blobs, 0, sizeof(blob_array));
00187 
00188   // Request number of blobs from the count register
00189   NXTCAM_I2CRequest[0] = 2;                 // Message size
00190   NXTCAM_I2CRequest[1] = address;           // I2C Address
00191   NXTCAM_I2CRequest[2] = NXTCAM_COUNT_REG;  // Register used to hold number of blobs detected
00192 
00193   if (!writeI2C(link, NXTCAM_I2CRequest, 1))
00194     return -1;
00195 
00196   if(!readI2C(link, NXTCAM_I2CReply, 1))
00197     return -1;
00198 
00199   _nblobs = NXTCAM_I2CReply[0];
00200   if (_nblobs > MAX_BLOBS) {
00201     return -1;
00202   }
00203 
00204   // Get nblobs of blob data from the camera
00205   for (int _i = 0; _i < _nblobs; _i++) {
00206 
00207     // Request blob data
00208     NXTCAM_I2CRequest[0] = 2;                         // Message size
00209     NXTCAM_I2CRequest[1] = address;           // I2C Address
00210     NXTCAM_I2CRequest[2] = NXTCAM_DATA_REG + _i * 5;  // Register containing data pertaining to blob
00211 
00212     if (!writeI2C(link, NXTCAM_I2CRequest, 5))
00213       return -1;
00214 
00215     if (!readI2C(link, NXTCAM_I2CReply, 5))
00216       return -1;
00217 
00218     // Put the I2C data into the blob
00219     blobs[_i].colour    = (int)NXTCAM_I2CReply[0];
00220     blobs[_i].x1        = (int)NXTCAM_I2CReply[1];
00221     blobs[_i].y1        = (int)NXTCAM_I2CReply[2];
00222     blobs[_i].x2        = (int)NXTCAM_I2CReply[3];
00223     blobs[_i].y2        = (int)NXTCAM_I2CReply[4];
00224     blobs[_i].size      = abs(blobs[_i].x2 - blobs[_i].x1) * abs(blobs[_i].y2 * blobs[_i].y1);
00225   }
00226   return _nblobs;
00227 }
00228 
00229 /**
00230  * Go through the blobs and calls _mergeBlobs.
00231  *
00232  * Note: this is an internal function and should not be called directly.
00233  * @param nblobs the number of blobs
00234  * @param blobs the array of blobs
00235  * @return the number of blobs detected
00236  */
00237 int _merge(int nblobs, blob_array &blobs) {
00238   still_merging = true;
00239   while (still_merging == true) {
00240     still_merging = false;
00241     for(int i = 0; i < nblobs; i++) {
00242       for(int j = i+1; j < nblobs; j++) {
00243         nblobs = _mergeBlobs(i,j, nblobs, blobs);
00244       }
00245     }
00246   }
00247   return nblobs;
00248 }
00249 
00250 /**
00251  * Check if two blobs can be merged into one.  blob1 will be replaced with the
00252  * new merged blob and blob2 will be destroyed.
00253  *
00254  * Note: this is an internal function and should not be called directly.
00255  * @param blob1 the index number of the first blob
00256  * @param blob2 the index number of the second blob
00257  * @param nblobs the number of blobs
00258  * @param blobs the array of blobs
00259  * @return the number of blobs detected
00260  */
00261 int _mergeBlobs(int blob1, int blob2, int nblobs, blob_array &blobs) {
00262   int _blob1_center;
00263   int _blob2_center;
00264   int _blob1_proj;
00265   int _blob2_proj;
00266   bool _overlapx = false;
00267   bool _overlapy = false;
00268 
00269   // If either pf the blobs are size 0, just skip them
00270   if (blobs[blob1].size == 0 || blobs[blob2].size == 0)
00271     return nblobs;
00272 
00273   // If the colours don't match, don't _merge them
00274   if (blobs[blob1].colour != blobs[blob2].colour)
00275     return nblobs;
00276 
00277   // Find the center of the top sides of each blob and their projections onto
00278   // the X plane from center to right corner
00279   _blob1_center = SIDE_CENTER(blobs[blob1].x1, blobs[blob1].x2);
00280   _blob2_center = SIDE_CENTER(blobs[blob2].x1, blobs[blob2].x2);
00281   _blob1_proj = blobs[blob1].x2 - _blob1_center;
00282   _blob2_proj = blobs[blob2].x2 - _blob2_center;
00283 
00284   // Check if the projections overlap
00285   if ((_blob1_proj + _blob2_proj) > abs(_blob1_center - _blob2_center))
00286     _overlapx = true;
00287 
00288   // Find the center of the left sides of each blob and their projections onto
00289   // the Y plane from center to bottom corner
00290   _blob1_center = SIDE_CENTER(blobs[blob1].y1, blobs[blob1].y2);
00291   _blob2_center = SIDE_CENTER(blobs[blob2].y1, blobs[blob2].y2);
00292   _blob1_proj = blobs[blob1].y2 - _blob1_center;
00293   _blob2_proj = blobs[blob2].y2 - _blob2_center;
00294 
00295   // Check if the projections overlap
00296   if ((_blob1_proj + _blob2_proj) > abs(_blob1_center - _blob2_center))
00297     _overlapy = true;
00298 
00299   // If both projections are overlapping, then the blobs are _merged.
00300   // Find the smallest bounding box for both blobs and replace blob1
00301   // with this new information.  Blob2 gets set to 0 size and
00302   // recursively replaced by the next in line.
00303   if ((_overlapx == true) && (_overlapy == true)) {
00304     blobs[blob1].x1 = min2(blobs[blob1].x1, blobs[blob2].x1);
00305     blobs[blob1].y1 = min2(blobs[blob1].y1, blobs[blob2].y1);
00306     blobs[blob1].x2 = max2(blobs[blob1].x2, blobs[blob2].x2);
00307     blobs[blob1].y2 = max2(blobs[blob1].y2, blobs[blob2].y2);
00308     blobs[blob1].size = abs(blobs[blob1].x2 - blobs[blob1].x1) * abs(blobs[blob1].y2 - blobs[blob1].y1);
00309 
00310     for (int _i = blob2; _i < nblobs - 1; _i++) {
00311       memcpy(blobs[_i], blobs[_i+1], sizeof(blob));
00312     }
00313 
00314     nblobs--;
00315     memset(blobs[nblobs], 0, sizeof(blob));
00316     still_merging = true;
00317   }
00318   _sortBlobs(nblobs, blobs);
00319 
00320   // return the new number of blobs
00321   return nblobs;
00322 }
00323 
00324 /**
00325  * This function sorts the blobs in the array using insertion sort.
00326  *
00327  * Note: this is an internal function and should not be called directly.
00328  * @param nblobs the number of blobs
00329  * @param blobs the array of blobs
00330  */
00331 void _sortBlobs(int nblobs, blob_array &blobs) {
00332   blob _tmpBlob;
00333   int i, j;
00334 
00335   // Outer boundary definition
00336   for (i = 1; i < nblobs; i++) {
00337     if (blobs[i-1].size >= blobs[i].size )
00338       continue;
00339 
00340     memcpy(_tmpBlob, blobs[i], sizeof(blob));
00341 
00342     // inner loop: elements shifted down until insertion point found
00343     for (j = i-1; j >= 0; j--) {
00344       if ( blobs[j].size >= _tmpBlob.size )
00345         break;
00346       memcpy(blobs[j+1], blobs[j], sizeof(blob));
00347     }
00348     // insert the blob
00349     memcpy(blobs[j+1], _tmpBlob, sizeof(blob));
00350   }
00351 }
00352 
00353 /**
00354  * Calculate the average center of all the blobs of a specific colour.
00355  *
00356  * Note: this is an experimental function and may not function properly yet.
00357  * @param blobs the array of blobs
00358  * @param nblobs the number of blobs
00359  * @param colourindex the colour of the blobs of which the average center is to be calculated
00360  * @param x x-coordinate of the center
00361  * @param y y-coordinate of the center
00362  */
00363 void NXTCAMgetAverageCenter(blob_array &blobs, int nblobs, int colourindex, int &x, int &y) {
00364   int _totalX = 0;
00365   int _totalY = 0;
00366   int _counter = 0;
00367 
00368   for (int i = 0; i < nblobs; i++){
00369     if ((blobs[i].colour == colourindex) && (blobs[i].size > 400)) {
00370       _totalX += SIDE_CENTER(blobs[i].x1, blobs[i].x2);
00371       _totalY += SIDE_CENTER(blobs[i].y1, blobs[i].y2);
00372       _counter++;
00373     }
00374   }
00375   x = _totalX / (_counter - 1);
00376   y = _totalY / (_counter  -1);
00377 }
00378 
00379 
00380 /**
00381  * Calculate the center of a specified blob.
00382  *
00383  * Note: this is an experimental function and may not function properly yet.
00384  * @param blobs the array of blobs
00385  * @param index the blob that you want to calculate the center of
00386  * @param x x-coordinate of the center
00387  * @param y y-coordinate of the center
00388  */
00389 void NXTCAMgetCenter(blob_array &blobs, int index, int &x, int &y) {
00390   x = SIDE_CENTER(blobs[index].x1, blobs[index].x2);
00391   y = SIDE_CENTER(blobs[index].y1, blobs[index].y2);
00392 }
00393 
00394 #endif // __NXTCAM_H__
00395 
00396 /*
00397  * $Id: NXTCAM-driver.h 56 2011-05-30 19:47:12Z xander $
00398  */
00399 /* @} */
00400 /* @} */