37 #include <visp3/core/vpCameraParameters.h> 
   38 #include <visp3/detection/vpDetectorAprilTag.h> 
   39 #include <visp3/gui/vpDisplayOpenCV.h> 
   40 #include <visp3/gui/vpPlot.h> 
   41 #include <visp3/io/vpImageIo.h> 
   42 #include <visp3/visual_features/vpFeatureThetaU.h> 
   43 #include <visp3/visual_features/vpFeatureTranslation.h> 
   44 #include <visp3/vs/vpServo.h> 
   45 #include <visp3/vs/vpServoDisplay.h> 
   50 #include <geometry_msgs/WrenchStamped.h> 
   54                           std::vector< vpImagePoint > *traj_vip )
 
   56   for ( 
size_t i = 0; i < vip.size(); i++ )
 
   58     if ( traj_vip[i].size() )
 
   61       if ( vpImagePoint::distance( vip[i], traj_vip[i].back() ) > 1. )
 
   63         traj_vip[i].push_back( vip[i] );
 
   68       traj_vip[i].push_back( vip[i] );
 
   71   for ( 
size_t i = 0; i < vip.size(); i++ )
 
   73     for ( 
size_t j = 1; j < traj_vip[i].size(); j++ )
 
   75       vpDisplay::displayLine( I, traj_vip[i][j - 1], traj_vip[i][j], vpColor::green, 2 );
 
   81 Ta( 
const vpHomogeneousMatrix &edMe )
 
   83   vpMatrix Lx( 6, 6 ), Lw( 3, 3 ), skew_u( 3, 3 );
 
   86   vpThetaUVector tu( edMe );
 
   91   tu.extract( theta, u );
 
   92   skew_u = vpColVector::skew( u );
 
   96     Lw -= 0.5 * theta * skew_u;
 
   97     Lw += ( 1 - ( ( vpMath::sinc( theta ) ) / ( vpMath::sqr( vpMath::sinc( theta * 0.5 ) ) ) ) ) * skew_u * skew_u;
 
  100   Lx.insert( Lw, 3, 3 );
 
  105 vpColVector 
We( 6, 0 );
 
  112   We[0] = sensor_wrench.wrench.force.x;
 
  113   We[1] = sensor_wrench.wrench.force.y;
 
  114   We[2] = sensor_wrench.wrench.force.z;
 
  115   We[3] = sensor_wrench.wrench.torque.x;
 
  116   We[4] = sensor_wrench.wrench.torque.y;
 
  117   We[5] = sensor_wrench.wrench.torque.z;
 
  124   vpMatrix Lx( 6, 6 ), Lw( 3, 3 ), skew_u( 3, 3 );
 
  126   vpRotationMatrix cdRc( cdMc );
 
  127   vpThetaUVector tu( cdMc );
 
  132   tu.extract( theta, u );
 
  133   skew_u = vpColVector::skew( u );
 
  135   Lw += 0.5 * theta * skew_u;
 
  136   Lw += ( 1 - ( ( vpMath::sinc( theta ) ) / ( vpMath::sqr( vpMath::sinc( theta * 0.5 ) ) ) ) ) * skew_u * skew_u;
 
  138   Lx.insert( cdRc, 0, 0 );
 
  139   Lx.insert( Lw, 3, 3 );
 
  147   double opt_tagSize             = 0.08;
 
  148   bool display_tag               = 
true;
 
  149   int opt_quad_decimate          = 2;
 
  150   bool opt_verbose               = 
false;
 
  151   bool opt_plot                  = 
false;
 
  152   bool opt_adaptive_gain         = 
false;
 
  153   double convergence_threshold_t = 0.005, convergence_threshold_tu = 0.5;
 
  154   bool opt_coppeliasim_sync_mode = 
false;
 
  156   for ( 
int i = 1; i < argc; i++ )
 
  158     if ( std::string( argv[i] ) == 
"--tag_size" && i + 1 < argc )
 
  160       opt_tagSize = std::stod( argv[i + 1] );
 
  162     else if ( std::string( argv[i] ) == 
"--verbose" || std::string( argv[i] ) == 
"-v" )
 
  166     else if ( std::string( argv[i] ) == 
"--plot" )
 
  170     else if ( std::string( argv[i] ) == 
"--adaptive_gain" )
 
  172       opt_adaptive_gain = 
