Paparazzi UAS v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
nps_fdm_gazebo.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017 Tom van Dijk, Kirk Scheper
3 *
4 * This file is part of paparazzi.
5 *
6 * paparazzi 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, or (at your option)
9 * any later version.
10 *
11 * paparazzi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with paparazzi; see the file COPYING. If not, write to
18 * the Free Software Foundation, 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
29#include <cstdio>
30#include <cstdlib>
31#include <string>
32#include <iostream>
33#include <sys/time.h>
34
35#include <gazebo/gazebo.hh>
36#include <gazebo/physics/physics.hh>
37#include <gazebo/sensors/sensors.hh>
38#include <sdf/sdf.hh>
39
40#include <gazebo/gazebo_config.h>
41#if GAZEBO_MAJOR_VERSION < 8
42#error "Paparazzi only supports gazebo versions > 7, please upgrade to a more recent version of Gazebo"
43#endif
44
45extern "C" {
46#include "nps_fdm.h"
47#include "nps_autopilot.h"
48
49#include "generated/airframe.h"
50#include "generated/flight_plan.h"
51#include "autopilot.h"
52#include "modules/core/abi.h"
53
54#include "math/pprz_isa.h"
58
60
63#include "state.h"
64}
65
66#if defined(NPS_DEBUG_VIDEO)
67// Opencv tools
68#include <opencv2/core/core.hpp>
69#include <opencv2/highgui/highgui.hpp>
70#include <opencv2/imgproc/imgproc.hpp>
71#endif
72
73using namespace std;
74
75#ifndef NPS_GAZEBO_WORLD
76#define NPS_GAZEBO_WORLD "empty.world"
77#endif
78#ifndef NPS_GAZEBO_AC_NAME
79#define NPS_GAZEBO_AC_NAME "ardrone"
80#endif
81
82// Add video handling functions if req'd.
83#if NPS_SIMULATE_VIDEO
84extern "C" {
88#include "mcu_periph/sys_time.h"
91struct mt9f002_t mt9f002 __attribute__((weak)); // Prevent undefined reference errors when Bebop code is not linked.
92}
93
94static void init_gazebo_video(void);
95static void gazebo_read_video(void);
96static void read_image(
97 struct image_t *img,
98 gazebo::sensors::CameraSensorPtr cam);
99struct gazebocam_t {
100 gazebo::sensors::CameraSensorPtr cam;
101 gazebo::common::Time last_measurement_time;
102};
104{ { NULL, 0 } };
105
106// Reduce resolution of the simulated MT9F002 sensor (Bebop) to improve runtime
107// performance at the cost of image resolution.
108// Recommended values: 1 (realistic), 2, 4 (fast but slightly blurry)
109#ifndef NPS_MT9F002_SENSOR_RES_DIVIDER
110#define NPS_MT9F002_SENSOR_RES_DIVIDER 1
111#endif
112#endif // NPS_SIMULATE_VIDEO
113
122
124
125#if NPS_SIMULATE_LASER_RANGE_ARRAY
126extern "C" {
127#include "modules/core/abi.h"
128}
129
130static void gazebo_init_range_sensors(void);
131static void gazebo_read_range_sensors(void);
132
133#endif
134
135std::shared_ptr<gazebo::sensors::SonarSensor> sonar = NULL;
136
138struct NpsFdm fdm;
139
140// Pointer to Gazebo data
141static bool gazebo_initialized = false;
142static gazebo::physics::ModelPtr model = NULL;
143
144// Get contact sensor
145static gazebo::sensors::ContactSensorPtr ct;
146
147// Helper functions
148static void init_gazebo(void);
149static void gazebo_read(void);
150static void gazebo_write(double act_commands[], int commands_nb);
151
152// Conversion routines
153inline struct EcefCoor_d to_pprz_ecef(ignition::math::Vector3d ecef_i)
154{
155 struct EcefCoor_d ecef_p;
156 ecef_p.x = ecef_i.X();
157 ecef_p.y = ecef_i.Y();
158 ecef_p.z = ecef_i.Z();
159 return ecef_p;
160}
161
162inline struct NedCoor_d to_pprz_ned(ignition::math::Vector3d global)
163{
164 struct NedCoor_d ned;
165 ned.x = global.Y();
166 ned.y = global.X();
167 ned.z = -global.Z();
168 return ned;
169}
170
171inline struct LlaCoor_d to_pprz_lla(ignition::math::Vector3d lla_i)
172{
173 struct LlaCoor_d lla_p;
174 lla_p.lat = lla_i.X();
175 lla_p.lon = lla_i.Y();
176 lla_p.alt = lla_i.Z();
177 return lla_p;
178}
179
180inline struct DoubleVect3 to_pprz_body(ignition::math::Vector3d body_g)
181{
182 struct DoubleVect3 body_p;
183 body_p.x = body_g.X();
184 body_p.y = -body_g.Y();
185 body_p.z = -body_g.Z();
186 return body_p;
187}
188
189inline struct DoubleRates to_pprz_rates(ignition::math::Vector3d body_g)
190{
191 struct DoubleRates body_p;
192 body_p.p = body_g.X();
193 body_p.q = -body_g.Y();
194 body_p.r = -body_g.Z();
195 return body_p;
196}
197
198inline struct DoubleEulers to_pprz_eulers(ignition::math::Quaterniond quat)
199{
200 struct DoubleEulers eulers;
201 eulers.psi = -quat.Yaw();
202 eulers.theta = -quat.Pitch();
203 eulers.phi = quat.Roll();
204 return eulers;
205}
206
207inline struct DoubleEulers to_global_pprz_eulers(ignition::math::Quaterniond quat)
208{
209 struct DoubleEulers eulers;
210 eulers.psi = -quat.Yaw() - M_PI / 2;
211 eulers.theta = -quat.Pitch();
212 eulers.phi = quat.Roll();
213 return eulers;
214}
215
216inline struct DoubleVect3 to_pprz_ltp(ignition::math::Vector3d xyz)
217{
218 struct DoubleVect3 ltp;
219 ltp.x = xyz.Y();
220 ltp.y = xyz.X();
221 ltp.z = -xyz.Z();
222 return ltp;
223}
224
225// External functions, interface with Paparazzi's NPS as declared in nps_fdm.h
226
231void nps_fdm_init(double dt)
232{
233 fdm.init_dt = dt; // JSBsim specific
234 fdm.curr_dt = dt; // JSBsim specific
235 fdm.nan_count = 0; // JSBsim specific
236
237#ifdef NPS_ACTUATOR_TIME_CONSTANTS
238 // Set up low-pass filter to simulate delayed actuator response
240 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
242 }
243#ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
244 // Set up high-pass filter to simulate spinup torque
246 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
249 }
250#endif
251#endif
252}
253
261 bool launch __attribute__((unused)),
262 double *act_commands,
263 int commands_nb)
264{
265 // Initialize Gazebo if req'd.
266 // Initialization is peformed here instead of in nps_fdm_init because:
267 // - Video devices need to added at this point. Video devices have not been
268 // added yet when nps_fdm_init is called.
269 // - nps_fdm_init runs on a different thread then nps_fdm_run_step, which
270 // causes problems with Gazebo.
271 if (!gazebo_initialized) {
272 init_gazebo();
273 gazebo_read();
274#if NPS_SIMULATE_VIDEO
276#endif
277#if NPS_SIMULATE_LASER_RANGE_ARRAY
279#endif
280 gazebo_initialized = true;
281 }
282
283 // Update the simulation for a single timestep.
284 gazebo::runWorld(model->GetWorld(), 1);
285 gazebo::sensors::run_once();
287 gazebo_read();
288#if NPS_SIMULATE_VIDEO
290#endif
291#if NPS_SIMULATE_LASER_RANGE_ARRAY
293#endif
294
295}
296
297// TODO Atmosphere functions have not been implemented yet.
298// Starting at version 8, Gazebo has its own atmosphere and wind model.
300 double speed __attribute__((unused)),
301 double dir __attribute__((unused)))
302{
303}
304
306 double wind_north __attribute__((unused)),
307 double wind_east __attribute__((unused)),
308 double wind_down __attribute__((unused)))
309{
310}
311
313 double wind_speed __attribute__((unused)),
314 int turbulence_severity __attribute__((unused)))
315{
316}
317
320 double temp __attribute__((unused)),
321 double h __attribute__((unused)))
322{
323}
324
325// Internal functions
337static void init_gazebo(void)
338{
339 string gazebo_home = "/conf/simulator/gazebo/";
340 string pprz_home(getenv("PAPARAZZI_HOME"));
342 cout << "Gazebo directory: " << gazebodir << endl;
343
344 if (getenv("ROS_MASTER_URI")) {
345 // Launch with ROS support
346 cout << "Add ROS plugins... ";
347 gazebo::addPlugin("libgazebo_ros_paths_plugin.so");
348 gazebo::addPlugin("libgazebo_ros_api_plugin.so");
349 cout << "ok" << endl;
350 }
351
352 if (!gazebo::setupServer(0, NULL)) {
353 cout << "Failed to start Gazebo, exiting." << endl;
354 std::exit(-1);
355 }
356
357 cout << "Add Paparazzi paths: " << gazebodir << endl;
358 gazebo::common::SystemPaths::Instance()->AddModelPaths(gazebodir + "models/");
359 sdf::addURIPath("model://", gazebodir + "models/");
360 sdf::addURIPath("world://", gazebodir + "world/");
361
362 cout << "Add TU Delft paths: " << pprz_home + "/sw/ext/tudelft_gazebo_models/" << endl;
363 gazebo::common::SystemPaths::Instance()->AddModelPaths(pprz_home + "/sw/ext/tudelft_gazebo_models/models/");
364 sdf::addURIPath("model://", pprz_home + "/sw/ext/tudelft_gazebo_models/models/");
365 sdf::addURIPath("world://", pprz_home + "/sw/ext/tudelft_gazebo_models/world/");
366
367 // get vehicles
368 string vehicle_uri = "model://" + string(NPS_GAZEBO_AC_NAME) + "/" + string(NPS_GAZEBO_AC_NAME) + ".sdf";
369 string vehicle_filename = sdf::findFile(vehicle_uri, false);
370 if (vehicle_filename.empty()) {
371 cout << "ERROR, could not find vehicle " + vehicle_uri << endl;
372 std::exit(-1);
373 }
374 cout << "Load vehicle: " << vehicle_filename << endl;
375 sdf::SDFPtr vehicle_sdf(new sdf::SDF());
376 sdf::init(vehicle_sdf);
377 if (!sdf::readFile(vehicle_filename, vehicle_sdf)) {
378 cout << "ERROR, could not read vehicle " + vehicle_filename << endl;
379 std::exit(-1);
380 }
381
382 // add or set up sensors before the vehicle gets loaded
383#if NPS_SIMULATE_VIDEO
384 // Cameras
385 sdf::ElementPtr link = vehicle_sdf->Root()->GetFirstElement()->GetElement("link");
386 while (link) {
387 if (link->Get<string>("name") == "front_camera" && link->GetElement("sensor")->Get<string>("name") == "mt9f002") {
389 int w = link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("width")->Get<int>();
390 int h = link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("height")->Get<int>();
391 int env = link->GetElement("sensor")->GetElement("camera")->GetElement("lens")->GetElement("env_texture_size")->Get<int>();
392 link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("width")->Set(w / NPS_MT9F002_SENSOR_RES_DIVIDER);
393 link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("height")->Set(h / NPS_MT9F002_SENSOR_RES_DIVIDER);
394 link->GetElement("sensor")->GetElement("camera")->GetElement("lens")->GetElement("env_texture_size")->Set(env / NPS_MT9F002_SENSOR_RES_DIVIDER);
395 }
397 int fps = Min(MT9F002_TARGET_FPS, link->GetElement("sensor")->GetElement("update_rate")->Get<int>());
398 link->GetElement("sensor")->GetElement("update_rate")->Set(fps);
399 }
400 } else if (link->Get<string>("name") == "bottom_camera" && link->GetElement("sensor")->Get<string>("name") == "mt9v117") {
402 int fps = Min(MT9V117_TARGET_FPS, link->GetElement("sensor")->GetElement("update_rate")->Get<int>());
403 link->GetElement("sensor")->GetElement("update_rate")->Set(fps);
404 }
405 }
406 link = link->GetNextElement("link");
407 }
408#endif
409
410 // laser range array
411#if NPS_SIMULATE_LASER_RANGE_ARRAY
412 vehicle_sdf->Root()->GetFirstElement()->AddElement("include")->GetElement("uri")->Set("model://range_sensors");
413 sdf::ElementPtr range_joint = vehicle_sdf->Root()->GetFirstElement()->AddElement("joint");
414 range_joint->GetAttribute("name")->Set("range_sensor_joint");
415 range_joint->GetAttribute("type")->Set("fixed");
416 range_joint->GetElement("parent")->Set("chassis");
417 range_joint->GetElement("child")->Set("range_sensors::base");
418#endif
419
420 // get world
421 string world_uri = "world://" + string(NPS_GAZEBO_WORLD);
422 string world_filename = sdf::findFile(world_uri, false);
423 if (world_filename.empty()) {
424 cout << "ERROR, could not find world " + world_uri << endl;
425 std::exit(-1);
426 }
427 cout << "Load world: " << world_filename << endl;
428 sdf::SDFPtr world_sdf(new sdf::SDF());
429 sdf::init(world_sdf);
430 if (!sdf::readFile(world_filename, world_sdf)) {
431 cout << "ERROR, could not read world " + world_filename << endl;
432 std::exit(-1);
433 }
434
435 // add vehicles
436 world_sdf->Root()->GetFirstElement()->InsertElement(vehicle_sdf->Root()->GetFirstElement());
437
438 world_sdf->Write(pprz_home + "/var/gazebo.world");
439
440 gazebo::physics::WorldPtr world = gazebo::loadWorld(pprz_home + "/var/gazebo.world");
441 if (!world) {
442 cout << "Failed to open world, exiting." << endl;
443 std::exit(-1);
444 }
445
446 cout << "Get pointer to aircraft: " << NPS_GAZEBO_AC_NAME << endl;
447 model = world->ModelByName(NPS_GAZEBO_AC_NAME);
448 if (!model) {
449 cout << "Failed to find '" << NPS_GAZEBO_AC_NAME << "', exiting."
450 << endl;
451 std::exit(-1);
452 }
453
454 // Initialize sensors
455 gazebo::sensors::run_once(true);
456 gazebo::sensors::run_threads();
457 gazebo::runWorld(world, 1);
458 cout << "Sensors initialized..." << endl;
459
460 // Find sensors
461 // Contact sensor
462 gazebo::sensors::SensorManager *mgr = gazebo::sensors::SensorManager::Instance();
463 ct = static_pointer_cast < gazebo::sensors::ContactSensor > (mgr->GetSensor("contactsensor"));
464 ct->SetActive(true);
465 // Sonar
467 if(sonar) {
468 cout << "Found sonar" << endl;
469 }
470
471 gazebo::physics::LinkPtr sonar_link = model->GetLink("sonar");
472 if (sonar_link) {
473 // Get a pointer to the sensor using its full name
474 if (sonar_link->GetSensorCount() != 1) {
475 cout << "ERROR: Link '" << sonar_link->GetName()
476 << "' should only contain 1 sensor!" << endl;
477 } else {
478 string name = sonar_link->GetSensorName(0);
480 if (!sonar) {
481 cout << "ERROR: Could not get pointer to '" << name << "'!" << endl;
482 } else {
483 // Activate sensor
484 sonar->SetActive(true);
485 }
486 }
487 }
488
489 // Overwrite motor directions as defined by motor_mixing
490#ifdef MOTOR_MIXING_YAW_COEF
491 const double yaw_coef[] = MOTOR_MIXING_YAW_COEF;
492
493 for (uint8_t i = 0; i < NPS_COMMANDS_NB; i++) {
496 }
497#endif
498 cout << "Gazebo initialized successfully!" << endl;
499}
500
509static void gazebo_read(void)
510{
511 static ignition::math::Vector3d vel_prev;
512 static double time_prev;
513
514 gazebo::physics::WorldPtr world = model->GetWorld();
515 ignition::math::Pose3d pose = model->WorldPose(); // In LOCAL xyz frame
516 ignition::math::Vector3d vel = model->WorldLinearVel();
517 ignition::math::Vector3d ang_vel = model->WorldAngularVel();
518 gazebo::common::SphericalCoordinatesPtr sphere = world->SphericalCoords();
519 ignition::math::Quaterniond local_to_global_quat(0, 0, -sphere->HeadingOffset().Radian());
520
521 /* Fill FDM struct */
522 fdm.time = world->SimTime().Double();
523
524 // Find world acceleration by differentiating velocity
525 // model->GetWorldLinearAccel() does not seem to take the velocity_decay into account!
526 // Derivation of the velocity also follows the IMU implementation of Gazebo itself:
527 // https://bitbucket.org/osrf/gazebo/src/e26144434b932b4b6a760ddaa19cfcf9f1734748/gazebo/sensors/ImuSensor.cc?at=default&fileviewer=file-view-default#ImuSensor.cc-370
528 double dt = fdm.time - time_prev;
529 ignition::math::Vector3d accel = (vel - vel_prev) / dt;
530 vel_prev = vel;
532
533 // init_dt: unused
534 // curr_dt: unused
535 // on_ground: unused
536 // nan_count: unused
537
538 // Transform ltp definition to double for accuracy
539 struct LtpDef_d ltpdef_d;
546 for (int i = 0; i < 3 * 3; i++) {
547 ltpdef_d.ltp_of_ecef.m[i] = state.ned_origin_f.ltp_of_ecef.m[i];
548 }
550
551 /* position */
552 fdm.ltpprz_pos = to_pprz_ned(sphere->PositionTransform(pose.Pos(), gazebo::common::SphericalCoordinates::LOCAL,
553 gazebo::common::SphericalCoordinates::GLOBAL)); // Allows Gazebo worlds rotated wrt north.
557
558 /* debug positions */
559 fdm.lla_pos_pprz = fdm.lla_pos; // Don't really care...
563
564 if(sonar) {
565 double agl = sonar->Range();
566 if (agl > sonar->RangeMax()) agl = -1.0;
567 fdm.agl = agl;
568 } else {
569 fdm.agl = pose.Pos().Z(); // TODO Measure with sensor
570 }
571
572 /* velocity */
573 fdm.ltp_ecef_vel = to_pprz_ned(sphere->VelocityTransform(vel, gazebo::common::SphericalCoordinates::LOCAL,
574 gazebo::common::SphericalCoordinates::GLOBAL));
576 fdm.body_ecef_vel = to_pprz_body(pose.Rot().RotateVectorReverse(vel)); // Note: unused
578
579 /* acceleration */
580 fdm.ltp_ecef_accel = to_pprz_ned(sphere->VelocityTransform(accel, gazebo::common::SphericalCoordinates::LOCAL,
581 gazebo::common::SphericalCoordinates::GLOBAL)); // Note: unused
583 fdm.body_ecef_accel = to_pprz_body(pose.Rot().RotateVectorReverse(accel));
584 fdm.body_inertial_accel = fdm.body_ecef_accel; // Approximate, unused.
585 fdm.body_accel = to_pprz_body(pose.Rot().RotateVectorReverse(accel - world->Gravity()));
587
588 /* attitude */
589 // ecef_to_body_quat: unused
594
595 /* angular velocity */
596 fdm.body_ecef_rotvel = to_pprz_rates(pose.Rot().RotateVectorReverse(ang_vel));
598
599 /* angular acceleration */
600 // body_ecef_rotaccel: unused
601 // body_inertial_rotaccel: unused
602 /* misc */
603 fdm.ltp_g = to_pprz_ltp(sphere->VelocityTransform(-1 * world->Gravity(), gazebo::common::SphericalCoordinates::LOCAL,
604 gazebo::common::SphericalCoordinates::GLOBAL)); // unused
605 fdm.ltp_h = to_pprz_ltp(sphere->VelocityTransform(world->MagneticField(), gazebo::common::SphericalCoordinates::LOCAL,
606 gazebo::common::SphericalCoordinates::GLOBAL));
607
608 /* atmosphere */
609#if GAZEBO_MAJOR_VERSION >= 8 && 0 // TODO implement
610
611#else
612 // Gazebo versions < 8 do not have atmosphere or wind support
613 // Use placeholder values. Valid for low altitude, low speed flights.
614 fdm.wind = (struct DoubleVect3) {0, 0, 0};
615 fdm.pressure_sl = 101325; // Pa
616
617 fdm.airspeed = 0;
620 fdm.temperature = 20.0; // C
621 fdm.aoa = 0; // rad
622 fdm.sideslip = 0; // rad
623#endif
624 /* flight controls: unused */
625 fdm.elevator = 0;
626 fdm.flap = 0;
627 fdm.left_aileron = 0;
628 fdm.right_aileron = 0;
629 fdm.rudder = 0;
630 /* engine: unused */
631 fdm.num_engines = 0;
632}
633
651static void gazebo_write(double act_commands[], int commands_nb)
652{
653 for (int i = 0; i < commands_nb; ++i) {
654 // Thrust setpoint
655 double sp = autopilot.motors_on ? act_commands[i] : 0.0; // Normalized thrust setpoint
656
657 // Actuator dynamics, forces and torques
658#ifdef NPS_ACTUATOR_TIME_CONSTANTS
659 // Delayed response from actuator
660 double u = update_first_order_low_pass(&gazebo_actuators.lowpass[i], sp);// Normalized actual thrust
661#else
662 double u = sp;
663#endif
664 double thrust = gazebo_actuators.thrusts[i] * u;
665 double torque = gazebo_actuators.torques[i] * u;
666
667#ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
668 // Spinup torque
670 double spinup_torque = gazebo_actuators.max_ang_momentum[i] / (2.0 * sqrt(u > 0.05 ? u : 0.05)) * udot;
672#endif
673
674 // Apply force and torque to gazebo model
675 gazebo::physics::LinkPtr link = model->GetLink(gazebo_actuators.names[i]);
676 link->AddRelativeForce(ignition::math::Vector3d(0, 0, thrust));
677 link->AddRelativeTorque(ignition::math::Vector3d(0, 0, torque));
678 }
679}
680
681#if NPS_SIMULATE_VIDEO
696static void init_gazebo_video(void)
697{
698 gazebo::sensors::SensorManager *mgr = gazebo::sensors::SensorManager::Instance();
699
700 cout << "Initializing cameras..." << endl;
701 // Loop over cameras registered in video_thread_nps
702 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
703 // Find link in gazebo model
704 cout << "Setting up '" << cameras[i]->dev_name << "'... ";
705 gazebo::physics::LinkPtr link = model->GetLink(cameras[i]->dev_name);
706 if (!link) {
707 cout << "ERROR: Link '" << cameras[i]->dev_name << "' not found!"
708 << endl;
709 ;
710 continue;
711 }
712 // Get a pointer to the sensor using its full name
713 if (link->GetSensorCount() != 1) {
714 cout << "ERROR: Link '" << link->GetName()
715 << "' should only contain 1 sensor!" << endl;
716 continue;
717 }
718 string name = link->GetSensorName(0);
719 gazebo::sensors::CameraSensorPtr cam = static_pointer_cast
720 < gazebo::sensors::CameraSensor > (mgr->GetSensor(name));
721 if (!cam) {
722 cout << "ERROR: Could not get pointer to '" << name << "'!" << endl;
723 continue;
724 }
725 // Activate sensor
726 cam->SetActive(true);
727
728 // Add to list of cameras
729 gazebo_cams[i].cam = cam;
730 gazebo_cams[i].last_measurement_time = cam->LastMeasurementTime();
731
732 // set default camera settings
733 // Copy video_config settings from Gazebo's camera
734 cameras[i]->output_size.w = cam->ImageWidth();
735 cameras[i]->output_size.h = cam->ImageHeight();
736 cameras[i]->sensor_size.w = cam->ImageWidth();
737 cameras[i]->sensor_size.h = cam->ImageHeight();
738 cameras[i]->crop.w = cam->ImageWidth();
739 cameras[i]->crop.h = cam->ImageHeight();
740 cameras[i]->fps = 0;
745
746 if (cam->Name() == "mt9f002") {
747 // See boards/bebop/mt9f002.c
757 .focal_y = MT9F002_FOCAL_Y,
758 .center_x = MT9F002_CENTER_X,
759 .center_y = MT9F002_CENTER_Y,
760 .Dhane_k = MT9F002_DHANE_K
761 };
762 } else if (cam->Name() == "mt9v117") {
763 // See boards/bebop/mt9v117.h
767 .focal_y = MT9V117_FOCAL_Y,
768 .center_x = MT9V117_CENTER_X,
769 .center_y = MT9V117_CENTER_Y,
770 .Dhane_k = MT9V117_DHANE_K
771 };
772 }
773 cout << "ok" << endl;
774 }
775}
776
785static void gazebo_read_video(void)
786{
787 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS; ++i) {
788 gazebo::sensors::CameraSensorPtr &cam = gazebo_cams[i].cam;
789 // Skip unregistered or unfound cameras
790 if (cam == NULL) { continue; }
791 // Skip if not updated
792 // Also skip when LastMeasurementTime() is zero (workaround)
793 if ((cam->LastMeasurementTime() - gazebo_cams[i].last_measurement_time).Float() < 0.005
794 || cam->LastMeasurementTime() == 0) { continue; }
795 // Grab image, convert and send to video thread
796 struct image_t img;
797 read_image(&img, cam);
798
799#if NPS_DEBUG_VIDEO
800 cv::Mat RGB_cam(cam->ImageHeight(), cam->ImageWidth(), CV_8UC3, (uint8_t *)cam->ImageData());
801 cv::cvtColor(RGB_cam, RGB_cam, cv::COLOR_RGB2BGR);
802 cv::namedWindow(cameras[i]->dev_name, cv::WINDOW_AUTOSIZE); // Create a window for display.
803 cv::imshow(cameras[i]->dev_name, RGB_cam);
804 cv::waitKey(1);
805#endif
806
808 // Free image buffer after use.
809 image_free(&img);
810 // Keep track of last update time.
811 gazebo_cams[i].last_measurement_time = cam->LastMeasurementTime();
812 }
813}
814
825static void read_image(struct image_t *img, gazebo::sensors::CameraSensorPtr cam)
826{
827 bool is_mt9f002 = false;
828 if (cam->Name() == "mt9f002") {
830 is_mt9f002 = true;
831 } else {
832 image_create(img, cam->ImageWidth(), cam->ImageHeight(), IMAGE_YUV422);
833 }
834
835 // Convert Gazebo's *RGB888* image to Paparazzi's YUV422
836 const uint8_t *data_rgb = cam->ImageData();
837 uint8_t *data_yuv = (uint8_t *)(img->buf);
838 for (int x_yuv = 0; x_yuv < img->w; ++x_yuv) {
839 for (int y_yuv = 0; y_yuv < img->h; ++y_yuv) {
840 int x_rgb = x_yuv;
841 int y_rgb = y_yuv;
842 if (is_mt9f002) {
843 // Change sampling points for zoomed and/or cropped image.
844 // Use nearest-neighbour sampling for now.
846 / CFG_MT9F002_PIXEL_ARRAY_WIDTH * cam->ImageWidth();
848 / CFG_MT9F002_PIXEL_ARRAY_HEIGHT * cam->ImageHeight();
849 }
850 int idx_rgb = 3 * (cam->ImageWidth() * y_rgb + x_rgb);
851 int idx_yuv = 2 * (img->w * y_yuv + x_yuv);
852 int idx_px = img->w * y_yuv + x_yuv;
853 if (idx_px % 2 == 0) { // Pick U or V
854 data_yuv[idx_yuv] = - 0.148 * data_rgb[idx_rgb]
855 - 0.291 * data_rgb[idx_rgb + 1]
856 + 0.439 * data_rgb[idx_rgb + 2] + 128; // U
857 } else {
858 data_yuv[idx_yuv] = 0.439 * data_rgb[idx_rgb]
859 - 0.368 * data_rgb[idx_rgb + 1]
860 - 0.071 * data_rgb[idx_rgb + 2] + 128; // V
861 }
862 data_yuv[idx_yuv + 1] = 0.257 * data_rgb[idx_rgb]
863 + 0.504 * data_rgb[idx_rgb + 1]
864 + 0.098 * data_rgb[idx_rgb + 2] + 16; // Y
865 }
866 }
867 // Fill miscellaneous fields
868 gazebo::common::Time ts = cam->LastMeasurementTime();
869 img->ts.tv_sec = ts.sec;
870 img->ts.tv_usec = ts.nsec / 1000.0;
871 img->pprz_ts = ts.Double() * 1e6;
872 img->buf_idx = 0; // unused
873}
874#endif
875
876#if NPS_SIMULATE_LASER_RANGE_ARRAY
877/*
878 * Simulate range sensors:
879 *
880 * In the airframe file, set NPS_SIMULATE_LASER_RANGE_ARRAY if you want to make use of the integrated Ray sensors.
881 * These are defined in their own model which is added to the ardrone.sdf (called range_sensors). Here you can add
882 * single ray point sensors with a specified position and orientation. It is also possible add noise.
883 *
884 * Within the airframe file (see e.g ardrone2_rangesensors_gazebo.xml), the amount of sensors
885 * (LASER_RANGE_ARRAY_NUM_SENSORS) and their orientations
886 * (LASER_RANGE_ARRAY_ORIENTATIONS={phi_1,theta_1,psi_1...phi_n,theta_n,psi_n} n = number of sensors) need to be
887 * specified as well. This is to keep it generic since this need to be done on the real platform with an external
888 * ray sensor. The function will compare the orientations from the ray sensors of gazebo, with the ones specified
889 * in the airframe, and will fill up an array to send and abi message to be used by other modules.
890 *
891 * NPS_GAZEBO_RANGE_SEND_AGL defines if the sensor that is defined as down should be used to send an AGL message.
892 * to send an OBSTACLE_DETECTION message.
893 *
894 * Functions:
895 * gazebo_init_range_sensors() -> Finds and initializes all ray sensors in gazebo
896 * gazebo_read_range_sensors() -> Reads and evaluates the ray sensors values, and sending it to other pprz modules
897 */
898
899struct gazebo_ray_t {
900 gazebo::sensors::RaySensorPtr sensor;
901 gazebo::common::Time last_measurement_time;
902};
903
906static uint8_t ray_sensor_agl_index = 255;
907
908#define VL53L0_MAX_VAL 8.191f
909
910static void gazebo_init_range_sensors(void)
911{
912 gazebo::sensors::SensorManager *mgr = gazebo::sensors::SensorManager::Instance();
913 gazebo::sensors::Sensor_V sensors = mgr->GetSensors(); // list of all sensors
914
915 uint16_t sensor_count = model->GetSensorCount();
916
917 gazebo::sensors::RaySensorPtr ray_sensor;
919
920 // Loop though all sensors and only select ray sensors, which are saved within a struct
921 for (uint16_t i = 0; i < sensor_count; i++) {
923 break;
924 }
925
926 if (sensors.at(i)->Type() == "ray") {
928
929 if (!ray_sensor) {
930 cout << "ERROR: Could not get pointer to raysensor " << i << "!" << endl;
931 continue;
932 }
933
934 // Read out the pose from per ray sensors in gazebo relative to body
935 struct DoubleEulers pose_sensor = to_pprz_eulers(ray_sensor->Pose().Rot());
936
937 struct DoubleQuat q_ray, q_def;
939
940 /* Check the orientations of the ray sensors found in gazebo, if they are similar (within 5 deg) to the orientations
941 * given in the airframe file in LASER_RANGE_ARRAY_RANGE_ORIENTATION
942 */
943 for (int j = 0; j < LASER_RANGE_ARRAY_NUM_SENSORS; j++) {
944 struct DoubleEulers def = {0, range_orientation[j * 2], range_orientation[j * 2 + 1]};
946 // get angle between required angle and ray angle
947 double angle = acos(QUAT_DOT_PRODUCT(q_ray, q_def));
948
949 if (fabs(angle) < RadOfDeg(5)) {
951 rays[j].sensor = ray_sensor;
952 rays[j].sensor->SetActive(true);
953
954#if LASER_RANGE_ARRAY_SEND_AGL
955 // find the sensor pointing down
956 if (fabs(range_orientation[j * 2] + M_PI_2) < RadOfDeg(5)) {
958 }
959#endif
960 break;
961 }
962 }
963 }
964 }
965
967 cout << "ERROR: you have defined " << LASER_RANGE_ARRAY_NUM_SENSORS << " sensors in your airframe file, but only "
968 << ray_sensor_count_selected << " sensors have been found in the gazebo simulator, "
969 "with the same orientation as in the airframe file " << endl;
970 exit(0);
971 }
972 cout << "Initialized laser range array" << endl;
973}
974
975static void gazebo_read_range_sensors(void)
976{
977 static float range;
978
979 // Loop through all ray sensors found in gazebo
980 for (int i = 0; i < LASER_RANGE_ARRAY_NUM_SENSORS; i++) {
981 if ((rays[i].sensor->LastMeasurementTime() - rays[i].last_measurement_time).Float() < 0.005
982 || rays[i].sensor->LastMeasurementTime() == 0) { continue; }
983
984 if (rays[i].sensor->Range(0) < 1e-5 || rays[i].sensor->Range(0) > VL53L0_MAX_VAL) {
985 range = VL53L0_MAX_VAL;
986 } else {
987 range = rays[i].sensor->Range(0);
988 }
990 range_orientation[i * 2 + 1]);
991
992 if (i == ray_sensor_agl_index) {
994 float agl = rays[i].sensor->Range(0);
995 // Down range sensor as agl
996 if (agl > 1e-5 && !isinf(agl)) {
998 }
999 }
1000 rays[i].last_measurement_time = rays[i].sensor->LastMeasurementTime();
1001 }
1002}
1003#endif // NPS_SIMULATE_LASER_RANGE_ARRAY
1004
1005#pragma GCC diagnostic pop // Ignore -Wdeprecated-declarations
static void h(const real32_T x[7], const real32_T q[4], real32_T y[6])
Main include for ABI (AirBorneInterface).
#define OBS_DETECTION_RANGE_ARRAY_NPS_ID
#define AGL_RAY_SENSOR_GAZEBO_ID
struct pprz_autopilot autopilot
Global autopilot structure.
Definition autopilot.c:49
Core autopilot interface common to all firmwares.
bool motors_on
motor status
Definition autopilot.h:68
#define sensors(...)
uint32_t get_sys_time_usec(void)
Get the time in microseconds since startup.
void cv_run_device(struct video_config_t *device, struct image_t *img)
Definition cv.c:178
Computer vision framework for onboard processing.
#define Min(x, y)
Definition esc_dshot.c:109
double psi
in radians
double p
in rad/s^2
double theta
in radians
double phi
in radians
void double_quat_of_eulers(struct DoubleQuat *q, struct DoubleEulers *e)
Roation quaternion.
angular rates
float m[3 *3]
#define QUAT_DOT_PRODUCT(_qa, _qb)
double x
in meters
struct EcefCoor_d ecef
origin of local frame in ECEF
double z
in meters
double lat
in radians
double x
in meters
double gc_of_gd_lat_d(double gd_lat, double hmsl)
void ecef_of_ned_vect_d(struct EcefCoor_d *ecef, struct LtpDef_d *def, struct NedCoor_d *ned)
void lla_of_ecef_d(struct LlaCoor_d *lla, struct EcefCoor_d *ecef)
void ecef_of_ned_point_d(struct EcefCoor_d *ecef, struct LtpDef_d *def, struct NedCoor_d *ned)
vector in EarthCenteredEarthFixed coordinates
vector in Latitude, Longitude and Altitude
definition of the local (flat earth) coordinate system
vector in North East Down coordinates Units: meters
static float pprz_isa_pressure_of_height(float height, float ref_p)
Get pressure in Pa from height (using simplified equation).
Definition pprz_isa.h:129
struct State state
Definition state.c:36
struct LtpDef_f ned_origin_f
Definition of the local (flat earth) coordinate system.
Definition state.h:248
Simple high pass filter with double precision.
static void init_first_order_high_pass(struct FirstOrderHighPass *filter, float tau, float sample_time, float value)
Init first order high pass filter.
static float update_first_order_high_pass(struct FirstOrderHighPass *filter, float value)
Update first order high pass filter state with a new value.
First order high pass filter structure.
void image_free(struct image_t *img)
Free the image.
Definition image.c:75
void image_create(struct image_t *img, uint16_t width, uint16_t height, enum image_type type)
Create a new image.
Definition image.c:43
Image helper functions like resizing, color filter, converters...
uint16_t h
height of the cropped area
Definition image.h:96
struct timeval ts
The timestamp of creation.
Definition image.h:48
uint16_t h
The height.
Definition image.h:88
uint16_t w
Width of the cropped area.
Definition image.h:95
@ IMAGE_YUV422
UYVY format (uint16 per pixel)
Definition image.h:36
uint16_t w
The width.
Definition image.h:87
#define VL53L0_MAX_VAL
Simple first order low pass filter with bilinear transform.
static float update_first_order_low_pass(struct FirstOrderLowPass *filter, float value)
Update first order low pass filter state with a new value.
static void init_first_order_low_pass(struct FirstOrderLowPass *filter, float tau, float sample_time, float value)
Init first order low pass filter.
First order low pass filter structure.
uint16_t foo
Definition main_demo5.c:58
static const int32_t yaw_coef[MOTOR_MIXING_NB_MOTOR]
Common Motor Mixing configuration types.
#define MOTOR_MIXING_YAW_COEF
struct mt9f002_t mt9f002
Definition mt9f002.c:123
Initialization and configuration of the MT9F002 CMOS Chip.
#define MT9F002_CENTER_X
Definition mt9f002.h:116
#define MT9F002_DHANE_K
Definition mt9f002.h:122
uint16_t offset_x
Offset from left in pixels.
Definition mt9f002.h:165
#define CFG_MT9F002_PIXEL_ARRAY_WIDTH
Definition mt9f002.h:39
#define MT9F002_CENTER_Y
Definition mt9f002.h:119
#define MT9F002_FOCAL_Y
Definition mt9f002.h:113
uint16_t offset_y
Offset from top in pixels.
Definition mt9f002.h:166
#define MT9F002_OUTPUT_WIDTH
Definition mt9f002.h:82
#define CFG_MT9F002_PIXEL_ARRAY_HEIGHT
Definition mt9f002.h:38
uint16_t sensor_width
Definition mt9f002.h:168
uint16_t sensor_height
Definition mt9f002.h:169
#define MT9F002_TARGET_FPS
Definition mt9f002.h:105
#define MT9F002_FOCAL_X
Definition mt9f002.h:110
#define MT9F002_OUTPUT_HEIGHT
Definition mt9f002.h:85
Initialization and configuration of the MT9V117 CMOS Chip.
#define MT9V117_CENTER_Y
Definition mt9v117.h:50
#define MT9V117_TARGET_FPS
Definition mt9v117.h:36
#define MT9V117_DHANE_K
Definition mt9v117.h:53
#define MT9V117_FOCAL_X
Definition mt9v117.h:41
#define MT9V117_CENTER_X
Definition mt9v117.h:47
#define MT9V117_FOCAL_Y
Definition mt9v117.h:44
#define NPS_COMMANDS_NB
Number of commands sent to the FDM of NPS.
float rudder
Definition nps_fdm.h:123
double time
Definition nps_fdm.h:46
double aoa
angle of attack in rad
Definition nps_fdm.h:115
struct NedCoor_d ltpprz_ecef_vel
velocity in ltppprz frame, wrt ECEF frame
Definition nps_fdm.h:79
struct DoubleVect3 ltp_g
Definition nps_fdm.h:104
double pressure_sl
pressure at sea level in Pascal
Definition nps_fdm.h:114
float elevator
Definition nps_fdm.h:119
struct DoubleQuat ltpprz_to_body_quat
Definition nps_fdm.h:93
double init_dt
Definition nps_fdm.h:47
double hmsl
Definition nps_fdm.h:56
struct EcefCoor_d ecef_pos
Definition nps_fdm.h:53
struct NedCoor_d ltpprz_pos
Definition nps_fdm.h:54
struct LlaCoor_d lla_pos_pprz
Definition nps_fdm.h:58
struct DoubleVect3 body_inertial_accel
acceleration in body frame, wrt ECI inertial frame
Definition nps_fdm.h:84
struct DoubleRates body_inertial_rotvel
Definition nps_fdm.h:101
double airspeed
equivalent airspeed in m/s
Definition nps_fdm.h:109
struct NedCoor_d ltp_ecef_vel
velocity in LTP frame, wrt ECEF frame
Definition nps_fdm.h:74
struct NedCoor_d ltpprz_ecef_accel
accel in ltppprz frame, wrt ECEF frame
Definition nps_fdm.h:81
struct DoubleVect3 body_ecef_vel
velocity in body frame, wrt ECEF frame
Definition nps_fdm.h:69
double agl
Definition nps_fdm.h:61
struct DoubleVect3 ltp_h
Definition nps_fdm.h:105
double dynamic_pressure
dynamic pressure in Pascal
Definition nps_fdm.h:112
struct DoubleEulers ltp_to_body_eulers
Definition nps_fdm.h:92
struct EcefCoor_d ecef_ecef_accel
acceleration in ECEF frame, wrt ECEF frame
Definition nps_fdm.h:66
double pressure
current (static) atmospheric pressure in Pascal
Definition nps_fdm.h:110
double curr_dt
Definition nps_fdm.h:48
struct DoubleQuat ltp_to_body_quat
Definition nps_fdm.h:91
struct DoubleRates body_ecef_rotvel
Definition nps_fdm.h:97
struct EcefCoor_d ecef_ecef_vel
velocity in ECEF frame, wrt ECEF frame
Definition nps_fdm.h:64
struct DoubleVect3 body_ecef_accel
acceleration in body frame, wrt ECEF frame
Definition nps_fdm.h:71
double temperature
current temperature in degrees Celcius
Definition nps_fdm.h:113
float left_aileron
Definition nps_fdm.h:121
float right_aileron
Definition nps_fdm.h:122
struct DoubleEulers ltpprz_to_body_eulers
Definition nps_fdm.h:94
struct LlaCoor_d lla_pos
Definition nps_fdm.h:55
double sideslip
sideslip angle in rad
Definition nps_fdm.h:116
struct DoubleVect3 body_accel
acceleration in body frame as measured by an accelerometer (incl.
Definition nps_fdm.h:87
struct LlaCoor_d lla_pos_geoc
Definition nps_fdm.h:60
struct DoubleVect3 wind
velocity in m/s in NED
Definition nps_fdm.h:107
int nan_count
Definition nps_fdm.h:50
float flap
Definition nps_fdm.h:120
uint32_t num_engines
Definition nps_fdm.h:126
struct LlaCoor_d lla_pos_geod
Definition nps_fdm.h:59
struct NedCoor_d ltp_ecef_accel
acceleration in LTP frame, wrt ECEF frame
Definition nps_fdm.h:76
void nps_fdm_set_wind_ned(double wind_north, double wind_east, double wind_down)
struct FirstOrderLowPass lowpass[NPS_COMMANDS_NB]
void nps_fdm_init(double dt)
Initialize actuator dynamics, set unused fields in fdm.
struct gazebo_actuators_t gazebo_actuators
void nps_fdm_run_step(bool launch, double *act_commands, int commands_nb)
Update the simulation state.
struct DoubleEulers to_global_pprz_eulers(ignition::math::Quaterniond quat)
static void gazebo_write(double act_commands[], int commands_nb)
Write actuator commands to Gazebo.
struct DoubleRates to_pprz_rates(ignition::math::Vector3d body_g)
string names[NPS_COMMANDS_NB]
#define NPS_GAZEBO_WORLD
double max_ang_momentum[NPS_COMMANDS_NB]
void nps_fdm_set_temperature(double temp, double h)
Set temperature in degrees Celcius at given height h above MSL.
double torques[NPS_COMMANDS_NB]
struct NedCoor_d to_pprz_ned(ignition::math::Vector3d global)
struct LlaCoor_d to_pprz_lla(ignition::math::Vector3d lla_i)
double thrusts[NPS_COMMANDS_NB]
static void gazebo_read(void)
Read Gazebo's simulation state and store the results in the fdm struct used by NPS.
static gazebo::physics::ModelPtr model
static gazebo::sensors::ContactSensorPtr ct
std::shared_ptr< gazebo::sensors::SonarSensor > sonar
void nps_fdm_set_turbulence(double wind_speed, int turbulence_severity)
struct DoubleVect3 to_pprz_ltp(ignition::math::Vector3d xyz)
static void init_gazebo(void)
Set up a Gazebo server.
struct EcefCoor_d to_pprz_ecef(ignition::math::Vector3d ecef_i)
#define NPS_GAZEBO_AC_NAME
static bool gazebo_initialized
void nps_fdm_set_wind(double speed, double dir)
struct NpsFdm fdm
Holds all necessary NPS FDM state information.
struct FirstOrderHighPass highpass[NPS_COMMANDS_NB]
struct DoubleVect3 to_pprz_body(ignition::math::Vector3d body_g)
struct DoubleEulers to_pprz_eulers(ignition::math::Quaterniond quat)
Paparazzi double precision floating point algebra.
Paparazzi double-precision floating point math for geodetic calculations.
Paparazzi floating point math for geodetic calculations.
float alt
in meters (normally above WGS84 reference ellipsoid)
float z
in meters
float lon
in radians
float x
in meters
float lat
in radians
float hmsl
Height above mean sea level in meters.
float y
in meters
struct FloatRMat ltp_of_ecef
rotation from ECEF to local frame
struct EcefCoor_f ecef
origin of local frame in ECEF
struct LlaCoor_f lla
origin of local frame in LLA
Paparazzi atmospheric pressure conversion utilities.
static const float dir[]
API to get/set the generic vehicle states.
Architecture independent timing functions.
float focal_y
focal length in the y-direction in pixels
float center_x
center image coordinate in the x-direction
float focal_x
focal length in the x-direction in pixels
struct camera_intrinsics_t camera_intrinsics
Intrinsics of the camera; camera calibration parameters and distortion parameter(s)
int fps
Target FPS.
float center_y
center image coordinate in the y-direction
struct crop_t crop
Cropped area definition.
struct img_size_t output_size
Output image size.
char * dev_name
path to device
struct img_size_t sensor_size
Original sensor size.
#define VIDEO_THREAD_MAX_CAMERAS
static struct video_config_t * cameras[VIDEO_THREAD_MAX_CAMERAS]
This header gives NPS access to the list of added cameras.
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.