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;