true;
 
  174     else if ( std::string( argv[i] ) == 
"--quad_decimate" && i + 1 < argc )
 
  176       opt_quad_decimate = std::stoi( argv[i + 1] );
 
  178     else if ( std::string( argv[i] ) == 
"--no-convergence-threshold" )
 
  180       convergence_threshold_t  = 0.;
 
  181       convergence_threshold_tu = 0.;
 
  183     else if ( std::string( argv[i] ) == 
"--enable-coppeliasim-sync-mode" )
 
  185       opt_coppeliasim_sync_mode = 
true;
 
  187     else if ( std::string( argv[i] ) == 
"--help" || std::string( argv[i] ) == 
"-h" )
 
  189       std::cout << argv[0] << 
"[--tag_size <marker size in meter; default " << opt_tagSize << 
">] " 
  190                 << 
"[--quad_decimate <decimation; default " << opt_quad_decimate << 
">] " 
  191                 << 
"[--adaptive_gain] " 
  193                 << 
"[--task_sequencing] " 
  194                 << 
"[--no-convergence-threshold] " 
  195                 << 
"[--enable-coppeliasim-sync-mode] " 
  196                 << 
"[--verbose] [-v] " 
  197                 << 
"[--help] [-h]" << std::endl;
 
  217     right_arm.
connect( 
"right_arm" );
 
  219     left_arm.
connect( 
"left_arm" );
 
  221     std::cout << 
"Coppeliasim sync mode enabled: " << ( opt_coppeliasim_sync_mode ? 
"yes" : 
"no" ) << std::endl;
 
  227     vpHomogeneousMatrix bMfra( vpTranslationVector( 0.040, -0.03, 0.17 ),
 
  228                                vpRotationMatrix( { 1, 0, 0, 0, 0, -1, 0, 1, 0 } ) );
 
  229     vpHomogeneousMatrix bMfla( vpTranslationVector( 0.040, 0.03, 0.17 ),
 
  230                                vpRotationMatrix( { 1, 0, 0, 0, 0, 1, 0, -1, 0 } ) );
 
  232     vpImage< unsigned char > I;
 
  236     g.
open( argc, argv );
 
  240     std::cout << 
"Image size: " << I.getWidth() << 
" x " << I.getHeight() << std::endl;
 
  241     vpCameraParameters cam;
 
  244     std::cout << cam << std::endl;
 
  245     vpDisplayOpenCV dc( I, 10, 10, 
"Color image" );
 
  247     vpDetectorAprilTag::vpAprilTagFamily tagFamily                  = vpDetectorAprilTag::TAG_36h11;
 
  248     vpDetectorAprilTag::vpPoseEstimationMethod poseEstimationMethod = vpDetectorAprilTag::HOMOGRAPHY_VIRTUAL_VS;
 
  249     vpDetectorAprilTag detector( tagFamily );
 
  250     detector.setAprilTagPoseEstimationMethod( poseEstimationMethod );
 
  251     detector.setDisplayTag( display_tag );
 
  252     detector.setAprilTagQuadDecimate( opt_quad_decimate );
 
  255     vpHomogeneousMatrix ccMo, cMo, oMo, ccMc, cdMcc, wMo, wMt;
 
  257     vpHomogeneousMatrix cdMo( vpTranslationVector( 0.0, 0.0, 0.2 ),
 
  258                               vpRotationMatrix( { 1, 0, 0, 0, -1, 0, 0, 0, -1 } ) );
 
  261     vpFeatureTranslation t( vpFeatureTranslation::cdMc );
 
  262     vpFeatureThetaU tu( vpFeatureThetaU::cdRc );
 
  264     tu.buildFrom( ccMc );
 
  266     vpFeatureTranslation td( vpFeatureTranslation::cdMc );
 
  267     vpFeatureThetaU tud( vpFeatureThetaU::cdRc );
 
  271     task.addFeature( t, td );
 
  272     task.addFeature( tu, tud );
 
  274     task.setServo( vpServo::EYEINHAND_CAMERA );
 
  275     task.setInteractionMatrixType( vpServo::CURRENT );
 
  277     if ( opt_adaptive_gain )
 
  279       std::cout << 
"Enable adaptive gain" << std::endl;
 
  280       vpAdaptiveGain lambda( 4, 1.2, 25 ); 
 
  281       task.setLambda( lambda );
 
  285       task.setLambda( 1.2 );
 
  288     vpPlot *plotter      = 
nullptr;
 
  289     vpPlot *plotter_left = 
nullptr;
 
  293       plotter = 
