This tutorial expains how to perform a visual servoing task based on a 2D confidence map, using Viper 850 robot and ultrasonix station.
Note that the source code used in this tutorial can be downloaded using the following command :
The following code shows how to perform a visual servoing task on a 2D confidence map in real-time, using the 6-DOF robot Viper850 (holding the ultrasound probe). The code is splitted in 3 threads :
#include <iostream>
#include <visp3/ustk_core/usConfig.h>
#if (defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) && \
defined(VISP_HAVE_VIPER850)
#include <QApplication>
#include <QStringList>
#include <QtCore/QThread>
#include <visp3/ustk_grabber/usNetworkGrabberPreScan2D.h>
#include <visp3/ustk_confidence_map/usScanlineConfidence2D.h>
#include <visp3/gui/vpDisplayX.h>
#include <visp3/robot/vpRobotViper850.h>
vpMutex s_mutex_control_velocity;
vpColVector s_controlVelocity(6, 0.);
vpMutex s_mutex_capture;
typedef enum { capture_waiting, capture_started, capture_stopped } t_CaptureState;
t_CaptureState s_capture_state = capture_waiting;
vpThread::Return controlFunction(vpThread::Args args)
{
(void)args;
vpRobotViper850 robot;
vpMatrix eJe;
vpHomogeneousMatrix eMs;
eMs[2][3] = -0.062;
vpHomogeneousMatrix sMp;
vpHomogeneousMatrix sMe;
eMs.inverse(sMe);
vpVelocityTwistMatrix sVe;
sVe.buildFrom(sMe);
vpColVector sHs(6);
vpColVector sHs_star(6);
vpColVector pHp_star(6);
vpColVector gHg(6);
vpMatrix lambdaProportionnal(6, 6);
vpMatrix lambdaDerivate(6, 6);
vpMatrix lambdaIntegral(6, 6);
vpColVector sEs(6, 0);
vpColVector sEs_last(6, 0);
vpColVector sEs_sum(6, 0);
vpTranslationVector stg;
vpColVector sHs_bias(6);
vpColVector v_s(6);
vpColVector q_dot(6);
lambdaProportionnal = 0;
for (int i = 0; i < 3; i++)
lambdaProportionnal[i][i] = 0.015;
for (int i = 3; i < 6; i++)
lambdaProportionnal[i][i] = 0;
lambdaDerivate = 0;
for (int i = 0; i < 3; i++)
lambdaDerivate[i][i] = 0.09;
for (int i = 3; i < 6; i++)
lambdaDerivate[i][i] = 0;
lambdaIntegral = 0;
for (int i = 0; i < 3; i++)
lambdaIntegral[i][i] = 0;
for (int i = 3; i < 6; i++)
lambdaIntegral[i][i] = 0;
pHp_star = 0;
pHp_star[1] = 4;
sMp[0][0] = 0;
sMp[1][1] = 0;
sMp[2][2] = 0;
sMp[0][2] = 1;
sMp[1][0] = 1;
sMp[2][1] = 1;
sMp[1][3] = 0.262;
gHg[2] = -(0.696 + 0.476) * 9.81;
stg[0] = 0;
stg[1] = 0;
stg[2] = 0.088;
vpHomogeneousMatrix eMp = eMs * sMp;
vpVelocityTwistMatrix eVp(eMp);
vpColVector q;
vpHomogeneousMatrix fMe;
vpHomogeneousMatrix fMs;
vpRotationMatrix sRf;
robot.getPosition(vpRobot::ARTICULAR_FRAME, q);
robot.get_fMe(q, fMe);
fMs = fMe * eMs;
vpHomogeneousMatrix sMf;
fMs.inverse(sMf);
sMf.extract(sRf);
vpForceTwistMatrix sFg(sMf);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
sFg[i + 3][j] = (stg.skew() * sRf)[i][j];
vpForceTwistMatrix sFp(sMp);
std::cout << "\nBias the force/torque sensor...\n " << std::endl;
robot.biasForceTorqueSensor();
robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL);
unsigned int iter = 0;
t_CaptureState capture_state_;
unsigned int bufferSize = 80;
double signalBuffer[bufferSize];
for (unsigned int i = 0; i < bufferSize; i++)
signalBuffer[i] = 0;
std::cout << "Starting control loop..." << std::endl;
double t0 = vpTime::measureTimeMs();
double deltaTmilliseconds = 1;
do {
t0 = vpTime::measureTimeMs();
s_mutex_capture.lock();
capture_state_ = s_capture_state;
s_mutex_capture.unlock();
if (capture_state_ == capture_started) {
sHs = robot.getForceTorque();
sHs *= -1;
robot.getPosition(vpRobot::ARTICULAR_FRAME, q);
robot.get_fMe(q, fMe);
fMs = fMe * eMs;
fMs.inverse(sMf);
sMf.extract(sRf);
sFg.buildFrom(sMf);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
sFg[i + 3][j] = (stg.skew() * sRf)[i][j];
if (iter == 0) {
sHs_bias = sHs - sFg * gHg;
}
vpColVector v_p;
{
vpMutex::vpScopedLock lock(s_mutex_control_velocity);
v_p = s_controlVelocity;
}
v_p[0] = 0;
v_p[1] = 0;
v_p[2] = 0;
v_p[3] = 0;
v_p[4] = 0;
sEs_last = sEs;
sEs = sFp * pHp_star - (sHs - sFg * gHg - sHs_bias);
signalBuffer[iter % bufferSize] = sEs[2];
double sum = 0;
unsigned int realBufferSize = iter + 1 < bufferSize ? iter + 1 : bufferSize;
for (unsigned int i = 0; i < realBufferSize; i++) {
sum += signalBuffer[i];
}
sEs[2] = sum / realBufferSize;
sEs_sum += sEs;
if (iter == 0) {
sEs_last = sEs;
}
v_s =
lambdaProportionnal * sEs + lambdaDerivate * (sEs - sEs_last) / deltaTmilliseconds + lambdaIntegral * sEs_sum;
v_s[0] = 0.0;
v_s[1] = 0.0;
v_s[3] = 0.0;
v_s[4] = 0.0;
v_s[5] = 0.0;
vpVelocityTwistMatrix eVs;
sVe.inverse(eVs);
vpColVector v_e = eVs * v_s + eVp * v_p;
robot.get_eJe(eJe);
q_dot = eJe.pseudoInverse() * v_e;
robot.setVelocity(vpRobot::ARTICULAR_FRAME, q_dot);
iter++;
}
vpTime::wait(t0, deltaTmilliseconds);
} while (capture_state_ != capture_stopped);
std::cout << "End of control thread" << std::endl;
return 0;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
if (qApp->arguments().contains(QString("--probeID"))) {
header.
probeId = qApp->arguments().at(qApp->arguments().indexOf(QString(
"--probeID")) + 1).toInt();
} else
if (qApp->arguments().contains(QString("--slotID"))) {
header.
slotId = qApp->arguments().at(qApp->arguments().indexOf(QString(
"--slotID")) + 1).toInt();
} else
if (qApp->arguments().contains(QString("--imagingMode"))) {
header.
imagingMode = qApp->arguments().at(qApp->arguments().indexOf(QString(
"--imagingMode")) + 1).toInt();
} else
double lambdaVisualError = 5;
vpDisplay *displayConf = NULL;
bool displayInit = false;
bool captureRunning = true;
vpThread thread_control((vpThread::Fn)controlFunction);
std::cout << "waiting ultrasound initialisation..." << std::endl;
do {
grabbedFrame = qtGrabber->
acquire();
confidenceProcessor.
run(confidence, *grabbedFrame);
double I_sum = 0.0;
for (unsigned int i = 0; i < confidence.getHeight(); ++i)
for (unsigned int j = 0; j < confidence.getWidth(); ++j)
I_sum += static_cast<double>(confidence[i][j]);
double yc = 0.0;
for (unsigned int x = 0; x < confidence.getHeight(); ++x)
for (unsigned int y = 0; y < confidence.getWidth(); ++y) {
yc += y * confidence[x][y];
}
yc /= I_sum;
{
vpMutex::vpScopedLock lock(s_mutex_control_velocity);
s_controlVelocity = 0.0;
s_controlVelocity[5] = lambdaVisualError * tc;
}
s_mutex_capture.lock();
s_capture_state = capture_started;
s_mutex_capture.unlock();
if (!displayInit && grabbedFrame->getHeight() != 0 && grabbedFrame->getWidth() != 0) {
#ifdef VISP_HAVE_X11
display =
new vpDisplayX(*grabbedFrame);
displayConf = new vpDisplayX(confidence, (*grabbedFrame).getWidth() + 60, 10);
#elif defined(VISP_HAVE_GDI)
display =
new vpDisplayGDI(*grabbedFrame);
displayConf = new vpDisplayGDI(confidence, (*grabbedFrame).getWidth() + 60, 10);
#endif
displayInit = true;
}
if (displayInit) {
vpDisplay::display(*grabbedFrame);
vpDisplay::display(confidence);
vpDisplay::displayText(confidence, 10, 10, "target", vpColor::green);
vpDisplay::displayLine(confidence, 0, confidence.getWidth() / 2 - 1, confidence.getHeight() - 1,
confidence.getWidth() / 2 - 1, vpColor::green);
vpDisplay::displayText(confidence, 25, 10, "current", vpColor::red);
vpDisplay::displayLine(confidence, 0, static_cast<int>(yc), confidence.getHeight() - 1, static_cast<int>(yc),
vpColor::red);
if (vpDisplay::getClick(confidence, false))
captureRunning = false;
if (vpDisplay::getClick(*grabbedFrame, false))
captureRunning = false;
vpDisplay::flush(*grabbedFrame);
vpDisplay::flush(confidence);
vpTime::wait(10);
}
} while (captureRunning);
std::cout << "stop capture" << std::endl;
if (displayInit) {
if (display)
if (displayConf)
delete displayConf;
}
{
vpMutex::vpScopedLock lock(s_mutex_control_velocity);
s_controlVelocity = 0.0;
s_controlVelocity[5] = -0.05;
}
vpTime::wait(500);
{
vpMutex::vpScopedLock lock(s_mutex_control_velocity);
s_controlVelocity = 0.0;
}
s_mutex_capture.lock();
s_capture_state = capture_stopped;
s_mutex_capture.unlock();
thread_control.join();
app.quit();
std::cout << "end main thread" << std::endl;
return 0;
}
#else
int main()
{
std::cout << "You should intall Qt5 (with wigdets and network modules), and display X to run this tutorial"
<< std::endl;
return 0;
}
#endif
Class to store additionnal informations arriving on the network with ultrasound images grabbed,...
Specific class to grab pre-scan frames from the ultrasound station on the network.
usFrameGrabbedInfo< usImagePreScan2D< unsigned char > > * acquire()
void useVpDisplay(vpDisplay *display)
bool initAcquisition(const usNetworkGrabber::usInitHeaderSent &header)
bool sendAcquisitionParameters()
void setMotorPosition(int motorPosition)
Process a pre-scan image to determine the confidence map.
void run(usImagePreScan2D< unsigned char > &preScanConfidence, const usImagePreScan2D< unsigned char > &preScanImage)
double getFieldOfView() const
double getScanLinePitch() const