45 #include <geometry_msgs/Pose.h> 
   46 #include <geometry_msgs/PoseArray.h> 
   47 #include <geometry_msgs/Twist.h> 
   53 #include <visp3/core/vpConfig.h> 
   54 #include <visp3/core/vpExponentialMap.h> 
   55 #include <visp3/core/vpImageConvert.h> 
   56 #include <visp3/core/vpMomentAreaNormalized.h> 
   57 #include <visp3/core/vpMomentBasic.h> 
   58 #include <visp3/core/vpMomentCentered.h> 
   59 #include <visp3/core/vpMomentDatabase.h> 
   60 #include <visp3/core/vpMomentGravityCenter.h> 
   61 #include <visp3/core/vpMomentGravityCenterNormalized.h> 
   62 #include <visp3/core/vpMomentObject.h> 
   63 #include <visp3/core/vpPixelMeterConversion.h> 
   64 #include <visp3/core/vpPoint.h> 
   65 #include <visp3/core/vpTime.h> 
   66 #include <visp3/detection/vpDetectorAprilTag.h> 
   67 #include <visp3/gui/vpDisplayX.h> 
   68 #include <visp3/visual_features/vpFeatureBuilder.h> 
   69 #include <visp3/visual_features/vpFeatureMomentAreaNormalized.h> 
   70 #include <visp3/visual_features/vpFeatureMomentGravityCenterNormalized.h> 
   71 #include <visp3/visual_features/vpFeatureVanishingPoint.h> 
   72 #include <visp3/vs/vpServo.h> 
   73 #include <visp3/vs/vpServoDisplay.h> 
   78 #if VISP_VERSION_INT > VP_VERSION_INT( 3, 2, 0 ) // ViSP >= 3.0.0 
   92     ROS_WARN( 
"No tag size parameter has been found, using default value : 0.14 meters." );
 
  101   if ( nh.
hasParam( 
"/vs/distanceToTag" ) )
 
  103     nh.
getParam( 
"/vs/distanceToTag", res );
 
  108     ROS_WARN( 
"No distance to tag parameter has been found, using default value : 1.5 meters." );
 
  114 compareImagePoint( std::pair< size_t, vpImagePoint > p1, std::pair< size_t, vpImagePoint > p2 )
 
  116   return ( p1.second.get_v() < p2.second.get_v() );
 
  123   virtual ~bebopVSNodelet();
 
  135   bool bebop_has_been_setup;
 
  137   vpDisplayX m_display;
 
  138   bool m_display_setup;
 
  139   vpImage< unsigned char > I;
 
  148   vpDetectorAprilTag m_detector;
 
  149   vpCameraParameters m_cam;
 
  151   bool m_vec_ip_has_been_sorted;
 
  152   std::vector< std::pair< size_t, vpImagePoint > > m_vec_ip_sorted;
 
  154   vpVelocityTwistMatrix m_cVe;
 
  164   vpMomentObject m_obj, m_obj_d;
 
  165   vpMomentDatabase mdb, mdb_d;
 
  167   vpMomentGravityCenter mg, mg_d;
 
  168   vpMomentCentered mc, mc_d;
 
  169   vpMomentAreaNormalized man, man_d;
 
  170   vpMomentGravityCenterNormalized mgn, mgn_d;
 
  172   vpFeatureMomentGravityCenterNormalized s_mgn, s_mgn_d;
 
  173   vpFeatureMomentAreaNormalized s_man, s_man_d;
 
  174   vpFeatureVanishingPoint s_vp, s_vp_d;
 
  177   vpColVector velocityToPosition( vpColVector &vel_cmd, 
double delta_t );
 
  182 bebopVSNodelet::bebopVSNodelet()
 
  183   : bebop_has_been_setup( false )
 
  184   , m_display_setup( false )
 
  185   , m_tagSize( getTagSize( m_nh ) )   
 
  186   , m_Z_d( getDistanceToTag( m_nh ) ) 
 
  187   , m_cameraTilt( 0.0 )
 
  189   , m_detector( vpDetectorAprilTag::TAG_36h11 )
 
  190   , m_vec_ip_has_been_sorted( false )
 
  191   , m_eJe( vpMatrix( 6, 4, 0 ) )
 
  200   , s_mgn( mdb, 
A, B, C )
 
  201   , s_mgn_d( mdb_d, 
A, B, C )
 
  202   , s_man( mdb, 
A, B, C )
 
  203   , s_man_d( mdb_d, 
A, B, C )
 
  207 bebopVSNodelet::~bebopVSNodelet() { m_task.kill(); }
 
  210 bebopVSNodelet::onInit()
 
  213   m_nh = getNodeHandle();
 
  214   std::stringstream params_str;
 
  215   params_str << 