new vpPlot( 3, 
static_cast< int >( 250 * 2 ), 600, 
static_cast< int >( I.getWidth() ) + 80, 10,
 
  294                             "Real time curves plotter" );
 
  295       plotter->setTitle( 0, 
"Visual features error" );
 
  296       plotter->setTitle( 1, 
"Camera velocities" );
 
  297       plotter->setTitle( 2, 
"Measured wrench" );
 
  298       plotter->initGraph( 0, 6 );
 
  299       plotter->initGraph( 1, 6 );
 
  300       plotter->initGraph( 2, 6 );
 
  301       plotter->setLegend( 0, 0, 
"error_feat_tx" );
 
  302       plotter->setLegend( 0, 1, 
"error_feat_ty" );
 
  303       plotter->setLegend( 0, 2, 
"error_feat_tz" );
 
  304       plotter->setLegend( 0, 3, 
"error_feat_theta_ux" );
 
  305       plotter->setLegend( 0, 4, 
"error_feat_theta_uy" );
 
  306       plotter->setLegend( 0, 5, 
"error_feat_theta_uz" );
 
  307       plotter->setLegend( 1, 0, 
"vc_x" );
 
  308       plotter->setLegend( 1, 1, 
"vc_y" );
 
  309       plotter->setLegend( 1, 2, 
"vc_z" );
 
  310       plotter->setLegend( 1, 3, 
"wc_x" );
 
  311       plotter->setLegend( 1, 4, 
"wc_y" );
 
  312       plotter->setLegend( 1, 5, 
"wc_z" );
 
  313       plotter->setLegend( 2, 0, 
"F_x" );
 
  314       plotter->setLegend( 2, 1, 
"F_y" );
 
  315       plotter->setLegend( 2, 2, 
"F_z" );
 
  316       plotter->setLegend( 2, 3, 
"Tau_x" );
 
  317       plotter->setLegend( 2, 4, 
"Tau_y" );
 
  318       plotter->setLegend( 2, 5, 
"Tau_z" );
 
  320       plotter_left = 
new vpPlot( 4, 500, 600, 10, 10, 
"Real time curves plotter" );
 
  321       plotter_left->setTitle( 0, 
"EE Pose [m] - [rad]" );
 
  322       plotter_left->initGraph( 0, 6 );
 
  323       plotter_left->setLegend( 0, 0, 
"x" );
 
  324       plotter_left->setLegend( 0, 1, 
"y" );
 
  325       plotter_left->setLegend( 0, 2, 
"z" );
 
  326       plotter_left->setLegend( 0, 3, 
"tu_x" );
 
  327       plotter_left->setLegend( 0, 4, 
"tu_y" );
 
  328       plotter_left->setLegend( 0, 5, 
"tu_z" );
 
  330       plotter_left->setTitle( 1, 
"EE pose error [m] - [rad]" );
 
  331       plotter_left->initGraph( 1, 6 );
 
  332       plotter_left->setLegend( 1, 0, 
"e_x" );
 
  333       plotter_left->setLegend( 1, 1, 
"e_y" );
 
  334       plotter_left->setLegend( 1, 2, 
"e_z" );
 
  335       plotter_left->setLegend( 1, 3, 
"e_tu_x" );
 
  336       plotter_left->setLegend( 1, 4, 
"e_tu_y" );
 
  337       plotter_left->setLegend( 1, 5, 
"e_tu_z" );
 
  339       plotter_left->setTitle( 2, 
"Joint torque command [Nm]" );
 
  340       plotter_left->initGraph( 2, 7 );
 
  341       plotter_left->setLegend( 2, 0, 
"Tau1" );
 
  342       plotter_left->setLegend( 2, 1, 
"Tau2" );
 
  343       plotter_left->setLegend( 2, 2, 
"Tau3" );
 
  344       plotter_left->setLegend( 2, 3, 
"Tau4" );
 
  345       plotter_left->setLegend( 2, 4, 
"Tau5" );
 
  346       plotter_left->setLegend( 2, 5, 
"Tau6" );
 
  347       plotter_left->setLegend( 2, 6, 
"Tau7" );
 
  349       plotter_left->setTitle( 3, 
"Pose error norm [rad]" );
 
  350       plotter_left->initGraph( 3, 1 );
 
  351       plotter_left->setLegend( 3, 0, 
"||e||" ); 
 
  354     bool final_quit                           = 
false;
 
  355     bool has_converged                        = 
false;
 
  356     bool send_cmd                             = 
false;
 
  357     bool restart                              = 
false;
 
  358     std::vector< vpImagePoint > *traj_corners = 
