00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "Robots/HeliBot/HeliPose.H"
00039
00040 #include "Component/ParamMap.H"
00041 #include "Util/Assert.H"
00042 #include "Util/Types.H"
00043 #include "Util/log.H"
00044 #include "Image/ColorOps.H"
00045 #include "Image/DrawOps.H"
00046
00047 #include <cmath>
00048 #include <unistd.h>
00049
00050 #ifdef HAVE_OPENCV
00051 namespace
00052 {
00053 const int thresh = 50;
00054
00055
00056
00057
00058 double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )
00059 {
00060 double dx1 = pt1->x - pt0->x;
00061 double dy1 = pt1->y - pt0->y;
00062 double dx2 = pt2->x - pt0->x;
00063 double dy2 = pt2->y - pt0->y;
00064 return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
00065 }
00066
00067 CvSeq* findSquares(const Image<PixRGB<byte> >& in, CvMemStorage* storage,
00068 const int minarea, const int maxarea, const double mincos)
00069 {
00070 const int N = 11;
00071
00072 IplImage* img = img2ipl(in);
00073
00074 CvSize sz = cvSize( img->width & -2, img->height & -2 );
00075 IplImage* timg = cvCloneImage( img );
00076 IplImage* gray = cvCreateImage( sz, 8, 1 );
00077 IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
00078
00079
00080 CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
00081
00082
00083
00084 cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
00085
00086
00087 cvPyrDown( timg, pyr, 7 );
00088 cvPyrUp( pyr, timg, 7 );
00089 IplImage* tgray = cvCreateImage( sz, 8, 1 );
00090
00091
00092 for (int c = 0; c < 3; ++c)
00093 {
00094
00095 cvSetImageCOI( timg, c+1 );
00096 cvCopy( timg, tgray, 0 );
00097
00098
00099 for (int l = 0; l < N; ++l)
00100 {
00101
00102
00103 if( l == 0 )
00104 {
00105
00106
00107 cvCanny( tgray, gray, 0, thresh, 5 );
00108
00109
00110 cvDilate( gray, gray, 0, 1 );
00111 }
00112 else
00113 {
00114
00115
00116 cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
00117 }
00118
00119
00120 CvSeq* contours = 0;
00121 cvFindContours( gray, storage, &contours, sizeof(CvContour),
00122 CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
00123
00124
00125 while( contours )
00126 {
00127
00128
00129 CvSeq* result =
00130 cvApproxPoly( contours, sizeof(CvContour), storage,
00131 CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
00132
00133
00134
00135
00136
00137
00138 const double area = fabs(cvContourArea(result,CV_WHOLE_SEQ));
00139 if (result->total == 4 &&
00140 area >= minarea && area <= maxarea &&
00141 cvCheckContourConvexity(result))
00142 {
00143 double s = 0;
00144
00145 for (int i = 0; i < 4; ++i)
00146 {
00147
00148
00149 const double t =
00150 fabs(angle((CvPoint*)cvGetSeqElem( result, i % 4 ),
00151 (CvPoint*)cvGetSeqElem( result, (i-2) % 4 ),
00152 (CvPoint*)cvGetSeqElem( result, (i-1) % 4 )));
00153 s = s > t ? s : t;
00154 }
00155
00156
00157
00158
00159
00160 if (s < mincos)
00161 {
00162 for (int i = 0; i < 4; ++i)
00163 cvSeqPush(squares,
00164 (CvPoint*)cvGetSeqElem( result, i ));
00165
00166 }
00167 }
00168
00169
00170 contours = contours->h_next;
00171 }
00172 }
00173 }
00174
00175
00176 cvReleaseImage( &gray );
00177 cvReleaseImage( &pyr );
00178 cvReleaseImage( &tgray );
00179 cvReleaseImage( &timg );
00180 cvReleaseImageHeader( &img );
00181
00182 return squares;
00183 }
00184
00185
00186 Image<PixRGB<byte> > drawSquares(const Image<PixRGB<byte> >&in, CvSeq* squares)
00187 {
00188 Image<PixRGB<byte> > out(in);
00189
00190 CvSeqReader reader;
00191
00192
00193 cvStartReadSeq(squares, &reader, 0);
00194
00195
00196 for (int i = 0; i < squares->total; i += 4)
00197 {
00198 CvPoint pt[4];
00199
00200
00201 CV_READ_SEQ_ELEM( pt[0], reader );
00202 CV_READ_SEQ_ELEM( pt[1], reader );
00203 CV_READ_SEQ_ELEM( pt[2], reader );
00204 CV_READ_SEQ_ELEM( pt[3], reader );
00205
00206 for (int j = 0; j < 4; ++j)
00207 drawLine(out,
00208 Point2D<int>(pt[j].x, pt[j].y),
00209 Point2D<int>(pt[(j+1)%4].x, pt[(j+1)%4].y),
00210 PixRGB<byte>(0, 255, 0),
00211 2);
00212 }
00213
00214 return out;
00215 }
00216
00217 std::vector<Point2D<int> > getRectangle(CvSeq* cards)
00218 {
00219 CvSeqReader reader;
00220
00221 cvStartReadSeq( cards, &reader, 0 );
00222
00223 std::vector<Point2D<int> > corners;
00224
00225 if (cards->total > 0)
00226 {
00227 CvPoint pt[4];
00228
00229 CV_READ_SEQ_ELEM( pt[0], reader );
00230 CV_READ_SEQ_ELEM( pt[1], reader );
00231 CV_READ_SEQ_ELEM( pt[2], reader );
00232 CV_READ_SEQ_ELEM( pt[3], reader );
00233
00234 corners.push_back(Point2D<int>(pt[0].x, pt[0].y));
00235 corners.push_back(Point2D<int>(pt[1].x, pt[1].y));
00236 corners.push_back(Point2D<int>(pt[2].x, pt[2].y));
00237 corners.push_back(Point2D<int>(pt[3].x, pt[3].y));
00238
00239 }
00240
00241 return corners;
00242 }
00243 }
00244
00245 #endif //HAVE_OPENCV
00246
00247
00248 void* HeliPose_run(void *r0);
00249
00250
00251 void* HeliPose_run(void *r0)
00252 {
00253 HeliPose *r = (HeliPose *)r0;
00254
00255 r->run_imuPose(); return NULL;
00256 }
00257
00258
00259 HeliPose::HeliPose(OptionManager& mgr,
00260 nub::ref<InputFrameSeries>& ifs,
00261 nub::ref<OutputFrameSeries>& ofs,
00262 const std::string& descrName,
00263 const std::string& tagName) :
00264 ModelComponent(mgr, descrName, tagName),
00265 itsIfs(ifs),
00266 itsOfs(ofs),
00267 itsIMU(new IMU_SFE_Atomic(mgr))
00268 {
00269 addSubComponent(itsIMU);
00270
00271 running = false;
00272 itsDebug = true;
00273
00274 pthread_mutex_init(&itsPoseLock, NULL);
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303 itsCurrentPose.translation = Point3D<float>(0,0,0);
00304 itsCurrentPose.velocity = Point3D<float>(0,0,0);
00305 itsCurrentPose.rotation = Point3D<float>(0,0,0);
00306
00307 itsVelocityScale.x = 0.1;
00308 itsVelocityScale.y = 1;
00309 itsVelocityScale.z = 1;
00310 itsRotationScale.x = 1;
00311 itsRotationScale.y = 1;
00312 itsRotationScale.z = M_PI/((17870-12360)*2);
00313
00314 itsVelocityBias = Point3D<float>(0,0,0);
00315 itsRotationBias = Point3D<float>(0,0,0);
00316 itsVelocitySigma = Point3D<float>(0,0,0);
00317 itsVelocitySigma = Point3D<float>(0,0,0);
00318
00319
00320 }
00321
00322
00323 void HeliPose::start1()
00324 {
00325 }
00326
00327
00328 void HeliPose::start2()
00329 {
00330
00331 pthread_create(&runner, NULL, &HeliPose_run, (void *)this);
00332 }
00333
00334
00335 void HeliPose::stop1()
00336 {
00337
00338 running = false; while(running == false) usleep(5);
00339 usleep(50); running = false;
00340 }
00341
00342
00343 HeliPose::~HeliPose()
00344 {
00345 cvReleaseMemStorage(&itsStorage);
00346 pthread_mutex_destroy(&itsPoseLock);
00347
00348 }
00349
00350
00351 std::vector<Point2D<int> > HeliPose::getExtrinsic(Image<byte>& img)
00352 {
00353 int rows = 3;
00354 int cols = 4;
00355 std::vector<CvPoint2D32f> corners(rows*cols);
00356
00357 int count = 0;
00358 int result = cvFindChessboardCorners(img2ipl(img),
00359 cvSize(rows,cols),
00360 &corners[0], &count,
00361 CV_CALIB_CB_ADAPTIVE_THRESH |
00362 CV_CALIB_CB_NORMALIZE_IMAGE |
00363 CV_CALIB_CB_FILTER_QUADS);
00364
00365
00366
00367 if(result != 0)
00368 {
00369 cvFindCornerSubPix(img2ipl(img),
00370 &corners[0],
00371 count,
00372 cvSize(10,10),
00373 cvSize(-1,-1),
00374 cvTermCriteria(CV_TERMCRIT_ITER,1000,0.01) );
00375
00376
00377
00378 CvMat *image_points_ex = cvCreateMat( corners.size(), 2, CV_64FC1);
00379
00380 for (uint j = 0; j < corners.size(); j++){
00381 cvSetReal2D( image_points_ex, j, 0, corners[j].x);
00382 cvSetReal2D( image_points_ex, j, 1, corners[j].y);
00383 }
00384
00385 CvMat *object_points_ex = cvCreateMat( corners.size(), 3, CV_64FC1);
00386 for (uint j = 0; j < corners.size(); j++){
00387 cvSetReal2D( object_points_ex, j, 0, ( j % rows) * 50 );
00388 cvSetReal2D( object_points_ex, j, 1, ( j / rows) * 50 );
00389 cvSetReal2D( object_points_ex, j, 2, 0.0 );
00390 }
00391
00392 cvFindExtrinsicCameraParams2( object_points_ex,
00393 image_points_ex,
00394 itsIntrinsicMatrix,
00395 itsDistortionCoeffs,
00396 itsCameraRotation,
00397 itsCameraTranslation);
00398
00399 cvReleaseMat( &image_points_ex);
00400 cvReleaseMat( &object_points_ex);
00401
00402 }
00403
00404
00405 return std::vector<Point2D<int> >();
00406 }
00407
00408
00409 std::vector<Point2D<int> > HeliPose::getExtrinsic(Image<PixRGB<byte> >& img)
00410 {
00411 CvSeq* square = findSquares(itsCurrentImg, itsStorage,
00412 1500,
00413 15000,
00414 0.3
00415 );
00416 std::vector<Point2D<int> > corners = getRectangle(square);
00417 if (corners.size() > 0)
00418 {
00419
00420 CvMat *image_points_ex = cvCreateMat( corners.size(), 2, CV_64FC1);
00421
00422 for (uint j = 0; j < corners.size(); j++){
00423 cvSetReal2D( image_points_ex, j, 0, (float)corners[j].i);
00424 cvSetReal2D( image_points_ex, j, 1, (float)corners[j].j);
00425 }
00426
00427 CvMat *object_points_ex = cvCreateMat( corners.size(), 3, CV_64FC1);
00428
00429 cvSetReal2D( object_points_ex, 0, 0, 0 );
00430 cvSetReal2D( object_points_ex, 0, 1, 0 );
00431 cvSetReal2D( object_points_ex, 0, 2, 0.0 );
00432
00433 cvSetReal2D( object_points_ex, 1, 0, 0 );
00434 cvSetReal2D( object_points_ex, 1, 1, 203 );
00435 cvSetReal2D( object_points_ex, 1, 2, 0.0 );
00436
00437 cvSetReal2D( object_points_ex, 2, 0, 127) ;
00438 cvSetReal2D( object_points_ex, 2, 1, 203 );
00439 cvSetReal2D( object_points_ex, 2, 2, 0.0 );
00440
00441 cvSetReal2D( object_points_ex, 3, 0, 127);
00442 cvSetReal2D( object_points_ex, 3, 1, 0 );
00443 cvSetReal2D( object_points_ex, 3, 2, 0.0 );
00444
00445 cvFindExtrinsicCameraParams2( object_points_ex,
00446 image_points_ex,
00447 itsIntrinsicMatrix,
00448 itsDistortionCoeffs,
00449 itsCameraRotation,
00450 itsCameraTranslation);
00451
00452 cvReleaseMat( &image_points_ex);
00453 cvReleaseMat( &object_points_ex);
00454
00455
00456 }
00457 return corners;
00458
00459 }
00460
00461
00462 void HeliPose::displayExtrinsic(Image<byte>& img)
00463 {
00464 int NUM_GRID = 12;
00465 CvMat *my_3d_point = cvCreateMat( 3, NUM_GRID * NUM_GRID + 1, CV_64FC1);
00466 CvMat *my_image_point = cvCreateMat( 2, NUM_GRID * NUM_GRID + 1, CV_64FC1);
00467
00468 for ( int i = 0; i < NUM_GRID; i++){
00469 for ( int j = 0; j < NUM_GRID; j++){
00470 cvSetReal2D( my_3d_point, 0, i * NUM_GRID + j, (i * 50));
00471 cvSetReal2D( my_3d_point, 1, i * NUM_GRID + j, (j * 50));
00472 cvSetReal2D( my_3d_point, 2, i * NUM_GRID + j, 0.0);
00473 }
00474 }
00475
00476 cvSetReal2D( my_3d_point, 0, NUM_GRID*NUM_GRID, 0.0);
00477 cvSetReal2D( my_3d_point, 1, NUM_GRID*NUM_GRID, 0.0);
00478 cvSetReal2D( my_3d_point, 2, NUM_GRID*NUM_GRID, 15);
00479
00480
00481 cvProjectPoints2( my_3d_point,
00482 itsCameraRotation,
00483 itsCameraTranslation,
00484 itsIntrinsicMatrix,
00485 itsDistortionCoeffs,
00486 my_image_point);
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497 for ( int i = 0; i < NUM_GRID; i++){
00498 for ( int j = 0; j < NUM_GRID-1; j++){
00499 int im_x1 = (int)cvGetReal2D( my_image_point, 0, i * NUM_GRID + j);
00500 int im_y1 = (int)cvGetReal2D( my_image_point, 1, i * NUM_GRID + j);
00501 int im_x2 = (int)cvGetReal2D( my_image_point, 0, i * NUM_GRID + j+1);
00502 int im_y2 = (int)cvGetReal2D( my_image_point, 1, i * NUM_GRID + j+1);
00503
00504 cvLine( img2ipl(img), cvPoint( im_x1, im_y1), cvPoint( im_x2, im_y2), CV_RGB( 0, 255, 0), 1);
00505 }
00506 }
00507 for ( int j = 0; j < NUM_GRID; j++){
00508 for ( int i = 0; i < NUM_GRID-1; i++){
00509 int im_x1 = (int)cvGetReal2D( my_image_point, 0, i * NUM_GRID + j);
00510 int im_y1 = (int)cvGetReal2D( my_image_point, 1, i * NUM_GRID + j);
00511 int im_x2 = (int)cvGetReal2D( my_image_point, 0, (i+1) * NUM_GRID + j);
00512 int im_y2 = (int)cvGetReal2D( my_image_point, 1, (i+1) * NUM_GRID + j);
00513
00514 cvLine( img2ipl(img), cvPoint( im_x1, im_y1), cvPoint( im_x2, im_y2), CV_RGB( 0, 255, 0), 1);
00515 }
00516 }
00517
00518 int im_x0 = (int)cvGetReal2D( my_image_point, 0, 0);
00519 int im_y0 = (int)cvGetReal2D( my_image_point, 1, 0);
00520 int im_x = (int)cvGetReal2D( my_image_point, 0, NUM_GRID*NUM_GRID);
00521 int im_y = (int)cvGetReal2D( my_image_point, 1, NUM_GRID*NUM_GRID);
00522 cvLine( img2ipl(img), cvPoint( im_x0, im_y0), cvPoint( im_x, im_y), CV_RGB( 255, 0, 0), 2);
00523
00524 cvReleaseMat( &my_3d_point);
00525 cvReleaseMat( &my_image_point);
00526
00527 }
00528
00529 HeliPose::Pose HeliPose::getPose()
00530 {
00531 pthread_mutex_lock(&itsPoseLock);
00532 Pose pose = itsCurrentPose;
00533 pthread_mutex_unlock(&itsPoseLock);
00534 return pose;
00535 }
00536
00537
00538 Image<PixRGB<byte> > HeliPose::getImg()
00539 {
00540 pthread_mutex_lock(&itsImgLock);
00541 Image<PixRGB<byte> > img = itsCurrentImg;
00542 pthread_mutex_unlock(&itsImgLock);
00543
00544 return img;
00545 }
00546
00547
00548 void HeliPose::run_cameraPose()
00549 {
00550 running = true;
00551
00552 while(running)
00553 {
00554 GenericFrame input = itsIfs->readFrame();
00555
00556 pthread_mutex_lock(&itsImgLock);
00557 itsCurrentImg = input.asRgb();
00558 pthread_mutex_unlock(&itsImgLock);
00559
00560
00561 CvSeq* square = findSquares(itsCurrentImg, itsStorage,
00562 1500,
00563 15000,
00564 0.3
00565 );
00566 std::vector<Point2D<int> > corners = getRectangle(square);
00567
00568
00569
00570
00571
00572 Point2D<float> center(0,0);
00573 for(uint i=0; i<corners.size(); i++)
00574 {
00575 center.i += corners[i].i;
00576 center.j += corners[i].j;
00577 }
00578 center /= corners.size();
00579
00580
00581 if (itsDebug)
00582 {
00583 if (corners.size() > 0)
00584 {
00585 for(uint i=0; i<corners.size(); i++)
00586 drawCircle(itsCurrentImg, corners[i], 6, PixRGB<byte>(255,0,0), 4);
00587 drawCircle(itsCurrentImg, Point2D<int>(center), 6, PixRGB<byte>(0,255,0), 4);
00588 }
00589
00590
00591 itsOfs->writeRGB(itsCurrentImg, "HeliPose", FrameInfo("heliPose", SRC_POS));
00592 }
00593
00594
00595 pthread_mutex_lock(&itsPoseLock);
00596
00597 if (corners.size() > 0)
00598 {
00599 itsCurrentPose.translation = Point3D<float>(center.i - (320/2), center.j - (240/2), 0);
00600 itsCurrentPose.valid = true;
00601 } else {
00602 itsCurrentPose.valid = false;
00603 }
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623 pthread_mutex_unlock(&itsPoseLock);
00624
00625 cvClearMemStorage(itsStorage);
00626
00627
00628
00629 usleep(10000);
00630 }
00631
00632
00633
00634 pthread_exit(0);
00635 }
00636
00637 void HeliPose::run_imuPose()
00638 {
00639 running = true;
00640
00641 while(running)
00642 {
00643 IMU_SFE_Atomic::IMUData imuData = itsIMU->readIMUData();
00644
00645 pthread_mutex_lock(&itsPoseLock);
00646 Pose currentPose = itsCurrentPose;
00647 pthread_mutex_unlock(&itsPoseLock);
00648
00649
00650 currentPose.rotation.z += (imuData.yaw*itsRotationScale.z) - itsRotationBias.z;
00651 currentPose.velocity.x += (imuData.accelX*itsVelocityScale.x) - itsVelocityBias.x;
00652 currentPose.translation.x += currentPose.velocity.x*itsVelocityScale.x;
00653
00654
00655
00656 pthread_mutex_lock(&itsPoseLock);
00657
00658 itsCurrentPose.valid = true;
00659 itsCurrentPose.translation = currentPose.translation;
00660 itsCurrentPose.velocity = currentPose.velocity;
00661 itsCurrentPose.rotation = currentPose.rotation;
00662 itsCurrentPose.accelX = imuData.accelX*itsVelocityScale.x;
00663 itsCurrentPose.accelY = imuData.accelY;
00664 itsCurrentPose.accelZ = imuData.accelZ;
00665 itsCurrentPose.roll = imuData.roll;
00666 itsCurrentPose.pitch = imuData.pitch;
00667 itsCurrentPose.yaw = imuData.yaw*itsRotationScale.z;
00668
00669 pthread_mutex_unlock(&itsPoseLock);
00670
00671
00672 usleep(10000);
00673 }
00674
00675
00676
00677 pthread_exit(0);
00678 }
00679
00680 void HeliPose::getIMUBias()
00681 {
00682
00683
00684
00685 pthread_mutex_lock(&itsPoseLock);
00686 for(int i=0; i<50; i++)
00687 {
00688 IMU_SFE_Atomic::IMUData imuData = itsIMU->readIMUData();
00689
00690 float yawVal = imuData.yaw*itsRotationScale.z;
00691 float xAcc = imuData.accelX*itsVelocityScale.x;
00692
00693 itsRotationBias.z = onlineMean(itsRotationBias.z, yawVal, i+1);
00694 itsVelocityBias.x = onlineMean(itsVelocityBias.x, xAcc, i+1);
00695 LINFO("%f %f",
00696 itsRotationBias.z, itsVelocityBias.x);
00697 }
00698
00699 itsCurrentPose.translation = Point3D<float>(0,0,0);
00700 itsCurrentPose.rotation = Point3D<float>(0,0,0);
00701 itsCurrentPose.velocity = Point3D<float>(0,0,0);
00702 pthread_mutex_unlock(&itsPoseLock);
00703
00704 }
00705
00706
00707
00708
00709
00710
00711