ViSP  2.6.2

Example that shows how to control the Pioneer mobile robot by IBVS visual servoing with respect to a segment. The segment consists in two horizontal dots. The current visual features that are used are ${\bf s} = (x_n, l_n, \alpha)$. The desired one are ${\bf s^*} = (0, l_n*, 0)$, with:

The degrees of freedom that are controlled are $(v_x, w_z, \dot{q})$, the translational and rotational velocity of the mobile platform at point M located at the middle between the two wheels, the head pan velocity respectively.

The depth of the points is estimated from the surface of the blob.

* $Id: servoPioneerPanSegment3D.cpp 3842 2012-07-13 22:21:42Z fspindle $
* This file is part of the ViSP software.
* Copyright (C) 2005 - 2012 by INRIA. All rights reserved.
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* ("GPL") version 2 as published by the Free Software Foundation.
* See the file LICENSE.txt at the root directory of this source
* distribution for additional information about the GNU GPL.
* For using ViSP with software that can not be combined with the GNU
* GPL, please contact INRIA about acquiring a ViSP Professional
* Edition License.
* See for more information.
* This software was developed at:
* INRIA Rennes - Bretagne Atlantique
* Campus Universitaire de Beaulieu
* 35042 Rennes Cedex
* France
* If you have questions regarding the use of this file, please contact
* INRIA at
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* Description:
* IBVS on Pioneer P3DX mobile platform
* Authors:
* Fabien Spindler
#include <iostream>
#include <visp/vpConfig.h>
#include <visp/vpRobotPioneer.h> // Include before vpDisplayX to avoid build issues
#include <visp/vpRobotBiclops.h>
#include <visp/vpCameraParameters.h>
#include <visp/vpDisplayGDI.h>
#include <visp/vpDisplayX.h>
#include <visp/vpDot2.h>
#include <visp/vpFeatureBuilder.h>
#include <visp/vpFeatureSegment.h>
#include <visp/vpHomogeneousMatrix.h>
#include <visp/vpImage.h>
#include <visp/vp1394TwoGrabber.h>
#include <visp/vp1394CMUGrabber.h>
#include <visp/vpV4l2Grabber.h>
#include <visp/vpPioneerPan.h>
#include <visp/vpPlot.h>
#include <visp/vpServo.h>
#include <visp/vpVelocityTwistMatrix.h>
#if defined(VISP_HAVE_PIONEER) && defined(VISP_HAVE_BICLOPS)
int main(int argc, char **argv)
#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394)
#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)
vpImage<unsigned char> I; // Create a gray level image container
double lambda = 0.05;
// Scale parameter used to estimate the depth Z of the blob from its surface
double coef = 0.9/14.85; // At 0.9m, the blob has a surface of 14.85
double L = 0.21; // 3D horizontal segment length
double Z_d = 1.; // Desired distance along Z between camera and segment
// Warning: To have a non singular task of rank 3, Y_d should be different from 0 so that
// the optical axis doesn't intersect the horizontal segment
double Y_d = .18; // Desired distance along Y between camera and segment.
vpColVector qm(2); // Measured head position
qm = 0;
double qm_pan = 0; // Measured pan position (tilt is not handled in that example)
// Initialize the biclops head
vpRobotBiclops biclops("/usr/share/BiclopsDefault.cfg");
// Move to the initial position
q[0] = vpMath::rad(63);
q[1] = vpMath::rad(12); // introduce a tilt angle to compensate camera sphere tilt so that the camera is parallel to the plane
qm_pan = qm[0];
// Now the head will be controlled in velocity
// Initialize the pioneer robot
vpRobotPioneer pioneer;
ArArgumentParser parser(&argc, argv);
// ArRobotConnector connects to the robot, get some initial data from it such as type and name,
// and then loads parameter files for this robot.
ArRobotConnector robotConnector(&parser, &pioneer);
ArLog::log(ArLog::Terse, "Could not connect to the pioneer robot.");
if (!Aria::parseArgs())
return false;
pioneer.useSonar(false); // disable the sonar device usage
std::cout << "Pioneer robot connected" << std::endl;
vpPioneerPan robot_pan; // Generic robot that computes the velocities for the pioneer and the biclops head
// Create the camera framegrabber
#if defined(VISP_HAVE_V4L2)
// Create a grabber based on v4l2 third party lib (for usb cameras under Linux)
#elif defined(VISP_HAVE_DC1394_2)
// Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux)
vp1394TwoGrabber g(false);
#elif defined(VISP_HAVE_CMU1394)
// Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows)
g.setVideoMode(0, 5); // 640x480 MONO8
g.setFramerate(4); // 30 Hz;
// Acquire an image from the grabber
// Create an image viewer
#if defined(VISP_HAVE_X11)
vpDisplayX d(I, 10, 10, "Current frame");
#elif defined(VISP_HAVE_GDI)
vpDisplayGDI d(I, 10, 10, "Current frame");
// The 3D segment consists in two horizontal dots
vpDot2 dot[2];
for (int i=0; i <2; i++)
dot[i].setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape
dot[i].setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation
dot[i].setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level
// Camera parameters. In this experiment we don't need a precise calibration of the camera
cam.initPersProjWithoutDistortion(558, 555, 312, 210);
vpServo task;
task.setLambda(lambda) ;
vpVelocityTwistMatrix cVe ; // keep to identity
cVe = robot_pan.get_cVe() ;
task.set_cVe(cVe) ;
std::cout << "cVe: \n" << cVe << std::endl;
vpMatrix eJe;
// Update the robot jacobian that depends on the pan position
// Get the robot jacobian
eJe = robot_pan.get_eJe() ;
task.set_eJe(eJe) ;
std::cout << "eJe: \n" << eJe << std::endl;
// Define a 3D horizontal segment an its cordinates in the image plane
vpPoint P[2];
P[0].setWorldCoordinates(-L/2, 0, 0);
P[1].setWorldCoordinates( L/2, 0, 0);
// Define the desired camera position
vpHomogeneousMatrix cMo(0, Y_d, Z_d, 0, 0, 0); // Here we are in front of the segment
for (int i=0; i <2; i++)
P[i].project(); // Here the x,y parameters obtained by perspective projection are computed
// Estimate the depth of the segment extremity points
double surface[2];
double Z[2]; // Depth of the segment points
for (int i=0; i<2; i++)
// Surface of the blob estimated from the image moment m00 and converted in meters
surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py()));
// Initial depth of the blob
Z[i] = coef * surface[i] ;
// Use here a feature segment builder
bool normalized = true; // segment normilized features are used
vpFeatureSegment s_segment(normalized), s_segment_d(normalized); // From the segment feature we use only alpha
vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]);
// Set the desired feature
vpFeatureBuilder::create(s_segment_d, P[0], P[1]);
s_segment.setZ1( P[0].get_Z() ); // Desired depth
s_segment.setZ2( P[1].get_Z() );
task.addFeature(s_segment, s_segment_d,
//Create a window (500 by 500) at position (700, 10) with two graphics
vpPlot graph(2, 500, 500, 700, 10, "Curves...");
//The first graphic contains 3 curve and the second graphic contains 3 curves
graph.setTitle(0, "Velocities");
graph.setTitle(1, "Error s-s*");
graph.setLegend(0, 0, "vx");
graph.setLegend(0, 1, "wz");
graph.setLegend(0, 2, "w_pan");
graph.setLegend(1, 0, "xm/l");
graph.setLegend(1, 1, "1/l");
graph.setLegend(1, 2, "alpha");
vpColVector v; // vz, wx
unsigned int iter = 0;
// Get the new pan position
qm_pan = qm[0];
// Acquire a new image
// Set the image as background of the viewer
// Display the desired position of the segment
for (int i=0; i<2; i++)
P[i].display(I, cam, vpColor::red, 3);
// Does the blob tracking
for (int i=0; i<2; i++)
for (int i=0; i<2; i++)
// Surface of the blob estimated from the image moment m00 and converted in meters
surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py()));
// Initial depth of the blob
Z[i] = coef * surface[i] ;
// Update the features
vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]);
// Update the depth of the point. Useful only if current interaction matrix is used
// when task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) is set
// Update the robot jacobian that depends on the pan position
// Get the robot jacobian
eJe = robot_pan.get_eJe();
// Update the jacobian that will be used to compute the control law
// Compute the control law. Velocities are computed in the mobile robot reference frame
v = task.computeControlLaw();
// std::cout << "-----" << std::endl;
// std::cout << "v: " << v.t() << std::endl;
// std::cout << "error: " << task.getError().t() << std::endl;
// std::cout << "L:\n " << task.getInteractionMatrix() << std::endl;
// std::cout << "eJe:\n " << task.get_eJe() << std::endl;
// std::cout << "cVe:\n " << task.get_cVe() << std::endl;
// std::cout << "L_cVe_eJe:\n" << task.getInteractionMatrix() * task.get_cVe() * task.get_eJe() << std::endl;
// task.print() ;
if (task.getTaskRank() != 3)
std::cout << "Warning: task is of rank " << task.getTaskRank() << std::endl;
graph.plot(0, iter, v); // plot velocities applied to the robot
graph.plot(1, iter, task.getError()); // plot error vector
// Send the velocity to the robot
vpColVector v_pioneer(2); // vx, wz
v_pioneer[0] = v[0];
v_pioneer[1] = v[1];
vpColVector v_biclops(2); // qdot pan and tilt
v_biclops[0] = v[2];
v_biclops[1] = 0;
biclops.setVelocity(vpRobot::ARTICULAR_FRAME, v_biclops) ;
// Draw a vertical line which corresponds to the desired x coordinate of the dot cog
vpDisplay::displayLine(I, 0, cam.get_u0(), 479, cam.get_u0(), vpColor::red);
// A click in the viewer to exit
if ( vpDisplay::getClick(I, false) )
iter ++;
std::cout << "Ending robot thread..." << std::endl;
// wait for the thread to stop
// Kill the servo task
task.print() ;
int main()
std::cout << "ViSP is not able to control the Pioneer robot" << std::endl;
return 0;