tutorial-franka-real-ibvs-apriltag.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2021 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  *****************************************************************************/
32 
58 #include <iostream>
59 
60 #include <visp3/core/vpCameraParameters.h>
61 #include <visp3/detection/vpDetectorAprilTag.h>
62 #include <visp3/gui/vpDisplayOpenCV.h>
63 #include <visp3/gui/vpPlot.h>
64 #include <visp3/io/vpImageIo.h>
65 #include <visp3/visual_features/vpFeatureBuilder.h>
66 #include <visp3/visual_features/vpFeaturePoint.h>
67 #include <visp3/vs/vpServo.h>
68 #include <visp3/vs/vpServoDisplay.h>
69 
70 #include <visp3/robot/vpRobotFranka.h>
71 #include <visp3/sensor/vpRealSense2.h>
72 
73 #if defined( VISP_HAVE_REALSENSE2 ) && ( VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11 ) && \
74  ( defined( VISP_HAVE_OPENCV ) ) && defined( VISP_HAVE_FRANKA )
75 
76 void
77 display_point_trajectory( const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &vip,
78  std::vector< vpImagePoint > *traj_vip )
79 {
80  for ( size_t i = 0; i < vip.size(); i++ )
81  {
82  if ( traj_vip[i].size() )
83  {
84  // Add the point only if distance with the previous > 1 pixel
85  if ( vpImagePoint::distance( vip[i], traj_vip[i].back() ) > 1. )
86  {
87  traj_vip[i].push_back( vip[i] );
88  }
89  }
90  else
91  {
92  traj_vip[i].push_back( vip[i] );
93  }
94  }
95  for ( size_t i = 0; i < vip.size(); i++ )
96  {
97  for ( size_t j = 1; j < traj_vip[i].size(); j++ )
98  {
99  vpDisplay::displayLine( I, traj_vip[i][j - 1], traj_vip[i][j], vpColor::green, 2 );
100  }
101  }
102 }
103 
104 int
105 main( int argc, char **argv )
106 {
107  double opt_tagSize = 0.08;
108  std::string opt_robot_ip = "192.168.1.1";
109  std::string opt_eMc_filename = "";
110  bool display_tag = true;
111  int opt_quad_decimate = 2;
112  bool opt_verbose = false;
113  bool opt_plot = false;
114  bool opt_adaptive_gain = false;
115  bool opt_task_sequencing = false;
116  double convergence_threshold = 0.00005;
117 
118  for ( int i = 1; i < argc; i++ )
119  {
120  if ( std::string( argv[i] ) == "--tag_size" && i + 1 < argc )
121  {
122  opt_tagSize = std::stod( argv[i + 1] );
123  }
124  else if ( std::string( argv[i] ) == "--ip" && i + 1 < argc )
125  {
126  opt_robot_ip = std::string( argv[i + 1] );
127  }
128  else if ( std::string( argv[i] ) == "--eMc" && i + 1 < argc )
129  {
130  opt_eMc_filename = std::string( argv[i + 1] );
131  }
132  else if ( std::string( argv[i] ) == "--verbose" )
133  {
134  opt_verbose = true;
135  }
136  else if ( std::string( argv[i] ) == "--plot" )
137  {
138  opt_plot = true;
139  }
140  else if ( std::string( argv[i] ) == "--adaptive_gain" )
141  {
142  opt_adaptive_gain = true;
143  }
144  else if ( std::string( argv[i] ) == "--task_sequencing" )
145  {
146  opt_task_sequencing = true;
147  }
148  else if ( std::string( argv[i] ) == "--quad_decimate" && i + 1 < argc )
149  {
150  opt_quad_decimate = std::stoi( argv[i + 1] );
151  }
152  else if ( std::string( argv[i] ) == "--no-convergence-threshold" )
153  {
154  convergence_threshold = 0.;
155  }
156  else if ( std::string( argv[i] ) == "--help" || std::string( argv[i] ) == "-h" )
157  {
158  std::cout
159  << argv[0] << " [--ip <default " << opt_robot_ip << ">] [--tag_size <marker size in meter; default "
160  << opt_tagSize << ">] [--eMc <eMc extrinsic file>] "
161  << "[--quad_decimate <decimation; default " << opt_quad_decimate
162  << ">] [--adaptive_gain] [--plot] [--task_sequencing] [--no-convergence-threshold] [--verbose] [--help] [-h]"
163  << "\n";
164  return EXIT_SUCCESS;
165  }
166  }
167 
168  vpRobotFranka robot;
169 
170  try
171  {
172  std::cout << "Try to connect to robot ip: " << opt_robot_ip << std::endl;
173  robot.connect( opt_robot_ip );
174  std::cout << "Panda robot connected" << std::endl;
175 
176  vpRealSense2 rs;
177  rs2::config config;
178  unsigned int width = 640, height = 480;
179  config.enable_stream( RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_RGBA8, 30 );
180  config.enable_stream( RS2_STREAM_DEPTH, 640, 480, RS2_FORMAT_Z16, 30 );
181  config.enable_stream( RS2_STREAM_INFRARED, 640, 480, RS2_FORMAT_Y8, 30 );
182  std::cout << "Try to connect to Realsense camera " << std::endl;
183  rs.open( config );
184  std::cout << "Camera connected" << std::endl;
185 
186  // Get camera extrinsics
187  vpPoseVector ePc;
188  // Set camera extrinsics default values corresponding to
189  // - an Intel RealSense D435 camera
190  // - attached to the Franka end-effector using the mechanical interface corresponding to
191  // the cad model given in franka-rs-D435-camera-holder.stl file
192  ePc[0] = 0.0564668;
193  ePc[1] = -0.0375079;
194  ePc[2] = -0.150416 ;
195  ePc[3] = 0.0102548;
196  ePc[4] = -0.0012236;
197  ePc[5] = 1.5412;
198 
199  // If provided, read camera extrinsics from --eMc <file>
200  if ( !opt_eMc_filename.empty() )
201  {
202  ePc.loadYAML( opt_eMc_filename, ePc );
203  }
204  else
205  {
206  std::cout << "Warning, opt_eMc_filename is empty! Use hard coded values."
207  << "\n";
208  }
209  vpHomogeneousMatrix eMc( ePc );
210  std::cout << "eMc:\n" << eMc << "\n";
211  std::cout << "ePc:\n" << vpPoseVector(eMc).t() << "\n";
212 
213  // Get camera intrinsics
214  vpCameraParameters cam =
215  rs.getCameraParameters( RS2_STREAM_COLOR, vpCameraParameters::perspectiveProjWithDistortion );
216  std::cout << "cam:\n" << cam << "\n";
217 
218  vpImage< unsigned char > I( height, width );
219 
220  vpDisplayOpenCV dc( I, 10, 10, "Color image" );
221 
222  vpDetectorAprilTag::vpAprilTagFamily tagFamily = vpDetectorAprilTag::TAG_36h11;
223  vpDetectorAprilTag::vpPoseEstimationMethod poseEstimationMethod = vpDetectorAprilTag::HOMOGRAPHY_VIRTUAL_VS;
224  // vpDetectorAprilTag::vpPoseEstimationMethod poseEstimationMethod = vpDetectorAprilTag::BEST_RESIDUAL_VIRTUAL_VS;
225  vpDetectorAprilTag detector( tagFamily );
226  detector.setAprilTagPoseEstimationMethod( poseEstimationMethod );
227  detector.setDisplayTag( display_tag );
228  detector.setAprilTagQuadDecimate( opt_quad_decimate );
229 
230  // Servo
231  vpHomogeneousMatrix cMo, oMo;
232 
233  // Desired pose used to compute the desired features
234  vpHomogeneousMatrix cdMo( vpTranslationVector( 0, 0, opt_tagSize * 3 ), // 3 times tag with along camera z axis
235  vpRotationMatrix( { 1, 0, 0, 0, -1, 0, 0, 0, -1 } ) );
236 
237  // Create visual features
238  std::vector< vpFeaturePoint > p( 4 ), pd( 4 ); // We use 4 points
239 
240  // Define 4 3D points corresponding to the CAD model of the Apriltag
241  std::vector< vpPoint > point( 4 );
242  point[0].setWorldCoordinates( -opt_tagSize / 2., -opt_tagSize / 2., 0 );
243  point[1].setWorldCoordinates( opt_tagSize / 2., -opt_tagSize / 2., 0 );
244  point[2].setWorldCoordinates( opt_tagSize / 2., opt_tagSize / 2., 0 );
245  point[3].setWorldCoordinates( -opt_tagSize / 2., opt_tagSize / 2., 0 );
246 
247  vpServo task;
248  // Add the 4 visual feature points
249  for ( size_t i = 0; i < p.size(); i++ )
250  {
251  task.addFeature( p[i], pd[i] );
252  }
253  task.setServo( vpServo::EYEINHAND_CAMERA );
254  task.setInteractionMatrixType( vpServo::CURRENT );
255 
256  if ( opt_adaptive_gain )
257  {
258  vpAdaptiveGain lambda( 1.5, 0.4, 30 ); // lambda(0)=4, lambda(oo)=0.4 and lambda'(0)=30
259  task.setLambda( lambda );
260  }
261  else
262  {
263  task.setLambda( 0.5 );
264  }
265 
266  vpPlot *plotter = nullptr;
267  int iter_plot = 0;
268 
269  if ( opt_plot )
270  {
271  plotter = new vpPlot( 2, static_cast< int >( 250 * 2 ), 500, static_cast< int >( I.getWidth() ) + 80, 10,
272  "Real time curves plotter" );
273  plotter->setTitle( 0, "Visual features error" );
274  plotter->setTitle( 1, "Camera velocities" );
275  plotter->initGraph( 0, 8 );
276  plotter->initGraph( 1, 6 );
277  plotter->setLegend( 0, 0, "error_feat_p1_x" );
278  plotter->setLegend( 0, 1, "error_feat_p1_y" );
279  plotter->setLegend( 0, 2, "error_feat_p2_x" );
280  plotter->setLegend( 0, 3, "error_feat_p2_y" );
281  plotter->setLegend( 0, 4, "error_feat_p3_x" );
282  plotter->setLegend( 0, 5, "error_feat_p3_y" );
283  plotter->setLegend( 0, 6, "error_feat_p4_x" );
284  plotter->setLegend( 0, 7, "error_feat_p4_y" );
285  plotter->setLegend( 1, 0, "vc_x" );
286  plotter->setLegend( 1, 1, "vc_y" );
287  plotter->setLegend( 1, 2, "vc_z" );
288  plotter->setLegend( 1, 3, "wc_x" );
289  plotter->setLegend( 1, 4, "wc_y" );
290  plotter->setLegend( 1, 5, "wc_z" );
291  }
292 
293  bool final_quit = false;
294  bool has_converged = false;
295  bool send_velocities = false;
296  bool servo_started = false;
297  std::vector< vpImagePoint > *traj_corners = nullptr; // To memorize point trajectory
298 
299  static double t_init_servo = vpTime::measureTimeMs();
300 
301  robot.set_eMc( eMc ); // Set location of the camera wrt end-effector frame
302  robot.setRobotState( vpRobot::STATE_VELOCITY_CONTROL );
303 
304  while ( !has_converged && !final_quit )
305  {
306  double t_start = vpTime::measureTimeMs();
307 
308  rs.acquire( I );
309 
310  vpDisplay::display( I );
311 
312  std::vector< vpHomogeneousMatrix > cMo_vec;
313  detector.detect( I, opt_tagSize, cam, cMo_vec );
314 
315  {
316  std::stringstream ss;
317  ss << "Left click to " << ( send_velocities ? "stop the robot" : "servo the robot" )
318  << ", right click to quit.";
319  vpDisplay::displayText( I, 20, 20, ss.str(), vpColor::red );
320  }
321 
322  vpColVector v_c( 6 );
323 
324  // Only one tag is detected
325  if ( cMo_vec.size() == 1 )
326  {
327  cMo = cMo_vec[0];
328 
329  static bool first_time = true;
330  if ( first_time )
331  {
332  // Introduce security wrt tag positionning in order to avoid PI rotation
333  std::vector< vpHomogeneousMatrix > v_oMo( 2 ), v_cdMc( 2 );
334  v_oMo[1].buildFrom( 0, 0, 0, 0, 0, M_PI );
335  for ( size_t i = 0; i < 2; i++ )
336  {
337  v_cdMc[i] = cdMo * v_oMo[i] * cMo.inverse();
338  }
339  if ( std::fabs( v_cdMc[0].getThetaUVector().getTheta() ) <
340  std::fabs( v_cdMc[1].getThetaUVector().getTheta() ) )
341  {
342  oMo = v_oMo[0];
343  }
344  else
345  {
346  std::cout << "Desired frame modified to avoid PI rotation of the camera" << std::endl;
347  oMo = v_oMo[1]; // Introduce PI rotation
348  }
349 
350  // Compute the desired position of the features from the desired pose
351  for ( size_t i = 0; i < point.size(); i++ )
352  {
353  vpColVector cP, p_;
354  point[i].changeFrame( cdMo * oMo, cP );
355  point[i].projection( cP, p_ );
356 
357  pd[i].set_x( p_[0] );
358  pd[i].set_y( p_[1] );
359  pd[i].set_Z( cP[2] );
360  }
361  }
362 
363  // Get tag corners
364  std::vector< vpImagePoint > corners = detector.getPolygon( 0 );
365 
366  // Update visual features
367  for ( size_t i = 0; i < corners.size(); i++ )
368  {
369  // Update the point feature from the tag corners location
370  vpFeatureBuilder::create( p[i], cam, corners[i] );
371  // Set the feature Z coordinate from the pose
372  vpColVector cP;
373  point[i].changeFrame( cMo, cP );
374 
375  p[i].set_Z( cP[2] );
376  }
377 
378  if ( opt_task_sequencing )
379  {
380  if ( !servo_started )
381  {
382  if ( send_velocities )
383  {
384  servo_started = true;
385  }
386  t_init_servo = vpTime::measureTimeMs();
387  }
388  v_c = task.computeControlLaw( ( vpTime::measureTimeMs() - t_init_servo ) / 1000. );
389  }
390  else
391  {
392  v_c = task.computeControlLaw();
393  }
394 
395  // Display the current and desired feature points in the image display
396  vpServoDisplay::display( task, cam, I );
397  for ( size_t i = 0; i < corners.size(); i++ )
398  {
399  std::stringstream ss;
400  ss << i;
401  // Display current point indexes
402  vpDisplay::displayText( I, corners[i] + vpImagePoint( 15, 15 ), ss.str(), vpColor::red );
403  // Display desired point indexes
404  vpImagePoint ip;
405  vpMeterPixelConversion::convertPoint( cam, pd[i].get_x(), pd[i].get_y(), ip );
406  vpDisplay::displayText( I, ip + vpImagePoint( 15, 15 ), ss.str(), vpColor::red );
407  }
408  if ( first_time )
409  {
410  traj_corners = new std::vector< vpImagePoint >[corners.size()];
411  }
412  // Display the trajectory of the points used as features
413  display_point_trajectory( I, corners, traj_corners );
414 
415  if ( opt_plot )
416  {
417  plotter->plot( 0, iter_plot, task.getError() );
418  plotter->plot( 1, iter_plot, v_c );
419  iter_plot++;
420  }
421 
422  if ( opt_verbose )
423  {
424  std::cout << "v_c: " << v_c.t() << std::endl;
425  }
426 
427  double error = task.getError().sumSquare();
428  std::stringstream ss;
429  ss << "||error||: " << error;
430  vpDisplay::displayText( I, 20, static_cast< int >( I.getWidth() ) - 150, ss.str(), vpColor::red );
431 
432  if ( opt_verbose )
433  std::cout << ss.str() << std::endl;
434 
435  if ( !has_converged && error < convergence_threshold )
436  {
437  has_converged = true;
438  std::cout << "Servo task has converged"
439  << "\n";
440  vpDisplay::displayText( I, 100, 20, "Servo task has converged", vpColor::red );
441  }
442  if ( first_time )
443  {
444  first_time = false;
445  }
446  } // end if (cMo_vec.size() == 1)
447  else
448  {
449  v_c = 0;
450  }
451 
452  if ( !send_velocities )
453  {
454  v_c = 0;
455  }
456 
457  // Send to the robot
458  robot.setVelocity( vpRobot::CAMERA_FRAME, v_c );
459 
460  {
461  std::stringstream ss;
462  ss << "Loop time: " << vpTime::measureTimeMs() - t_start << " ms";
463  vpDisplay::displayText( I, 40, 20, ss.str(), vpColor::red );
464  }
465  vpDisplay::flush( I );
466 
467  vpMouseButton::vpMouseButtonType button;
468  if ( vpDisplay::getClick( I, button, false ) )
469  {
470  switch ( button )
471  {
472  case vpMouseButton::button1:
473  send_velocities = !send_velocities;
474  break;
475 
476  case vpMouseButton::button3:
477  final_quit = true;
478  v_c = 0;
479  break;
480 
481  default:
482  break;
483  }
484  }
485  }
486  std::cout << "Stop the robot " << std::endl;
487  robot.setRobotState( vpRobot::STATE_STOP );
488 
489  if ( opt_plot && plotter != nullptr )
490  {
491  delete plotter;
492  plotter = nullptr;
493  }
494 
495  if ( !final_quit )
496  {
497  while ( !final_quit )
498  {
499  rs.acquire( I );
500  vpDisplay::display( I );
501 
502  vpDisplay::displayText( I, 20, 20, "Click to quit the program.", vpColor::red );
503  vpDisplay::displayText( I, 40, 20, "Visual servo converged.", vpColor::red );
504 
505  if ( vpDisplay::getClick( I, false ) )
506  {
507  final_quit = true;
508  }
509 
510  vpDisplay::flush( I );
511  }
512  }
513  if ( traj_corners )
514  {
515  delete[] traj_corners;
516  }
517  }
518  catch ( const vpException &e )
519  {
520  std::cout << "ViSP exception: " << e.what() << std::endl;
521  std::cout << "Stop the robot " << std::endl;
522  robot.setRobotState( vpRobot::STATE_STOP );
523  return EXIT_FAILURE;
524  }
525  catch ( const franka::NetworkException &e )
526  {
527  std::cout << "Franka network exception: " << e.what() << std::endl;
528  std::cout << "Check if you are connected to the Franka robot"
529  << " or if you specified the right IP using --ip command line option set by default to 192.168.1.1. "
530  << std::endl;
531  return EXIT_FAILURE;
532  }
533  catch ( const std::exception &e )
534  {
535  std::cout << "Franka exception: " << e.what() << std::endl;
536  return EXIT_FAILURE;
537  }
538 
539  return 0;
540 }
541 #else
542 int
544 {
545 #if !defined( VISP_HAVE_REALSENSE2 )
546  std::cout << "Install librealsense-2.x" << std::endl;
547 #endif
548 #if ( VISP_CXX_STANDARD < VISP_CXX_STANDARD_11 )
549  std::cout << "Build ViSP with c++11 or higher compiler flag (cmake -DUSE_CXX_STANDARD=11)." << std::endl;
550 #endif
551 #if !defined( VISP_HAVE_FRANKA )
552  std::cout << "Install libfranka." << std::endl;
553 #endif
554  return 0;
555 }
556 #endif
main
int main()
Definition: tutorial-franka-real-ibvs-apriltag.cpp:543
display_point_trajectory
void display_point_trajectory(const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &vip, std::vector< vpImagePoint > *traj_vip)
Definition: tutorial-franka-coppeliasim-dual-arm.cpp:53


visp_ros
Author(s): Francois Pasteau, Fabien Spindler, Gatien Gaumerais, Alexander Oliva
autogenerated on Wed Mar 2 2022 01:13:33