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

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