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 Parrot Bebop 2 drone.
33 *
34 * Authors:
35 * Gatien Gaumerais
36 * Fabien Spindler
37 *
38 *****************************************************************************/
39
40 #include <visp3/core/vpConfig.h>
41
42 #ifdef VISP_HAVE_ARSDK
43
44 #include <visp3/robot/vpRobotBebop2.h>
45
46 #include <visp3/core/vpExponentialMap.h> // For velocity computation
47
48 #ifdef VISP_HAVE_FFMPEG
49 extern "C" {
50 #include <libavcodec/avcodec.h>
51 #include <libavformat/avformat.h>
52 #include <libavutil/imgutils.h>
53 }
54 #include <visp3/core/vpImageConvert.h>
55 #endif
56
57 #include <iostream>
58 #include <math.h>
59
60 #define TAG "vpRobotBebop2" // For error messages of ARSDK
61
62 /*!
63 Bebop coordinates system :
64
65 Camera reference :
66 + x : right
67 + y : down
68 + z : forward
69
70 Effective reference :
71 + x : forward
72 + y : right
73 + z : down
74 */
75
76 bool vpRobotBebop2::m_running = false;
77 ARCONTROLLER_Device_t *vpRobotBebop2::m_deviceController = NULL;
78
79 /*!
80
81 Constructor (only one available).
82
83 Initialises drone control, by discovering drones with IP \e ipAddress and port \e discoveryPort on the wifi network
84 the computer is currently connected to.
85 Sets up signal handling to safely land the drone when something bad happens.
86
87 \warning This constructor should be called after the drone is turned on, and after the computer is connected to the
88 drone wifi network.
89
90 \warning If the connection to the drone failed, the programm will throw an exception.
91
92 After having called this constructor, it is recommanded to check if the drone is running with isRunning() before
93 sending commands to the drone.
94
95 \param[in] verbose : turn verbose on or off
96 If verbose is true : info, warning, error and fatal error messages are displayed.
97 If verbose is false : only warning, error and fatal error messages are displayed.
98
99 \param[in] setDefaultSettings : set default settings or not
100 If setDefaultSettings is true : the drone is reset to factory settings and the following parameters are set :
101 - Resolution of streamed video to 480p (856x480).
102 - Max roll and pitch to 10 degrees.
103 - Video stabilisation to 0 (no stabilisation).
104 - Video exposure compensation to 0.
105 - Video streaming mode to 0 (lowest latency, average reliability).
106 - Camera orientation to 0 degrees for tilt and 0 degrees for pan
107 If setDefaultSettings is false : the current settings are unchanged.
108
109 \param[in] ipAddress : ip address used to discover the drone on the wifi network.
110 \param[in] discoveryPort : port used to discover the drone on the wifi network.
111
112 \exception vpException::fatalError : If the program failed to connect to the drone.
113 */
vpRobotBebop2(bool verbose,bool setDefaultSettings,std::string ipAddress,int discoveryPort)114 vpRobotBebop2::vpRobotBebop2(bool verbose, bool setDefaultSettings, std::string ipAddress, int discoveryPort)
115 : m_ipAddress(ipAddress), m_discoveryPort(discoveryPort)
116 {
117 // Setting up signal handling
118 memset(&m_sigAct, 0, sizeof(m_sigAct));
119 m_sigAct.sa_handler = vpRobotBebop2::sighandler;
120 sigaction(SIGINT, &m_sigAct, 0);
121 sigaction(SIGBUS, &m_sigAct, 0);
122 sigaction(SIGSEGV, &m_sigAct, 0);
123 sigaction(SIGKILL, &m_sigAct, 0);
124 sigaction(SIGQUIT, &m_sigAct, 0);
125
126 #ifdef VISP_HAVE_FFMPEG
127 m_codecContext = NULL;
128 m_packet = AVPacket();
129 m_picture = NULL;
130 m_bgr_picture = NULL;
131 m_img_convert_ctx = NULL;
132 m_buffer = NULL;
133 m_videoDecodingStarted = false;
134 #endif
135
136 m_batteryLevel = 100;
137
138 m_exposureSet = true;
139 m_flatTrimFinished = true;
140 m_relativeMoveEnded = true;
141 m_videoResolutionSet = true;
142 m_streamingStarted = false;
143 m_streamingModeSet = false;
144 m_settingsReset = false;
145
146 m_update_codec_params = false;
147 m_codec_params_data = std::vector<uint8_t>();
148
149 m_maxTilt = -1;
150
151 m_cameraHorizontalFOV = -1;
152 m_currentCameraTilt = -1;
153 m_minCameraTilt = -1;
154 m_maxCameraTilt = -1;
155 m_currentCameraPan = -1;
156 m_minCameraPan = -1;
157 m_maxCameraPan = -1;
158
159 setVerbose(verbose);
160
161 m_errorController = ARCONTROLLER_OK;
162 m_deviceState = ARCONTROLLER_DEVICE_STATE_MAX;
163
164 // Initialises a semaphore
165 ARSAL_Sem_Init(&(m_stateSem), 0, 0);
166
167 // Creates a discovery device to find the drone on the wifi network
168 ARDISCOVERY_Device_t *discoverDevice = discoverDrone();
169
170 // Creates a drone controller with the discovery device
171 createDroneController(discoverDevice);
172
173 // Sets up callbacks
174 setupCallbacks();
175
176 // Start the drone controller, connects to the drone. If an error occurs, it will set m_errorController to the error.
177 startController();
178
179 // We check if the drone was actually found and connected to the controller
180 if ((m_errorController != ARCONTROLLER_OK) || (m_deviceState != ARCONTROLLER_DEVICE_STATE_RUNNING)) {
181 cleanUp();
182 m_running = false;
183
184 throw(vpException(vpException::fatalError,
185 "Failed to connect to bebop2 with ip %s and port %d. Make sure that the ip address is correct "
186 "and that your computer is connected to the drone Wifi spot before starting",
187 ipAddress.c_str(), discoveryPort));
188 } else {
189 m_running = true;
190
191 #ifdef VISP_HAVE_FFMPEG
192 setVideoResolution(0);
193 #endif
194 if (setDefaultSettings) {
195 resetAllSettings();
196
197 setMaxTilt(10);
198
199 #ifdef VISP_HAVE_FFMPEG
200 setVideoStabilisationMode(0);
201 setExposure(0);
202 setStreamingMode(0);
203 #endif
204 setCameraOrientation(0, 0, true);
205 }
206 }
207 }
208
209 /*!
210
211 Destructor. Lands the drone if not landed, safely disconnects everything, and deactivates video streaming and
212 decoding if needed.
213 */
~vpRobotBebop2()214 vpRobotBebop2::~vpRobotBebop2() { cleanUp(); }
215
216 /*!
217
218 Sends a flat trim command to the robot, to calibrate accelerometer and gyro.
219
220 \warning Should be executed only if the drone is landed and on a flat surface.
221 */
doFlatTrim()222 void vpRobotBebop2::doFlatTrim()
223 {
224 if (isRunning() && m_deviceController != NULL && isLanded()) {
225
226 m_flatTrimFinished = false;
227
228 m_deviceController->aRDrone3->sendPilotingFlatTrim(m_deviceController->aRDrone3);
229
230 // m_flatTrimFinished is set back to true when the drone has finished the calibration, via a callback
231 while (!m_flatTrimFinished) {
232 vpTime::sleepMs(1);
233 }
234 } else {
235 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't do a flat trim : drone isn't landed.");
236 }
237 }
238
239 /*!
240
241 Gets the drone IP address used during wifi discovery.
242 */
getIpAddress()243 std::string vpRobotBebop2::getIpAddress() { return m_ipAddress; }
244
245 /*!
246
247 Gets the drone port used during wifi discovery.
248 */
getDiscoveryPort()249 int vpRobotBebop2::getDiscoveryPort() { return m_discoveryPort; }
250
251 /*!
252
253 Gets the current max pitch and roll values allowed for the drone.
254 */
getMaxTilt()255 double vpRobotBebop2::getMaxTilt() { return m_maxTilt; }
256
257 /*!
258
259 Gets current battery level.
260
261 \warning When the drone battery gets to 0, it will automatically land and won't process any command.
262 */
getBatteryLevel()263 unsigned int vpRobotBebop2::getBatteryLevel() { return m_batteryLevel; }
264
265 /*!
266
267 Gets camera horizontal FOV (degrees).
268 */
getCameraHorizontalFOV() const269 double vpRobotBebop2::getCameraHorizontalFOV() const { return m_cameraHorizontalFOV; }
270
271 /*!
272
273 Gets current camera tilt (degrees).
274 */
getCurrentCameraTilt() const275 double vpRobotBebop2::getCurrentCameraTilt() const { return m_currentCameraTilt; }
276
277 /*!
278
279 Gets minimum camera tilt (degrees).
280 */
getMinCameraTilt() const281 double vpRobotBebop2::getMinCameraTilt() const { return m_minCameraTilt; }
282
283 /*!
284
285 Gets maximum camera tilt (degrees).
286 */
getMaxCameraTilt() const287 double vpRobotBebop2::getMaxCameraTilt() const { return m_maxCameraTilt; }
288
289 /*!
290
291 Gets current camera pan (degrees).
292 */
getCurrentCameraPan() const293 double vpRobotBebop2::getCurrentCameraPan() const { return m_currentCameraPan; }
294
295 /*!
296
297 Gets minimum camera pan (degrees).
298 */
getMinCameraPan() const299 double vpRobotBebop2::getMinCameraPan() const { return m_minCameraPan; }
300
301 /*!
302
303 Gets maximum camera pan (degrees).
304 */
getMaxCameraPan() const305 double vpRobotBebop2::getMaxCameraPan() const { return m_maxCameraPan; }
306
307 /*!
308
309 Sets camera orientation : tilt and pan (degrees).
310
311 \warning The camera movement is gradual. It takes some time for the camera to reach the desired orientation.
312
313 \param[in] tilt : The desired tilt.
314 \param[in] pan : The desired pan.
315 \param[in] blocking : If true, the function returns when the camera reached the desired position.
316 If false, returns immediately. You can check if the camera reached the desired position
317 using getCurrentCameraTilt() and getCurrentCameraPan().
318 */
setCameraOrientation(double tilt,double pan,bool blocking)319 void vpRobotBebop2::setCameraOrientation(double tilt, double pan, bool blocking)
320 {
321 if (isRunning() && m_deviceController != NULL) {
322
323 m_deviceController->aRDrone3->sendCameraOrientationV2(m_deviceController->aRDrone3, static_cast<float>(tilt),
324 static_cast<float>(pan));
325
326 if (blocking) {
327 while (std::abs(tilt - m_currentCameraTilt) > 0.01 || std::abs(pan - m_currentCameraPan) > 0.01) {
328 vpTime::sleepMs(1);
329 }
330 }
331
332 } else {
333 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set camera orientation : drone isn't running.");
334 }
335 }
336
337 /*!
338
339 Sets camera tilt (degrees).
340
341 \warning The camera movement is gradual. It takes some time for the camera to reach the desired tilt.
342
343 \param[in] tilt : The desired tilt.
344 \param[in] blocking : If true, the function returns when the camera reached the desired position.
345 If false, returns immediately. You can check if the camera reached the desired position
346 using getCurrentCameraTilt().
347 */
setCameraTilt(double tilt,bool blocking)348 void vpRobotBebop2::setCameraTilt(double tilt, bool blocking)
349 {
350 if (isRunning() && m_deviceController != NULL) {
351
352 m_deviceController->aRDrone3->sendCameraOrientationV2(m_deviceController->aRDrone3, static_cast<float>(tilt),
353 static_cast<float>(getCurrentCameraPan()));
354
355 if (blocking) {
356 while (std::abs(tilt - m_currentCameraTilt) > 0.01) {
357 vpTime::sleepMs(1);
358 }
359 }
360
361 } else {
362 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set camera tilt value : drone isn't running.");
363 }
364 }
365
366 /*!
367
368 Sets camera pan (degrees).
369
370 \warning The camera movement is gradual. It takes some time for the camera to reach the desired pan.
371
372 \param[in] pan : The desired pan.
373 \param[in] blocking : If true, the function returns when the camera reached the desired position.
374 If false, returns immediately. You can check if the camera reached the desired position
375 using getCurrentCameraPan().
376 */
setCameraPan(double pan,bool blocking)377 void vpRobotBebop2::setCameraPan(double pan, bool blocking)
378 {
379 if (isRunning() && m_deviceController != NULL) {
380
381 m_deviceController->aRDrone3->sendCameraOrientationV2(
382 m_deviceController->aRDrone3, static_cast<float>(getCurrentCameraTilt()), static_cast<float>(pan));
383
384 if (blocking) {
385 while (std::abs(pan - m_currentCameraPan) > 0.01) {
386 vpTime::sleepMs(1);
387 }
388 }
389
390 } else {
391 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set camera pan value : drone isn't running.");
392 }
393 }
394
395 /*!
396
397 Checks if the drone is running, ie if the drone is connected and ready to receive commands.
398 */
isRunning()399 bool vpRobotBebop2::isRunning()
400 {
401 if (m_deviceController == NULL) {
402 return false;
403 } else {
404 return m_running;
405 }
406 }
407
408 /*!
409
410 Checks if the drone is currently streaming and decoding the video from its camera.
411 */
isStreaming()412 bool vpRobotBebop2::isStreaming()
413 {
414 #ifdef VISP_HAVE_FFMPEG
415 return m_videoDecodingStarted;
416 #else
417 return false;
418 #endif
419 }
420
421 /*!
422
423 Checks if the drone is currently hovering, ie if the drone is up in the air but not moving.
424 */
isHovering()425 bool vpRobotBebop2::isHovering()
426 {
427 return getFlyingState() == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING;
428 }
429
430 /*!
431
432 Checks if the drone is currently flying, ie if the drone is up in the air and moving.
433 */
isFlying()434 bool vpRobotBebop2::isFlying()
435 {
436 return getFlyingState() == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_FLYING;
437 }
438
439 /*!
440
441 Checks if the drone is currently landed.
442 */
isLanded()443 bool vpRobotBebop2::isLanded()
444 {
445 return getFlyingState() == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED;
446 }
447
448 /*!
449
450 Sends take off command.
451
452 \param[in] blocking : If true, the function returns when take off is achieved. If false, returns immediately. You can
453 check if take off is finished using isHovering().
454 */
takeOff(bool blocking)455 void vpRobotBebop2::takeOff(bool blocking)
456 {
457 if (isRunning() && isLanded() && m_deviceController != NULL) {
458
459 m_deviceController->aRDrone3->sendPilotingTakeOff(m_deviceController->aRDrone3);
460
461 if (blocking) {
462 while (!isHovering()) {
463 vpTime::sleepMs(1);
464 }
465 }
466
467 } else {
468 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't take off : drone isn't landed.");
469 }
470 }
471
472 /*!
473
474 Sends landing command.
475
476 \warning This function is static because it needs to be called by the signal handler in case of a detected signal.
477 */
land()478 void vpRobotBebop2::land()
479 {
480 if (m_deviceController != NULL) {
481 m_deviceController->aRDrone3->sendPilotingLanding(m_deviceController->aRDrone3);
482 }
483 }
484
485 /*!
486
487 Sets the vertical speed, expressed as signed percentage of the maximum vertical speed.
488
489 \warning The drone will not stop moving in that direction until you send another motion command.
490
491 \param[in] value : desired vertical speed in signed percentage, between 100 and -100.
492 Positive values will make the drone go up
493 Negative values will make the drone go down
494 */
setVerticalSpeed(int value)495 void vpRobotBebop2::setVerticalSpeed(int value)
496 {
497 if (isRunning() && m_deviceController != NULL && (isFlying() || isHovering())) {
498 m_errorController =
499 m_deviceController->aRDrone3->setPilotingPCMDGaz(m_deviceController->aRDrone3, static_cast<char>(value));
500
501 if (m_errorController != ARCONTROLLER_OK) {
502 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error when sending move command : %s",
503 ARCONTROLLER_Error_ToString(m_errorController));
504 }
505
506 } else {
507 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set vertical speed : drone isn't flying or hovering.");
508 }
509 }
510
511 /*!
512
513 Sets the yaw speed, expressed as signed percentage of the maximum yaw speed.
514
515 \warning The drone will not stop moving in that direction until you send another motion command.
516
517 \param[in] value : desired yaw speed in signed percentage, between 100 and -100.
518 Positive values will make the drone turn to its right / clockwise
519 Negative values will make the drone turn to its left / counterclockwise
520 */
setYawSpeed(int value)521 void vpRobotBebop2::setYawSpeed(int value)
522 {
523 if (isRunning() && m_deviceController != NULL && (isFlying() || isHovering())) {
524
525 m_errorController =
526 m_deviceController->aRDrone3->setPilotingPCMDYaw(m_deviceController->aRDrone3, static_cast<char>(value));
527
528 if (m_errorController != ARCONTROLLER_OK) {
529 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error when sending move command : %s",
530 ARCONTROLLER_Error_ToString(m_errorController));
531 }
532
533 } else {
534 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set yaw speed : drone isn't flying or hovering.");
535 }
536 }
537
538 /*!
539
540 Sets the pitch angle, expressed as signed percentage of the maximum pitch angle.
541
542 \warning The drone will not stop moving in that direction until you send another motion command.
543
544 \param[in] value : desired pitch in signed percentage, between 100 and -100.
545 Positive values will make the drone tilt and go forward
546 Negative values will make the drone tilt and go backward
547 */
setPitch(int value)548 void vpRobotBebop2::setPitch(int value)
549 {
550 if (isRunning() && m_deviceController != NULL && (isFlying() || isHovering())) {
551
552 m_errorController =
553 m_deviceController->aRDrone3->setPilotingPCMDPitch(m_deviceController->aRDrone3, static_cast<char>(value));
554 m_errorController = m_deviceController->aRDrone3->setPilotingPCMDFlag(m_deviceController->aRDrone3, 1);
555
556 if (m_errorController != ARCONTROLLER_OK) {
557 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error when sending move command : %s",
558 ARCONTROLLER_Error_ToString(m_errorController));
559 }
560
561 } else {
562 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set pitch value : drone isn't flying or hovering.");
563 }
564 }
565
566 /*!
567
568 Sets the roll angle, expressed as signed percentage of the maximum roll angle.
569
570 \warning The drone will not stop moving in that direction until you send another motion command.
571
572 \param[in] value : desired roll in signed percentage, between 100 and -100.
573 Positive values will make the drone tilt and go to its right
574 Negative values will make the drone tilt and go to its left
575 */
setRoll(int value)576 void vpRobotBebop2::setRoll(int value)
577 {
578 if (isRunning() && m_deviceController != NULL && (isFlying() || isHovering())) {
579
580 m_errorController =
581 m_deviceController->aRDrone3->setPilotingPCMDRoll(m_deviceController->aRDrone3, static_cast<char>(value));
582 m_errorController = m_deviceController->aRDrone3->setPilotingPCMDFlag(m_deviceController->aRDrone3, 1);
583
584 if (m_errorController != ARCONTROLLER_OK) {
585 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error when sending move command : %s",
586 ARCONTROLLER_Error_ToString(m_errorController));
587 }
588
589 } else {
590 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set roll value : drone isn't flying or hovering.");
591 }
592 }
593
594 /*!
595
596 Cuts the motors. Should only be used in emergency cases.
597
598 \warning The drone will fall.
599 */
cutMotors()600 void vpRobotBebop2::cutMotors()
601 {
602 if (m_deviceController != NULL) {
603 m_errorController = m_deviceController->aRDrone3->sendPilotingEmergency(m_deviceController->aRDrone3);
604 }
605 }
606
607 /*!
608
609 Moves the drone by the given amounts \e dX, \e dY, \e dZ (meters) and rotate the heading by \e dPsi (radian).
610 Doesn't do anything if the drone isn't flying or hovering.
611
612 \param[in] dX : displacement along X axis (meters).
613 \param[in] dY : displacement along Y axis (meters).
614 \param[in] dZ : displacement along Z axis (meters).
615 \param[in] dPsi : rotation of the heading (radians).
616 \param[in] blocking : specifies whether the function should be blocking or not.
617 If blocking is true, the function will wait until the drone has finished moving.
618 If blocking is false, the function will return even if the drone is still moving.
619
620 \sa setPosition(const vpHomogeneousMatrix &M, bool blocking)
621 */
setPosition(float dX,float dY,float dZ,float dPsi,bool blocking)622 void vpRobotBebop2::setPosition(float dX, float dY, float dZ, float dPsi, bool blocking)
623 {
624 if (isRunning() && m_deviceController != NULL && (isFlying() || isHovering())) {
625
626 m_relativeMoveEnded = false;
627 m_deviceController->aRDrone3->sendPilotingMoveBy(m_deviceController->aRDrone3, dX, dY, dZ, dPsi);
628
629 if (blocking) {
630
631 // m_relativeMoveEnded is set back to true when the drone has finished his move, via a callback
632 while (!m_relativeMoveEnded) {
633 vpTime::sleepMs(1);
634 }
635 }
636 } else {
637 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't move : drone isn't flying or hovering.");
638 }
639 }
640
641 /*!
642
643 Changes the position of the drone based on a homogeneous transformation matrix.
644
645 \param[in] M : homogeneous matrix. Translation should be expressed in meters and rotation in radians.
646 \param[in] blocking : specifies whether the function should be blocking or not.
647 If blocking is true, the function will wait until the drone has finished moving.
648 If blocking is false, the function will return even if the drone is still moving.
649
650 \warning The rotation around the X and Y axes should be equal to 0, as the drone cannot rotate around these axes.
651
652 \sa setPosition(float dX, float dY, float dZ, float dPsi, bool blocking)
653 */
setPosition(const vpHomogeneousMatrix & M,bool blocking)654 void vpRobotBebop2::setPosition(const vpHomogeneousMatrix &M, bool blocking)
655 {
656 double epsilon = (std::numeric_limits<double>::epsilon());
657 if (std::abs(M.getRotationMatrix().getThetaUVector()[0]) >= epsilon) {
658 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't move : rotation around X axis should be 0.");
659 return;
660 }
661 if (std::abs(M.getRotationMatrix().getThetaUVector()[1]) >= epsilon) {
662 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't move : rotation around Y axis should be 0.");
663 return;
664 }
665 float dThetaZ = static_cast<float>(M.getRotationMatrix().getThetaUVector()[2]);
666 vpTranslationVector t = M.getTranslationVector();
667 setPosition(static_cast<float>(t[0]), static_cast<float>(t[1]), static_cast<float>(t[2]), dThetaZ, blocking);
668 }
669
670 /*!
671
672 Sets the drone velocity.
673
674 \param[in] vel_cmd : Drone velocity commands, vx, vy, vz, wz. Translation velocities (vx, vy, vz) should be expressed
675 in meters and rotation velocity (wz) in radians.
676 \param[in] delta_t : Sampling time (in seconds), time during which the
677 velocity \e vel_cmd is applied.
678
679 \warning The dimension of the velocity vector should be equal to 4, as the drone cannot rotate around X and Y axes.
680 */
setVelocity(const vpColVector & vel_cmd,double delta_t)681 void vpRobotBebop2::setVelocity(const vpColVector &vel_cmd, double delta_t)
682 {
683
684 if (vel_cmd.size() != 4) {
685 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR",
686 "Can't set velocity : dimension of the velocity vector should be equal to 4.");
687 stopMoving();
688 return;
689 }
690
691 vpColVector ve(6);
692
693 ve[0] = vel_cmd[0];
694 ve[1] = vel_cmd[1];
695 ve[2] = vel_cmd[2];
696 ve[5] = vel_cmd[3];
697
698 vpHomogeneousMatrix M = vpExponentialMap::direct(ve, delta_t);
699 setPosition(M, false);
700 }
701
702 /*!
703
704 Sets the verbose level for console prints.
705
706 \param[in] verbose : specifies the desired verbose level.
707 If verbose is true : info, warning, error and fatal error messages are displayed.
708 If verbose is false : only warning, error and fatal error messages are displayed.
709 */
setVerbose(bool verbose)710 void vpRobotBebop2::setVerbose(bool verbose)
711 {
712 if (verbose) {
713 ARSAL_Print_SetMinimumLevel(ARSAL_PRINT_INFO);
714 } else {
715 ARSAL_Print_SetMinimumLevel(ARSAL_PRINT_WARNING);
716 }
717 }
718
719 /*!
720
721 Resets drone settings (like max tilt or streaming mode) to factory defaults.
722 */
resetAllSettings()723 void vpRobotBebop2::resetAllSettings()
724 {
725 if (isRunning() && m_deviceController != NULL) {
726
727 m_settingsReset = false;
728 m_deviceController->common->sendSettingsReset(m_deviceController->common);
729
730 while (!m_settingsReset) {
731 vpTime::sleepMs(1);
732 }
733
734 } else {
735 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't reset drone settings : drone isn't running.");
736 }
737 }
738
739 /*!
740
741 Sets the max pitch and roll values for the drone.
742 The smallest possible angle is 5, the maximum is 35.
743
744 \param[in] maxTilt : new maximum pitch and roll value for the drone (degrees).
745
746 \warning This value is only taken into account by the drone when issuing percentage-of-max-tilt-value-based commands.
747
748 \warning This value is not taken into account by the drone when using setPosition or setVelocity functions.
749 */
setMaxTilt(double maxTilt)750 void vpRobotBebop2::setMaxTilt(double maxTilt)
751 {
752 if (isRunning() && m_deviceController != NULL) {
753 m_deviceController->aRDrone3->sendPilotingSettingsMaxTilt(m_deviceController->aRDrone3,
754 static_cast<float>(maxTilt));
755 } else {
756 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set tilt value : drone isn't running.");
757 }
758 }
759
760 /*!
761
762 Stops any drone movement.
763
764 \warning Depending on the speed of the drone when the function is called, the drone may still move a bit until it
765 stabilizes.
766 */
stopMoving()767 void vpRobotBebop2::stopMoving()
768 {
769 if (isRunning() && !isLanded() && m_deviceController != NULL) {
770 m_errorController = m_deviceController->aRDrone3->setPilotingPCMD(m_deviceController->aRDrone3, 0, 0, 0, 0, 0, 0);
771 }
772 }
773
774 //*** ***//
775 //*** Streaming functions ***//
776 //*** ***//
777
778 #ifdef VISP_HAVE_FFMPEG // Functions related to video streaming and decoding requiers FFmpeg
779
780 /*!
781 \warning This function is only available if ViSP is build with ffmpeg support.
782
783 Gets the last streamed and decoded image from the drone camera feed in grayscale.
784 The image obtained has a width of 856 and a height of 480.
785
786 \param[in,out] I : grayscale image that will contain last streamed and decoded image after the function ends.
787 */
getGrayscaleImage(vpImage<unsigned char> & I)788 void vpRobotBebop2::getGrayscaleImage(vpImage<unsigned char> &I)
789 {
790 if (m_videoDecodingStarted) {
791
792 if (m_bgr_picture->data[0] != NULL) {
793 I.resize(static_cast<unsigned int>(m_videoHeight), static_cast<unsigned int>(m_videoWidth));
794
795 m_bgr_picture_mutex.lock();
796 vpImageConvert::BGRToGrey(m_bgr_picture->data[0], reinterpret_cast<unsigned char *>(I.bitmap), I.getWidth(),
797 I.getHeight());
798 m_bgr_picture_mutex.unlock();
799 } else {
800 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Error while getting current grayscale image : image data is NULL");
801 }
802
803 } else {
804 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't get current image : video streaming isn't started.");
805 }
806 }
807
808 /*!
809 \warning This function is only available if ViSP is build with ffmpeg support.
810
811 Gets the last streamed and decoded image from the drone camera feed in RGBa.
812 The image obtained has a width of 856 and a height of 480.
813
814 \param[in,out] I : RGBa image that will contain last streamed and decoded image after the function ends.
815 */
getRGBaImage(vpImage<vpRGBa> & I)816 void vpRobotBebop2::getRGBaImage(vpImage<vpRGBa> &I)
817 {
818 if (m_videoDecodingStarted) {
819
820 if (m_bgr_picture->data[0] != NULL) {
821 I.resize(static_cast<unsigned int>(m_videoHeight), static_cast<unsigned int>(m_videoWidth));
822
823 m_bgr_picture_mutex.lock();
824 vpImageConvert::BGRToRGBa(m_bgr_picture->data[0], reinterpret_cast<unsigned char *>(I.bitmap), I.getWidth(),
825 I.getHeight());
826 m_bgr_picture_mutex.unlock();
827 } else {
828 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Error while getting current RGBa image : image data is NULL");
829 }
830
831 } else {
832 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't get current image : video streaming isn't started.");
833 }
834 }
835
836 /*!
837 \warning This function is only available if ViSP is build with ffmpeg support.
838
839 Gets the height of the video streamed by the drone (pixels).
840 */
getVideoHeight()841 int vpRobotBebop2::getVideoHeight() { return m_videoHeight; }
842
843 /*!
844 \warning This function is only available if ViSP is build with ffmpeg support.
845
846 Gets the width of the video streamed by the drone (pixels).
847 */
getVideoWidth()848 int vpRobotBebop2::getVideoWidth() { return m_videoWidth; }
849
850 /*!
851 \warning This function is only available if ViSP is build with ffmpeg support.
852
853 Sets the exposure compensation of the video (min : -1.5, max : 1.5).
854
855 \param[in] expo : desired video exposure compensation.
856 */
setExposure(float expo)857 void vpRobotBebop2::setExposure(float expo)
858 {
859 if (isRunning() && m_deviceController != NULL) {
860 expo = std::min(1.5f, std::max(-1.5f, expo));
861
862 m_exposureSet = false;
863 m_deviceController->aRDrone3->sendPictureSettingsExpositionSelection(m_deviceController->aRDrone3, expo);
864
865 // m_exposureSet is set back to true when the drone has finished his move, via a callback
866 while (!m_exposureSet) {
867 vpTime::sleepMs(1);
868 }
869 } else {
870 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set exposure : drone isn't running.");
871 }
872 }
873
874 /*!
875 \warning This function is only available if ViSP is build with ffmpeg support.
876
877 Sets the streaming mode.
878
879 \warning This function should be called only if the drone isn't flying and the streaming isn't started.
880
881 \param[in] mode : desired streaming mode.
882 If mode = 0 (default mode), the streaming minimizes latency with average reliability (best for piloting).
883 If mode = 1, the streaming maximizes the reliability with an average latency (best when streaming quality is
884 important but not the latency).
885 If mode = 2, the streaming maximizes the reliability using a framerate decimation with an average latency (best when
886 streaming quality is important but not the latency).
887 */
setStreamingMode(int mode)888 void vpRobotBebop2::setStreamingMode(int mode)
889 {
890 if (isRunning() && m_deviceController != NULL) {
891
892 if (!isStreaming() && isLanded()) {
893 eARCOMMANDS_ARDRONE3_MEDIASTREAMING_VIDEOSTREAMMODE_MODE cmd_mode =
894 ARCOMMANDS_ARDRONE3_MEDIASTREAMING_VIDEOSTREAMMODE_MODE_LOW_LATENCY;
895 switch (mode) {
896 case 0:
897 cmd_mode = ARCOMMANDS_ARDRONE3_MEDIASTREAMING_VIDEOSTREAMMODE_MODE_LOW_LATENCY;
898 break;
899 case 1:
900 cmd_mode = ARCOMMANDS_ARDRONE3_MEDIASTREAMING_VIDEOSTREAMMODE_MODE_HIGH_RELIABILITY;
901 break;
902 case 2:
903 cmd_mode = ARCOMMANDS_ARDRONE3_MEDIASTREAMING_VIDEOSTREAMMODE_MODE_HIGH_RELIABILITY_LOW_FRAMERATE;
904 break;
905 default:
906 break;
907 }
908 m_streamingModeSet = false;
909 m_deviceController->aRDrone3->sendMediaStreamingVideoStreamMode(m_deviceController->aRDrone3, cmd_mode);
910
911 // m_streamingModeSet is set back to true when the drone has finished setting the stream mode, via a callback
912 while (!m_streamingModeSet) {
913 vpTime::sleepMs(1);
914 }
915
916 } else {
917 ARSAL_PRINT(
918 ARSAL_PRINT_ERROR, "ERROR",
919 "Can't set streaming mode : drone has to be landed and not streaming in order to set streaming mode.");
920 }
921 } else {
922 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set streaming mode : drone isn't running.");
923 }
924 }
925
926 /*!
927 \warning This function is only available if ViSP is build with ffmpeg support.
928
929 Sets the streaming mode.
930
931 \warning This function should be called only if the drone isn't flying and the streaming isn't started.
932
933 \param[in] mode : desired streaming mode.
934 If mode = 0 (default mode), the resolution is 480p (856x480).
935 If mode = 1, the resolution is 720p (1280x720).
936 */
setVideoResolution(int mode)937 void vpRobotBebop2::setVideoResolution(int mode)
938 {
939 if (isRunning() && m_deviceController != NULL) {
940
941 if (!isStreaming() && isLanded()) {
942
943 eARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEORESOLUTIONS_TYPE cmd_mode;
944
945 switch (mode) {
946
947 case 0:
948 default:
949 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEORESOLUTIONS_TYPE_REC1080_STREAM480;
950 m_videoWidth = 856;
951 m_videoHeight = 480;
952 break;
953
954 case 1:
955 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEORESOLUTIONS_TYPE_REC720_STREAM720;
956 m_videoWidth = 1280;
957 m_videoHeight = 720;
958 break;
959 }
960
961 m_videoResolutionSet = false;
962 m_deviceController->aRDrone3->sendPictureSettingsVideoResolutions(m_deviceController->aRDrone3, cmd_mode);
963
964 // m_videoResolutionSet is set back to true when the drone has finished setting the resolution, via a callback
965 while (!m_videoResolutionSet) {
966 vpTime::sleepMs(1);
967 }
968
969 } else {
970 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR",
971 "Can't set video resolution : drone has to be landed and not streaming in order to set streaming "
972 "parameters.");
973 }
974 } else {
975 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set video resolution : drone isn't running.");
976 }
977 }
978
979 /*!
980 \warning This function is only available if ViSP is build with ffmpeg support.
981
982 Sets the video stabilisation mode.
983
984 \param[in] mode : desired stabilisation mode.
985 If mode = 0, the video follows drone angles (no stabilisation, default).
986 If mode = 1, the video is stabilised on roll only.
987 If mode = 2, the video is stabilised on pitch only.
988 If mode = 3, the video is stabilised on both pitch and roll.
989 */
setVideoStabilisationMode(int mode)990 void vpRobotBebop2::setVideoStabilisationMode(int mode)
991 {
992 if (isRunning() && m_deviceController != NULL) {
993
994 eARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE cmd_mode =
995 ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE_NONE;
996 switch (mode) {
997
998 case 0:
999 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE_NONE;
1000 break;
1001 case 1:
1002 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE_ROLL;
1003 break;
1004 case 2:
1005 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE_PITCH;
1006 break;
1007 case 3:
1008 cmd_mode = ARCOMMANDS_ARDRONE3_PICTURESETTINGS_VIDEOSTABILIZATIONMODE_MODE_ROLL_PITCH;
1009 break;
1010
1011 default:
1012 break;
1013 }
1014 m_deviceController->aRDrone3->sendPictureSettingsVideoStabilizationMode(m_deviceController->aRDrone3, cmd_mode);
1015
1016 } else {
1017 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't set video stabilisation mode : drone isn't running.");
1018 }
1019 }
1020
1021 /*!
1022 \warning This function is only available if ViSP is build with ffmpeg support.
1023
1024 Starts the video streaming from the drone camera. Every time a frame is received, it is decoded and stored into \e
1025 m_currentImage, which can be obtained with getImage().
1026
1027 \sa getImage(vpImage<unsigned char> &I)
1028 */
startStreaming()1029 void vpRobotBebop2::startStreaming()
1030 {
1031 if (isRunning() && m_deviceController != NULL) {
1032 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Starting video streaming ... ");
1033
1034 // Sending command to the drone to start the video stream
1035 m_errorController = m_deviceController->aRDrone3->sendMediaStreamingVideoEnable(m_deviceController->aRDrone3, 1);
1036
1037 if (m_errorController == ARCONTROLLER_OK) {
1038 m_streamingStarted = false;
1039 // Blocking until streaming is started
1040 while (!m_streamingStarted) {
1041 vpTime::sleepMs(1);
1042 }
1043 startVideoDecoding();
1044
1045 /* We wait for the streaming to actually start (it has a delay before actually
1046 sending frames, even if it is indicated as started by the drone), or else the
1047 decoder is doesn't have time to synchronize with the stream */
1048 vpTime::sleepMs(4000);
1049
1050 } else {
1051 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error :%s", ARCONTROLLER_Error_ToString(m_errorController));
1052 }
1053
1054 } else {
1055 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't start streaming : drone isn't running.");
1056 }
1057 }
1058
1059 /*!
1060 \warning This function is only available if ViSP is build with ffmpeg support.
1061
1062 Stops the streaming and decoding of the drone camera video
1063 */
stopStreaming()1064 void vpRobotBebop2::stopStreaming()
1065 {
1066 if (m_videoDecodingStarted && m_deviceController != NULL) {
1067 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Stopping video streaming ... ");
1068
1069 // Sending command to the drone to stop the video stream
1070 m_errorController = m_deviceController->aRDrone3->sendMediaStreamingVideoEnable(m_deviceController->aRDrone3, 0);
1071
1072 if (m_errorController == ARCONTROLLER_OK) {
1073
1074 // Blocking until streaming is stopped
1075 while (getStreamingState() != ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_DISABLED) {
1076 vpTime::sleepMs(1);
1077 }
1078 vpTime::sleepMs(500); // We wait for the streaming to actually stops or else it causes segmentation fault.
1079 stopVideoDecoding();
1080
1081 } else {
1082 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error :%s", ARCONTROLLER_Error_ToString(m_errorController));
1083 }
1084
1085 } else {
1086 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Can't stop streaming : streaming already stopped.");
1087 }
1088 }
1089
1090 #endif // #ifdef VISP_HAVE_FFMPEG
1091
1092 //*** ***//
1093 //*** Private Functions ***//
1094 //*** ***//
1095
1096 /*!
1097
1098 Signal handler. Lands the drone if possible in order to prevent accidents.
1099 */
sighandler(int signo)1100 void vpRobotBebop2::sighandler(int signo)
1101 {
1102 std::cout << "Stopping Bebop2 because of detected signal (" << signo << "): " << static_cast<char>(7);
1103 switch (signo) {
1104 case SIGINT:
1105 std::cout << "SIGINT (stopped by ^C) " << std::endl;
1106 break;
1107 case SIGBUS:
1108 std::cout << "SIGBUS (stopped due to a bus error) " << std::endl;
1109 break;
1110 case SIGSEGV:
1111 std::cout << "SIGSEGV (stopped due to a segmentation fault) " << std::endl;
1112 break;
1113 case SIGKILL:
1114 std::cout << "SIGKILL (stopped by CTRL \\) " << std::endl;
1115 break;
1116 case SIGQUIT:
1117 std::cout << "SIGQUIT " << std::endl;
1118 break;
1119 default:
1120 std::cout << signo << std::endl;
1121 }
1122
1123 vpRobotBebop2::m_running = false;
1124
1125 // Landing the drone
1126 if (m_deviceController != NULL) {
1127 m_deviceController->aRDrone3->sendPilotingLanding(m_deviceController->aRDrone3);
1128 }
1129 std::exit(EXIT_FAILURE);
1130 }
1131
1132 /*!
1133
1134 Gets the current flying state of the drone.
1135 */
getFlyingState()1136 eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE vpRobotBebop2::getFlyingState()
1137 {
1138 if (m_deviceController != NULL) {
1139 eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState =
1140 ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_MAX;
1141 eARCONTROLLER_ERROR error;
1142
1143 ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary = ARCONTROLLER_ARDrone3_GetCommandElements(
1144 m_deviceController->aRDrone3, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED, &error);
1145
1146 if (error == ARCONTROLLER_OK && elementDictionary != NULL) {
1147 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1148 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1149
1150 HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1151
1152 if (element != NULL)
1153 {
1154 //Suppress warnings
1155 // Get the value
1156 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);
1157
1158 if (arg != NULL)
1159 {
1160 // Enums are stored as I32
1161 flyingState = static_cast<eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE>(arg->value.I32);
1162 }
1163 }
1164 }
1165 return flyingState;
1166 } else {
1167 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Error when checking flying state : drone isn't connected.");
1168 return ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_MAX;
1169 }
1170 }
1171
1172 /*!
1173
1174 Gets the current streaming state of the drone.
1175 */
getStreamingState()1176 eARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED vpRobotBebop2::getStreamingState()
1177 {
1178 if (m_deviceController != NULL) {
1179 eARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED streamingState =
1180 ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_MAX;
1181 eARCONTROLLER_ERROR error;
1182
1183 ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary = ARCONTROLLER_ARDrone3_GetCommandElements(
1184 m_deviceController->aRDrone3, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED,
1185 &error);
1186
1187 if (error == ARCONTROLLER_OK && elementDictionary != NULL) {
1188 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1189 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1190
1191 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1192
1193 if (element != NULL) {
1194 // Get the value
1195 HASH_FIND_STR(element->arguments,
1196 ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED, arg);
1197
1198 if (arg != NULL) {
1199 // Enums are stored as I32
1200 streamingState =
1201 static_cast<eARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED>(arg->value.I32);
1202 }
1203 }
1204 }
1205 return streamingState;
1206 } else {
1207 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Error when checking streaming state : drone isn't connected.");
1208 return ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_MAX;
1209 }
1210 }
1211
1212 /*!
1213
1214 Discovers the drone on the wifi network and returns the detected device.
1215 */
discoverDrone()1216 ARDISCOVERY_Device_t * vpRobotBebop2::discoverDrone()
1217 {
1218 eARDISCOVERY_ERROR errorDiscovery = ARDISCOVERY_OK;
1219
1220 ARDISCOVERY_Device_t * device = ARDISCOVERY_Device_New(&errorDiscovery);
1221
1222 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Starting drone Wifi discovery ...");
1223 const char * charIpAddress = m_ipAddress.c_str();
1224 errorDiscovery = ARDISCOVERY_Device_InitWifi(device, ARDISCOVERY_PRODUCT_BEBOP_2, "bebop2", charIpAddress, m_discoveryPort);
1225
1226 if (errorDiscovery != ARDISCOVERY_OK)
1227 {
1228 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Discovery error :%s", ARDISCOVERY_Error_ToString(errorDiscovery));
1229 }
1230 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Drone controller created.");
1231 return device;
1232 }
1233
1234 /*!
1235
1236 Create a drone controller based on a discovered device.
1237
1238 \param[in] discoveredDrone : discovered drone to control with the controller. Deleted at the end of this function.
1239 */
createDroneController(ARDISCOVERY_Device_t * discoveredDrone)1240 void vpRobotBebop2::createDroneController(ARDISCOVERY_Device_t * discoveredDrone)
1241 {
1242 m_deviceController = ARCONTROLLER_Device_New (discoveredDrone, &m_errorController);
1243 if (m_errorController != ARCONTROLLER_OK)
1244 {
1245 ARSAL_PRINT (ARSAL_PRINT_ERROR, TAG, "Creation of deviceController failed.");
1246 }
1247 ARDISCOVERY_Device_Delete (&discoveredDrone);
1248 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Device created.");
1249 }
1250
1251 /*!
1252
1253 Sets up callbacks, which are functions called by the drone for different kinds of events.
1254 */
setupCallbacks()1255 void vpRobotBebop2::setupCallbacks()
1256 {
1257 //Adding stateChanged callback, called when the state of the controller has changed
1258 m_errorController = ARCONTROLLER_Device_AddStateChangedCallback(m_deviceController, stateChangedCallback, this);
1259 if(m_errorController != ARCONTROLLER_OK)
1260 {
1261 ARSAL_PRINT (ARSAL_PRINT_ERROR, TAG, "add State callback failed.");
1262 }
1263
1264 //Adding commendReceived callback, called when the a command has been received from the device
1265 m_errorController = ARCONTROLLER_Device_AddCommandReceivedCallback(m_deviceController, commandReceivedCallback, this);
1266
1267 if(m_errorController != ARCONTROLLER_OK)
1268 {
1269 ARSAL_PRINT (ARSAL_PRINT_ERROR, TAG, "add Command callback failed.");
1270 }
1271
1272 #ifdef VISP_HAVE_FFMPEG
1273 //Adding frame received callback, called when a streaming frame has been received from the device
1274 m_errorController = ARCONTROLLER_Device_SetVideoStreamCallbacks (m_deviceController, decoderConfigCallback, didReceiveFrameCallback, NULL , this);
1275
1276 if(m_errorController != ARCONTROLLER_OK)
1277 {
1278 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error: %s", ARCONTROLLER_Error_ToString(m_errorController));
1279 }
1280 #endif
1281 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Callbacks set up.");
1282 }
1283
1284 /*!
1285
1286 Starts the drone controller, which is then ready to receive commands.
1287 */
startController()1288 void vpRobotBebop2::startController()
1289 {
1290 //Starts the controller
1291 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Connecting ...");
1292 m_errorController = ARCONTROLLER_Device_Start (m_deviceController);
1293
1294 if(m_errorController != ARCONTROLLER_OK)
1295 {
1296 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error :%s", ARCONTROLLER_Error_ToString(m_errorController));
1297 }
1298
1299 // Waits for the stateChangedCallback to unclock the semaphore
1300 ARSAL_Sem_Wait (&(m_stateSem));
1301
1302 //Checks the device state
1303 m_deviceState = ARCONTROLLER_Device_GetState (m_deviceController, &m_errorController);
1304
1305 if((m_errorController != ARCONTROLLER_OK) || (m_deviceState != ARCONTROLLER_DEVICE_STATE_RUNNING))
1306 {
1307 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- deviceState :%d", m_deviceState);
1308 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "- error :%s", ARCONTROLLER_Error_ToString(m_errorController));
1309 }
1310 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Controller started.");
1311 }
1312
1313 #ifdef VISP_HAVE_FFMPEG
1314 /*!
1315 \warning This function is only available if ViSP is build with ffmpeg support.
1316
1317 Initialises the codec used to decode the drone H264 video stream.
1318 */
initCodec()1319 void vpRobotBebop2::initCodec()
1320 {
1321 av_register_all();
1322 avcodec_register_all();
1323 avformat_network_init();
1324
1325 // Finds the correct codec
1326 AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
1327 if (!codec) {
1328 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Codec not found.");
1329 return;
1330 }
1331
1332 // Allocates memory for codec
1333 m_codecContext = avcodec_alloc_context3(codec);
1334
1335 if (!m_codecContext) {
1336 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Failed to allocate codec context.");
1337 return;
1338 }
1339
1340 // Sets codec parameters (TODO : should be done automaticaly by drone callback decoderConfigCallback)
1341 m_codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
1342 m_codecContext->skip_frame = AVDISCARD_DEFAULT;
1343 m_codecContext->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
1344 m_codecContext->skip_loop_filter = AVDISCARD_DEFAULT;
1345 m_codecContext->workaround_bugs = AVMEDIA_TYPE_VIDEO;
1346 m_codecContext->codec_id = AV_CODEC_ID_H264;
1347 m_codecContext->skip_idct = AVDISCARD_DEFAULT;
1348
1349 m_codecContext->width = m_videoWidth;
1350 m_codecContext->height = m_videoHeight;
1351
1352 if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
1353 m_codecContext->flags |= AV_CODEC_FLAG_TRUNCATED;
1354 }
1355 m_codecContext->flags2 |= AV_CODEC_FLAG2_CHUNKS;
1356
1357 // Opens the codec
1358 if (avcodec_open2(m_codecContext, codec, NULL) < 0) {
1359 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Failed to open codec.");
1360 return;
1361 }
1362
1363 AVPixelFormat pFormat = AV_PIX_FMT_BGR24;
1364 int numBytes = av_image_get_buffer_size(pFormat, m_codecContext->width, m_codecContext->height, 1);
1365 m_buffer = static_cast<uint8_t *>(av_malloc(static_cast<unsigned long>(numBytes) * sizeof(uint8_t)));
1366
1367 av_init_packet(&m_packet); // Packed used to send data to the decoder
1368 m_picture = av_frame_alloc(); // Frame used to receive data from the decoder
1369
1370 m_bgr_picture_mutex.lock();
1371 m_bgr_picture = av_frame_alloc(); // Frame used to store rescaled frame received from the decoder
1372 m_bgr_picture_mutex.unlock();
1373
1374 m_img_convert_ctx = sws_getContext(m_codecContext->width, m_codecContext->height, m_codecContext->pix_fmt,
1375 m_codecContext->width, m_codecContext->height, pFormat, SWS_BICUBIC, NULL, NULL,
1376 NULL); // Used to rescale frame received from the decoder
1377 }
1378
1379 /*!
1380 \warning This function is only available if ViSP is build with ffmpeg support.
1381
1382 Safely frees any memory that was allocated by the codec.
1383 */
cleanUpCodec()1384 void vpRobotBebop2::cleanUpCodec()
1385 {
1386 m_videoDecodingStarted = false;
1387 av_packet_unref(&m_packet);
1388
1389 if (m_codecContext) {
1390 avcodec_flush_buffers(m_codecContext);
1391 avcodec_free_context(&m_codecContext);
1392 }
1393
1394 if (m_picture) {
1395 av_frame_free(&m_picture);
1396 }
1397
1398 if (m_bgr_picture) {
1399 m_bgr_picture_mutex.lock();
1400 av_frame_free(&m_bgr_picture);
1401 m_bgr_picture_mutex.unlock();
1402 }
1403
1404 if (m_img_convert_ctx) {
1405 sws_freeContext(m_img_convert_ctx);
1406 }
1407 if (m_buffer) {
1408 av_free(m_buffer);
1409 }
1410 }
1411
1412 /*!
1413 \warning This function is only available if ViSP is build with ffmpeg support.
1414
1415 Starts the video decoding : initialises the codec and the image stored by the drone.
1416 */
startVideoDecoding()1417 void vpRobotBebop2::startVideoDecoding()
1418 {
1419 if (!m_videoDecodingStarted) {
1420 initCodec();
1421 m_videoDecodingStarted = true;
1422 } else {
1423 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Video decoding is already started.");
1424 }
1425 }
1426
1427 /*!
1428 \warning This function is only available if ViSP is build with ffmpeg support.
1429
1430 Stops the video decoding : safely cleans up memory allocated by the codec.
1431 */
stopVideoDecoding()1432 void vpRobotBebop2::stopVideoDecoding()
1433 {
1434 if (m_videoDecodingStarted) {
1435 cleanUpCodec();
1436 } else {
1437 ARSAL_PRINT(ARSAL_PRINT_ERROR, "ERROR", "Video decoding is already stopped.");
1438 }
1439 }
1440
1441 /*!
1442 \warning This function is only available if ViSP is build with ffmpeg support.
1443
1444 Decodes a H264 frame received from the drone.
1445
1446 \param[in] frame : pointer to the frame sent by the drone. Called during didReceiveFrameCallback.
1447 */
computeFrame(ARCONTROLLER_Frame_t * frame)1448 void vpRobotBebop2::computeFrame(ARCONTROLLER_Frame_t *frame)
1449 {
1450
1451 // Updating codec parameters from SPS and PPS buffers received from the drone in decoderConfigCallback
1452 if (m_update_codec_params && m_codec_params_data.size()) {
1453 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Updating H264 codec parameters (Buffer Size: %lu) ...",
1454 m_codec_params_data.size());
1455
1456 m_packet.data = &m_codec_params_data[0];
1457 m_packet.size = static_cast<int>(m_codec_params_data.size());
1458
1459 int ret = avcodec_send_packet(m_codecContext, &m_packet);
1460
1461 if (ret == 0) {
1462
1463 ret = avcodec_receive_frame(m_codecContext, m_picture);
1464
1465 if (ret == 0 || ret == AVERROR(EAGAIN)) {
1466 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "H264 codec parameters updated.");
1467 } else {
1468 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Unexpected error while updating H264 parameters.");
1469 }
1470 } else {
1471 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Unexpected error while sending H264 parameters.");
1472 }
1473 m_update_codec_params = false;
1474 av_packet_unref(&m_packet);
1475 av_frame_unref(m_picture);
1476 }
1477
1478 // Decoding frame comming from the drone
1479 m_packet.data = frame->data;
1480 m_packet.size = static_cast<int>(frame->used);
1481
1482 int ret = avcodec_send_packet(m_codecContext, &m_packet);
1483 if (ret < 0) {
1484
1485 char *errbuff = new char[AV_ERROR_MAX_STRING_SIZE];
1486 av_strerror(ret, errbuff, AV_ERROR_MAX_STRING_SIZE);
1487 std::string err(errbuff);
1488 delete[] errbuff;
1489 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Error sending a packet for decoding : %d, %s", ret, err.c_str());
1490
1491 } else {
1492
1493 ret = avcodec_receive_frame(m_codecContext, m_picture);
1494
1495 if (ret < 0) {
1496
1497 if (ret == AVERROR(EAGAIN)) {
1498 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "AVERROR(EAGAIN)"); // Not an error, need to send data again
1499 } else if (ret == AVERROR_EOF) {
1500 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "AVERROR_EOF"); // End of file reached, should not happen with drone footage
1501 } else {
1502
1503 char *errbuff = new char[AV_ERROR_MAX_STRING_SIZE];
1504 av_strerror(ret, errbuff, AV_ERROR_MAX_STRING_SIZE);
1505 std::string err(errbuff);
1506 delete[] errbuff;
1507 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Error receiving a decoded frame : %d, %s", ret, err.c_str());
1508 }
1509 } else {
1510 m_bgr_picture_mutex.lock();
1511 av_frame_unref(m_bgr_picture);
1512 av_image_fill_arrays(m_bgr_picture->data, m_bgr_picture->linesize, m_buffer, AV_PIX_FMT_BGR24,
1513 m_codecContext->width, m_codecContext->height, 1);
1514
1515 sws_scale(m_img_convert_ctx, (m_picture)->data, (m_picture)->linesize, 0, m_codecContext->height,
1516 (m_bgr_picture)->data, (m_bgr_picture)->linesize);
1517
1518 m_bgr_picture_mutex.unlock();
1519 }
1520 }
1521
1522 av_packet_unref(&m_packet);
1523
1524 av_frame_unref(m_picture);
1525 }
1526 #endif // #ifdef VISP_HAVE_FFMPEG
1527 /*!
1528
1529 Safely stops the drone controller and everything needed during drone control. Called by the destructor.
1530 The drone is not running anymore after this is called.
1531 */
cleanUp()1532 void vpRobotBebop2::cleanUp()
1533 {
1534 if (m_deviceController != NULL) {
1535 // Lands the drone if not landed
1536 land();
1537
1538 #ifdef VISP_HAVE_FFMPEG
1539 // Stops the streaming if not stopped
1540 stopStreaming();
1541 #endif
1542
1543 // Deletes the controller
1544 m_deviceState = ARCONTROLLER_Device_GetState(m_deviceController, &m_errorController);
1545 if ((m_errorController == ARCONTROLLER_OK) && (m_deviceState != ARCONTROLLER_DEVICE_STATE_STOPPED)) {
1546
1547 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Disconnecting ...");
1548 m_errorController = ARCONTROLLER_Device_Stop(m_deviceController);
1549
1550 if (m_errorController == ARCONTROLLER_OK) {
1551 // Wait for the semaphore to increment, it will when the controller changes its state to 'stopped'
1552 ARSAL_Sem_Wait(&(m_stateSem));
1553 }
1554 }
1555 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Deleting device controller ...");
1556 ARCONTROLLER_Device_Delete(&m_deviceController);
1557
1558 // Destroys the semaphore
1559 ARSAL_Sem_Destroy(&(m_stateSem));
1560
1561 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "- Cleanup done.");
1562 } else {
1563
1564 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Error while cleaning up memory.");
1565 }
1566 m_running = false;
1567 }
1568
1569 //*** ***//
1570 //*** Callbacks ***//
1571 //*** ***//
1572
1573 /*!
1574
1575 Callback. Called by the drone when the drone controller changes state.
1576
1577 \param[in] newState : new controller state to handle.
1578 \param[in] error : not used.
1579 \param[in] customData : pointer to custom data, here used to point to the drone.
1580 */
stateChangedCallback(eARCONTROLLER_DEVICE_STATE newState,eARCONTROLLER_ERROR error,void * customData)1581 void vpRobotBebop2::stateChangedCallback(eARCONTROLLER_DEVICE_STATE newState, eARCONTROLLER_ERROR error,
1582 void *customData)
1583 {
1584 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Controller state changed, new state: %d.", newState);
1585 (void)error;
1586
1587 vpRobotBebop2 *drone = static_cast<vpRobotBebop2 *>(customData);
1588 switch (newState)
1589 {
1590 case ARCONTROLLER_DEVICE_STATE_STOPPED:
1591 // Stopping the programm
1592 drone->m_running = false;
1593 // Incrementing semaphore
1594 ARSAL_Sem_Post(&(drone->m_stateSem));
1595 break;
1596
1597 case ARCONTROLLER_DEVICE_STATE_RUNNING:
1598 // Incrementing semaphore
1599 ARSAL_Sem_Post(&(drone->m_stateSem));
1600 break;
1601
1602 default:
1603 break;
1604 }
1605 }
1606
1607 #ifdef VISP_HAVE_FFMPEG
1608 /*!
1609
1610 Callback. Called when streaming is started, allows to get codec parameters for the H264 video streammed by the drone.
1611 Currently not used, as the codec parameter are currently manually set up. Function only available when ffmpeg available.
1612
1613 \param[in] codec : codec used to stream the drone camera video.
1614 \param[in] customData : pointer to custom data.
1615 */
decoderConfigCallback(ARCONTROLLER_Stream_Codec_t codec,void * customData)1616 eARCONTROLLER_ERROR vpRobotBebop2::decoderConfigCallback(ARCONTROLLER_Stream_Codec_t codec, void * customData)
1617 {
1618 vpRobotBebop2 *drone = static_cast<vpRobotBebop2 *>(customData);
1619
1620 uint8_t *sps_buffer_ptr = codec.parameters.h264parameters.spsBuffer;
1621 uint32_t sps_buffer_size = static_cast<uint32_t>(codec.parameters.h264parameters.spsSize);
1622 uint8_t *pps_buffer_ptr = codec.parameters.h264parameters.ppsBuffer;
1623 uint32_t pps_buffer_size = static_cast<uint32_t>(codec.parameters.h264parameters.ppsSize);
1624
1625 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "H264 configuration packet received: #SPS: %d #PPS: %d", sps_buffer_size,
1626 pps_buffer_size);
1627
1628 drone->m_update_codec_params = (sps_buffer_ptr && pps_buffer_ptr && sps_buffer_size && pps_buffer_size &&
1629 (pps_buffer_size < 32) && (sps_buffer_size < 32));
1630
1631 if (drone->m_update_codec_params) {
1632 // If codec parameters where received from the drone, we store them to pass them to the decoder in the next call of
1633 // computeFrame
1634 drone->m_codec_params_data.resize(sps_buffer_size + pps_buffer_size);
1635 std::copy(sps_buffer_ptr, sps_buffer_ptr + sps_buffer_size, drone->m_codec_params_data.begin());
1636 std::copy(pps_buffer_ptr, pps_buffer_ptr + pps_buffer_size, drone->m_codec_params_data.begin() + sps_buffer_size);
1637 } else {
1638 // If data is invalid, we clear the vector
1639 drone->m_codec_params_data.clear();
1640 }
1641 return ARCONTROLLER_OK;
1642 }
1643
1644 /*!
1645
1646 Callback. Called when the drone sends a frame to decode. Function only available when ffmpeg available.
1647 Sends the frame to the computeFrame() function.
1648
1649 \param[in] frame : the frame coded in H264 to decode.
1650 \param[in] customData : pointer to custom data, here used to point to the drone.
1651 */
didReceiveFrameCallback(ARCONTROLLER_Frame_t * frame,void * customData)1652 eARCONTROLLER_ERROR vpRobotBebop2::didReceiveFrameCallback(ARCONTROLLER_Frame_t *frame, void *customData)
1653 {
1654 vpRobotBebop2 *drone = static_cast<vpRobotBebop2 *>(customData);
1655
1656 if (frame != NULL) {
1657
1658 if (drone->m_videoDecodingStarted) {
1659 drone->computeFrame(frame);
1660 }
1661
1662
1663 } else {
1664 ARSAL_PRINT(ARSAL_PRINT_WARNING, TAG, "frame is NULL.");
1665 }
1666
1667 return ARCONTROLLER_OK;
1668 }
1669 #endif // #ifdef VISP_HAVE_FFMPEG
1670
1671
1672 /*!
1673
1674 Gets the current battery level of the drone when a battery-level-changed callback is called.
1675
1676 \param[in] elementDictionary : the object containing the data received.
1677 \param[in] drone : pointer to the drone who called the callback.
1678
1679 \sa commandReceivedCallback()
1680 */
cmdBatteryStateChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1681 void vpRobotBebop2::cmdBatteryStateChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary,
1682 vpRobotBebop2 *drone)
1683 {
1684 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1685 ARCONTROLLER_DICTIONARY_ELEMENT_t *singleElement = NULL;
1686
1687 if (elementDictionary == NULL) {
1688 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "elements is NULL");
1689 return;
1690 }
1691
1692 // Get the command received in the device controller
1693 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, singleElement);
1694
1695 if (singleElement == NULL) {
1696 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "singleElement is NULL");
1697 return;
1698 }
1699
1700 // Get the value
1701 HASH_FIND_STR(singleElement->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT,
1702 arg);
1703
1704 if (arg == NULL) {
1705 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "arg is NULL");
1706 return;
1707 }
1708 drone->m_batteryLevel = arg->value.U8;
1709 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Battery level changed : %u percent remaining.", drone->m_batteryLevel);
1710
1711 if (drone->m_batteryLevel <= 5) {
1712 ARSAL_PRINT(ARSAL_PRINT_WARNING, TAG, " - WARNING, very low battery level, drone will stop soon !");
1713 } else if (drone->m_batteryLevel <= 10) {
1714 ARSAL_PRINT(ARSAL_PRINT_WARNING, TAG, " - Warning, low battery level !");
1715 }
1716 }
1717
1718 /*!
1719
1720 Gets the camera pan and tilt values when a camera-orientation-changed callback is called.
1721 Stores the values in m_currentCameraPan and m_currentCameraTilt.
1722
1723 \param[in] elementDictionary : the object containing the data received.
1724 \param[in] drone : pointer to the drone who called the callback.
1725
1726 \sa commandReceivedCallback()
1727 */
cmdCameraOrientationChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1728 void vpRobotBebop2::cmdCameraOrientationChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary,
1729 vpRobotBebop2 *drone)
1730 {
1731 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1732 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1733 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1734 if (element != NULL) {
1735 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_CAMERASTATE_ORIENTATIONV2_TILT, arg);
1736
1737 if (arg != NULL) {
1738 drone->m_currentCameraTilt = static_cast<double>(arg->value.Float);
1739 }
1740
1741 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_CAMERASTATE_ORIENTATIONV2_PAN, arg);
1742 if (arg != NULL) {
1743 drone->m_currentCameraPan = static_cast<double>(arg->value.Float);
1744 }
1745 }
1746 }
1747
1748 /*!
1749
1750 Gets the camera orientation values : min and max pan and tilt values which are sent via a callback when the drone
1751 connects.
1752 Stores the values in m_minCameraTilt, m_maxCameraTilt, m_minCameraPan and m_maxCameraPan.
1753
1754 \param[in] elementDictionary : the object containing the data received.
1755 \param[in] drone : pointer to the drone who called the callback.
1756
1757 \sa commandReceivedCallback()
1758 */
cmdCameraSettingsRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1759 void vpRobotBebop2::cmdCameraSettingsRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, vpRobotBebop2 *drone)
1760 {
1761 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1762 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1763 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1764 if (element != NULL) {
1765 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED_FOV,
1766 arg);
1767 if (arg != NULL) {
1768 drone->m_cameraHorizontalFOV = static_cast<double>(arg->value.Float);
1769 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Camera horizontal FOV : %f degrees.",
1770 static_cast<double>(drone->m_cameraHorizontalFOV));
1771 }
1772 HASH_FIND_STR(element->arguments,
1773 ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED_PANMAX, arg);
1774 if (arg != NULL) {
1775 drone->m_maxCameraPan = static_cast<double>(arg->value.Float);
1776 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Max camera pan : %f degrees.",
1777 static_cast<double>(drone->m_maxCameraPan));
1778 }
1779 HASH_FIND_STR(element->arguments,
1780 ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED_PANMIN, arg);
1781 if (arg != NULL) {
1782 drone->m_minCameraPan = static_cast<double>(arg->value.Float);
1783 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Min camera pan : %f degrees.",
1784 static_cast<double>(drone->m_minCameraPan));
1785 }
1786 HASH_FIND_STR(element->arguments,
1787 ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED_TILTMAX, arg);
1788 if (arg != NULL) {
1789 drone->m_maxCameraTilt = static_cast<double>(arg->value.Float);
1790 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Max camera tilt : %f degrees.",
1791 static_cast<double>(drone->m_maxCameraTilt));
1792 }
1793 HASH_FIND_STR(element->arguments,
1794 ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED_TILTMIN, arg);
1795 if (arg != NULL) {
1796 drone->m_minCameraTilt = static_cast<double>(arg->value.Float);
1797 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, " - Min camera tilt : %f degrees.",
1798 static_cast<double>(drone->m_minCameraTilt));
1799 }
1800 }
1801 }
1802
1803 /*!
1804
1805 Gets the current max pitch and roll values of the drone when a maxPitchRoll-changed callback is called.
1806 Used to save the value in a attribute of the drone.
1807
1808 \param[in] elementDictionary : the object containing the data received.
1809 \param[in] drone : pointer to the drone who called the callback.
1810
1811 \sa commandReceivedCallback()
1812 */
cmdMaxPitchRollChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1813 void vpRobotBebop2::cmdMaxPitchRollChangedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary,
1814 vpRobotBebop2 *drone)
1815 {
1816 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1817 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1818
1819 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1820 if (element != NULL) {
1821 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSETTINGSSTATE_MAXTILTCHANGED_CURRENT,
1822 arg);
1823 if (arg != NULL) {
1824 drone->m_maxTilt = static_cast<double>(arg->value.Float);
1825 }
1826 }
1827 }
1828
1829 /*!
1830
1831 Called when the drone sends a relativeMoveEnded callback.
1832 Used to know when the drone has finished a relative move.
1833
1834 \param[in] elementDictionary : the object containing the data received.
1835 \param[in] drone : pointer to the drone who called the callback.
1836
1837 \sa commandReceivedCallback()
1838 */
cmdRelativeMoveEndedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1839 void vpRobotBebop2::cmdRelativeMoveEndedRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, vpRobotBebop2 *drone)
1840 {
1841 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1842 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1843
1844 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1845
1846 if (element != NULL) {
1847 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGEVENT_MOVEBYEND_ERROR, arg);
1848
1849 if (arg != NULL) {
1850 eARCOMMANDS_ARDRONE3_PILOTINGEVENT_MOVEBYEND_ERROR error =
1851 static_cast<eARCOMMANDS_ARDRONE3_PILOTINGEVENT_MOVEBYEND_ERROR>(arg->value.I32);
1852 if ((error != ARCOMMANDS_ARDRONE3_PILOTINGEVENT_MOVEBYEND_ERROR_OK) &&
1853 (error != ARCOMMANDS_ARDRONE3_PILOTINGEVENT_MOVEBYEND_ERROR_INTERRUPTED)) {
1854 ARSAL_PRINT(ARSAL_PRINT_ERROR, TAG, "Relative move ended with error %d", error);
1855 }
1856 drone->m_relativeMoveEnded = true;
1857 }
1858 }
1859 }
1860
1861 /*!
1862
1863 Called when the drone sends a exposureChanged callback.
1864 Used to know when the drone has changed its video exposure level.
1865
1866 \param[in] elementDictionary : the object containing the data received.
1867 \param[in] drone : pointer to the drone who called the callback.
1868
1869 \sa commandReceivedCallback()
1870 */
cmdExposureSetRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,vpRobotBebop2 * drone)1871 void vpRobotBebop2::cmdExposureSetRcv(ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, vpRobotBebop2 *drone)
1872 {
1873 ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
1874 ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
1875
1876 HASH_FIND_STR(elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
1877
1878 if (element != NULL) {
1879
1880 HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PICTURESETTINGSSTATE_EXPOSITIONCHANGED_VALUE,
1881 arg);
1882
1883 if (arg != NULL) {
1884 drone->m_exposureSet = true;
1885 }
1886 }
1887 }
1888
1889 /*!
1890
1891 Callback. Computes a command received from the drone.
1892
1893 \param[in] : the type of command received from the drone.
1894 \param[in] elementDictionary : the object containing the data received.
1895 \param[in] customData : pointer to custom data, here used to point to the drone.
1896
1897 */
commandReceivedCallback(eARCONTROLLER_DICTIONARY_KEY commandKey,ARCONTROLLER_DICTIONARY_ELEMENT_t * elementDictionary,void * customData)1898 void vpRobotBebop2::commandReceivedCallback(eARCONTROLLER_DICTIONARY_KEY commandKey,
1899 ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData)
1900 {
1901 vpRobotBebop2 *drone = static_cast<vpRobotBebop2 *>(customData);
1902
1903 if (drone == NULL)
1904 return;
1905
1906 switch (commandKey) {
1907 case ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED:
1908 // If the command received is a battery state changed
1909 cmdBatteryStateChangedRcv(elementDictionary, drone);
1910 break;
1911
1912 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSETTINGSSTATE_MAXTILTCHANGED:
1913 // If the command receivend is a max pitch/roll changed
1914 cmdMaxPitchRollChangedRcv(elementDictionary, drone);
1915 break;
1916
1917 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGEVENT_MOVEBYEND:
1918 // If the command received is a relative move ended
1919 cmdRelativeMoveEndedRcv(elementDictionary, drone);
1920 break;
1921
1922 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLATTRIMCHANGED:
1923 // If the command received is a flat trim finished
1924 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Flat trim finished ...");
1925 drone->m_flatTrimFinished = true;
1926 break;
1927
1928 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PICTURESETTINGSSTATE_EXPOSITIONCHANGED:
1929 // If the command received is a exposition changed
1930 cmdExposureSetRcv(elementDictionary, drone);
1931 break;
1932
1933 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PICTURESETTINGSSTATE_VIDEORESOLUTIONSCHANGED:
1934 // If the command received is a resolution changed
1935 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Video resolution set ...");
1936 drone->m_videoResolutionSet = true;
1937 break;
1938
1939 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED:
1940 // If the command received is a streaming started
1941 drone->m_streamingStarted = true;
1942 break;
1943
1944 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOSTREAMMODECHANGED:
1945 // If the command received is a streaming mode changed
1946 drone->m_streamingModeSet = true;
1947 break;
1948
1949 case ARCONTROLLER_DICTIONARY_KEY_COMMON_SETTINGSSTATE_RESETCHANGED:
1950 // If the command received is a settings reset
1951 ARSAL_PRINT(ARSAL_PRINT_INFO, TAG, "Settings reset ...");
1952 drone->m_settingsReset = true;
1953 break;
1954
1955 case ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_CAMERASTATE_ORIENTATIONV2:
1956 // If the command received is a camera orientation changed
1957 cmdCameraOrientationChangedRcv(elementDictionary, drone);
1958 break;
1959
1960 case ARCONTROLLER_DICTIONARY_KEY_COMMON_CAMERASETTINGSSTATE_CAMERASETTINGSCHANGED:
1961 // If the command received is a camera information sent
1962 cmdCameraSettingsRcv(elementDictionary, drone);
1963 break;
1964
1965 default:
1966 break;
1967 }
1968 }
1969
1970 #undef TAG
1971
1972 #elif !defined(VISP_BUILD_SHARED_LIBS)
1973 // Work arround to avoid warning: libvisp_robot.a(vpRobotBebop2.cpp.o) has
1974 // no symbols
dummy_vpRobotBebop2()1975 void dummy_vpRobotBebop2(){};
1976 #endif // VISP_HAVE_ARSDK
1977