Paparazzi UAS  v5.14.0_stable-0-g3f680d1
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
nps_fdm_gazebo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017 Tom van Dijk, Kirk Scheeper
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 
45 extern "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 "subsystems/abi.h"
53 
54 #include "math/pprz_isa.h"
58 
60 }
61 
62 #if defined(NPS_DEBUG_VIDEO)
63 // Opencv tools
64 #include <opencv2/core/core.hpp>
65 #include <opencv2/highgui/highgui.hpp>
66 #include <opencv2/imgproc/imgproc.hpp>
67 #endif
68 
69 using namespace std;
70 
71 #ifndef NPS_GAZEBO_WORLD
72 #define NPS_GAZEBO_WORLD "empty.world"
73 #endif
74 #ifndef NPS_GAZEBO_AC_NAME
75 #define NPS_GAZEBO_AC_NAME "ardrone"
76 #endif
77 
78 // Add video handling functions if req'd.
79 #if NPS_SIMULATE_VIDEO
80 extern "C" {
84 #include "mcu_periph/sys_time.h"
85 }
86 
87 static void init_gazebo_video(void);
88 static void gazebo_read_video(void);
89 static void read_image(
90  struct image_t *img,
91  gazebo::sensors::CameraSensorPtr cam);
92 struct gazebocam_t {
93  gazebo::sensors::CameraSensorPtr cam;
94  gazebo::common::Time last_measurement_time;
95 };
96 static struct gazebocam_t gazebo_cams[VIDEO_THREAD_MAX_CAMERAS] =
97 { { NULL, 0 } };
98 #if NPS_SIMULATE_MT9F002
99 #include "boards/bebop/mt9f002.h"
100 #endif
101 #endif // NPS_SIMULATE_VIDEO
102 
104  string names[NPS_COMMANDS_NB];
105  double thrusts[NPS_COMMANDS_NB];
106  double torques[NPS_COMMANDS_NB];
109  double max_ang_momentum[NPS_COMMANDS_NB];
110 };
111 
112 struct gazebo_actuators_t gazebo_actuators = { NPS_ACTUATOR_NAMES, NPS_ACTUATOR_THRUSTS, NPS_ACTUATOR_TORQUES, { }, { }, { }};
113 
114 #if NPS_SIMULATE_LASER_RANGE_ARRAY
115 extern "C" {
116 #include "subsystems/abi.h"
117 }
118 
119 static void gazebo_init_range_sensors(void);
120 static void gazebo_read_range_sensors(void);
121 
122 #endif
123 
125 struct NpsFdm fdm;
126 
127 // Pointer to Gazebo data
128 static bool gazebo_initialized = false;
129 static gazebo::physics::ModelPtr model = NULL;
130 
131 // Get contact sensor
132 static gazebo::sensors::ContactSensorPtr ct;
133 
134 // Helper functions
135 static void init_gazebo(void);
136 static void gazebo_read(void);
137 static void gazebo_write(double act_commands[], int commands_nb);
138 
139 // Conversion routines
140 inline struct EcefCoor_d to_pprz_ecef(ignition::math::Vector3d ecef_i)
141 {
142  struct EcefCoor_d ecef_p;
143  ecef_p.x = ecef_i.X();
144  ecef_p.y = ecef_i.Y();
145  ecef_p.z = ecef_i.Z();
146  return ecef_p;
147 }
148 
149 inline struct NedCoor_d to_pprz_ned(ignition::math::Vector3d global)
150 {
151  struct NedCoor_d ned;
152  ned.x = global.Y();
153  ned.y = global.X();
154  ned.z = -global.Z();
155  return ned;
156 }
157 
158 inline struct LlaCoor_d to_pprz_lla(ignition::math::Vector3d lla_i)
159 {
160  struct LlaCoor_d lla_p;
161  lla_p.lat = lla_i.X();
162  lla_p.lon = lla_i.Y();
163  lla_p.alt = lla_i.Z();
164  return lla_p;
165 }
166 
167 inline struct DoubleVect3 to_pprz_body(ignition::math::Vector3d body_g)
168 {
169  struct DoubleVect3 body_p;
170  body_p.x = body_g.X();
171  body_p.y = -body_g.Y();
172  body_p.z = -body_g.Z();
173  return body_p;
174 }
175 
176 inline struct DoubleRates to_pprz_rates(ignition::math::Vector3d body_g)
177 {
178  struct DoubleRates body_p;
179  body_p.p = body_g.X();
180  body_p.q = -body_g.Y();
181  body_p.r = -body_g.Z();
182  return body_p;
183 }
184 
185 inline struct DoubleEulers to_pprz_eulers(ignition::math::Quaterniond quat)
186 {
187  struct DoubleEulers eulers;
188  eulers.psi = -quat.Yaw();
189  eulers.theta = -quat.Pitch();
190  eulers.phi = quat.Roll();
191  return eulers;
192 }
193 
194 inline struct DoubleEulers to_global_pprz_eulers(ignition::math::Quaterniond quat)
195 {
196  struct DoubleEulers eulers;
197  eulers.psi = -quat.Yaw() - M_PI / 2;
198  eulers.theta = -quat.Pitch();
199  eulers.phi = quat.Roll();
200  return eulers;
201 }
202 
203 inline struct DoubleVect3 to_pprz_ltp(ignition::math::Vector3d xyz)
204 {
205  struct DoubleVect3 ltp;
206  ltp.x = xyz.Y();
207  ltp.y = xyz.X();
208  ltp.z = -xyz.Z();
209  return ltp;
210 }
211 
212 // External functions, interface with Paparazzi's NPS as declared in nps_fdm.h
213 
218 void nps_fdm_init(double dt)
219 {
220  fdm.init_dt = dt; // JSBsim specific
221  fdm.curr_dt = dt; // JSBsim specific
222  fdm.nan_count = 0; // JSBsim specific
223 
224 #ifdef NPS_ACTUATOR_TIME_CONSTANTS
225  // Set up low-pass filter to simulate delayed actuator response
226  const float tau[NPS_COMMANDS_NB] = NPS_ACTUATOR_TIME_CONSTANTS;
227  for (int i = 0; i < NPS_COMMANDS_NB; i++) {
228  init_first_order_low_pass(&gazebo_actuators.lowpass[i], tau[i], dt, 0.f);
229  }
230 #ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
231  // Set up high-pass filter to simulate spinup torque
232  const float Iwmax[NPS_COMMANDS_NB] = NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM;
233  for (int i = 0; i < NPS_COMMANDS_NB; i++) {
234  init_first_order_high_pass(&gazebo_actuators.highpass[i], tau[i], dt, 0.f);
235  gazebo_actuators.max_ang_momentum[i] = Iwmax[i];
236  }
237 #endif
238 #endif
239 }
240 
248  bool launch __attribute__((unused)),
249  double *act_commands,
250  int commands_nb)
251 {
252  // Initialize Gazebo if req'd.
253  // Initialization is peformed here instead of in nps_fdm_init because:
254  // - Video devices need to added at this point. Video devices have not been
255  // added yet when nps_fdm_init is called.
256  // - nps_fdm_init runs on a different thread then nps_fdm_run_step, which
257  // causes problems with Gazebo.
258  if (!gazebo_initialized) {
259  init_gazebo();
260  gazebo_read();
261 #if NPS_SIMULATE_VIDEO
262  init_gazebo_video();
263 #endif
264 #if NPS_SIMULATE_LASER_RANGE_ARRAY
265  gazebo_init_range_sensors();
266 #endif
267  gazebo_initialized = true;
268  }
269 
270  // Update the simulation for a single timestep.
271  gazebo::runWorld(model->GetWorld(), 1);
272  gazebo::sensors::run_once();
273  gazebo_write(act_commands, commands_nb);
274  gazebo_read();
275 #if NPS_SIMULATE_VIDEO
276  gazebo_read_video();
277 #endif
278 #if NPS_SIMULATE_LASER_RANGE_ARRAY
279  gazebo_read_range_sensors();
280 #endif
281 
282 }
283 
284 // TODO Atmosphere functions have not been implemented yet.
285 // Starting at version 8, Gazebo has its own atmosphere and wind model.
287  double speed __attribute__((unused)),
288  double dir __attribute__((unused)))
289 {
290 }
291 
293  double wind_north __attribute__((unused)),
294  double wind_east __attribute__((unused)),
295  double wind_down __attribute__((unused)))
296 {
297 }
298 
300  double wind_speed __attribute__((unused)),
301  int turbulence_severity __attribute__((unused)))
302 {
303 }
304 
307  double temp __attribute__((unused)),
308  double h __attribute__((unused)))
309 {
310 }
311 
312 // Internal functions
324 static void init_gazebo(void)
325 {
326  string gazebo_home = "/conf/simulator/gazebo/";
327  string pprz_home(getenv("PAPARAZZI_HOME"));
328  string gazebodir = pprz_home + gazebo_home;
329  cout << "Gazebo directory: " << gazebodir << endl;
330 
331  if (!gazebo::setupServer(0, NULL)) {
332  cout << "Failed to start Gazebo, exiting." << endl;
333  std::exit(-1);
334  }
335 
336  cout << "Add Paparazzi paths: " << gazebodir << endl;
337  gazebo::common::SystemPaths::Instance()->AddModelPaths(gazebodir + "models/");
338  sdf::addURIPath("model://", gazebodir + "models/");
339  sdf::addURIPath("world://", gazebodir + "world/");
340 
341  cout << "Add TU Delft paths: " << pprz_home + "/sw/ext/tudelft_gazebo_models/" << endl;
342  gazebo::common::SystemPaths::Instance()->AddModelPaths(pprz_home + "/sw/ext/tudelft_gazebo_models/models/");
343  sdf::addURIPath("model://", pprz_home + "/sw/ext/tudelft_gazebo_models/models/");
344  sdf::addURIPath("world://", pprz_home + "/sw/ext/tudelft_gazebo_models/world/");
345 
346  // get vehicles
347  string vehicle_uri = "model://" + string(NPS_GAZEBO_AC_NAME) + "/" + string(NPS_GAZEBO_AC_NAME) + ".sdf";
348  string vehicle_filename = sdf::findFile(vehicle_uri, false);
349  if (vehicle_filename.empty()) {
350  cout << "ERROR, could not find vehicle " + vehicle_uri << endl;
351  std::exit(-1);
352  }
353  cout << "Load vehicle: " << vehicle_filename << endl;
354  sdf::SDFPtr vehicle_sdf(new sdf::SDF());
355  sdf::init(vehicle_sdf);
356  if (!sdf::readFile(vehicle_filename, vehicle_sdf)) {
357  cout << "ERROR, could not read vehicle " + vehicle_filename << endl;
358  std::exit(-1);
359  }
360 
361  // add or set up sensors before the vehicle gets loaded
362  // laser range array
363 #if NPS_SIMULATE_LASER_RANGE_ARRAY
364  vehicle_sdf->Root()->GetFirstElement()->AddElement("include")->GetElement("uri")->Set("model://range_sensors");
365  sdf::ElementPtr range_joint = vehicle_sdf->Root()->GetFirstElement()->AddElement("joint");
366  range_joint->GetAttribute("name")->Set("range_sensor_joint");
367  range_joint->GetAttribute("type")->Set("fixed");
368  range_joint->GetElement("parent")->Set("chassis");
369  range_joint->GetElement("child")->Set("range_sensors::base");
370 #endif
371  // bebop front camera
372 #ifdef NPS_SIMULATE_MT9F002
373  sdf::ElementPtr link = vehicle_sdf->Root()->GetFirstElement()->GetElement("link");
374  while (link) {
375  if (link->Get<string>("name") == "front_camera") {
376  int w = link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("width")->Get<int>();
377  link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("width")->Set(
379  int h = link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("height")->Get<int>();
380  link->GetElement("sensor")->GetElement("camera")->GetElement("image")->GetElement("height")->Set(
381  h * MT9F002_OUTPUT_SCALER);
382  int env = link->GetElement("sensor")->GetElement("camera")->GetElement("lens")->GetElement("env_texture_size")->Get<int>();
383  link->GetElement("sensor")->GetElement("camera")->GetElement("lens")->GetElement("env_texture_size")->Set(
384  env * MT9F002_OUTPUT_SCALER);
385  cout << "Applied MT9F002_OUTPUT_SCALER (=" << MT9F002_OUTPUT_SCALER << ") to " << link->Get<string>("name") << endl;
386  link->GetElement("sensor")->GetElement("update_rate")->Set(MT9F002_TARGET_FPS);
387  cout << "Applied MT9F002_TARGET_FPS (=" << MT9F002_TARGET_FPS << ") to " << link->Get<string>("name") << endl;
388  }
389  link = link->GetNextElement("link");
390  }
391 #endif // NPS_SIMULATE_MT9F002
392 
393 
394  // get world
395  string world_uri = "world://" + string(NPS_GAZEBO_WORLD);
396  string world_filename = sdf::findFile(world_uri, false);
397  if (world_filename.empty()) {
398  cout << "ERROR, could not find world " + world_uri << endl;
399  std::exit(-1);
400  }
401  cout << "Load world: " << world_filename << endl;
402  sdf::SDFPtr world_sdf(new sdf::SDF());
403  sdf::init(world_sdf);
404  if (!sdf::readFile(world_filename, world_sdf)) {
405  cout << "ERROR, could not read world " + world_filename << endl;
406  std::exit(-1);
407  }
408 
409  // add vehicles
410  world_sdf->Root()->GetFirstElement()->InsertElement(vehicle_sdf->Root()->GetFirstElement());
411 
412  world_sdf->Write(pprz_home + "/var/gazebo.world");
413 
414  gazebo::physics::WorldPtr world = gazebo::loadWorld(pprz_home + "/var/gazebo.world");
415  if (!world) {
416  cout << "Failed to open world, exiting." << endl;
417  std::exit(-1);
418  }
419 
420  cout << "Get pointer to aircraft: " << NPS_GAZEBO_AC_NAME << endl;
421  model = world->ModelByName(NPS_GAZEBO_AC_NAME);
422  if (!model) {
423  cout << "Failed to find '" << NPS_GAZEBO_AC_NAME << "', exiting."
424  << endl;
425  std::exit(-1);
426  }
427 
428  // Initialize sensors
429  gazebo::sensors::run_once(true);
430  gazebo::sensors::run_threads();
431  gazebo::runWorld(world, 1);
432  cout << "Sensors initialized..." << endl;
433 
434  // activate collision sensor
435  gazebo::sensors::SensorManager *mgr = gazebo::sensors::SensorManager::Instance();
436  ct = static_pointer_cast < gazebo::sensors::ContactSensor > (mgr->GetSensor("contactsensor"));
437  ct->SetActive(true);
438 
439  // Overwrite motor directions as defined by motor_mixing
440 #ifdef MOTOR_MIXING_YAW_COEF
441  const double yaw_coef[] = MOTOR_MIXING_YAW_COEF;
442 
443  for (uint8_t i = 0; i < NPS_COMMANDS_NB; i++) {
444  gazebo_actuators.torques[i] = -fabs(gazebo_actuators.torques[i]) * yaw_coef[i] / fabs(yaw_coef[i]);
445  gazebo_actuators.max_ang_momentum[i] = -fabs(gazebo_actuators.max_ang_momentum[i]) * yaw_coef[i] / fabs(yaw_coef[i]);
446  }
447 #endif
448  cout << "Gazebo initialized successfully!" << endl;
449 }
450 
459 static void gazebo_read(void)
460 {
461  static ignition::math::Vector3d vel_prev;
462  static double time_prev;
463 
464  gazebo::physics::WorldPtr world = model->GetWorld();
465  ignition::math::Pose3d pose = model->WorldPose(); // In LOCAL xyz frame
466  ignition::math::Vector3d vel = model->WorldLinearVel();
467  ignition::math::Vector3d ang_vel = model->WorldAngularVel();
468  gazebo::common::SphericalCoordinatesPtr sphere = world->SphericalCoords();
469  ignition::math::Quaterniond local_to_global_quat(0, 0, -sphere->HeadingOffset().Radian());
470 
471  /* Fill FDM struct */
472  fdm.time = world->SimTime().Double();
473 
474  // Find world acceleration by differentiating velocity
475  // model->GetWorldLinearAccel() does not seem to take the velocity_decay into account!
476  // Derivation of the velocity also follows the IMU implementation of Gazebo itself:
477  // https://bitbucket.org/osrf/gazebo/src/e26144434b932b4b6a760ddaa19cfcf9f1734748/gazebo/sensors/ImuSensor.cc?at=default&fileviewer=file-view-default#ImuSensor.cc-370
478  double dt = fdm.time - time_prev;
479  ignition::math::Vector3d accel = (vel - vel_prev) / dt;
480  vel_prev = vel;
481  time_prev = fdm.time;
482 
483  // init_dt: unused
484  // curr_dt: unused
485  // on_ground: unused
486  // nan_count: unused
487 
488  /* position */
489  fdm.ecef_pos = to_pprz_ecef(sphere->PositionTransform(pose.Pos(), gazebo::common::SphericalCoordinates::LOCAL,
490  gazebo::common::SphericalCoordinates::ECEF));
491  fdm.ltpprz_pos = to_pprz_ned(sphere->PositionTransform(pose.Pos(), gazebo::common::SphericalCoordinates::LOCAL,
492  gazebo::common::SphericalCoordinates::GLOBAL));
493  fdm.lla_pos = to_pprz_lla(sphere->PositionTransform(pose.Pos(), gazebo::common::SphericalCoordinates::LOCAL,
494  gazebo::common::SphericalCoordinates::SPHERICAL));
495  fdm.hmsl = pose.Pos().Z();
496 
497  /* debug positions */
498  fdm.lla_pos_pprz = fdm.lla_pos; // Don't really care...
501  fdm.agl = pose.Pos().Z(); // TODO Measure with sensor
502 
503  /* velocity */
504  fdm.ecef_ecef_vel = to_pprz_ecef(sphere->VelocityTransform(vel, gazebo::common::SphericalCoordinates::LOCAL,
505  gazebo::common::SphericalCoordinates::ECEF));
506  fdm.body_ecef_vel = to_pprz_body(pose.Rot().RotateVectorReverse(vel)); // Note: unused
507  fdm.ltp_ecef_vel = to_pprz_ned(sphere->VelocityTransform(vel, gazebo::common::SphericalCoordinates::LOCAL,
508  gazebo::common::SphericalCoordinates::GLOBAL));
510 
511  /* acceleration */
512  fdm.ecef_ecef_accel = to_pprz_ecef(sphere->VelocityTransform(accel, gazebo::common::SphericalCoordinates::LOCAL,
513  gazebo::common::SphericalCoordinates::ECEF)); // Note: unused
514  fdm.body_ecef_accel = to_pprz_body(pose.Rot().RotateVectorReverse(accel));
515  fdm.ltp_ecef_accel = to_pprz_ned(sphere->VelocityTransform(accel, gazebo::common::SphericalCoordinates::LOCAL,
516  gazebo::common::SphericalCoordinates::GLOBAL)); // Note: unused
518  fdm.body_inertial_accel = fdm.body_ecef_accel; // Approximate, unused.
519  fdm.body_accel = to_pprz_body(pose.Rot().RotateVectorReverse(accel - world->Gravity()));
520 
521  /* attitude */
522  // ecef_to_body_quat: unused
523  fdm.ltp_to_body_eulers = to_global_pprz_eulers(local_to_global_quat * pose.Rot());
527 
528  /* angular velocity */
529  fdm.body_ecef_rotvel = to_pprz_rates(pose.Rot().RotateVectorReverse(ang_vel));
530  fdm.body_inertial_rotvel = fdm.body_ecef_rotvel; // Approximate
531 
532  /* angular acceleration */
533  // body_ecef_rotaccel: unused
534  // body_inertial_rotaccel: unused
535  /* misc */
536  fdm.ltp_g = to_pprz_ltp(sphere->VelocityTransform(-1 * world->Gravity(), gazebo::common::SphericalCoordinates::LOCAL,
537  gazebo::common::SphericalCoordinates::GLOBAL)); // unused
538  fdm.ltp_h = to_pprz_ltp(sphere->VelocityTransform(world->MagneticField(), gazebo::common::SphericalCoordinates::LOCAL,
539  gazebo::common::SphericalCoordinates::GLOBAL));
540 
541  /* atmosphere */
542 #if GAZEBO_MAJOR_VERSION >= 8 && 0 // TODO implement
543 
544 #else
545  // Gazebo versions < 8 do not have atmosphere or wind support
546  // Use placeholder values. Valid for low altitude, low speed flights.
547  fdm.wind = (struct DoubleVect3) {0, 0, 0};
548  fdm.pressure_sl = 101325; // Pa
549 
550  fdm.airspeed = 0;
553  fdm.temperature = 20.0; // C
554  fdm.aoa = 0; // rad
555  fdm.sideslip = 0; // rad
556 #endif
557  /* flight controls: unused */
558  fdm.elevator = 0;
559  fdm.flap = 0;
560  fdm.left_aileron = 0;
561  fdm.right_aileron = 0;
562  fdm.rudder = 0;
563  /* engine: unused */
564  fdm.num_engines = 0;
565 }
566 
584 static void gazebo_write(double act_commands[], int commands_nb)
585 {
586  for (int i = 0; i < commands_nb; ++i) {
587  // Thrust setpoint
588  double sp = autopilot.motors_on ? act_commands[i] : 0.0; // Normalized thrust setpoint
589 
590  // Actuator dynamics, forces and torques
591 #ifdef NPS_ACTUATOR_TIME_CONSTANTS
592  // Delayed response from actuator
593  double u = update_first_order_low_pass(&gazebo_actuators.lowpass[i], sp);// Normalized actual thrust
594 #else
595  double u = sp;
596 #endif
597  double thrust = gazebo_actuators.thrusts[i] * u;
598  double torque = gazebo_actuators.torques[i] * u;
599 
600 #ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
601  // Spinup torque
602  double udot = update_first_order_high_pass(&gazebo_actuators.highpass[i], sp);
603  double spinup_torque = gazebo_actuators.max_ang_momentum[i] / (2.0 * sqrt(u > 0.05 ? u : 0.05)) * udot;
604  torque += spinup_torque;
605 #endif
606 
607  // Apply force and torque to gazebo model
608  gazebo::physics::LinkPtr link = model->GetLink(gazebo_actuators.names[i]);
609  link->AddRelativeForce(ignition::math::Vector3d(0, 0, thrust));
610  link->AddRelativeTorque(ignition::math::Vector3d(0, 0, torque));
611  }
612 }
613 
614 #if NPS_SIMULATE_VIDEO
615 
629 static void init_gazebo_video(void)
630 {
631  gazebo::sensors::SensorManager *mgr =
632  gazebo::sensors::SensorManager::Instance();
633 
634  cout << "Initializing cameras..." << endl;
635  // Loop over cameras registered in video_thread_nps
636  for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
637  // Find link in gazebo model
638  cout << "Setting up '" << cameras[i]->dev_name << "'... ";
639  gazebo::physics::LinkPtr link = model->GetLink(cameras[i]->dev_name);
640  if (!link) {
641  cout << "ERROR: Link '" << cameras[i]->dev_name << "' not found!"
642  << endl;
643  ;
644  continue;
645  }
646  // Get a pointer to the sensor using its full name
647  if (link->GetSensorCount() != 1) {
648  cout << "ERROR: Link '" << link->GetName()
649  << "' should only contain 1 sensor!" << endl;
650  continue;
651  }
652  string name = link->GetSensorName(0);
653  gazebo::sensors::CameraSensorPtr cam = static_pointer_cast
654  < gazebo::sensors::CameraSensor > (mgr->GetSensor(name));
655  if (!cam) {
656  cout << "ERROR: Could not get pointer to '" << name << "'!" << endl;
657  continue;
658  }
659  // Activate sensor
660  cam->SetActive(true);
661  // Add to list of cameras
662  gazebo_cams[i].cam = cam;
663  gazebo_cams[i].last_measurement_time = cam->LastMeasurementTime();
664  // Copy video_config settings from Gazebo's camera
665  cameras[i]->output_size.w = cam->ImageWidth();
666  cameras[i]->output_size.h = cam->ImageHeight();
667  cameras[i]->sensor_size.w = cam->ImageWidth();
668  cameras[i]->sensor_size.h = cam->ImageHeight();
669  cameras[i]->crop.w = cam->ImageWidth();
670  cameras[i]->crop.h = cam->ImageHeight();
675 #if NPS_SIMULATE_MT9F002
676  // See boards/bebop/mt9f002.c
677  if (cam->Name() == "front_camera") {
684  cameras[i]->camera_intrinsics = {
686  .focal_y = MT9F002_FOCAL_Y,
687  .center_x = MT9F002_CENTER_X,
688  .center_y = MT9F002_CENTER_Y,
689  .Dhane_k = MT9F002_DHANE_K
690  };
691  }
692 #endif
693  cameras[i]->fps = cam->UpdateRate();
694  cout << "ok" << endl;
695  }
696 }
697 
706 static void gazebo_read_video(void)
707 {
708  for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS; ++i) {
709  gazebo::sensors::CameraSensorPtr &cam = gazebo_cams[i].cam;
710  // Skip unregistered or unfound cameras
711  if (cam == NULL) { continue; }
712  // Skip if not updated
713  // Also skip when LastMeasurementTime() is zero (workaround)
714  if ((cam->LastMeasurementTime() - gazebo_cams[i].last_measurement_time).Float() < 0.005
715  || cam->LastMeasurementTime() == 0) { continue; }
716  // Grab image, convert and send to video thread
717  struct image_t img;
718  read_image(&img, cam);
719 
720 #if NPS_DEBUG_VIDEO
721  cv::Mat RGB_cam(cam->ImageHeight(), cam->ImageWidth(), CV_8UC3, (uint8_t *)cam->ImageData());
722  cv::cvtColor(RGB_cam, RGB_cam, cv::COLOR_RGB2BGR);
723  cv::namedWindow(cameras[i]->dev_name, cv::WINDOW_AUTOSIZE); // Create a window for display.
724  cv::imshow(cameras[i]->dev_name, RGB_cam);
725  cv::waitKey(1);
726 #endif
727 
728  cv_run_device(cameras[i], &img);
729  // Free image buffer after use.
730  image_free(&img);
731  // Keep track of last update time.
732  gazebo_cams[i].last_measurement_time = cam->LastMeasurementTime();
733  }
734 }
735 
746 static void read_image(
747  struct image_t *img,
748  gazebo::sensors::CameraSensorPtr cam)
749 {
750  int xstart = 0;
751  int ystart = 0;
752 #if NPS_SIMULATE_MT9F002
753  if (cam->Name() == "front_camera") {
755  xstart = cam->ImageWidth() * (0.5 + MT9F002_INITIAL_OFFSET_X) - MT9F002_OUTPUT_WIDTH / 2;
756  ystart = cam->ImageHeight() * (0.5 + MT9F002_INITIAL_OFFSET_Y) - MT9F002_OUTPUT_HEIGHT / 2;
757  } else {
758  image_create(img, cam->ImageWidth(), cam->ImageHeight(), IMAGE_YUV422);
759  }
760 #else
761  image_create(img, cam->ImageWidth(), cam->ImageHeight(), IMAGE_YUV422);
762 #endif
763  // Convert Gazebo's *RGB888* image to Paparazzi's YUV422
764  const uint8_t *data_rgb = cam->ImageData();
765  uint8_t *data_yuv = (uint8_t *)(img->buf);
766  for (int x = 0; x < img->w; ++x) {
767  for (int y = 0; y < img->h; ++y) {
768  int idx_rgb = 3 * (cam->ImageWidth() * (y + ystart) + (x + xstart));
769  int idx_yuv = 2 * (img->w * y + x);
770  int idx_px = img->w * y + x;
771  if (idx_px % 2 == 0) { // Pick U or V
772  data_yuv[idx_yuv] = - 0.148 * data_rgb[idx_rgb]
773  - 0.291 * data_rgb[idx_rgb + 1]
774  + 0.439 * data_rgb[idx_rgb + 2] + 128; // U
775  } else {
776  data_yuv[idx_yuv] = 0.439 * data_rgb[idx_rgb]
777  - 0.368 * data_rgb[idx_rgb + 1]
778  - 0.071 * data_rgb[idx_rgb + 2] + 128; // V
779  }
780  data_yuv[idx_yuv + 1] = 0.257 * data_rgb[idx_rgb]
781  + 0.504 * data_rgb[idx_rgb + 1]
782  + 0.098 * data_rgb[idx_rgb + 2] + 16; // Y
783  }
784  }
785  // Fill miscellaneous fields
786  gazebo::common::Time ts = cam->LastMeasurementTime();
787  img->ts.tv_sec = ts.sec;
788  img->ts.tv_usec = ts.nsec / 1000.0;
789  img->pprz_ts = ts.Double() * 1e6;
790  img->buf_idx = 0; // unused
791 }
792 #endif // NPS_SIMULATE_VIDEO
793 
794 #if NPS_SIMULATE_LASER_RANGE_ARRAY
795 /*
796  * Simulate range sensors:
797  *
798  * In the airframe file, set NPS_SIMULATE_LASER_RANGE_ARRAY if you want to make use of the integrated Ray sensors.
799  * These are defined in their own model which is added to the ardrone.sdf (called range_sensors). Here you can add
800  * single ray point sensors with a specified position and orientation. It is also possible add noise.
801  *
802  * Within the airframe file (see e.g ardrone2_rangesensors_gazebo.xml), the amount of sensors
803  * (LASER_RANGE_ARRAY_NUM_SENSORS) and their orientations
804  * (LASER_RANGE_ARRAY_ORIENTATIONS={phi_1,theta_1,psi_1...phi_n,theta_n,psi_n} n = number of sensors) need to be
805  * specified as well. This is to keep it generic since this need to be done on the real platform with an external
806  * ray sensor. The function will compare the orientations from the ray sensors of gazebo, with the ones specified
807  * in the airframe, and will fill up an array to send and abi message to be used by other modules.
808  *
809  * NPS_GAZEBO_RANGE_SEND_AGL defines if the sensor that is defined as down should be used to send an AGL message.
810  * to send an OBSTACLE_DETECTION message.
811  *
812  * Functions:
813  * gazebo_init_range_sensors() -> Finds and initializes all ray sensors in gazebo
814  * gazebo_read_range_sensors() -> Reads and evaluates the ray sensors values, and sending it to other pprz modules
815  */
816 
817 struct gazebo_ray_t {
818  gazebo::sensors::RaySensorPtr sensor;
819  gazebo::common::Time last_measurement_time;
820 };
821 
822 static struct gazebo_ray_t rays[LASER_RANGE_ARRAY_NUM_SENSORS] = {{NULL, 0}};
823 static float range_orientation[] = LASER_RANGE_ARRAY_ORIENTATIONS;
824 static uint8_t ray_sensor_agl_index = 255;
825 
826 #define VL53L0_MAX_VAL 8.191f
827 
828 static void gazebo_init_range_sensors(void)
829 {
830  gazebo::sensors::SensorManager *mgr = gazebo::sensors::SensorManager::Instance();
831  gazebo::sensors::Sensor_V sensors = mgr->GetSensors(); // list of all sensors
832 
833  uint16_t sensor_count = model->GetSensorCount();
834 
835  gazebo::sensors::RaySensorPtr ray_sensor;
836  uint8_t ray_sensor_count_selected = 0;
837 
838  // Loop though all sensors and only select ray sensors, which are saved within a struct
839  for (uint16_t i = 0; i < sensor_count; i++) {
840  if (ray_sensor_count_selected > LASER_RANGE_ARRAY_NUM_SENSORS) {
841  break;
842  }
843 
844  if (sensors.at(i)->Type() == "ray") {
845  ray_sensor = static_pointer_cast<gazebo::sensors::RaySensor>(sensors.at(i));
846 
847  if (!ray_sensor) {
848  cout << "ERROR: Could not get pointer to raysensor " << i << "!" << endl;
849  continue;
850  }
851 
852  // Read out the pose from per ray sensors in gazebo relative to body
853  struct DoubleEulers pose_sensor = to_pprz_eulers(ray_sensor->Pose().Rot());
854 
855  struct DoubleQuat q_ray, q_def;
856  double_quat_of_eulers(&q_ray, &pose_sensor);
857 
858  /* Check the orientations of the ray sensors found in gazebo, if they are similar (within 5 deg) to the orientations
859  * given in the airframe file in LASER_RANGE_ARRAY_RANGE_ORIENTATION
860  */
861  for (int j = 0; j < LASER_RANGE_ARRAY_NUM_SENSORS; j++) {
862  struct DoubleEulers def = {0, range_orientation[j * 2], range_orientation[j * 2 + 1]};
863  double_quat_of_eulers(&q_def, &def);
864  // get angle between required angle and ray angle
865  double angle = acos(QUAT_DOT_PRODUCT(q_ray, q_def));
866 
867  if (fabs(angle) < RadOfDeg(5)) {
868  ray_sensor_count_selected++;
869  rays[j].sensor = ray_sensor;
870  rays[j].sensor->SetActive(true);
871 
872 #if LASER_RANGE_ARRAY_SEND_AGL
873  // find the sensor pointing down
874  if (fabs(range_orientation[j * 2] + M_PI_2) < RadOfDeg(5)) {
875  ray_sensor_agl_index = j;
876  }
877 #endif
878  break;
879  }
880  }
881  }
882  }
883 
884  if (ray_sensor_count_selected != LASER_RANGE_ARRAY_NUM_SENSORS) {
885  cout << "ERROR: you have defined " << LASER_RANGE_ARRAY_NUM_SENSORS << " sensors in your airframe file, but only "
886  << ray_sensor_count_selected << " sensors have been found in the gazebo simulator, "
887  "with the same orientation as in the airframe file " << endl;
888  exit(0);
889  }
890  cout << "Initialized laser range array" << endl;
891 }
892 
893 static void gazebo_read_range_sensors(void)
894 {
895  static float range;
896 
897  // Loop through all ray sensors found in gazebo
898  for (int i = 0; i < LASER_RANGE_ARRAY_NUM_SENSORS; i++) {
899  if ((rays[i].sensor->LastMeasurementTime() - rays[i].last_measurement_time).Float() < 0.005
900  || rays[i].sensor->LastMeasurementTime() == 0) { continue; }
901 
902  if (rays[i].sensor->Range(0) < 1e-5 || rays[i].sensor->Range(0) > VL53L0_MAX_VAL) {
903  range = VL53L0_MAX_VAL;
904  } else {
905  range = rays[i].sensor->Range(0);
906  }
907  AbiSendMsgOBSTACLE_DETECTION(OBS_DETECTION_RANGE_ARRAY_NPS_ID, range, range_orientation[i * 2],
908  range_orientation[i * 2 + 1]);
909 
910  if (i == ray_sensor_agl_index) {
911  float agl = rays[i].sensor->Range(0);
912  // Down range sensor as agl
913  if (agl > 1e-5 && !isinf(agl)) {
914  AbiSendMsgAGL(AGL_RAY_SENSOR_GAZEBO_ID, agl);
915  }
916  }
917  rays[i].last_measurement_time = rays[i].sensor->LastMeasurementTime();
918  }
919 }
920 #endif // NPS_SIMULATE_LASER_RANGE_ARRAY
921 
922 #pragma GCC diagnostic pop // Ignore -Wdeprecated-declarations
Roation quaternion.
double agl
Definition: nps_fdm.h:61
unsigned short uint16_t
Definition: types.h:16
double z
in meters
struct NedCoor_d to_pprz_ned(ignition::math::Vector3d global)
double time
Definition: nps_fdm.h:46
vector in North East Down coordinates Units: meters
struct NedCoor_d ltpprz_ecef_accel
accel in ltppprz frame, wrt ECEF frame
Definition: nps_fdm.h:81
#define MT9F002_CENTER_X
Definition: mt9f002.h:114
struct LlaCoor_d lla_pos_geoc
Definition: nps_fdm.h:60
void nps_fdm_set_temperature(double temp, double h)
Set temperature in degrees Celcius at given height h above MSL.
struct DoubleVect3 body_inertial_accel
acceleration in body frame, wrt ECI inertial frame
Definition: nps_fdm.h:84
float focal_x
focal length in the x-direction in pixels
Definition: video_device.h:47
struct crop_t crop
Cropped area definition.
Definition: video_device.h:58
struct gazebo_actuators_t gazebo_actuators
struct DoubleRates body_ecef_rotvel
Definition: nps_fdm.h:97
#define QUAT_DOT_PRODUCT(_qa, _qb)
Definition: pprz_algebra.h:650
uint16_t h
The height.
Definition: image.h:76
uint16_t h
height of the cropped area
Definition: image.h:84
euler angles
double phi
in radians
static void gazebo_write(double act_commands[], int commands_nb)
Write actuator commands to Gazebo.
float left_aileron
Definition: nps_fdm.h:121
struct img_size_t output_size
Output image size.
Definition: video_device.h:56
#define OBS_DETECTION_RANGE_ARRAY_NPS_ID
static gazebo::physics::ModelPtr model
#define NPS_COMMANDS_NB
Number of commands sent to the FDM of NPS.
Definition: nps_autopilot.h:18
#define MT9F002_OUTPUT_HEIGHT
Definition: mt9f002.h:35
Simple first order low pass filter with bilinear transform.
struct EcefCoor_d to_pprz_ecef(ignition::math::Vector3d ecef_i)
void image_free(struct image_t *img)
Free the image.
Definition: image.c:64
#define MT9F002_INITIAL_OFFSET_X
Definition: mt9f002.h:43
struct NedCoor_d ltp_ecef_accel
acceleration in LTP frame, wrt ECEF frame
Definition: nps_fdm.h:76
double psi
in radians
double lat
in radians
double alt
in meters above WGS84 reference ellipsoid
angular rates
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:127
struct NpsFdm fdm
Holds all necessary NPS FDM state information.
Main include for ABI (AirBorneInterface).
static struct video_config_t * cameras[VIDEO_THREAD_MAX_CAMERAS]
Definition: video_thread.c:67
void image_create(struct image_t *img, uint16_t width, uint16_t height, enum image_type type)
Create a new image.
Definition: image.c:39
Definition: image.h:43
double q
in rad/s^2
double thrusts[NPS_COMMANDS_NB]
#define MT9F002_CENTER_Y
Definition: mt9f002.h:117
double theta
in radians
struct LlaCoor_d lla_pos_pprz
Definition: nps_fdm.h:58
#define MT9F002_INITIAL_OFFSET_Y
Definition: mt9f002.h:47
First order low pass filter structure.
vector in Latitude, Longitude and Altitude
#define AGL_RAY_SENSOR_GAZEBO_ID
char * dev_name
path to device
Definition: video_device.h:59
struct DoubleRates body_inertial_rotvel
Definition: nps_fdm.h:101
double r
in rad/s^2
void nps_fdm_init(double dt)
Initialize actuator dynamics, set unused fields in fdm.
static void init_first_order_high_pass(struct FirstOrderHighPass *filter, float tau, float sample_time, float value)
Init first order high pass filter.
int nan_count
Definition: nps_fdm.h:50
void nps_fdm_set_wind_ned(double wind_north, double wind_east, double wind_down)
uint32_t pprz_ts
The timestamp in us since system startup.
Definition: image.h:49
#define MT9F002_OUTPUT_SCALER
Our output is only OUTPUT_SCALER of the pixels we take of the sensor It is programmable in 1/16 steps...
Definition: mt9f002.h:62
struct DoubleEulers ltp_to_body_eulers
Definition: nps_fdm.h:92
static float update_first_order_high_pass(struct FirstOrderHighPass *filter, float value)
Update first order high pass filter state with a new value.
struct NedCoor_d ltp_ecef_vel
velocity in LTP frame, wrt ECEF frame
Definition: nps_fdm.h:74
struct pprz_autopilot autopilot
Global autopilot structure.
Definition: autopilot.c:50
double pressure_sl
pressure at sea level in Pascal
Definition: nps_fdm.h:114
First order high pass filter structure.
Definition: nps_fdm.h:44
double x
in meters
struct NedCoor_d ltpprz_pos
Definition: nps_fdm.h:54
#define VL53L0_MAX_VAL
double hmsl
Definition: nps_fdm.h:56
uint16_t w
Width of the cropped area.
Definition: image.h:83
struct LlaCoor_d to_pprz_lla(ignition::math::Vector3d lla_i)
struct EcefCoor_d ecef_ecef_vel
velocity in ECEF frame, wrt ECEF frame
Definition: nps_fdm.h:64
uint8_t buf_idx
Buffer index for V4L2 freeing.
Definition: image.h:51
double airspeed
equivalent airspeed in m/s
Definition: nps_fdm.h:109
Image helper functions like resizing, color filter, converters...
double y
in meters
struct DoubleVect3 to_pprz_body(ignition::math::Vector3d body_g)
struct DoubleVect3 body_accel
acceleration in body frame as measured by an accelerometer (incl.
Definition: nps_fdm.h:87
#define MT9F002_FOCAL_Y
Definition: mt9f002.h:111
double x
in meters
double pressure
current (static) atmospheric pressure in Pascal
Definition: nps_fdm.h:110
float elevator
Definition: nps_fdm.h:119
void nps_fdm_set_turbulence(double wind_speed, int turbulence_severity)
string names[NPS_COMMANDS_NB]
Architecture independent timing functions.
void double_quat_of_eulers(struct DoubleQuat *q, struct DoubleEulers *e)
float right_aileron
Definition: nps_fdm.h:122
struct img_size_t sensor_size
Original sensor size.
Definition: video_device.h:57
#define MT9F002_DHANE_K
Definition: mt9f002.h:120
double init_dt
Definition: nps_fdm.h:47
Paparazzi atmospheric pressure conversion utilities.
uint16_t w
Image width.
Definition: image.h:45
uint32_t num_engines
Definition: nps_fdm.h:126
Computer vision framework for onboard processing.
uint16_t h
Image height.
Definition: image.h:46
struct DoubleVect3 wind
velocity in m/s in NED
Definition: nps_fdm.h:107
struct DoubleRates to_pprz_rates(ignition::math::Vector3d body_g)
void * buf
Image buffer (depending on the image_type)
Definition: image.h:53
struct LlaCoor_d lla_pos_geod
Definition: nps_fdm.h:59
struct DoubleEulers to_global_pprz_eulers(ignition::math::Quaterniond quat)
double torques[NPS_COMMANDS_NB]
struct NpsSensors sensors
Definition: nps_sensors.c:6
double dynamic_pressure
dynamic pressure in Pascal
Definition: nps_fdm.h:112
void nps_fdm_run_step(bool launch, double *act_commands, int commands_nb)
Update the simulation state.
vector in EarthCenteredEarthFixed coordinates
This header gives NPS access to the list of added cameras.
#define NPS_GAZEBO_AC_NAME
#define MT9F002_OUTPUT_WIDTH
Definition: mt9f002.h:39
double curr_dt
Definition: nps_fdm.h:48
static const int32_t yaw_coef[MOTOR_MIXING_NB_MOTOR]
Definition: motor_mixing.c:91
int fps
Target FPS.
Definition: video_device.h:67
Core autopilot interface common to all firmwares.
struct LlaCoor_d lla_pos
Definition: nps_fdm.h:55
Initialization and configuration of the MT9F002 CMOS Chip.
struct DoubleVect3 body_ecef_vel
velocity in body frame, wrt ECEF frame
Definition: nps_fdm.h:69
double max_ang_momentum[NPS_COMMANDS_NB]
static void init_gazebo(void)
Set up a Gazebo server.
void cv_run_device(struct video_config_t *device, struct image_t *img)
Definition: cv.c:177
static void h(const real32_T x[7], const real32_T q[4], real32_T y[6])
struct DoubleVect3 to_pprz_ltp(ignition::math::Vector3d xyz)
struct FirstOrderLowPass lowpass[NPS_COMMANDS_NB]
double temperature
current temperature in degrees Celcius
Definition: nps_fdm.h:113
bool motors_on
arming status
Definition: autopilot.h:63
unsigned char uint8_t
Definition: types.h:14
struct timeval ts
The timestamp of creation.
Definition: image.h:47
struct DoubleEulers to_pprz_eulers(ignition::math::Quaterniond quat)
struct DoubleQuat ltpprz_to_body_quat
Definition: nps_fdm.h:93
double lon
in radians
Simple high pass filter with double precision.
float focal_y
focal length in the y-direction in pixels
Definition: video_device.h:48
float center_y
center image coordinate in the y-direction
Definition: video_device.h:50
struct camera_intrinsics_t camera_intrinsics
Intrinsics of the camera; camera calibration parameters and distortion parameter(s) ...
Definition: video_device.h:68
struct EcefCoor_d ecef_pos
Definition: nps_fdm.h:53
static void init_first_order_low_pass(struct FirstOrderLowPass *filter, float tau, float sample_time, float value)
Init first order low pass filter.
UYVY format (uint16 per pixel)
Definition: image.h:36
struct DoubleVect3 body_ecef_accel
acceleration in body frame, wrt ECEF frame
Definition: nps_fdm.h:71
struct DoubleVect3 ltp_g
Definition: nps_fdm.h:104
double z
in meters
#define VIDEO_THREAD_MAX_CAMERAS
Definition: video_thread.c:63
float rudder
Definition: nps_fdm.h:123
void nps_fdm_set_wind(double speed, double dir)
static void gazebo_read(void)
Read Gazebo's simulation state and store the results in the fdm struct used by NPS.
Paparazzi double precision floating point algebra.
struct NedCoor_d ltpprz_ecef_vel
velocity in ltppprz frame, wrt ECEF frame
Definition: nps_fdm.h:79
#define NPS_GAZEBO_WORLD
double p
in rad/s^2
static bool gazebo_initialized
float flap
Definition: nps_fdm.h:120
uint16_t w
The width.
Definition: image.h:75
#define MT9F002_TARGET_FPS
Definition: mt9f002.h:75
static gazebo::sensors::ContactSensorPtr ct
struct DoubleQuat ltp_to_body_quat
Definition: nps_fdm.h:91
double sideslip
sideslip angle in rad
Definition: nps_fdm.h:116
double y
in meters
struct EcefCoor_d ecef_ecef_accel
acceleration in ECEF frame, wrt ECEF frame
Definition: nps_fdm.h:66
Common Motor Mixing configuration types.
#define MT9F002_FOCAL_X
Definition: mt9f002.h:108
struct DoubleEulers ltpprz_to_body_eulers
Definition: nps_fdm.h:94
struct FirstOrderHighPass highpass[NPS_COMMANDS_NB]
double aoa
angle of attack in rad
Definition: nps_fdm.h:115
struct DoubleVect3 ltp_h
Definition: nps_fdm.h:105
static float update_first_order_low_pass(struct FirstOrderLowPass *filter, float value)
Update first order low pass filter state with a new value.
float center_x
center image coordinate in the x-direction
Definition: video_device.h:49