"TAG PARAMETERS (meters) : tagSize : " << m_tagSize << 
" - distanceToTag : " << m_Z_d << std::endl;
 
  220   NODELET_INFO( 
"Setting up publisher and subscriber..." );
 
  221   m_pubTwist = m_nh.advertise< geometry_msgs::Twist >( 
"cmd_moveby", 1000 );
 
  222   m_subImg   = m_nh.subscribe( 
"image_raw", 1000, &bebopVSNodelet::imageCallback, 
this );
 
  223   m_pubCam   = m_nh.advertise< geometry_msgs::Twist >( 
"camera_control", 1000 );
 
  224   NODELET_INFO( 
"Publisher and subscriber set up. Waiting for image callbacks..." );
 
  228 bebopVSNodelet::imageCallback( 
const sensor_msgs::ImageConstPtr &msg )
 
  230   if ( !bebop_has_been_setup )
 
  232     geometry_msgs::Twist out_cam_ctrl;
 
  233     out_cam_ctrl.linear.x  = 0;
 
  234     out_cam_ctrl.linear.y  = 0;
 
  235     out_cam_ctrl.linear.z  = 0;
 
  236     out_cam_ctrl.angular.x = 0;
 
  237     out_cam_ctrl.angular.y = m_cameraTilt;
 
  238     out_cam_ctrl.angular.z = m_cameraPan;
 
  239     m_pubCam.publish( out_cam_ctrl );
 
  241     bebop_has_been_setup = 
true;
 
  242     NODELET_INFO( 
"Setting desired camera orientation..." );
 
  246   vpImageConvert::convert( frame, I );
 
  248   if ( !m_display_setup )
 
  250     m_display.init( I, 100, 100, 
"DRONE VIEW" );
 
  251     m_display_setup = 
true;
 
  253   vpDisplay::display( I );
 
  255   std::vector< vpHomogeneousMatrix > cMo_vec;
 
  256   m_detector.detect( I, m_tagSize, m_cam, cMo_vec ); 
 
  258   geometry_msgs::Twist out_cmd_pos;
 
  260   if ( m_detector.getNbObjects() == 1 )
 
  263     std::vector< vpImagePoint > vec_ip = m_detector.getPolygon( 0 );
 
  264     std::vector< vpPoint > vec_P;
 
  265     for ( 
size_t i = 0; i < vec_ip.size(); i++ )
 
  268       vpPixelMeterConversion::convertPoint( m_cam, vec_ip[i], x, y );
 
  272       vec_P.push_back( P );
 
  276     m_obj.setType( vpMomentObject::DENSE_POLYGON ); 
 
  277     m_obj.fromVector( vec_P );                      
 
  283     mdb.updateAll( m_obj );     
 
  286     man.setDesiredArea( area ); 
 
  290     s_mgn.update( 
A, B, C );
 
  291     s_mgn.compute_interaction();
 
  292     s_man.update( 
A, B, C );
 
  293     s_man.compute_interaction();
 
  298     if ( !m_vec_ip_has_been_sorted )
 
  300       for ( 
size_t i = 0; i < vec_ip.size(); i++ )
 
  303         std::pair< size_t, vpImagePoint > index_pair = std::pair< size_t, vpImagePoint >( i, vec_ip[i] );
 
  304         m_vec_ip_sorted.push_back( index_pair );
 
  308       std::sort( m_vec_ip_sorted.begin(), m_vec_ip_sorted.end(), compareImagePoint );
 
  310       m_vec_ip_has_been_sorted = 
true;
 
  314     vpFeatureBuilder::create( s_vp, m_cam, vec_ip[m_vec_ip_sorted[0].first], vec_ip[m_vec_ip_sorted[1].first],
 
  315                               vec_ip[m_vec_ip_sorted[2].first], vec_ip[m_vec_ip_sorted[3].first],
 
  316                               vpFeatureVanishingPoint::selectAtanOneOverRho() );
 
  318     m_task.set_cVe( m_cVe );
 
  319     m_task.set_eJe( m_eJe );
 
  322     vpColVector ve = m_task.computeControlLaw();
 
  323     std::stringstream ve_str;
 
  324     ve_str << 
