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:
32 * Interface for Flir Ptu Cpi robot.
33 *
34 * Authors:
35 * Fabien Spindler
36 *
37 *****************************************************************************/
38
39 #include <visp3/core/vpConfig.h>
40
41 #ifdef VISP_HAVE_FLIR_PTU_SDK
42
43 #include <signal.h>
44 #include <stdexcept>
45
46 extern "C" {
47 #include <cpi.h>
48 }
49
50 /*!
51 \file vpRobotFlirPtu.cpp
52 \brief Interface for Flir Ptu Cpi robot.
53 */
54
55 #include <visp3/core/vpHomogeneousMatrix.h>
56 #include <visp3/robot/vpRobotFlirPtu.h>
57
58 /*!
59
60 Emergency stops the robot if the program is interrupted by a SIGINT
61 (CTRL C), SIGSEGV (segmentation fault), SIGBUS (bus error), SIGKILL
62 or SIGQUIT signal.
63
64 */
emergencyStop(int signo)65 void vpRobotFlirPtu::emergencyStop(int signo)
66 {
67 std::stringstream msg;
68 msg << "Stop the FLIR PTU by signal (" << signo << "): " << (char)7;
69 switch (signo) {
70 case SIGINT:
71 msg << "SIGINT (stop by ^C) ";
72 break;
73 case SIGSEGV:
74 msg << "SIGSEGV (stop due to a segmentation fault) ";
75 break;
76 #ifndef WIN32
77 case SIGBUS:
78 msg << "SIGBUS (stop due to a bus error) ";
79 break;
80 case SIGKILL:
81 msg << "SIGKILL (stop by CTRL \\) ";
82 break;
83 case SIGQUIT:
84 msg << "SIGQUIT ";
85 break;
86 #endif
87 default:
88 msg << signo << std::endl;
89 }
90
91 throw vpRobotException(vpRobotException::signalException, msg.str());
92 }
93
94 /*!
95 Basic initialization.
96 */
init()97 void vpRobotFlirPtu::init()
98 {
99 // If you want to control the robot in Cartesian in a tool frame, set the corresponding transformation in m_eMc
100 // that is set to identity by default in the constructor.
101
102 maxRotationVelocity = maxRotationVelocityDefault;
103 maxTranslationVelocity = maxTranslationVelocityDefault;
104
105 // Set here the robot degrees of freedom number
106 nDof = 2; // Flir Ptu has 2 dof
107 }
108
109 /*!
110 Default constructor.
111 */
vpRobotFlirPtu()112 vpRobotFlirPtu::vpRobotFlirPtu()
113 : m_eMc(), m_cer(NULL), m_status(0), m_pos_max_tics(2), m_pos_min_tics(2), m_vel_max_tics(2), m_res(2),
114 m_connected(false), m_njoints(2), m_positioning_velocity(20.)
115 {
116 signal(SIGINT, vpRobotFlirPtu::emergencyStop);
117 signal(SIGSEGV, vpRobotFlirPtu::emergencyStop);
118 #ifndef WIN32
119 signal(SIGBUS, vpRobotFlirPtu::emergencyStop);
120 signal(SIGKILL, vpRobotFlirPtu::emergencyStop);
121 signal(SIGQUIT, vpRobotFlirPtu::emergencyStop);
122 #endif
123
124 init();
125 }
126
127 /*!
128 Destructor.
129 */
~vpRobotFlirPtu()130 vpRobotFlirPtu::~vpRobotFlirPtu()
131 {
132 stopMotion();
133 disconnect();
134 }
135
136 /*
137 At least one of these function has to be implemented to control the robot with a
138 Cartesian velocity:
139 - get_eJe()
140 - get_fJe()
141 */
142
143 /*!
144 Get the robot Jacobian expressed in the end-effector frame.
145 \return End-effector frame Jacobian.
146
147 \sa get_fJe()
148 */
get_eJe()149 vpMatrix vpRobotFlirPtu::get_eJe()
150 {
151 vpMatrix eJe;
152 vpColVector q;
153 getPosition(vpRobot::JOINT_STATE, q);
154 eJe.resize(6, 2);
155 eJe = 0;
156 eJe[3][0] = -sin(q[1]);
157 eJe[4][1] = -1;
158 eJe[5][0] = -cos(q[1]);
159 return eJe;
160 }
161
162 /*!
163 Get the robot Jacobian expressed in the end-effector frame.
164 \param[out] eJe : End-effector frame Jacobian.
165
166 \sa get_fJe()
167 */
get_eJe(vpMatrix & eJe)168 void vpRobotFlirPtu::get_eJe(vpMatrix &eJe) { eJe = get_eJe(); }
169
170 /*!
171 Get the robot Jacobian expressed in the robot reference frame.
172 \return Base (or reference) frame Jacobian.
173
174 \sa get_eJe()
175 */
get_fJe()176 vpMatrix vpRobotFlirPtu::get_fJe()
177 {
178 vpMatrix fJe;
179 vpColVector q;
180 getPosition(vpRobot::JOINT_STATE, q);
181 fJe.resize(6, 2);
182 fJe = 0;
183 fJe[3][1] = -sin(q[1]);
184 fJe[4][1] = -cos(q[1]);
185 fJe[5][0] = -1;
186
187 return fJe;
188 }
189
190 /*!
191 Get the robot Jacobian expressed in the robot reference frame.
192 \param[out] fJe : Base (or reference) frame Jacobian.
193
194 \sa get_fJe()
195 */
get_fJe(vpMatrix & fJe)196 void vpRobotFlirPtu::get_fJe(vpMatrix &fJe) { fJe = get_fJe(); }
197
198 /*!
199 Get the robot geometric model corresponding to the homogeneous transformation between base (or reference) frame and
200 end-effector frame. \return Homogeneous transformation between base (or reference) frame and end-effector frame.
201 */
get_fMe()202 vpMatrix vpRobotFlirPtu::get_fMe()
203 {
204 vpHomogeneousMatrix fMe;
205 vpColVector q;
206 getPosition(vpRobot::JOINT_STATE, q);
207 double c1 = cos(q[0]);
208 double c2 = cos(q[1]);
209 double s1 = sin(q[0]);
210 double s2 = sin(q[1]);
211
212 fMe[0][0] = c1 * c2;
213 fMe[0][1] = s1;
214 fMe[0][2] = -c1 * s2;
215
216 fMe[1][0] = -s1 * c2;
217 fMe[1][1] = c1;
218 fMe[1][2] = s1 * s2;
219
220 fMe[2][0] = s2;
221 fMe[2][1] = 0;
222 fMe[2][2] = c2;
223
224 return fMe;
225 }
226
227 /*!
228
229 Return the velocity twist transformation matrix from camera frame to end-effector
230 frame. This transformation allows to transform a velocity twist expressed
231 in the end-effector frame into the camera frame thanks to the homogeneous matrix
232 eMc set using set_eMc().
233
234 \return Velocity twist transformation.
235
236 \sa set_eMc(), get_eMc()
237
238 */
get_cVe() const239 vpVelocityTwistMatrix vpRobotFlirPtu::get_cVe() const
240 {
241 vpVelocityTwistMatrix cVe;
242 cVe.buildFrom(m_eMc.inverse());
243
244 return cVe;
245 }
246
247 /*
248 At least one of these function has to be implemented to control the robot:
249 - setCartVelocity()
250 - setJointVelocity()
251 */
252
253 /*!
254 Send to the controller a 6-dim velocity skew vector expressed in a Cartesian frame.
255 \param[in] frame : Cartesian control frame (either tool frame or end-effector) in which the velocity \e v is
256 expressed. Units are m/s for translation and rad/s for rotation velocities. \param[in] v : 6-dim vector that contains
257 the 6 components of the velocity skew to send to the robot. Units are m/s and rad/s.
258 */
setCartVelocity(const vpRobot::vpControlFrameType frame,const vpColVector & v)259 void vpRobotFlirPtu::setCartVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &v)
260 {
261 if (v.size() != 6) {
262 throw(vpException(vpException::fatalError,
263 "Cannot send a velocity-skew vector in tool frame that is not 6-dim (%d)", v.size()));
264 }
265
266 vpColVector v_e; // This is the velocity that the robot is able to apply in the end-effector frame
267 switch (frame) {
268 case vpRobot::TOOL_FRAME: {
269 // We have to transform the requested velocity in the end-effector frame.
270 // Knowing that the constant transformation between the tool frame and the end-effector frame obtained
271 // by extrinsic calibration is set in m_eMc we can compute the velocity twist matrix eVc that transform
272 // a velocity skew from tool (or camera) frame into end-effector frame
273 vpVelocityTwistMatrix eVc(m_eMc);
274 v_e = eVc * v;
275 break;
276 }
277
278 case vpRobot::END_EFFECTOR_FRAME:
279 case vpRobot::REFERENCE_FRAME: {
280 v_e = v;
281 break;
282 }
283 case vpRobot::JOINT_STATE:
284 case vpRobot::MIXT_FRAME:
285 // Out of the scope
286 break;
287 }
288
289 // Implement your stuff here to send the end-effector velocity skew v_e
290 // - If the SDK allows to send cartesian velocities in the end-effector, it's done. Just wrap data in v_e
291 // - If the SDK allows to send cartesian velocities in the reference (or base) frame you have to implement
292 // the robot Jacobian in set_fJe() and call:
293 // vpColVector v = get_fJe().inverse() * v_e;
294 // At this point you have to wrap data in v that is the 6-dim velocity to apply to the robot
295 // - If the SDK allows to send only joint velocities you have to implement the robot Jacobian in set_eJe()
296 // and call:
297 // vpColVector qdot = get_eJe().inverse() * v_e;
298 // setJointVelocity(qdot);
299 // - If the SDK allows to send only a cartesian position trajectory of the end-effector position in the base frame
300 // called fMe (for fix frame to end-effector homogeneous transformation) you can transform the cartesian
301 // velocity in the end-effector into a displacement eMe using the exponetial map:
302 // double delta_t = 0.010; // in sec
303 // vpHomogenesousMatrix eMe = vpExponentialMap::direct(v_e, delta_t);
304 // vpHomogenesousMatrix fMe = getPosition(vpRobot::REFERENCE_FRAME);
305 // the new position to reach is than given by fMe * eMe
306 // vpColVector fpe(vpPoseVector(fMe * eMe));
307 // setPosition(vpRobot::REFERENCE_FRAME, fpe);
308
309 std::cout << "Not implemented ! " << std::endl;
310 std::cout << "To implement me you need : " << std::endl;
311 std::cout << "\t to known the robot jacobian expressed in ";
312 std::cout << "the end-effector frame (eJe) " << std::endl;
313 std::cout << "\t the frame transformation between tool or camera frame ";
314 std::cout << "and end-effector frame (cMe)" << std::endl;
315 }
316
317 /*!
318 Send a joint velocity to the controller.
319 \param[in] qdot : Joint velocities vector. Units are rad/s for a robot arm.
320 */
setJointVelocity(const vpColVector & qdot)321 void vpRobotFlirPtu::setJointVelocity(const vpColVector &qdot)
322 {
323 if (!m_connected) {
324 disconnect();
325 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
326 }
327
328 std::vector<int> vel_tics(2);
329
330 for (int i = 0; i < 2; i++) {
331 vel_tics[i] = rad2tics(i, qdot[i]);
332 if (std::fabs(vel_tics[i]) > m_vel_max_tics[i]) {
333 disconnect();
334 throw(vpException(vpException::fatalError, "Cannot set joint %d velocity %f (deg/s). Out of limits [-%f, %f].", i,
335 vpMath::deg(qdot[i]), -tics2deg(i, m_vel_max_tics[i]), tics2deg(i, m_vel_max_tics[i])));
336 }
337 }
338
339 if (cpi_ptcmd(m_cer, &m_status, OP_PAN_DESIRED_SPEED_SET, vel_tics[0]) ||
340 cpi_ptcmd(m_cer, &m_status, OP_TILT_DESIRED_SPEED_SET, vel_tics[1])) {
341 throw(vpException(vpException::fatalError, "Unable to set velocity."));
342 }
343 }
344
345 /*!
346 Send to the controller a velocity in a given frame.
347 \param[in] frame : Control frame in which the velocity \e vel is expressed.
348 Velocities could be joint velocities, or cartesian velocities. Units are m/s for translation and
349 rad/s for rotation velocities.
350 \param[in] vel : Vector that contains the velocity to apply to the robot.
351 */
setVelocity(const vpRobot::vpControlFrameType frame,const vpColVector & vel)352 void vpRobotFlirPtu::setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel)
353 {
354 if (vpRobot::STATE_VELOCITY_CONTROL != getRobotState()) {
355 throw vpRobotException(vpRobotException::wrongStateError,
356 "Cannot send a velocity to the robot. "
357 "Call setRobotState(vpRobot::STATE_VELOCITY_CONTROL) once before "
358 "entering your control loop.");
359 }
360
361 vpColVector vel_sat(6);
362
363 // Velocity saturation
364 switch (frame) {
365 // Saturation in cartesian space
366 case vpRobot::TOOL_FRAME:
367 case vpRobot::REFERENCE_FRAME:
368 case vpRobot::END_EFFECTOR_FRAME:
369 case vpRobot::MIXT_FRAME: {
370 if (vel.size() != 6) {
371 throw(vpException(vpException::dimensionError,
372 "Cannot apply a Cartesian velocity that is not a 6-dim vector (%d)", vel.size()));
373 }
374 vpColVector vel_max(6);
375
376 for (unsigned int i = 0; i < 3; i++)
377 vel_max[i] = getMaxTranslationVelocity();
378 for (unsigned int i = 3; i < 6; i++)
379 vel_max[i] = getMaxRotationVelocity();
380
381 vel_sat = vpRobot::saturateVelocities(vel, vel_max, true);
382
383 setCartVelocity(frame, vel_sat);
384 break;
385 }
386 // Saturation in joint space
387 case vpRobot::JOINT_STATE: {
388 if (vel.size() != static_cast<size_t>(nDof)) {
389 throw(vpException(vpException::dimensionError, "Cannot apply a joint velocity that is not a %d-dim vector (%d)",
390 nDof, vel.size()));
391 }
392 vpColVector vel_max(vel.size());
393
394 // Since the robot has only rotation axis all the joint max velocities are set to getMaxRotationVelocity()
395 vel_max = getMaxRotationVelocity();
396
397 vel_sat = vpRobot::saturateVelocities(vel, vel_max, true);
398
399 setJointVelocity(vel_sat);
400 }
401 }
402 }
403
404 /*
405 THESE FUNCTIONS ARE NOT MENDATORY BUT ARE USUALLY USEFUL
406 */
407
408 /*!
409 Get robot joint positions.
410 \param[in] q : Joint position as a 2-dim vector [pan, tilt] with values in radians.
411 */
getJointPosition(vpColVector & q)412 void vpRobotFlirPtu::getJointPosition(vpColVector &q)
413 {
414 if (!m_connected) {
415 disconnect();
416 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
417 }
418
419 std::vector<int> pos_tics(2);
420
421 if (cpi_ptcmd(m_cer, &m_status, OP_PAN_CURRENT_POS_GET, &pos_tics[0])) {
422 disconnect();
423 throw(vpException(vpException::fatalError, "Unable to query pan position."));
424 }
425 if (cpi_ptcmd(m_cer, &m_status, OP_TILT_CURRENT_POS_GET, &pos_tics[1])) {
426 disconnect();
427 throw(vpException(vpException::fatalError, "Unable to query pan position."));
428 }
429
430 q.resize(2);
431 for (int i = 0; i < 2; i++) {
432 q[i] = tics2rad(i, pos_tics[i]);
433 }
434 }
435
436 /*!
437 Get robot position.
438 \param[in] frame : Considered cartesian frame or joint state.
439 \param[out] q : Position of the arm.
440 */
getPosition(const vpRobot::vpControlFrameType frame,vpColVector & q)441 void vpRobotFlirPtu::getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q)
442 {
443 if (frame == JOINT_STATE) {
444 getJointPosition(q);
445 } else {
446 std::cout << "Not implemented ! " << std::endl;
447 }
448 }
449
450 /*!
451 Set a position to reach.
452 \param[in] frame : Considered cartesian frame or joint state.
453 \param[in] q : Position to reach.
454 */
setPosition(const vpRobot::vpControlFrameType frame,const vpColVector & q)455 void vpRobotFlirPtu::setPosition(const vpRobot::vpControlFrameType frame, const vpColVector &q)
456 {
457 if (frame != vpRobot::JOINT_STATE) {
458 std::cout << "FLIR PTU positioning is not implemented in this frame" << std::endl;
459 return;
460 }
461
462 if (q.size() != 2) {
463 disconnect();
464 throw(vpException(vpException::fatalError, "FLIR PTU has only %d joints. Cannot set a position that is %d-dim.",
465 m_njoints, q.size()));
466 }
467 if (!m_connected) {
468 disconnect();
469 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
470 }
471
472 double vmin = 0.01, vmax = 100.;
473 if (m_positioning_velocity < vmin || m_positioning_velocity > vmax) {
474 disconnect();
475 throw(
476 vpException(vpException::fatalError, "FLIR PTU Positioning velocity %f is not in range [%f, %f]", vmin, vmax));
477 }
478
479 std::vector<int> pos_tics(2);
480
481 for (int i = 0; i < 2; i++) {
482 pos_tics[i] = rad2tics(i, q[i]);
483 if (pos_tics[i] < m_pos_min_tics[i] || pos_tics[i] > m_pos_max_tics[i]) {
484 disconnect();
485 throw(vpException(vpException::fatalError, "Cannot set joint %d position %f (deg). Out of limits [%f, %f].", i,
486 vpMath::deg(q[i]), tics2deg(i, m_pos_min_tics[i]), tics2deg(i, m_pos_max_tics[i])));
487 }
488 }
489
490 // Set desired speed wrt max pan/tilt speed
491 if (cpi_ptcmd(m_cer, &m_status, OP_PAN_DESIRED_SPEED_SET, (int)(m_vel_max_tics[0] * m_positioning_velocity / 100.)) ||
492 cpi_ptcmd(m_cer, &m_status, OP_TILT_DESIRED_SPEED_SET,
493 (int)(m_vel_max_tics[1] * m_positioning_velocity / 100.))) {
494 disconnect();
495 throw(vpException(vpException::fatalError, "Setting FLIR pan/tilt positioning velocity failed"));
496 }
497
498 if (cpi_ptcmd(m_cer, &m_status, OP_PAN_DESIRED_POS_SET, pos_tics[0]) ||
499 cpi_ptcmd(m_cer, &m_status, OP_TILT_DESIRED_POS_SET, pos_tics[1])) {
500 disconnect();
501 throw(vpException(vpException::fatalError, "FLIR PTU failed to go to position %d, %d (deg).", vpMath::deg(q[0]),
502 vpMath::deg(q[1])));
503 }
504
505 if (cpi_block_until(m_cer, NULL, NULL, OP_PAN_CURRENT_POS_GET, pos_tics[0]) ||
506 cpi_block_until(m_cer, NULL, NULL, OP_TILT_CURRENT_POS_GET, pos_tics[1])) {
507 disconnect();
508 throw(vpException(vpException::fatalError, "FLIR PTU failed to wait until position %d, %d reached (deg)",
509 vpMath::deg(q[0]), vpMath::deg(q[1])));
510 }
511 }
512
513 /*!
514 Get a displacement.
515 \param[in] frame : Considered cartesian frame or joint state.
516 \param[out] q : Displacement in meter and rad.
517 */
getDisplacement(const vpRobot::vpControlFrameType frame,vpColVector & q)518 void vpRobotFlirPtu::getDisplacement(const vpRobot::vpControlFrameType frame, vpColVector &q)
519 {
520 (void)frame;
521 (void)q;
522 std::cout << "Not implemented ! " << std::endl;
523 }
524
525 /*!
526 Connect to FLIR PTU.
527 \param[in] portname : Connect to serial/socket.
528 \param[in] baudrate : Use baud rate (default: 9600).
529 \sa disconnect()
530 */
connect(const std::string & portname,int baudrate)531 void vpRobotFlirPtu::connect(const std::string &portname, int baudrate)
532 {
533 char errstr[128];
534
535 if (m_connected) {
536 disconnect();
537 }
538
539 if (portname.empty()) {
540 disconnect();
541 throw(vpException(vpException::fatalError, "Port name is required to connect to FLIR PTU."));
542 }
543
544 if ((m_cer = (struct cerial *)malloc(sizeof(struct cerial))) == NULL) {
545 disconnect();
546 throw(vpException(vpException::fatalError, "Out of memory during FLIR PTU connection."));
547 }
548
549 // Open a port
550 if (ceropen(m_cer, portname.c_str(), 0)) {
551 #if WIN32
552 throw(vpException(vpException::fatalError, "Failed to open %s: %s.", portname.c_str(),
553 cerstrerror(m_cer, errstr, sizeof(errstr))));
554 #else
555 throw(vpException(vpException::fatalError, "Failed to open %s: %s.\nRun `sudo chmod a+rw %s`", portname.c_str(),
556 cerstrerror(m_cer, errstr, sizeof(errstr)), portname.c_str()));
557 #endif
558 }
559
560 // Set baudrate
561 // ignore errors since not all devices are serial ports
562 cerioctl(m_cer, CERIAL_IOCTL_BAUDRATE_SET, &baudrate);
563
564 // Flush any characters already buffered
565 cerioctl(m_cer, CERIAL_IOCTL_FLUSH_INPUT, NULL);
566
567 // Set two second timeout */
568 int timeout = 2000;
569 if (cerioctl(m_cer, CERIAL_IOCTL_TIMEOUT_SET, &timeout)) {
570 disconnect();
571 throw(vpException(vpException::fatalError, "cerial: timeout ioctl not supported."));
572 }
573
574 // Sync and lock
575 int trial = 0;
576 do {
577 trial++;
578 } while (trial <= 3 && (cpi_resync(m_cer) || cpi_ptcmd(m_cer, &m_status, OP_NOOP)));
579 if (trial > 3) {
580 disconnect();
581 throw(vpException(vpException::fatalError, "Cannot communicate with FLIR PTU."));
582 }
583
584 // Immediately execute commands (slave mode should be opt-in)
585 int rc;
586 if ((rc = cpi_ptcmd(m_cer, &m_status, OP_EXEC_MODE_SET, (cpi_enum)CPI_IMMEDIATE_MODE))) {
587 disconnect();
588 throw(vpException(vpException::fatalError, "Set Immediate Mode failed: %s", cpi_strerror(rc)));
589 }
590
591 m_connected = true;
592
593 getLimits();
594 }
595
596 /*!
597 Close connection to PTU.
598 \sa connect()
599 */
disconnect()600 void vpRobotFlirPtu::disconnect()
601 {
602 if (m_cer != NULL) {
603 cerclose(m_cer);
604 free(m_cer);
605 m_cer = NULL;
606 m_connected = false;
607 }
608 }
609
610 /*!
611 Read min/max position and speed.
612 */
getLimits()613 void vpRobotFlirPtu::getLimits()
614 {
615 if (!m_connected) {
616 disconnect();
617 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
618 }
619
620 int status;
621
622 if ((status = cpi_ptcmd(m_cer, &m_status, OP_PAN_MAX_POSITION, &m_pos_max_tics[0])) ||
623 (status = cpi_ptcmd(m_cer, &m_status, OP_PAN_MIN_POSITION, &m_pos_min_tics[0])) ||
624 (status = cpi_ptcmd(m_cer, &m_status, OP_TILT_MAX_POSITION, &m_pos_max_tics[1])) ||
625 (status = cpi_ptcmd(m_cer, &m_status, OP_TILT_MIN_POSITION, &m_pos_min_tics[1])) ||
626 (status = cpi_ptcmd(m_cer, &m_status, OP_PAN_UPPER_SPEED_LIMIT_GET, &m_vel_max_tics[0])) ||
627 (status = cpi_ptcmd(m_cer, &m_status, OP_TILT_UPPER_SPEED_LIMIT_GET, &m_vel_max_tics[1]))) {
628 disconnect();
629 throw(vpException(vpException::fatalError, "Failed to get limits (%d) %s.", status, cpi_strerror(status)));
630 }
631
632 // Get the ptu resolution so we can convert the angles to ptu positions
633 if ((status = cpi_ptcmd(m_cer, &m_status, OP_PAN_RESOLUTION, &m_res[0])) ||
634 (status = cpi_ptcmd(m_cer, &m_status, OP_TILT_RESOLUTION, &m_res[1]))) {
635 disconnect();
636 throw(vpException(vpException::fatalError, "Failed to get resolution (%d) %s.", status, cpi_strerror(status)));
637 }
638
639 for (size_t i = 0; i < 2; i++) {
640 m_res[i] /= 3600.; // Resolutions are in arc-seconds, but we want degrees
641 }
642 }
643
644 /*!
645 Return pan axis min and max position limits in radians as a 2-dim vector, with first value the pan min position and
646 second value, the pan max position.
647
648 \sa getTiltPosLimits(), getPanTiltVelMax(), setPanPosLimits()
649 */
getPanPosLimits()650 vpColVector vpRobotFlirPtu::getPanPosLimits()
651 {
652 if (!m_connected) {
653 disconnect();
654 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
655 }
656 vpColVector pan_pos_limits(2);
657 pan_pos_limits[0] = tics2rad(0, m_pos_min_tics[0]);
658 pan_pos_limits[1] = tics2rad(0, m_pos_max_tics[0]);
659
660 return pan_pos_limits;
661 }
662
663 /*!
664 Return tilt axis min and max position limits in radians as a 2-dim vector, with first value the tilt min position and
665 second value, the tilt max position.
666
667 \sa getPanPosLimits(), getPanTiltVelMax(), setTiltPosLimits()
668 */
getTiltPosLimits()669 vpColVector vpRobotFlirPtu::getTiltPosLimits()
670 {
671 if (!m_connected) {
672 disconnect();
673 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
674 }
675 vpColVector tilt_pos_limits(2);
676 tilt_pos_limits[0] = tics2rad(0, m_pos_min_tics[1]);
677 tilt_pos_limits[1] = tics2rad(0, m_pos_max_tics[1]);
678
679 return tilt_pos_limits;
680 }
681
682 /*!
683 Return pan/tilt axis max velocity in rad/s as a 2-dim vector, with first value the pan max velocity and second value,
684 the max tilt velocity.
685
686 \sa getPanPosLimits(), getTiltPosLimits()
687 */
getPanTiltVelMax()688 vpColVector vpRobotFlirPtu::getPanTiltVelMax()
689 {
690 if (!m_connected) {
691 disconnect();
692 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
693 }
694 vpColVector vel_max(2);
695 for (int i = 0; i < 2; i++) {
696 vel_max[i] = tics2rad(i, m_vel_max_tics[i]);
697 }
698 return vel_max;
699 }
700
701 /*!
702 Modify pan position limit.
703 \param pan_limits : 2-dim vector that contains pan min and max limits in rad.
704
705 \sa getPanPosLimits()
706 */
setPanPosLimits(const vpColVector & pan_limits)707 void vpRobotFlirPtu::setPanPosLimits(const vpColVector &pan_limits)
708 {
709 if (!m_connected) {
710 disconnect();
711 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
712 }
713 if (pan_limits.size() != 2) {
714 disconnect();
715 throw(vpException(vpException::fatalError, "Cannot set max position that is not a 2-dim vector."));
716 }
717 std::vector<int> pan_limits_tics(2);
718 for (int i = 0; i < 2; i++) {
719 pan_limits_tics[i] = rad2tics(i, pan_limits[i]);
720 }
721
722 int status;
723 if ((status = cpi_ptcmd(m_cer, &m_status, OP_PAN_USER_MIN_POS_SET, pan_limits_tics[0])) ||
724 (status = cpi_ptcmd(m_cer, &m_status, OP_PAN_USER_MAX_POS_SET, pan_limits_tics[1]))) {
725 disconnect();
726 throw(vpException(vpException::fatalError, "Failed to set pan position limits (%d) %s.", status,
727 cpi_strerror(status)));
728 }
729 }
730
731 /*!
732 Modify tilt position limit.
733 \param tilt_limits : 2-dim vector that contains tilt min and max limits in rad.
734
735 \sa getPanPosLimits()
736 */
setTiltPosLimits(const vpColVector & tilt_limits)737 void vpRobotFlirPtu::setTiltPosLimits(const vpColVector &tilt_limits)
738 {
739 if (!m_connected) {
740 disconnect();
741 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
742 }
743 if (tilt_limits.size() != 2) {
744 disconnect();
745 throw(vpException(vpException::fatalError, "Cannot set max position that is not a 2-dim vector."));
746 }
747 std::vector<int> tilt_limits_tics(2);
748 for (int i = 0; i < 2; i++) {
749 tilt_limits_tics[i] = rad2tics(i, tilt_limits[i]);
750 }
751
752 int status;
753 if ((status = cpi_ptcmd(m_cer, &m_status, OP_TILT_USER_MIN_POS_SET, tilt_limits_tics[0])) ||
754 (status = cpi_ptcmd(m_cer, &m_status, OP_TILT_USER_MAX_POS_SET, tilt_limits_tics[1]))) {
755 disconnect();
756 throw(vpException(vpException::fatalError, "Failed to set tilt position limits (%d) %s.", status,
757 cpi_strerror(status)));
758 }
759 }
760
761 /*!
762
763 Set the velocity used for a position control.
764
765 \param velocity : Velocity in % of the maximum velocity between [0, 100]. Default value is 20.
766 */
setPositioningVelocity(double velocity)767 void vpRobotFlirPtu::setPositioningVelocity(double velocity) { m_positioning_velocity = velocity; }
768
769 /*!
770
771 Change the robot state.
772
773 \param newState : New requested robot state if the robot is connected. If the robot is not connected, we return the
774 current state.
775 */
setRobotState(vpRobot::vpRobotStateType newState)776 vpRobot::vpRobotStateType vpRobotFlirPtu::setRobotState(vpRobot::vpRobotStateType newState)
777 {
778 if (!m_connected) {
779 return getRobotState();
780 }
781
782 switch (newState) {
783 case vpRobot::STATE_STOP: {
784 // Start primitive STOP only if the current state is Velocity
785 if (vpRobot::STATE_VELOCITY_CONTROL == getRobotState()) {
786 stopMotion();
787
788 // Set the PTU to pure velocity mode
789 if (cpi_ptcmd(m_cer, &m_status, OP_SPEED_CONTROL_MODE_SET, (cpi_enum)CPI_CONTROL_INDEPENDENT)) {
790 throw(vpException(vpException::fatalError, "Unable to set control mode independent."));
791 }
792 }
793 break;
794 }
795 case vpRobot::STATE_POSITION_CONTROL: {
796 if (vpRobot::STATE_VELOCITY_CONTROL == getRobotState()) {
797 std::cout << "Change the control mode from velocity to position control.\n";
798 stopMotion();
799
800 // Set the PTU to pure velocity mode
801 if (cpi_ptcmd(m_cer, &m_status, OP_SPEED_CONTROL_MODE_SET, (cpi_enum)CPI_CONTROL_INDEPENDENT)) {
802 throw(vpException(vpException::fatalError, "Unable to set control mode independent."));
803 }
804
805 } else {
806 // std::cout << "Change the control mode from stop to position
807 // control.\n";
808 }
809 break;
810 }
811 case vpRobot::STATE_VELOCITY_CONTROL: {
812 if (vpRobot::STATE_VELOCITY_CONTROL != getRobotState()) {
813 std::cout << "Change the control mode from stop to velocity control.\n";
814
815 // Set the PTU to pure velocity mode
816 if (cpi_ptcmd(m_cer, &m_status, OP_SPEED_CONTROL_MODE_SET, (cpi_enum)CPI_CONTROL_PURE_VELOCITY)) {
817 throw(vpException(vpException::fatalError, "Unable to set velocity control mode."));
818 }
819 }
820 break;
821 }
822 default:
823 break;
824 }
825
826 return vpRobot::setRobotState(newState);
827 }
828
829 /*!
830 Reset PTU axis.
831 */
reset()832 void vpRobotFlirPtu::reset()
833 {
834 if (!m_connected) {
835 return;
836 }
837
838 if (cpi_ptcmd(m_cer, &m_status, OP_RESET, NULL)) {
839 throw(vpException(vpException::fatalError, "Unable to reset PTU."));
840 }
841 }
842
843 /*!
844 Stop PTU motion in velocity control mode.
845 */
stopMotion()846 void vpRobotFlirPtu::stopMotion()
847 {
848 if (vpRobot::STATE_VELOCITY_CONTROL != getRobotState()) {
849 return;
850 }
851
852 if (!m_connected) {
853 return;
854 }
855
856 if (cpi_ptcmd(m_cer, &m_status, OP_HALT, NULL)) {
857 throw(vpException(vpException::fatalError, "Unable to stop PTU."));
858 }
859 }
860
861 /*!
862 When connected to the PTU by serial, get the PTU network IP address.
863
864 \sa getNetworkGateway()
865 */
getNetworkIP()866 std::string vpRobotFlirPtu::getNetworkIP()
867 {
868 if (!m_connected) {
869 disconnect();
870 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
871 }
872
873 char str[64];
874 if (cpi_ptcmd(m_cer, &m_status, OP_NET_IP_GET, (int)sizeof(str), NULL, &str)) {
875 throw(vpException(vpException::fatalError, "Unable to get Network IP."));
876 }
877
878 return (std::string(str));
879 }
880
881 /*!
882 When connected to the PTU by serial, get the PTU network gateway.
883
884 \sa getNetworkIP()
885 */
getNetworkGateway()886 std::string vpRobotFlirPtu::getNetworkGateway()
887 {
888 if (!m_connected) {
889 disconnect();
890 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
891 }
892
893 char str[64];
894 if (cpi_ptcmd(m_cer, &m_status, OP_NET_GATEWAY_GET, (int)sizeof(str), NULL, &str)) {
895 throw(vpException(vpException::fatalError, "Unable to get Network Gateway."));
896 }
897
898 return (std::string(str));
899 }
900
901 /*!
902 When connected to the PTU, get the PTU network hostname.
903
904 \sa getNetworkIP()
905 */
getNetworkHostName()906 std::string vpRobotFlirPtu::getNetworkHostName()
907 {
908 if (!m_connected) {
909 disconnect();
910 throw(vpException(vpException::fatalError, "FLIR PTU is not connected."));
911 }
912
913 char str[64];
914 if (cpi_ptcmd(m_cer, &m_status, OP_NET_HOSTNAME_GET, (int)sizeof(str), NULL, &str)) {
915 throw(vpException(vpException::fatalError, "Unable to get Network hostname."));
916 }
917
918 return (std::string(str));
919 }
920
921 /*!
922 Converts the angle from radian to robot position units using axis i resolution.
923 \param rad : Angle in radian.
924 \param i : Axis to consider, with 0 for pan and 1 for tilt axis.
925 \returns The position in robot units using axis resolution.
926
927 \sa pos2rad()
928 */
rad2tics(int axis,double rad)929 int vpRobotFlirPtu::rad2tics(int axis, double rad) { return (static_cast<int>(vpMath::deg(rad) / m_res[axis])); }
930
931 /*!
932 Converts the angle from robot position units into degrees using axis i resolution.
933 \param tics : Position in robot units.
934 \param i : Axis, with 0 for pan and 1 for tilt axis.
935 \returns The angle in degrees using axis resolution.
936
937 \sa pos2rad()
938 */
tics2deg(int axis,int tics)939 double vpRobotFlirPtu::tics2deg(int axis, int tics) { return (tics * m_res[axis]); }
940
941 /*!
942 Converts the angle from robot position units into radian using axis i resolution.
943 \param tics : Position in robot units.
944 \param i : Axis, with 0 for pan and 1 for tilt axis.
945 \returns The angle in radian using axis resolution.
946
947 \sa pos2rad()
948 */
tics2rad(int axis,int tics)949 double vpRobotFlirPtu::tics2rad(int axis, int tics) { return vpMath::rad(tics2deg(axis, tics)); }
950
951 #elif !defined(VISP_BUILD_SHARED_LIBS)
952 // Work arround to avoid warning: libvisp_robot.a(vpRobotFlirPtu.cpp.o) has
953 // no symbols
dummy_vpRobotFlirPtu()954 void dummy_vpRobotFlirPtu(){};
955 #endif
956