1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 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 http://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  * Description: Class which enables to project an image in the 3D space
32  * and get the view of a virtual camera.
33  *
34  * Authors:
35  * Fabien Spindler
36  *
37  *****************************************************************************/
38 
39 /*!
40   \file vpVirtuose.cpp
41   \brief Wrapper over Haption Virtuose SDK to control haptic devices.
42 */
43 
44 #include <visp3/core/vpException.h>
45 #include <visp3/robot/vpVirtuose.h>
46 
47 #ifdef VISP_HAVE_VIRTUOSE
48 
49 /*!
50  * Default constructor.
51  * Set command type to virtual mechanism by default (impedance mode).
52  * Authorize indexing on all movements by default.
53  */
vpVirtuose()54 vpVirtuose::vpVirtuose()
55   : m_virtContext(NULL), m_ip_port("localhost#5000"), m_verbose(false), m_apiMajorVersion(0), m_apiMinorVersion(0),
56     m_ctrlMajorVersion(0), m_ctrlMinorVersion(0), m_typeCommand(COMMAND_TYPE_IMPEDANCE), m_indexType(INDEXING_ALL),
57     m_is_init(false), m_period(0.001f), m_njoints(6)
58 {
59   virtAPIVersion(&m_apiMajorVersion, &m_apiMinorVersion);
60   std::cout << "API version: " << m_apiMajorVersion << "." << m_apiMinorVersion << std::endl;
61 }
62 
63 /*!
64  * Delete the VirtContext object.
65  */
close()66 void vpVirtuose::close()
67 {
68   if (m_virtContext != NULL) {
69     virtClose(m_virtContext);
70     m_virtContext = NULL;
71   }
72 }
73 
74 /*!
75  * Default destructor that delete the VirtContext object.
76  */
~vpVirtuose()77 vpVirtuose::~vpVirtuose()
78 {
79   close();
80 }
81 
82 /*!
83  * Set haptic device ip address and communication port.
84  * \param[in] ip: Host IP address. Default value set in constructor is "localhost".
85  * \param[in] port: Host communication port. Default value set in constructor is 5000.
86  */
setIpAddressAndPort(const std::string & ip,int port)87 void vpVirtuose::setIpAddressAndPort(const std::string &ip, int port)
88 {
89   std::stringstream ss;
90   ss << ip << "#" << port;
91 
92   m_ip_port = ss.str();
93 }
94 
95 /*!
96  * Add a force to be applied to the virtuose (impedance effort).
97  * This function works in every mode.
98  * \param force : Is 6 component dynamic tensor (three forces and three
99  * torques) wrt virtuose end-effector and is expressed in the coordinates of
100  * the base frame.
101  */
addForce(vpColVector & force)102 void vpVirtuose::addForce(vpColVector &force)
103 {
104   if (force.size() != 6) {
105     throw(vpException(vpException::dimensionError,
106                       "Cannot apply a force feedback (dim %d) to the haptic "
107                       "device that is not 6-dimension",
108                       force.size()));
109   }
110 
111   init();
112 
113   float virtforce[6];
114   for (unsigned int i = 0; i < 6; i++)
115     virtforce[i] = (float)force[i];
116 
117   if (virtAddForce(m_virtContext, virtforce)) {
118     int err = virtGetErrorCode(m_virtContext);
119     throw(vpException(vpException::fatalError, "Error calling virtAddForce: error code %d", err));
120   }
121 }
122 
123 /*!
124  * Activate or desactivate force feedback.
125  * \param enable : 1 to activate (system's default value), 0 to desactivate.
126  */
enableForceFeedback(int enable)127 void vpVirtuose::enableForceFeedback(int enable)
128 {
129   init();
130 
131   if (virtEnableForceFeedback(m_virtContext, enable)) {
132     int err = virtGetErrorCode(m_virtContext);
133     throw(vpException(vpException::fatalError, "Error calling virtEnableForceFeedback(): error code %d", err));
134   }
135 }
136 
137 /*!
138  * Return the 6 joint values of the virtuose.
139  */
getArticularPosition() const140 vpColVector vpVirtuose::getArticularPosition() const
141 {
142   if (!m_is_init) {
143     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
144   }
145 
146 
147   float articular_position_[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
148 
149   if (virtGetArticularPosition(m_virtContext, articular_position_)) {
150     int err = virtGetErrorCode(m_virtContext);
151     throw(vpException(vpException::fatalError, "Error calling virtGetArticularPosition(): error code %d", err));
152   }
153 
154   vpColVector articularPosition(m_njoints, 0);
155   for (unsigned int i = 0; i < m_njoints; i++)
156     articularPosition[i] = articular_position_[i];
157 
158   return articularPosition;
159 }
160 
161 /*!
162  * Return the 6 joint velocities of the virtuose.
163  */
getArticularVelocity() const164 vpColVector vpVirtuose::getArticularVelocity() const
165 {
166   if (!m_is_init) {
167     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
168   }
169 
170   float articular_velocity_[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
171 
172   if (virtGetArticularSpeed(m_virtContext, articular_velocity_)) {
173     int err = virtGetErrorCode(m_virtContext);
174     throw(vpException(vpException::fatalError, "Error calling virtGetArticularSpeed: error code %d", err));
175   }
176 
177   vpColVector articularVelocity(m_njoints, 0);
178 
179   for (unsigned int i = 0; i < m_njoints; i++)
180     articularVelocity[i] = articular_velocity_[i];
181 
182   return articularVelocity;
183 }
184 
185 /*!
186  * Return the indexed position of the end-effector, expressed in the
187  * coordinates of the environment reference frame. With respect to the
188  * function getPosition(), getAvatarPosition() takes into account current
189  * offsets (indexing) and motor scale factors. \sa getPosition(),
190  * getPhysicalPosition()
191  */
getAvatarPosition() const192 vpPoseVector vpVirtuose::getAvatarPosition() const
193 {
194   if (!m_is_init) {
195     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
196   }
197 
198   float position_[7];
199   vpPoseVector position;
200   vpTranslationVector translation;
201   vpQuaternionVector quaternion;
202 
203   if (virtGetAvatarPosition(m_virtContext, position_)) {
204     int err = virtGetErrorCode(m_virtContext);
205     throw(vpException(vpException::fatalError, "Error calling virtGetAvatarPosition: error code %d", err));
206   } else {
207     for (int i = 0; i < 3; i++)
208       translation[i] = position_[i];
209     for (int i = 0; i < 4; i++)
210       quaternion[i] = position_[3 + i];
211 
212     vpThetaUVector thetau(quaternion);
213 
214     position.buildFrom(translation, thetau);
215 
216     return position;
217   }
218 }
219 
220 /*!
221  * Return the current position of the base frame
222  * with respect to the observation reference frame.
223  *
224  * \sa setBaseFrame(), getObservationFrame()
225  */
getBaseFrame() const226 vpPoseVector vpVirtuose::getBaseFrame() const
227 {
228   if (!m_is_init) {
229     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
230   }
231 
232   vpPoseVector position;
233   float position_[7];
234   vpTranslationVector translation;
235   vpQuaternionVector quaternion;
236 
237   if (virtGetBaseFrame(m_virtContext, position_)) {
238     int err = virtGetErrorCode(m_virtContext);
239     throw(vpException(vpException::fatalError, "Error calling virtGetBaseFrame: error code %d", err));
240   } else {
241     for (int i = 0; i < 3; i++)
242       translation[i] = position_[i];
243     for (int i = 0; i < 4; i++)
244       quaternion[i] = position_[3 + i];
245 
246     vpThetaUVector thetau(quaternion);
247 
248     position.buildFrom(translation, thetau);
249 
250     return position;
251   }
252 }
253 
254 /*!
255  * Return the current command type.
256  */
getCommandType() const257 VirtCommandType vpVirtuose::getCommandType() const
258 {
259   if (!m_is_init) {
260     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
261   }
262 
263   VirtCommandType type;
264 
265   if (virtGetCommandType(m_virtContext, &type)) {
266     int err = virtGetErrorCode(m_virtContext);
267     throw(vpException(vpException::fatalError, "Error calling virtGetCommandType: error code %d", err));
268   }
269   return type;
270 }
271 
272 /*!
273  * Return the status of DeadMan sensor : true if the sensor is ON (a user is
274  * holding the handle) and false if the sensor is OFF (no user detected).
275  */
getDeadMan() const276 bool vpVirtuose::getDeadMan() const
277 {
278   if (!m_is_init) {
279     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
280   }
281 
282   int deadman;
283   if (virtGetDeadMan(m_virtContext, &deadman)) {
284     int err = virtGetErrorCode(m_virtContext);
285     throw(vpException(vpException::fatalError, "Error calling virtGetDeadMan: error code %d", err));
286   }
287   return (deadman ? true : false);
288 }
289 
290 /*!
291  * Return the status of the emergency stop button : true if the system is
292  * operational (button correctly plugged and not triggered) and false if the
293  * system is not operational (button not plugged or triggered).
294  */
getEmergencyStop() const295 bool vpVirtuose::getEmergencyStop() const
296 {
297   if (!m_is_init) {
298     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
299   }
300 
301   int emergencyStop;
302   if (virtGetEmergencyStop(m_virtContext, &emergencyStop)) {
303     int err = virtGetErrorCode(m_virtContext);
304     throw(vpException(vpException::fatalError, "Error calling virtGetEmergencyStop: error code %d", err));
305   }
306   return (emergencyStop ? true : false);
307 }
308 
309 /*!
310  * Return the 6-dimension force tensor to be applied to the object attached to
311  * the Virtuose, allowing the dynamic simulation of the scene.
312  */
getForce() const313 vpColVector vpVirtuose::getForce() const
314 {
315   if (!m_is_init) {
316     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
317   }
318 
319   vpColVector force(6, 0);
320   float force_[6];
321   if (virtGetForce(m_virtContext, force_)) {
322     int err = virtGetErrorCode(m_virtContext);
323     throw(vpException(vpException::fatalError, "Error calling virtGetForce: error code %d", err));
324   }
325 
326   for (unsigned int i = 0; i < 6; i++)
327     force[i] = force_[i];
328   return force;
329 }
330 
331 /*!
332   Return the handler used to communicate with the device.
333   This function could be used to access to a functionality that is not
334   implemented in vpVirtuose class.
335 
336   The following sample code shows how to use this function to get the
337   device joint positions. This functionality is already implemented
338   in getArticularPosition().
339   \code
340 #include <visp3/robot/vpVirtuose.h>
341 
342 int main()
343 {
344   vpVirtuose virtuose;
345 
346   virtuose.init();
347 
348   VirtContext handler = virtuose.getHandler();
349   float q[6];
350   if (virtGetArticularPosition(handler, q)) { // Use the handler to access to Haption API directly
351     std::cout << "Cannot get articular position" << std::endl;
352   }
353   std::cout << "Joint position: ";
354   for (unsigned int i=0; i<6; i++)
355     std::cout << q[i] << " ";
356   std::cout << std::endl;
357 }
358   \endcode
359 
360   \sa getArticularPosition()
361 
362  */
getHandler()363 VirtContext vpVirtuose::getHandler() { return m_virtContext; }
364 
365 /*!
366   Get device number of joints.
367 
368   \return The number of joints of the device. Sould be 6 for Virtuose, 9 for the glove fingers.
369  */
getJointsNumber() const370 unsigned int vpVirtuose::getJointsNumber() const
371 {
372   if (!m_is_init) {
373     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
374   }
375 
376   return m_njoints;
377 }
378 
379 
380 /*!
381  * Return the cartesian current position of the observation reference frame
382  * with respect to the environment reference frame.
383  *
384  * \sa setObservationFrame(), getBaseFrame()
385  */
getObservationFrame() const386 vpPoseVector vpVirtuose::getObservationFrame() const
387 {
388   if (!m_is_init) {
389     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
390   }
391 
392   vpPoseVector position;
393   float position_[7];
394   vpTranslationVector translation;
395   vpQuaternionVector quaternion;
396 
397   if (virtGetObservationFrame(m_virtContext, position_)) {
398     int err = virtGetErrorCode(m_virtContext);
399     throw(vpException(vpException::fatalError, "Error calling virtGetObservationFrame: error code %d", err));
400   } else {
401     for (int i = 0; i < 3; i++)
402       translation[i] = position_[i];
403     for (int i = 0; i < 4; i++)
404       quaternion[i] = position_[3 + i];
405 
406     vpThetaUVector thetau(quaternion);
407 
408     position.buildFrom(translation, thetau);
409   }
410   return position;
411 }
412 
413 /*!
414  * Return the cartesian physical position of the Virtuose expressed in the
415  * coordinates of the base reference frame.
416  *
417  * \sa getAvatarPosition(), getPosition()
418  */
getPhysicalPosition() const419 vpPoseVector vpVirtuose::getPhysicalPosition() const
420 {
421   if (!m_is_init) {
422     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
423   }
424 
425   vpPoseVector position;
426   float position_[7];
427   vpTranslationVector translation;
428   vpQuaternionVector quaternion;
429 
430   if (virtGetPhysicalPosition(m_virtContext, position_)) {
431     int err = virtGetErrorCode(m_virtContext);
432     throw(vpException(vpException::fatalError, "Error calling virtGetPhysicalPosition: error code %d", err));
433   } else {
434     for (int i = 0; i < 3; i++)
435       translation[i] = position_[i];
436     for (int i = 0; i < 4; i++)
437       quaternion[i] = position_[3 + i];
438 
439     vpThetaUVector thetau(quaternion);
440 
441     position.buildFrom(translation, thetau);
442   }
443   return position;
444 }
445 
446 /*!
447  * Return the physical cartesian velocity twist of the Virtuose expressed in
448  * the coordinates of the base reference frame. \return A 6-dimension velocity
449  * twist with 3 translation velocities and 3 rotation velocities. \sa
450  * getVelocity()
451  */
getPhysicalVelocity() const452 vpColVector vpVirtuose::getPhysicalVelocity() const
453 {
454   if (!m_is_init) {
455     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
456   }
457 
458   vpColVector vel(6, 0);
459   float speed[6];
460   if (virtGetPhysicalSpeed(m_virtContext, speed)) {
461     int err = virtGetErrorCode(m_virtContext);
462     throw(vpException(vpException::fatalError, "Error calling virtGetPhysicalSpeed: error code %s",
463                       virtGetErrorMessage(err)));
464   }
465   for (unsigned int i = 0; i < 6; i++)
466     vel[i] = speed[i];
467   return vel;
468 }
469 
470 /*!
471  * Return the cartesian position of the virtuose (or the object attached to
472  * it, if any) expressed in the coordinates of the environment reference
473  * frame. \sa getAvatarPosition(), getPhysicalPosition()
474  */
getPosition() const475 vpPoseVector vpVirtuose::getPosition() const
476 {
477   if (!m_is_init) {
478     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
479   }
480 
481   vpPoseVector position;
482   float position_[7];
483   vpTranslationVector translation;
484   vpQuaternionVector quaternion;
485 
486   if (virtGetPosition(m_virtContext, position_)) {
487     int err = virtGetErrorCode(m_virtContext);
488     throw(vpException(vpException::fatalError, "Error calling virtGetPosition: error code %d", err));
489   } else {
490     for (int i = 0; i < 3; i++)
491       translation[i] = position_[i];
492     for (int i = 0; i < 4; i++)
493       quaternion[i] = position_[3 + i];
494 
495     vpThetaUVector thetau(quaternion);
496 
497     position.buildFrom(translation, thetau);
498   }
499   return position;
500 }
501 
502 /*!
503  * Return status of the motors : true if motors are ON, false otherwise.
504  */
getPower() const505 bool vpVirtuose::getPower() const
506 {
507   if (!m_is_init) {
508     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
509   }
510 
511   int power;
512   virtGetPowerOn(m_virtContext, &power);
513   return (power ? true : false);
514 }
515 
516 /*!
517  * Return the cartesian velocity twist of the virtuose (or the object attached
518  * to it, if any) expressed in the coordinates of the environment reference
519  * frame. \return A 6-dimension velocity twist with 3 translation velocities
520  * and 3 rotation velocities. \sa getPhysicalVelocity()
521  */
getVelocity() const522 vpColVector vpVirtuose::getVelocity() const
523 {
524   if (!m_is_init) {
525     throw(vpException(vpException::fatalError, "Device not initialized. Call init()."));
526   }
527 
528   vpColVector vel(6, 0);
529   float speed[6];
530   if (virtGetSpeed(m_virtContext, speed)) {
531     int err = virtGetErrorCode(m_virtContext);
532     throw(vpException(vpException::fatalError, "Cannot get haptic device velocity: %s", virtGetErrorMessage(err)));
533   }
534   for (unsigned int i = 0; i < 6; i++)
535     vel[i] = speed[i];
536   return vel;
537 }
538 
539 /*!
540  * Initialize virtuose device opening the connection to the device and setting
541  * the default command type. If the device is already initialized, a call to
542  * init() does nothing.
543  */
init()544 void vpVirtuose::init()
545 {
546   if (!m_is_init) {
547     m_virtContext = virtOpen(m_ip_port.c_str());
548 
549     if (m_virtContext == NULL) {
550       int err = virtGetErrorCode(m_virtContext);
551       throw(vpException(vpException::fatalError, "Cannot open communication with haptic device using %s: %s. Check ip and port values",
552                         m_ip_port.c_str(), virtGetErrorMessage(err)));
553     }
554 
555     if (virtGetControlerVersion(m_virtContext, &m_ctrlMajorVersion, &m_ctrlMinorVersion)) {
556       int err = virtGetErrorCode(m_virtContext);
557       throw(vpException(vpException::fatalError, "Cannot get haptic device controller version: %s",
558                         virtGetErrorMessage(err)));
559     }
560 
561     if (m_verbose) {
562       std::cout << "Controller version: " << m_ctrlMajorVersion << "." << m_ctrlMinorVersion << std::endl;
563     }
564 
565     if (virtSetCommandType(m_virtContext, m_typeCommand)) {
566       int err = virtGetErrorCode(m_virtContext);
567       throw(
568           vpException(vpException::fatalError, "Cannot set haptic device command type: %s", virtGetErrorMessage(err)));
569     }
570 
571     if (virtSetTimeStep(m_virtContext, m_period)) {
572       int err = virtGetErrorCode(m_virtContext);
573       throw(vpException(vpException::fatalError, "Error calling virtSetTimeStep: error code %d", err));
574     }
575 
576     // Update number of joints
577     float articular_position_[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
578 
579     if (virtGetArticularPosition(m_virtContext, articular_position_)) {
580       int err = virtGetErrorCode(m_virtContext);
581       throw(vpException(vpException::fatalError, "Error calling virtGetArticularPosition() in int(): error code %d", err));
582     }
583 
584     m_njoints = 6; // At least 6 joints
585     for (unsigned int i=m_njoints; i < 20; i++) {
586       m_njoints = i;
587       if (std::fabs(articular_position_[i]) <= std::numeric_limits<float>::epsilon()) {
588         break;
589       }
590     }
591 
592     m_is_init = true;
593   }
594 }
595 
596 /*!
597  * Send a command of articular force to the Virtuose.
598  * setArticularForce() only works in mode COMMAND_TYPE_ARTICULAR_IMPEDANCE, to
599  * be set with setCommandType().
600  *
601  * \param articularForce : Six dimension torque vector.
602  */
setArticularForce(const vpColVector & articularForce)603 void vpVirtuose::setArticularForce(const vpColVector &articularForce)
604 {
605   init();
606 
607   if (articularForce.size() != m_njoints) {
608     throw(vpException(vpException::dimensionError,
609                       "Cannot apply an articular force feedback (dim %d) to "
610                       "the haptic device that is not 6-dimension",
611                       articularForce.size()));
612   }
613 
614   float *articular_force = new float [m_njoints];
615   for (unsigned int i = 0; i < m_njoints; i++)
616     articular_force[i] = (float)articularForce[i];
617 
618   if (virtSetArticularForce(m_virtContext, articular_force)) {
619     delete[] articular_force;
620     int err = virtGetErrorCode(m_virtContext);
621     throw(vpException(vpException::fatalError, "Error calling virtSetArticularForce: error code %d", err));
622   }
623 
624   delete [] articular_force;
625 }
626 
627 /*!
628  * Send a command of articular (joint) position to the virtuose.
629  * This function works only in COMMAND_TYPE_ARTICULAR mode, to be set with
630  * setCommandType().
631  *
632  * \param articularPosition : Six dimension joint position vector.
633  */
setArticularPosition(const vpColVector & articularPosition)634 void vpVirtuose::setArticularPosition(const vpColVector &articularPosition)
635 {
636   init();
637 
638   if (articularPosition.size() != m_njoints) {
639     throw(vpException(vpException::dimensionError,
640                       "Cannot send an articular position command (dim %d) to "
641                       "the haptic device that is not %d-dimension",
642                       m_njoints, articularPosition.size()));
643   }
644 
645   float *articular_position = new float[m_njoints];
646   for (unsigned int i = 0; i < m_njoints; i++)
647     articular_position[i] = (float)articularPosition[i];
648 
649   if (virtSetArticularPosition(m_virtContext, articular_position)) {
650     int err = virtGetErrorCode(m_virtContext);
651     delete [] articular_position;
652     throw(vpException(vpException::fatalError, "Error calling virtSetArticularPosition: error code %d", err));
653   }
654   delete[] articular_position;
655 }
656 
657 /*!
658  * Send a command of articular (joint) velocity to the virtuose.
659  * This function works only in COMMAND_TYPE_ARTICULAR mode, to be set with
660  * setCommandType().
661  *
662  * \param articularVelocity : Six dimension joint velocity vector.
663  */
setArticularVelocity(const vpColVector & articularVelocity)664 void vpVirtuose::setArticularVelocity(const vpColVector &articularVelocity)
665 {
666   init();
667 
668   if (articularVelocity.size() != m_njoints) {
669     throw(vpException(vpException::dimensionError,
670                       "Cannot send an articular velocity command (dim %d) to "
671                       "the haptic device that is not %d-dimension",
672                       m_njoints, articularVelocity.size()));
673   }
674 
675   float *articular_velocity = new float [m_njoints];
676   for (unsigned int i = 0; i < m_njoints; i++)
677     articular_velocity[i] = (float)articularVelocity[i];
678 
679   if (virtSetArticularSpeed(m_virtContext, articular_velocity)) {
680     int err = virtGetErrorCode(m_virtContext);
681     delete [] articular_velocity;
682     throw(vpException(vpException::fatalError, "Error calling virtSetArticularVelocity: error code %d", err));
683   }
684   delete[] articular_velocity;
685 }
686 
687 /*!
688  * Move the base frame with respect to the observation reference frame.
689  * It can be called at any time.
690  * \param position : Position of the base frame.
691  *
692  * \sa getBaseFrame(), setObservationFrame()
693  */
setBaseFrame(const vpPoseVector & position)694 void vpVirtuose::setBaseFrame(const vpPoseVector &position)
695 {
696   init();
697 
698   float position_[7];
699   vpTranslationVector translation;
700   vpQuaternionVector quaternion;
701 
702   position.extract(translation);
703   position.extract(quaternion);
704 
705   for (int i = 0; i < 3; i++)
706     position_[i] = (float)translation[i];
707   for (int i = 0; i < 4; i++)
708     position_[3 + i] = (float)quaternion[i];
709 
710   if (virtSetBaseFrame(m_virtContext, position_)) {
711     int err = virtGetErrorCode(m_virtContext);
712     throw(vpException(vpException::fatalError, "Error calling virtSetBaseFrame: error code %d", err));
713   }
714 }
715 
716 /*!
717  * Set the command type.
718  * \param type : Possible values:
719  * - COMMAND_TYPE_NONE : No possible movement.
720  * - COMMAND_TYPE_IMPEDANCE : Force/position control.
721  * - COMMAND_TYPE_VIRTMECH : Position/force control with virtual mechanism.
722  * - COMMAND_TYPE_ARTICULAR : Joint control.
723  * - COMMAND_TYPE_ARTICULAR_IMPEDANCE : Force/Position control in the joint
724  * space.
725  */
setCommandType(const VirtCommandType & type)726 void vpVirtuose::setCommandType(const VirtCommandType &type)
727 {
728   init();
729 
730   if (m_typeCommand != type) {
731     m_typeCommand = type;
732 
733     if (virtSetCommandType(m_virtContext, m_typeCommand)) {
734       int err = virtGetErrorCode(m_virtContext);
735       throw(vpException(vpException::fatalError, "Error calling virtSetCommandType: error code %d", err));
736     }
737   }
738 }
739 
740 /*!
741  * Set the force to be applied by the Virtuose.
742  * setForce() only works in COMMAND_TYPE_IMPEDANCE mode, to be set with
743  * setCommandType(). \param force : Force vector that represents a dynamic
744  * tensor with 6 components.
745  */
setForce(const vpColVector & force)746 void vpVirtuose::setForce(const vpColVector &force)
747 {
748   init();
749 
750   if (force.size() != 6) {
751     throw(vpException(vpException::dimensionError,
752                       "Cannot apply a force feedback (dim %d) to the haptic "
753                       "device that is not 6-dimension",
754                       force.size()));
755   }
756 
757   float virtforce[6];
758   for (unsigned int i = 0; i < 6; i++)
759     virtforce[i] = (float)force[i];
760 
761   if (virtSetForce(m_virtContext, virtforce)) {
762     int err = virtGetErrorCode(m_virtContext);
763     throw(vpException(vpException::fatalError, "Error calling virtSetForce: error code %d", err));
764   }
765 }
766 
767 /*!
768  * Set the force scale factor.
769  * \param forceFactor : Force scale factor applied to the force torque tensor
770  * set by setForce().
771  */
setForceFactor(const float & forceFactor)772 void vpVirtuose::setForceFactor(const float &forceFactor)
773 {
774   init();
775 
776   if (virtSetForceFactor(m_virtContext, forceFactor)) {
777     int err = virtGetErrorCode(m_virtContext);
778     throw(vpException(vpException::fatalError, "Error calling virtSetForceFactor: error code %d", err));
779   }
780 }
781 
782 /*!
783  * Set indexing (offset) mode.
784  * \param type : Possible choices are:
785  * - INDEXING_ALL : authorize indexing on all movements.
786  * - INDEXING_TRANS : authorize indexing on translation, i.e., the orientation
787  * of the object is always identical to the orientation of the device
788  * end-effector.
789  * - INDEXING_NONE : forbids indexing on all movements.
790  * - The following values are also implemented:
791  * INDEXING_ALL_FORCE_FEEDBACK_INHIBITION,
792  * INDEXING_TRANS_FORCE_FEEDBACK_INHIBITION. These values correspond to the
793  * same modes listed before, but the force feedback is inhibited during
794  * indexing.
795  */
setIndexingMode(const VirtIndexingType & type)796 void vpVirtuose::setIndexingMode(const VirtIndexingType &type)
797 {
798   init();
799 
800   if (m_indexType != type) {
801     m_indexType = type;
802 
803     if (virtSetIndexingMode(m_virtContext, m_indexType)) {
804       int err = virtGetErrorCode(m_virtContext);
805       throw(vpException(vpException::fatalError, "Error calling setIndexingMode: error code %d", err));
806     }
807   }
808 }
809 
810 /*!
811  * Move the observation frame with respect to the environment reference frame.
812  * It can be called at any time.
813  * \param position : Position of the observation frame.
814  *
815  * \sa getObservationFrame(), setBaseFrame()
816  */
setObservationFrame(const vpPoseVector & position)817 void vpVirtuose::setObservationFrame(const vpPoseVector &position)
818 {
819   init();
820 
821   float position_[7];
822   vpTranslationVector translation;
823   vpQuaternionVector quaternion;
824 
825   position.extract(translation);
826   position.extract(quaternion);
827 
828   for (int i = 0; i < 3; i++)
829     position_[i] = (float)translation[i];
830   for (int i = 0; i < 4; i++)
831     position_[3 + i] = (float)quaternion[i];
832 
833   if (virtSetObservationFrame(m_virtContext, position_)) {
834     int err = virtGetErrorCode(m_virtContext);
835     throw(vpException(vpException::fatalError, "Error calling virtSetObservationFrame: error code %d", err));
836   }
837 }
838 
839 /*!
840  * Register the periodic function.
841  * setPeriodicFunction() defines a callback function to be called at a fixed
842 period of time, as timing for the simulation.
843  * The callback function is synchronized with the Virtuose controller
844 (messages arrive at very constant time intervals from it)
845  * and generates hardware interrupts to be taken into account by the operating
846 system.
847  * In practice, this function is much more efficient for timing the simulation
848 than common software timers.
849  * This function is started using startPeriodicFunction() and stopped using
850 stopPeriodicFunction().
851  * \param CallBackVirt : Callback function.
852  *
853  * Example of the use of the periodic function:
854  \code
855 #include <visp3/robot/vpVirtuose.h>
856 
857 void CallBackVirtuose(VirtContext VC, void* ptr)
858 {
859   (void) VC;
860   vpVirtuose* p_virtuose=(vpVirtuose*)ptr;
861   vpPoseVector position = p_virtuose->getPosition();
862   return;
863 }
864 
865 int main()
866 {
867   vpVirtuose virtuose;
868   float period = 0.001;
869   virtuose.setTimeStep(period);
870   virtuose.setPeriodicFunction(CallBackVirtuose, period, virtuose);
871   virtuose.startPeriodicFunction();
872   virtuose.stopPeriodicFunction();
873 }
874  \endcode
875 
876  \sa startPeriodicFunction(), stopPeriodicFunction()
877  */
setPeriodicFunction(VirtPeriodicFunction CallBackVirt)878 void vpVirtuose::setPeriodicFunction(VirtPeriodicFunction CallBackVirt)
879 {
880   init();
881 
882   if (virtSetPeriodicFunction(m_virtContext, CallBackVirt, &m_period, this)) {
883     int err = virtGetErrorCode(m_virtContext);
884     throw(vpException(vpException::fatalError, "Error calling virtSetPeriodicFunction: error code %d", err));
885   }
886 }
887 
888 /*!
889  * Modify the current value of the control position and send it to the
890  * Virtuose. \param position : Position of the end-effector (or the attached
891  * object, if any).
892  */
setPosition(vpPoseVector & position)893 void vpVirtuose::setPosition(vpPoseVector &position)
894 {
895   init();
896 
897   float position_[7];
898   vpTranslationVector translation;
899   vpQuaternionVector quaternion;
900 
901   position.extract(translation);
902   position.extract(quaternion);
903 
904   for (int i = 0; i < 3; i++)
905     position_[i] = (float)translation[i];
906   for (int i = 0; i < 4; i++)
907     position_[3 + i] = (float)quaternion[i];
908 
909   if (virtSetPosition(m_virtContext, position_)) {
910     int err = virtGetErrorCode(m_virtContext);
911     throw(vpException(vpException::fatalError, "Error calling virtSetPosition: error code %d", err));
912   }
913 }
914 
915 /*!
916  * Turn the motor power OFF.
917  */
setPowerOff()918 void vpVirtuose::setPowerOff()
919 {
920   init();
921 
922   if (virtSetPowerOn(m_virtContext, 0)) {
923     int err = virtGetErrorCode(m_virtContext);
924     throw(vpException(vpException::fatalError, "Error calling virtSetPowerOff: error code %d", err));
925   }
926 }
927 
928 /*!
929  * Turn the motor power ON.
930  */
setPowerOn()931 void vpVirtuose::setPowerOn()
932 {
933   init();
934 
935   if (virtSetPowerOn(m_virtContext, 1)) {
936     int err = virtGetErrorCode(m_virtContext);
937     throw(vpException(vpException::fatalError, "Error calling virtSetPowerOn: error code %d", err));
938   }
939 }
940 
941 /*!
942  * Set saturation values of the force feedback
943  * \param forceLimit : Value expressed in N.
944  * \param torqueLimit : Value expressed in Nm.
945  */
setSaturation(const float & forceLimit,const float & torqueLimit)946 void vpVirtuose::setSaturation(const float &forceLimit, const float &torqueLimit)
947 {
948   init();
949 
950   if (virtSaturateTorque(m_virtContext, forceLimit, torqueLimit)) {
951     int err = virtGetErrorCode(m_virtContext);
952     throw(vpException(vpException::fatalError, "Error calling virtSaturateTorque: error code %d", err));
953   }
954 }
955 
956 /*!
957  * Set the the simulation time step.
958  * The function must be called before the selection of the type of control
959  * mode. \param timeStep : Simulation time step (seconds).
960  */
setTimeStep(const float & timeStep)961 void vpVirtuose::setTimeStep(const float &timeStep)
962 {
963   init();
964 
965   if (m_period != timeStep) {
966     m_period = timeStep;
967 
968     if (virtSetTimeStep(m_virtContext, m_period)) {
969       int err = virtGetErrorCode(m_virtContext);
970       throw(vpException(vpException::fatalError, "Error calling virtSetTimeStep: error code %d", err));
971     }
972   }
973 }
974 
975 /*!
976  *  Modify the current value of the control velocity and send it to the
977  * Virtuose. \param velocity : Velocity twist vector, where translations
978  * velocities are expressed in m/s and rotation velocities in rad/s.
979  */
setVelocity(vpColVector & velocity)980 void vpVirtuose::setVelocity(vpColVector &velocity)
981 {
982   init();
983 
984   if (velocity.size() != 6) {
985     throw(vpException(vpException::dimensionError, "Cannot set a velocity vector (dim %d) that is not 6-dimension",
986                       velocity.size()));
987   }
988 
989   float speed[6];
990   for (unsigned int i = 0; i < 6; i++)
991     speed[i] = (float)velocity[i];
992 
993   if (virtSetSpeed(m_virtContext, speed)) {
994     int err = virtGetErrorCode(m_virtContext);
995     throw(vpException(vpException::fatalError, "Error calling virtSetSpeed: error code %d", err));
996   }
997 }
998 
999 /*!
1000  * Set the speed factor.
1001  * \param velocityFactor : Scale factor applied to the velocities set using
1002  * setVelocity().
1003  */
setVelocityFactor(const float & velocityFactor)1004 void vpVirtuose::setVelocityFactor(const float &velocityFactor)
1005 {
1006   init();
1007 
1008   if (virtSetSpeedFactor(m_virtContext, velocityFactor)) {
1009     int err = virtGetErrorCode(m_virtContext);
1010     throw(vpException(vpException::fatalError, "Error calling setVelocityFactor: error code %d", err));
1011   }
1012 }
1013 
1014 /*!
1015  * Start the callback function set with setPeriodicFunction().
1016  *
1017  * \sa stopPeriodicFunction(), setPeriodicFunction()
1018  */
startPeriodicFunction()1019 void vpVirtuose::startPeriodicFunction()
1020 {
1021   init();
1022 
1023   if (virtStartLoop(m_virtContext)) {
1024     int err = virtGetErrorCode(m_virtContext);
1025     throw(vpException(vpException::fatalError, "Error calling startLoop: error code %d", err));
1026   } else
1027     std::cout << "Haptic loop open." << std::endl;
1028 }
1029 
1030 /*!
1031  * Stop the callback function set with setPeriodicFunction().
1032  *
1033  * \sa startPeriodicFunction(), setPeriodicFunction()
1034  */
stopPeriodicFunction()1035 void vpVirtuose::stopPeriodicFunction()
1036 {
1037   init();
1038 
1039   if (virtStopLoop(m_virtContext)) {
1040     int err = virtGetErrorCode(m_virtContext);
1041     throw(vpException(vpException::fatalError, "Error calling stopLoop: error code %d", err));
1042   } else
1043     std::cout << "Haptic loop closed." << std::endl;
1044 }
1045 
1046 #else
1047 // Work around to avoid warning
dummy_vpVirtuose()1048 void dummy_vpVirtuose(){};
1049 #endif
1050