"ve: " << ve.t() << std::endl;
 
  328     vpColVector cmd_pos = velocityToPosition( ve, 1.0 );
 
  332     out_cmd_pos.linear.x  = cmd_pos[0];
 
  333     out_cmd_pos.linear.y  = cmd_pos[1];
 
  334     out_cmd_pos.linear.z  = cmd_pos[2];
 
  335     out_cmd_pos.angular.x = 0;
 
  336     out_cmd_pos.angular.y = 0;
 
  337     out_cmd_pos.angular.z = cmd_pos[3];
 
  338     m_pubTwist.publish( out_cmd_pos );
 
  340     for ( 
size_t i = 0; i < 4; i++ )
 
  342       vpDisplay::displayCross( I, vec_ip[i], 15, vpColor::red, 1 );
 
  343       std::stringstream ss;
 
  345       vpDisplay::displayText( I, vec_ip[i] + vpImagePoint( 15, 15 ), ss.str(), vpColor::green );
 
  349     vpDisplay::displayPolygon( I, vec_ip, vpColor::green,
 
  351     vpDisplay::displayCross( I, m_detector.getCog( 0 ), 15, vpColor::green,
 
  353     vpDisplay::displayLine( I, 0, 
static_cast< int >( m_cam.get_u0() ), 
static_cast< int >( I.getHeight() ) - 1,
 
  354                             static_cast< int >( m_cam.get_u0() ), vpColor::red,
 
  356     vpDisplay::displayLine( I, 
static_cast< int >( m_cam.get_v0() ), 0, 
static_cast< int >( m_cam.get_v0() ),
 
  357                             static_cast< int >( I.getWidth() ) - 1, vpColor::red,
 
  361     vpDisplay::displayLine( I, vec_ip[m_vec_ip_sorted[0].first], vec_ip[m_vec_ip_sorted[1].first], vpColor::red, 1,
 
  363     vpDisplay::displayLine( I, vec_ip[m_vec_ip_sorted[2].first], vec_ip[m_vec_ip_sorted[3].first], vpColor::red, 1,
 
  366     vpDisplay::displayText( I, 10, 10, 
"Click to exit", vpColor::red );
 
  367     vpDisplay::flush( I );
 
  372     std::stringstream sserr;
 
  373     sserr << 
"Failed to detect an Apriltag, or detected multiple ones";
 
  374     vpDisplay::displayText( I, 120, 20, sserr.str(), vpColor::red );
 
  375     vpDisplay::flush( I );
 
  378     out_cmd_pos.linear.x  = 0;
 
  379     out_cmd_pos.linear.y  = 0;
 
  380     out_cmd_pos.linear.z  = 0;
 
  381     out_cmd_pos.angular.x = 0;
 
  382     out_cmd_pos.angular.y = 0;
 
  383     out_cmd_pos.angular.z = 0;
 
  384     m_pubTwist.publish( out_cmd_pos );
 
  389 bebopVSNodelet::initVS()
 
  393   m_detector.setAprilTagQuadDecimate( 4.0 );
 
  394   m_detector.setAprilTagNbThreads( 4 );
 
  395   m_detector.setDisplayTag( 
true );
 
  397   m_cam.initPersProjWithoutDistortion( 531.9213063, 520.8495788, 429.133986, 240.9464457 );
 
  398   m_cam.printParameters();
 
  401   vpAdaptiveGain lambda = vpAdaptiveGain( 1.5, 0.7, 30 );
 
  402   m_task.setServo( vpServo::EYEINHAND_L_cVe_eJe );
 
  403   m_task.setInteractionMatrixType( vpServo::CURRENT );
 
  404   m_task.setLambda( lambda );
 
  406   if ( m_nh.hasParam( 
"/vs/cameraTilt" ) )
 
  408     m_nh.getParam( 
"/vs/cameraTilt", m_cameraTilt );
 
  412     ROS_WARN( 
"No camera tilt value found, using default value : -15 degrees." );
 
  413     m_cameraTilt = -15.0;
 
  416   if ( m_nh.hasParam( 
"/vs/cameraPan" ) )
 
  418     m_nh.getParam( 
"/vs/cameraPan", m_cameraPan );
 
  422     ROS_WARN( 
"No camera pan value found, using default value : 0 degrees." );
 
  426   std::stringstream camparams_str;
 
  427   camparams_str << 
"CAMERA PARAMETERS (degrees) : camera Tilt : " << m_cameraTilt << 
" - camera pan : " << m_cameraPan
 
  431   vpRxyzVector c1_rxyz_c2( vpMath::rad( m_cameraTilt ), vpMath::rad( m_cameraPan ), 0 );
 
  433   vpRotationMatrix c1Rc2( c1_rxyz_c2 );                      
 
  434   vpHomogeneousMatrix c1Mc2( vpTranslationVector(), c1Rc2 ); 
 
  436   vpRotationMatrix c1Re{ 0, 1, 0, 0, 0, 1, 1, 0, 0 }; 
 
  437   vpTranslationVector c1te( 0, 0, -0.09 );            
 
  438   vpHomogeneousMatrix c1Me( c1te, c1Re );             
 
  440   vpHomogeneousMatrix c2Me = c1Mc2.inverse() * c1Me; 
 
  443   m_cVe = vpVelocityTwistMatrix( c2Me );
 
  444   m_task.set_cVe( m_cVe );
 
  452   double X[4] = { m_tagSize / 2., m_tagSize / 2., -m_tagSize / 2., -m_tagSize / 2. };
 
  453   double Y[4] = { m_tagSize / 2., -m_tagSize / 2., -m_tagSize / 2., m_tagSize / 2. };
 
  455   std::vector< vpPoint > vec_P, vec_P_d;
 
  456   for ( 
int i = 0; i < 4; i++ )
 
  458     vpPoint P_d( X[i], Y[i], 0 );
 
  459     vpHomogeneousMatrix cdMo( 0, 0, m_Z_d, 0, 0, 0 );
 
  461     vec_P_d.push_back( P_d );
 
  465   m_obj_d.setType( vpMomentObject::DENSE_POLYGON ); 
 
  466   m_obj_d.fromVector( vec_P_d );                    
 
  468   mb_d.linkTo( mdb_d );       
 
  469   mg_d.linkTo( mdb_d );       
 
  470   mc_d.linkTo( mdb_d );       
 
  471   man_d.linkTo( mdb_d );      
 
  472   mgn_d.linkTo( mdb_d );      
 
  473   mdb_d.updateAll( m_obj_d ); 
 
  477   if ( m_obj_d.getType() == vpMomentObject::DISCRETE )
 
  478     area = mb_d.get( 2, 0 ) + mb_d.get( 0, 2 );
 
  480     area = mb_d.get( 0, 0 );
 
  482   man_d.setDesiredArea( area );
 
  488   m_task.addFeature( s_mgn, s_mgn_d );
 
  489   m_task.addFeature( s_man, s_man_d );
 
  490   m_task.addFeature( s_vp, s_vp_d, vpFeatureVanishingPoint::selectAtanOneOverRho() );
 
  493   s_mgn_d.update( 
A, B, C );
 
  494   s_mgn_d.compute_interaction();
 
  496   s_man_d.update( 
A, B, C );
 
  497   s_man_d.compute_interaction();
 
  500   s_vp_d.setAtanOneOverRho( 0 );
 
  501   s_vp_d.setAlpha( 0 );
 
  507 bebopVSNodelet::velocityToPosition( vpColVector &vel_cmd, 
double delta_t )
 
  510   if ( vel_cmd.size() != 4 )
 
  512     ROS_ERROR( 
"Can't compute velocity : dimension of the velocity vector should be equal to 4." );
 
  513     res << 0., 0., 0., 0.;
 
  524   vpHomogeneousMatrix M = vpExponentialMap::direct( ve, delta_t );
 
  526   double epsilon = ( std::numeric_limits< double >::epsilon() );
 
  527   if ( std::abs( M.getRotationMatrix().getThetaUVector()[0] ) >= 
epsilon )
 
  529     ROS_ERROR( 
"Can't compute velocity : rotation around X axis should be 0." );
 
  530     res << 0., 0., 0., 0.;
 
  533   if ( std::abs( M.getRotationMatrix().getThetaUVector()[1] ) >= epsilon )
 
  535     ROS_ERROR( 
"Can't compute velocity : rotation around Y axis should be 0." );
 
  536     res << 0., 0., 0., 0.;
 
  540   float dThetaZ         = 
static_cast< float >( M.getRotationMatrix().getThetaUVector()[2] );
 
  541   vpTranslationVector t = M.getTranslationVector();
 
  543   res << t[0], t[1], t[2], dThetaZ;