nullptr; 
 
  361     double sim_time_prev        = sim_time;
 
  362     double sim_time_init_servo  = sim_time;
 
  363     double sim_time_img         = sim_time;
 
  364     double sim_time_img_prev    = sim_time;
 
  365     double sim_time_start       = sim_time;
 
  366     double sim_time_convergence = sim_time;
 
  369     vpMatrix K( 6, 6 ), D( 6, 6 );
 
  372     K.diag( { wp * wp, wp * wp, wp * wp, wo * wo, wo * wo, wo * wo } );
 
  373     D.diag( { 2 * wp, 2 * wp, 2 * wp, 2 * wo, 2 * wo, 2 * wo } );
 
  378     std::cout << 
"eMc:\n" << right_arm.
get_eMc() << std::endl;
 
  380     vpColVector v_c( 6, 0 ), q( 7, 0 ), dq( 7, 0 ), tau_d( 7, 0 ), C( 7, 0 ), pos( 6, 0 ), F( 7, 0 ), tau_d0( 7, 0 ),
 
  381         tau_cmd( 7, 0 ), x_e( 6, 0 ), dx_e( 6, 0 ), dx_ed( 6, 0 ), ddx_ed( 6, 0 );
 
  382     vpMatrix J( 6, 7 ), Ja( 6, 7 ), dJa( 6, 7 ), Ja_old( 6, 7 ), B( 7, 7 ), I7( 7, 7 ), Ja_pinv_B_t( 6, 7 ), Ls( 6, 6 );
 
  387     left_arm.
setRobotState( vpRobot::STATE_FORCE_TORQUE_CONTROL );
 
  392     vpRotationMatrix wRed0( { 1, 0, 0, 0, -1, 0, 0, 0, -1 } );
 
  393     vpHomogeneousMatrix wMed0( vpTranslationVector( 0.5, 0.0, 0.0 ), wRed0 );
 
  394     vpHomogeneousMatrix wMed;
 
  397     std::atomic_bool admittance_thread_running{ 
true };
 
  398     bool init_done = 
false;
 
  400     vpHomogeneousMatrix ftTee;
 
  405     std::cout << 
"ftTee:\n" << ftTee << 
"\n";
 
  407     vpVelocityTwistMatrix cTe;
 
  408     cTe.buildFrom( right_arm.
get_eMc().inverse() );
 
  410     vpForceTwistMatrix cFee, eeFft, eeFc;
 
  411     cFee.buildFrom( right_arm.
get_eMc().inverse() );
 
  412     eeFc.buildFrom( right_arm.
get_eMc() );
 
  413     eeFft.buildFrom( ftTee.inverse() );
 
  416     vpColVector dde_s( 6, 0 ), de_s( 6, 0 ), e_s( 6, 0 ), d_err( 6, 0 ), err( 6, 0 ), old_err( 6, 0 );
 
  417     vpMatrix Ks( 6, 6 ), Ds( 6, 6 ), Be( 6, 6 ), Bc_inv( 6, 6 ), I3( 3, 3 );
 
  422     Be.insert( 1 * I3, 0, 0 );   
 
  423     Be.insert( 0.1 * I3, 3, 3 ); 
 
  425     Bc_inv = cTe * Be.inverseByCholesky() * ( (vpMatrix)cTe ).t();
 
  427     vpColVector r( 3, 0 ), wFcog( 3, 0 ), ft_load( 6, 0 ), ft_meas( 6, 0 ), cfc_meas( 6, 0 ), Fs( 6, 0 );
 
  429     wFcog[2] = -9.81 * m;
 
  430     r        = right_arm.
