tutorial-franka-coppeliasim-pbvs-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 
34 
35 #include <iostream>
36 
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>
46 
47 #include <visp_ros/vpROSGrabber.h>
49 
50 void
51 display_point_trajectory( const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &vip,
52  std::vector< vpImagePoint > *traj_vip )
53 {
54  for ( size_t i = 0; i < vip.size(); i++ )
55  {
56  if ( traj_vip[i].size() )
57  {
58  // Add the point only if distance with the previous > 1 pixel
59  if ( vpImagePoint::distance( vip[i], traj_vip[i].back() ) > 1. )
60  {
61  traj_vip[i].push_back( vip[i] );
62  }
63  }
64  else
65  {
66  traj_vip[i].push_back( vip[i] );
67  }
68  }
69  for ( size_t i = 0; i < vip.size(); i++ )
70  {
71  for ( size_t j = 1; j < traj_vip[i].size(); j++ )
72  {
73  vpDisplay::displayLine( I, traj_vip[i][j - 1], traj_vip[i][j], vpColor::green, 2 );
74  }
75  }
76 }
77 
78 int
79 main( int argc, char **argv )
80 {
81  double opt_tagSize = 0.08;
82  bool display_tag = true;
83  int opt_quad_decimate = 2;
84  bool opt_verbose = false;
85  bool opt_plot = false;
86  bool opt_adaptive_gain = false;
87  bool opt_task_sequencing = false;
88  double convergence_threshold_t = 0.0005, convergence_threshold_tu = vpMath::rad( 0.5 );
89  bool opt_coppeliasim_sync_mode = false;
90 
91  for ( int i = 1; i < argc; i++ )
92  {
93  if ( std::string( argv[i] ) == "--tag_size" && i + 1 < argc )
94  {
95  opt_tagSize = std::stod( argv[i + 1] );
96  }
97  else if ( std::string( argv[i] ) == "--verbose" || std::string( argv[i] ) == "-v" )
98  {
99  opt_verbose = true;
100  }
101  else if ( std::string( argv[i] ) == "--plot" )
102  {
103  opt_plot = true;
104  }
105  else if ( std::string( argv[i] ) == "--adaptive_gain" )
106  {
107  opt_adaptive_gain = true;
108  }
109  else if ( std::string( argv[i] ) == "--task_sequencing" )
110  {
111  opt_task_sequencing = true;
112  }
113  else if ( std::string( argv[i] ) == "--quad_decimate" && i + 1 < argc )
114  {
115  opt_quad_decimate = std::stoi( argv[i + 1] );
116  }
117  else if ( std::string( argv[i] ) == "--no-convergence-threshold" )
118  {
119  convergence_threshold_t = 0.;
120  convergence_threshold_tu = 0.;
121  }
122  else if ( std::string( argv[i] ) == "--enable-coppeliasim-sync-mode" )
123  {
124  opt_coppeliasim_sync_mode = true;
125  }
126  else if ( std::string( argv[i] ) == "--help" || std::string( argv[i] ) == "-h" )
127  {
128  std::cout << argv[0] << "[--tag_size <marker size in meter; default " << opt_tagSize << ">] "
129  << "[--quad_decimate <decimation; default " << opt_quad_decimate << ">] "
130  << "[--adaptive_gain] "
131  << "[--plot] "
132  << "[--task_sequencing] "
133  << "[--no-convergence-threshold] "
134  << "[--enable-coppeliasim-sync-mode] "
135  << "[--verbose] [-v] "
136  << "[--help] [-h]" << std::endl;
137  return EXIT_SUCCESS;
138  }
139  }
140 
141  try
142  {
143  //------------------------------------------------------------------------//
144  //------------------------------------------------------------------------//
145  // ROS node
146  ros::init( argc, argv, "visp_ros" );
147  ros::NodeHandlePtr n = boost::make_shared< ros::NodeHandle >();
148  ros::Rate loop_rate( 1000 );
149  ros::spinOnce();
150 
152  robot.setVerbose( opt_verbose );
153  robot.connect();
154 
155  std::cout << "Coppeliasim sync mode enabled: " << ( opt_coppeliasim_sync_mode ? "yes" : "no" ) << std::endl;
156  robot.coppeliasimStopSimulation(); // Allows to reset simulation, moving the robot to initial position
157  robot.setCoppeliasimSyncMode( false );
159 
160  if ( 0 )
161  {
162  robot.setRobotState( vpRobot::STATE_POSITION_CONTROL );
163  vpColVector q;
164  robot.getPosition( vpRobot::JOINT_STATE, q );
165  std::cout << "Initial joint position: " << q.t() << std::endl;
166 
167  q[0] += vpMath::rad( 10 ); // Add 10 deg axis 1
168  std::cout << "Move to joint position: " << q.t() << std::endl;
169  robot.setPosition( vpRobot::JOINT_STATE, q );
170  }
171 
172  vpImage< unsigned char > I;
173  vpROSGrabber g;
174  g.setImageTopic( "/coppeliasim/franka/camera/image" );
175  g.setCameraInfoTopic( "/coppeliasim/franka/camera/camera_info" );
176  g.open( argc, argv );
177 
178  g.acquire( I );
179 
180  std::cout << "Image size: " << I.getWidth() << " x " << I.getHeight() << std::endl;
181  vpCameraParameters cam;
182 
183  g.getCameraInfo( cam );
184  std::cout << cam << std::endl;
185  vpDisplayOpenCV dc( I, 10, 10, "Color image" );
186 
187  vpDetectorAprilTag::vpAprilTagFamily tagFamily = vpDetectorAprilTag::TAG_36h11;
188  vpDetectorAprilTag::vpPoseEstimationMethod poseEstimationMethod = vpDetectorAprilTag::HOMOGRAPHY_VIRTUAL_VS;
189  vpDetectorAprilTag detector( tagFamily );
190  detector.setAprilTagPoseEstimationMethod( poseEstimationMethod );
191  detector.setDisplayTag( display_tag );
192  detector.setAprilTagQuadDecimate( opt_quad_decimate );
193 
194  // Servo
195  vpHomogeneousMatrix cdMc, cMo, oMo;
196 
197  // Desired pose used to compute the desired features
198  vpHomogeneousMatrix cdMo( vpTranslationVector( 0, 0.0, opt_tagSize * 3 ),
199  vpRotationMatrix( { 1, 0, 0, 0, -1, 0, 0, 0, -1 } ) );
200  cdMc = cdMo * cMo.inverse();
201 
202  // Create visual features
203  vpFeatureTranslation t( vpFeatureTranslation::cdMc );
204  vpFeatureThetaU tu( vpFeatureThetaU::cdRc );
205  t.buildFrom( cdMc );
206  tu.buildFrom( cdMc );
207 
208  vpFeatureTranslation td( vpFeatureTranslation::cdMc );
209  vpFeatureThetaU tud( vpFeatureThetaU::cdRc );
210 
211  vpServo task;
212  // Add the visual features
213  task.addFeature( t, td );
214  task.addFeature( tu, tud );
215 
216  task.setServo( vpServo::EYEINHAND_CAMERA );
217  task.setInteractionMatrixType( vpServo::CURRENT );
218 
219  if ( opt_adaptive_gain )
220  {
221  std::cout << "Enable adaptive gain" << std::endl;
222  vpAdaptiveGain lambda( 4, 1.2, 25 ); // lambda(0)=4, lambda(oo)=1.2 and lambda'(0)=25
223  task.setLambda( lambda );
224  }
225  else
226  {
227  task.setLambda( 1.2 );
228  }
229 
230  vpPlot *plotter = nullptr;
231 
232  if ( opt_plot )
233  {
234  plotter = new vpPlot( 2, static_cast< int >( 250 * 2 ), 500, static_cast< int >( I.getWidth() ) + 80, 10,
235  "Real time curves plotter" );
236  plotter->setTitle( 0, "Visual features error" );
237  plotter->setTitle( 1, "Camera velocities" );
238  plotter->initGraph( 0, 6 );
239  plotter->initGraph( 1, 6 );
240  plotter->setLegend( 0, 0, "error_feat_tx" );
241  plotter->setLegend( 0, 1, "error_feat_ty" );
242  plotter->setLegend( 0, 2, "error_feat_tz" );
243  plotter->setLegend( 0, 3, "error_feat_theta_ux" );
244  plotter->setLegend( 0, 4, "error_feat_theta_uy" );
245  plotter->setLegend( 0, 5, "error_feat_theta_uz" );
246  plotter->setLegend( 1, 0, "vc_x" );
247  plotter->setLegend( 1, 1, "vc_y" );
248  plotter->setLegend( 1, 2, "vc_z" );
249  plotter->setLegend( 1, 3, "wc_x" );
250  plotter->setLegend( 1, 4, "wc_y" );
251  plotter->setLegend( 1, 5, "wc_z" );
252  }
253 
254  bool final_quit = false;
255  bool has_converged = false;
256  bool send_velocities = false;
257  bool servo_started = false;
258  std::vector< vpImagePoint > *traj_corners = nullptr; // To memorize point trajectory
259 
260  double sim_time = robot.getCoppeliasimSimulationTime();
261  double sim_time_prev = sim_time;
262  double sim_time_init_servo = sim_time;
263  double sim_time_img = sim_time;
264 
265  if ( 0 )
266  {
267  // Instead of setting eMc from /coppeliasim/franka/eMc topic, we can set its value to introduce noise for example
268  vpHomogeneousMatrix eMc;
269  eMc.buildFrom( 0.05, -0.05, 0, 0, 0, M_PI_4 );
270  robot.set_eMc( eMc );
271  }
272  std::cout << "eMc:\n" << robot.get_eMc() << std::endl;
273 
274  robot.setRobotState( vpRobot::STATE_VELOCITY_CONTROL );
275  robot.setCoppeliasimSyncMode( opt_coppeliasim_sync_mode );
276 
277  while ( !final_quit )
278  {
279  sim_time = robot.getCoppeliasimSimulationTime();
280 
281  g.acquire( I, sim_time_img );
282  vpDisplay::display( I );
283 
284  std::vector< vpHomogeneousMatrix > cMo_vec;
285  detector.detect( I, opt_tagSize, cam, cMo_vec );
286 
287  {
288  std::stringstream ss;
289  ss << "Left click to " << ( send_velocities ? "stop the robot" : "servo the robot" )
290  << ", right click to quit.";
291  vpDisplay::displayText( I, 20, 20, ss.str(), vpColor::red );
292  }
293 
294  vpColVector v_c( 6 );
295 
296  // Only one tag is detected
297  if ( cMo_vec.size() == 1 )
298  {
299  cMo = cMo_vec[0];
300 
301  static bool first_time = true;
302  if ( first_time )
303  {
304  // Introduce security wrt tag positionning in order to avoid PI rotation
305  std::vector< vpHomogeneousMatrix > v_oMo( 2 ), v_cdMc( 2 );
306  v_oMo[1].buildFrom( 0, 0, 0, 0, 0, M_PI );
307  for ( size_t i = 0; i < 2; i++ )
308  {
309  v_cdMc[i] = cdMo * v_oMo[i] * cMo.inverse();
310  }
311  if ( std::fabs( v_cdMc[0].getThetaUVector().getTheta() ) <
312  std::fabs( v_cdMc[1].getThetaUVector().getTheta() ) )
313  {
314  oMo = v_oMo[0];
315  }
316  else
317  {
318  std::cout << "Desired frame modified to avoid PI rotation of the camera" << std::endl;
319  oMo = v_oMo[1]; // Introduce PI rotation
320  }
321  } // end first_time
322 
323  // Update visual features
324  cdMc = cdMo * oMo * cMo.inverse();
325  t.buildFrom( cdMc );
326  tu.buildFrom( cdMc );
327 
328  if ( opt_task_sequencing )
329  {
330  if ( !servo_started )
331  {
332  if ( send_velocities )
333  {
334  servo_started = true;
335  }
336  sim_time_init_servo = robot.getCoppeliasimSimulationTime();
337  }
338  v_c = task.computeControlLaw( robot.getCoppeliasimSimulationTime() - sim_time_init_servo );
339  }
340  else
341  {
342  v_c = task.computeControlLaw();
343  }
344 
345  // Display the current and desired feature points in the image display
346  // Display desired and current pose features
347  vpDisplay::displayFrame( I, cdMo * oMo, cam, opt_tagSize / 1.5, vpColor::yellow, 2 );
348  vpDisplay::displayFrame( I, cMo, cam, opt_tagSize / 2, vpColor::none, 3 );
349 
350  // Get tag corners
351  std::vector< vpImagePoint > corners = detector.getPolygon( 0 );
352 
353  // Get the tag cog corresponding to the projection of the tag frame in the image
354  corners.push_back( detector.getCog( 0 ) );
355  // Display the trajectory of the points
356  if ( first_time )
357  {
358  traj_corners = new std::vector< vpImagePoint >[corners.size()];
359  }
360 
361  // Display the trajectory of the points used as features
362  display_point_trajectory( I, corners, traj_corners );
363 
364  if ( opt_plot )
365  {
366  plotter->plot( 0, static_cast< double >( sim_time ), task.getError() );
367  plotter->plot( 1, static_cast< double >( sim_time ), v_c );
368  }
369 
370  if ( opt_verbose )
371  {
372  std::cout << "v_c: " << v_c.t() << std::endl;
373  }
374 
375  vpTranslationVector cd_t_c = cdMc.getTranslationVector();
376  vpThetaUVector cd_tu_c = cdMc.getThetaUVector();
377  double error_tr = sqrt( cd_t_c.sumSquare() );
378  double error_tu = vpMath::deg( sqrt( cd_tu_c.sumSquare() ) );
379 
380  std::stringstream ss;
381  ss << "error_t: " << error_tr;
382  vpDisplay::displayText( I, 20, static_cast< int >( I.getWidth() ) - 150, ss.str(), vpColor::red );
383  ss.str( "" );
384  ss << "error_tu: " << error_tu;
385  vpDisplay::displayText( I, 40, static_cast< int >( I.getWidth() ) - 150, ss.str(), vpColor::red );
386 
387  if ( opt_verbose )
388  std::cout << "error translation: " << error_tr << " ; error rotation: " << error_tu << std::endl;
389 
390  if ( !has_converged && error_tr < convergence_threshold_t && error_tu < convergence_threshold_tu )
391  {
392  has_converged = true;
393  std::cout << "Servo task has converged"
394  << "\n";
395  vpDisplay::displayText( I, 100, 20, "Servo task has converged", vpColor::red );
396  }
397 
398  if ( first_time )
399  {
400  first_time = false;
401  }
402  } // end if (cMo_vec.size() == 1)
403  else
404  {
405  v_c = 0; // Stop the robot
406  }
407 
408  if ( !send_velocities )
409  {
410  v_c = 0; // Stop the robot
411  }
412 
413  robot.setVelocity( vpRobot::CAMERA_FRAME, v_c );
414 
415  std::stringstream ss;
416  ss << "Loop time [s]: " << std::round( ( sim_time - sim_time_prev ) * 1000. ) / 1000.;
417  ss << " Simulation time [s]: " << sim_time;
418  sim_time_prev = sim_time;
419  vpDisplay::displayText( I, 40, 20, ss.str(), vpColor::red );
420 
421  vpMouseButton::vpMouseButtonType button;
422  if ( vpDisplay::getClick( I, button, false ) )
423  {
424  switch ( button )
425  {
426  case vpMouseButton::button1:
427  send_velocities = !send_velocities;
428  break;
429 
430  case vpMouseButton::button3:
431  final_quit = true;
432  v_c = 0;
433  break;
434 
435  default:
436  break;
437  }
438  }
439 
440  vpDisplay::flush( I );
441  robot.wait( sim_time, 0.020 ); // Slow down the loop to simulate a camera at 50 Hz
442  } // end while
443 
444  if ( opt_plot && plotter != nullptr )
445  {
446  delete plotter;
447  plotter = nullptr;
448  }
450 
451  if ( !final_quit )
452  {
453  while ( !final_quit )
454  {
455  g.acquire( I );
456  vpDisplay::display( I );
457 
458  vpDisplay::displayText( I, 20, 20, "Click to quit the program.", vpColor::red );
459  vpDisplay::displayText( I, 40, 20, "Visual servo converged.", vpColor::red );
460 
461  if ( vpDisplay::getClick( I, false ) )
462  {
463  final_quit = true;
464  }
465 
466  vpDisplay::flush( I );
467  }
468  }
469  if ( traj_corners )
470  {
471  delete[] traj_corners;
472  }
473  }
474  catch ( const vpException &e )
475  {
476  std::cout << "ViSP exception: " << e.what() << std::endl;
477  std::cout << "Stop the robot " << std::endl;
478  return EXIT_FAILURE;
479  }
480 
481  return 0;
482 }
boost::shared_ptr< NodeHandle >
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
vpROSGrabber::getCameraInfo
bool getCameraInfo(vpCameraParameters &cam)
Definition: vpROSGrabber.cpp:649
vpRobotFrankaSim::getPosition
virtual void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &position)
Definition: vpRobotFrankaSim.cpp:417
vpRobotFrankaSim::setVelocity
virtual void setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel)
Definition: vpRobotFrankaSim.cpp:683
vpROSGrabber
Class for cameras video capture for ROS cameras.
Definition: vpROSGrabber.h:104
vpROSRobotFrankaCoppeliasim
Definition: vpROSRobotFrankaCoppeliasim.h:87
ros::spinOnce
ROSCPP_DECL void spinOnce()
vpROSRobotFrankaCoppeliasim::setRobotState
vpRobot::vpRobotStateType setRobotState(vpRobot::vpRobotStateType newState)
Definition: vpROSRobotFrankaCoppeliasim.cpp:764
vpROSRobotFrankaCoppeliasim::wait
void wait(double timestamp_second, double duration_second)
Definition: vpROSRobotFrankaCoppeliasim.cpp:976
vpROSGrabber::open
void open(int argc, char **argv)
Definition: vpROSGrabber.cpp:98
vpRobotFrankaSim::setVerbose
void setVerbose(bool verbose)
Definition: vpRobotFrankaSim.h:111
vpROSRobotFrankaCoppeliasim.h
vpROSRobotFrankaCoppeliasim::coppeliasimStopSimulation
void coppeliasimStopSimulation(double sleep_ms=1000.)
Definition: vpROSRobotFrankaCoppeliasim.cpp:268
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-pbvs-apriltag.cpp:51
main
int main(int argc, char **argv)
Definition: tutorial-franka-coppeliasim-pbvs-apriltag.cpp:79
vpROSRobotFrankaCoppeliasim::setPosition
void setPosition(const vpRobot::vpControlFrameType frame, const vpColVector &position)
Definition: vpROSRobotFrankaCoppeliasim.cpp:947
vpROSGrabber::setImageTopic
void setImageTopic(const std::string &topic_name)
Definition: vpROSGrabber.cpp:592
vpROSRobotFrankaCoppeliasim::getCoppeliasimSimulationTime
double getCoppeliasimSimulationTime()
Definition: vpROSRobotFrankaCoppeliasim.cpp:342
vpROSGrabber::setCameraInfoTopic
void setCameraInfoTopic(const std::string &topic_name)
Definition: vpROSGrabber.cpp:579
vpROSRobotFrankaCoppeliasim::coppeliasimStartSimulation
void coppeliasimStartSimulation(double sleep_ms=1000.)
Definition: vpROSRobotFrankaCoppeliasim.cpp:248
vpROSRobotFrankaCoppeliasim::setCoppeliasimSyncMode
void setCoppeliasimSyncMode(bool enable, double sleep_ms=1000.)
Definition: vpROSRobotFrankaCoppeliasim.cpp:209
vpRobotFrankaSim::get_eMc
vpHomogeneousMatrix get_eMc() const
Definition: vpRobotFrankaSim.cpp:366
ros::Rate
vpROSGrabber::acquire
void acquire(vpImage< unsigned char > &I)
Definition: vpROSGrabber.cpp:412
vpROSGrabber.h
class for Camera video capture for ROS middleware.
vpROSRobotFrankaCoppeliasim::connect
void connect(const std::string &robot_ID="")
Definition: vpROSRobotFrankaCoppeliasim.cpp:132
vpRobotFrankaSim::set_eMc
virtual void set_eMc(const vpHomogeneousMatrix &eMc)
Definition: vpRobotFrankaSim.cpp:157


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