get_flMcom().getTranslationVector();
 
  432     std::thread fast_thread( [&]() {
 
  433       std::cout << 
"fast_thread: started... \n" << std::endl;
 
  436         while ( admittance_thread_running )
 
  442             dt            = sim_time - sim_time_prev;
 
  443             sim_time_prev = sim_time;
 
  446             vpColVector aux( 3, 0 );
 
  447             aux = ( ( bMfra * right_arm.
get_fMe() * right_arm.
get_flMe().inverse() ).
inverse() ).getRotationMatrix() *
 
  452             aux        = vpColVector::skew( r ) * aux;
 
  456             ft_meas    = 
We - ft_load;
 
  459             cfc_meas = cFee * eeFft * ft_meas;
 
  460             task.computeControlLaw();
 
  463             Fs = Ls * Bc_inv * cfc_meas;
 
  466             dde_s = -Ks * e_s - Ds * de_s + Fs;
 
  470             cdMcc.insert( (vpTranslationVector)e_s.extract( 0, 3 ) );
 
  471             if ( sqrt( e_s.extract( 3, 3 ).sumSquare() ) != 0.0 )
 
  473               vpRotationMatrix R_aux;
 
  474               R_aux.buildFrom( (vpThetaUVector)e_s.extract( 3, 3 ) );
 
  475               cdMcc.insert( R_aux );
 
  479               cdMcc.insert( vpRotationMatrix() );
 
  481             ccMo = cdMcc.inverse() * cdMo;
 
  482             ccMc = ccMo * oMo * cMo.inverse();
 
  484             tu.buildFrom( ccMc );
 
  486             v_c = task.computeControlLaw();
 
  497               wMed[0][3] = wMed0[0][3] + 0.05 * sin( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) );
 
  498               wMed[1][3] = wMed0[1][3] + 0.05 * ( 1 - cos( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) ) );
 
  501               dx_ed[0] = 2 * M_PI * 0.3 * 0.05 * cos( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) );
 
  502               dx_ed[1] = 2 * M_PI * 0.3 * 0.05 * sin( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) );
 
  506                   -2 * M_PI * 0.3 * 2 * M_PI * 0.3 * 0.05 * sin( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) );
 
  508                   2 * M_PI * 0.3 * 2 * M_PI * 0.3 * 0.05 * cos( 2 * M_PI * 0.3 * ( sim_time - sim_time_convergence ) );
 
  514             RR.insert( wMed.getRotationMatrix().t(), 0, 0 );
 
  515             RR.insert( wMed.getRotationMatrix().t(), 3, 3 );
 
  517             x_e = (vpColVector)vpPoseVector( wMed.inverse() * left_arm.
get_fMe() );
 
  518             Ja  = 
Ta( wMed.inverse() * left_arm.
get_fMe() ) * RR * J;
 
  520             dx_e = 
Ta( wMed.inverse() * left_arm.
get_fMe() ) * RR * ( dx_ed - J * dq );
 
  524               dJa = ( Ja - Ja_old ) / dt;
 
  532             Ja_pinv_B_t = ( Ja * B.inverseByCholesky() * Ja.t() ).inverseByCholesky() * Ja * B.inverseByCholesky();
 
  535             tau_d = B * Ja.pseudoInverse() * ( -K * ( x_e ) + D * (dx_e)-dJa * dq + ddx_ed ) + C + F -
 
  536                     ( I7 - Ja.t() * Ja_pinv_B_t ) * B * dq * 100;
 
  552               tau_cmd = tau_d - tau_d0 * std::exp( -mu * ( sim_time - c_time ) );
 
  555             right_arm.
setVelocity( vpRobot::CAMERA_FRAME, v_c );
 
  561           right_arm.
wait( sim_time, 0.002 );
 
  564       catch ( 
const vpException &e )
 
  566         std::cout << 
"ViSP exception: " << e.what() << std::endl;
 
  567         admittance_thread_running = 
false;
 
  571         std::cout << 
"Something wrong happens: " << std::endl;
 
  572         admittance_thread_running = 
false;
 
  574       std::cout << 
"fast_thread: exiting..." << std::endl;
 
  579     while ( !final_quit )
 
  582       vpDisplay::display( I );
 
  584       std::vector< vpHomogeneousMatrix > cMo_vec;
 
  585       detector.detect( I, opt_tagSize, cam, cMo_vec );
 
  588         std::stringstream ss;
 
  589         ss << 
"Left click to " << ( send_cmd ? 
"stop the robot" : 
"servo the robot" ) << 
", right click to quit.";
 
  590         vpDisplay::displayText( I, 20, 20, ss.str(), vpColor::red );
 
  594       if ( cMo_vec.size() == 1 )
 
  601           std::vector< vpHomogeneousMatrix > v_oMo( 2 ), v_cdMc( 2 );
 
  602           v_oMo[1].buildFrom( 0, 0, 0, 0, 0, M_PI );
 
  603           for ( 
size_t i = 0; i < 2; i++ )
 
  605             v_cdMc[i] = cdMo * v_oMo[i] * cMo.inverse();
 
  607           if ( std::fabs( v_cdMc[0].getThetaUVector().getTheta() ) <
 
  608                std::fabs( v_cdMc[1].getThetaUVector().getTheta() ) )
 
  614             std::cout << 
"Desired frame modified to avoid PI rotation of the camera" << std::endl;
 
  617           sim_time_start = sim_time;
 
  622         ccMc = ccMo * oMo * cMo.inverse();
 
  624         tu.buildFrom( ccMc );
 
  630         vpDisplay::displayFrame( I, cdMo * oMo, cam, opt_tagSize / 1.5, vpColor::yellow, 2 );
 
  631         vpDisplay::displayFrame( I, ccMo * oMo, cam, opt_tagSize / 2, vpColor::none, 3 );
 
  632         vpDisplay::displayFrame( I, cMo, cam, opt_tagSize / 2, vpColor::none, 3 );
 
  635         std::vector< vpImagePoint > corners = detector.getPolygon( 0 );
 
  638         corners.push_back( detector.getCog( 0 ) );
 
  642           traj_corners = 
new std::vector< vpImagePoint >[corners.size()];
 
  648         vpTranslationVector cd_t_c = ccMc.getTranslationVector();
 
  649         vpThetaUVector cd_tu_c     = ccMc.getThetaUVector();
 
  650         double error_tr            = sqrt( cd_t_c.sumSquare() );
 
  651         double error_tu            = vpMath::deg( sqrt( cd_tu_c.sumSquare() ) );
 
  653         std::stringstream ss;
 
  654         ss << 
"error_t [m]: " << error_tr;
 
  655         vpDisplay::displayText( I, 20, 
static_cast< int >( I.getWidth() ) - 160, ss.str(), vpColor::red );
 
  657         ss << 
"error_tu [deg]: " << error_tu;
 
  658         vpDisplay::displayText( I, 40, 
static_cast< int >( I.getWidth() ) - 160, ss.str(), vpColor::red );
 
  660         if ( !has_converged && error_tr < convergence_threshold_t && error_tu < convergence_threshold_tu )
 
  662           has_converged        = 
true;
 
  663           sim_time_convergence = sim_time;
 
  664           std::cout << 
"Servo task has converged \n";
 
  665           vpDisplay::displayText( I, 100, 20, 
"Servo task has converged", vpColor::red );
 
  681         plotter->plot( 0, 
static_cast< double >( sim_time ), task.getError() );
 
  682         plotter->plot( 1, 
static_cast< double >( sim_time ), v_c );
 
  683         plotter->plot( 2, 
static_cast< double >( sim_time ), cfc_meas );
 
  685         plotter_left->plot( 0, sim_time, (vpColVector)vpPoseVector( bMfla * left_arm.
get_fMe() ) );
 
  686         plotter_left->plot( 1, sim_time, x_e );
 
  687         plotter_left->plot( 2, sim_time, tau_cmd );
 
  688         plotter_left->plot( 3, sim_time, vpColVector( 1, sqrt( x_e.sumSquare() ) ) );
 
  691       std::stringstream ss;
 
  692       ss << 
"Loop time [s]: " << std::round( ( sim_time_img - sim_time_img_prev ) * 1000. ) / 1000.;
 
  693       ss << 
" Simulation time [s]: " << sim_time_img;
 
  694       sim_time_img_prev = sim_time_img;
 
  695       vpDisplay::displayText( I, 40, 20, ss.str(), vpColor::red );
 
  697       vpMouseButton::vpMouseButtonType button;
 
  698       if ( vpDisplay::getClick( I, button, 
false ) )
 
  702         case vpMouseButton::button1:
 
  703           send_cmd = !send_cmd;
 
  706         case vpMouseButton::button3:
 
  710           admittance_thread_running = 
false;
 
  718       vpDisplay::flush( I );
 
  721     if ( opt_plot && plotter != 
nullptr && plotter_left != 
nullptr )
 
  726       plotter_left = 
nullptr;
 
  734       while ( !final_quit )
 
  737         vpDisplay::display( I );
 
  739         vpDisplay::displayText( I, 20, 20, 
"Click to quit the program.", vpColor::red );
 
  740         vpDisplay::displayText( I, 40, 20, 
"Visual servo converged.", vpColor::red );
 
  742         if ( vpDisplay::getClick( I, 
false ) )
 
  747         vpDisplay::flush( I );
 
  750     if ( fast_thread.joinable() )
 
  753       std::cout << 
"fast thread joined.. \n ";
 
  757       delete[] traj_corners;
 
  760   catch ( 
const vpException &e )
 
  762     std::cout << 
"ViSP exception: " << e.what() << std::endl;
 
  763     std::cout << 
"Stop the robot " << std::endl;