Paparazzi UAS v7.1_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
nps_fdm_gz.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
48#include <cmath>
49#include <cstdio>
50#include <cstdlib>
51#include <string>
52#include <memory>
53#include <csignal>
54#include <chrono>
55#include <functional>
56#include <mutex>
57
58#include <gz/sim/Server.hh>
59#include <gz/sim/ServerConfig.hh>
60#include <gz/sim/System.hh>
61#include <gz/sim/Model.hh>
62#include <gz/sim/Link.hh>
63#include <gz/sim/World.hh>
64#include <gz/sim/components/Gravity.hh>
65#include <gz/sim/components/MagneticField.hh>
66#include <gz/sim/components/SphericalCoordinates.hh>
67#include <gz/math/Pose3.hh>
68#include <gz/math/Quaternion.hh>
69#include <gz/math/Vector3.hh>
70#include <gz/msgs/image.pb.h>
71#include <gz/transport/Node.hh>
72#include <sdf/sdf.hh>
73
74extern "C" {
75#include "nps_fdm.h"
76#include "nps_autopilot.h"
77
78#include "generated/airframe.h"
79#include "generated/flight_plan.h"
80#include "autopilot.h"
81
82#include "math/pprz_isa.h"
87#include "state.h"
88}
89
90#if NPS_SIMULATE_VIDEO
91extern "C" {
97struct mt9f002_t mt9f002 __attribute__((weak)); // Prevent undefined reference errors when Bebop code is not linked.
98}
99
100// Reduce resolution of the simulated MT9F002 sensor (Bebop) to improve runtime
101// performance at the cost of image resolution.
102// Recommended values: 1 (realistic), 2, 4 (fast but slightly blurry)
103#ifndef NPS_MT9F002_SENSOR_RES_DIVIDER
104#define NPS_MT9F002_SENSOR_RES_DIVIDER 1
105#endif
106
108struct gazebo_cam_t {
109 std::string sensor;
110 std::mutex mutex;
111 gz::msgs::Image msg;
112 bool fresh;
113};
115static std::unique_ptr<gz::transport::Node> gz_node;
116
117static void gazebo_setup_cameras(sdf::ElementPtr model);
118static void gazebo_init_video(void);
119static void gazebo_read_video(void);
120#endif
121
122#ifndef NPS_GAZEBO_WORLD
123#define NPS_GAZEBO_WORLD "empty.world"
124#endif
125#ifndef NPS_GAZEBO_AC_NAME
126#define NPS_GAZEBO_AC_NAME "bebop"
127#endif
128
130struct NpsFdm fdm;
131
132struct gazebo_actuators_t {
133 std::string names[NPS_COMMANDS_NB];
134 double thrusts[NPS_COMMANDS_NB];
135 double torques[NPS_COMMANDS_NB];
139};
140
142
144static struct {
145 gz::math::Pose3d pose;
146 gz::math::Vector3d linear_vel;
147 gz::math::Vector3d angular_vel;
148 gz::math::Vector3d gravity;
149 gz::math::Vector3d magnetic_field;
150 double heading_offset;
151 double sim_time;
152 bool valid;
154
156static struct {
157 double thrusts[NPS_COMMANDS_NB];
158 double torques[NPS_COMMANDS_NB];
160
167class PaparazziSystem : public gz::sim::System,
168 public gz::sim::ISystemConfigure,
169 public gz::sim::ISystemPreUpdate,
170 public gz::sim::ISystemPostUpdate
171{
172public:
173 void Configure(const gz::sim::Entity &entity,
174 const std::shared_ptr<const sdf::Element> &,
175 gz::sim::EntityComponentManager &ecm,
176 gz::sim::EventManager &) override
177 {
178 world_ = entity;
179 FindModel(ecm);
180 }
181
182 void PreUpdate(const gz::sim::UpdateInfo &info, gz::sim::EntityComponentManager &ecm) override
183 {
184 if (model_ == gz::sim::kNullEntity) {
185 FindModel(ecm);
186 return;
187 }
188 if (info.paused) {
189 return;
190 }
191 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
192 gz::sim::Link link(motor_links_[i]);
193 auto pose = link.WorldPose(ecm);
194 if (!pose) {
195 continue;
196 }
197 gz::math::Vector3d force = pose->Rot().RotateVector(gz::math::Vector3d(0, 0, gz_cmd.thrusts[i]));
198 gz::math::Vector3d torque = pose->Rot().RotateVector(gz::math::Vector3d(0, 0, gz_cmd.torques[i]));
199 link.AddWorldWrench(ecm, force, torque);
200 }
201 }
202
203 void PostUpdate(const gz::sim::UpdateInfo &info, const gz::sim::EntityComponentManager &ecm) override
204 {
205 if (model_ == gz::sim::kNullEntity) {
206 return;
207 }
208 gz::sim::Link body(body_link_);
209 auto pose = body.WorldPose(ecm);
210 auto vel = body.WorldLinearVelocity(ecm);
211 auto ang_vel = body.WorldAngularVelocity(ecm);
212 if (!pose || !vel || !ang_vel) {
213 return;
214 }
215 gz_state.pose = *pose;
216 gz_state.linear_vel = *vel;
217 gz_state.angular_vel = *ang_vel;
218
219 auto gravity = ecm.Component<gz::sim::components::Gravity>(world_);
220 gz_state.gravity = gravity ? gravity->Data() : gz::math::Vector3d(0, 0, -9.81);
221 auto mag = ecm.Component<gz::sim::components::MagneticField>(world_);
222 gz_state.magnetic_field = mag ? mag->Data() : gz::math::Vector3d::Zero;
223 auto sphere = ecm.Component<gz::sim::components::SphericalCoordinates>(world_);
224 gz_state.heading_offset = sphere ? sphere->Data().HeadingOffset().Radian() : M_PI;
225
226 gz_state.sim_time = std::chrono::duration<double>(info.simTime).count();
227 gz_state.valid = true;
228 }
229
230 bool Ready() const { return model_ != gz::sim::kNullEntity; }
231
232private:
233 void FindModel(gz::sim::EntityComponentManager &ecm)
234 {
235 gz::sim::Model model(gz::sim::World(world_).ModelByName(ecm, NPS_GAZEBO_AC_NAME));
236 if (!model.Valid(ecm)) {
237 return;
238 }
239 gz::sim::Entity motor_links[NPS_COMMANDS_NB];
240 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
241 motor_links[i] = model.LinkByName(ecm, gazebo_actuators.names[i]);
242 if (motor_links[i] == gz::sim::kNullEntity) {
243 fprintf(stderr, "NPS gz: link '%s' not found in model '%s'\n",
246 }
247 }
248 body_link_ = model.CanonicalLink(ecm);
249 gz::sim::Link body(body_link_);
250 body.EnableVelocityChecks(ecm, true);
251 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
253 }
254 model_ = model.Entity();
255 }
256
257 gz::sim::Entity world_{gz::sim::kNullEntity};
258 gz::sim::Entity model_{gz::sim::kNullEntity};
259 gz::sim::Entity body_link_{gz::sim::kNullEntity};
261};
262
263static std::unique_ptr<gz::sim::Server> server;
264static std::shared_ptr<PaparazziSystem> pprz_system;
265
266static void init_gazebo(void);
267static void gazebo_read(void);
268static void gazebo_write(double act_commands[], int commands_nb);
269
270/* Conversions. The local gz frame is ENU after rotating by (heading_offset - pi),
271 * see the file description. Body frames: gz FLU, paparazzi FRD. */
272
273static gz::math::Quaterniond local_to_enu(void)
274{
275 return gz::math::Quaterniond(0, 0, gz_state.heading_offset - M_PI);
276}
277
278static struct NedCoor_d ned_of_enu(const gz::math::Vector3d &enu)
279{
280 struct NedCoor_d ned = { enu.Y(), enu.X(), -enu.Z() };
281 return ned;
282}
283
284static struct DoubleVect3 vect_ned_of_enu(const gz::math::Vector3d &enu)
285{
286 struct DoubleVect3 ned = { enu.Y(), enu.X(), -enu.Z() };
287 return ned;
288}
289
290static struct DoubleVect3 frd_of_flu(const gz::math::Vector3d &flu)
291{
292 struct DoubleVect3 frd = { flu.X(), -flu.Y(), -flu.Z() };
293 return frd;
294}
295
296static struct DoubleRates rates_frd_of_flu(const gz::math::Vector3d &flu)
297{
298 struct DoubleRates frd = { flu.X(), -flu.Y(), -flu.Z() };
299 return frd;
300}
301
303static struct DoubleEulers eulers_ned_of_enu(const gz::math::Quaterniond &q_enu)
304{
305 struct DoubleEulers eulers;
306 eulers.phi = q_enu.Roll();
307 eulers.theta = -q_enu.Pitch();
308 eulers.psi = M_PI_2 - q_enu.Yaw();
309 eulers.psi = atan2(sin(eulers.psi), cos(eulers.psi));
310 return eulers;
311}
312
313// External functions, interface with Paparazzi's NPS as declared in nps_fdm.h
314
315void nps_fdm_init(double dt)
316{
317 fdm.init_dt = dt;
318 fdm.curr_dt = dt;
319 fdm.nan_count = 0;
320
321#ifdef NPS_ACTUATOR_TIME_CONSTANTS
322 // Low-pass filter to simulate the delayed actuator response
324 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
326 }
327#ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
328 // High-pass filter to simulate the spin-up torque
330 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
333 }
334#endif
335#endif
336}
337
339{
340 // nothing to do, gz is started at the first run step (video devices are added after init)
341}
342
343void nps_fdm_run_step(bool launch __attribute__((unused)), double *act_commands, int commands_nb)
344{
345 if (!server) {
346 init_gazebo();
347 }
349 server->RunOnce(false);
350 gazebo_read();
351#if NPS_SIMULATE_VIDEO
353#endif
354}
355
356// Atmosphere functions are not implemented (same as Gazebo Classic)
357void nps_fdm_set_wind(double speed __attribute__((unused)), double dir __attribute__((unused))) {}
359 double wind_down __attribute__((unused))) {}
360void nps_fdm_set_turbulence(double wind_speed __attribute__((unused)),
361 int turbulence_severity __attribute__((unused))) {}
362void nps_fdm_set_temperature(double temp __attribute__((unused)), double h __attribute__((unused))) {}
363
371static void init_gazebo(void)
372{
373 const char *home = getenv("PAPARAZZI_HOME");
374 if (home == NULL) {
375 fprintf(stderr, "NPS gz: PAPARAZZI_HOME is not set\n");
377 }
378 std::string pprz_home(home);
379 const std::string dirs[] = { pprz_home + "/conf/simulator/gazebo/", pprz_home + "/sw/ext/tudelft_gazebo_models/" };
380
381 std::string resource_path;
382 for (const std::string &dir : dirs) {
383 sdf::addURIPath("model://", dir + "models/");
384 sdf::addURIPath("world://", dir + "world/");
385 resource_path += dir + "models/:";
386 }
387 // models that only gz-sim needs (e.g. model://ground_plane, from the Classic model database)
388 sdf::addURIPath("model://", pprz_home + "/conf/simulator/gazebo/models_gz/");
389 resource_path += pprz_home + "/conf/simulator/gazebo/models_gz/:";
390 // model:// URIs in the merged world are resolved by gz-sim through this path
391 const char *env_path = getenv("GZ_SIM_RESOURCE_PATH");
392 if (env_path != NULL) {
394 }
395 setenv("GZ_SIM_RESOURCE_PATH", resource_path.c_str(), 1);
396
397 std::string vehicle_uri = std::string("model://") + NPS_GAZEBO_AC_NAME + "/" + NPS_GAZEBO_AC_NAME + ".sdf";
398 std::string vehicle_file = sdf::findFile(vehicle_uri, false);
399 sdf::SDFPtr vehicle_sdf(new sdf::SDF());
400 sdf::init(vehicle_sdf);
401 if (vehicle_file.empty() || !sdf::readFile(vehicle_file, vehicle_sdf)) {
402 fprintf(stderr, "NPS gz: could not load vehicle '%s'\n", vehicle_uri.c_str());
404 }
405
406 std::string world_uri = std::string("world://") + NPS_GAZEBO_WORLD;
407 std::string world_file = sdf::findFile(world_uri, false);
408 sdf::SDFPtr world_sdf(new sdf::SDF());
409 sdf::init(world_sdf);
410 if (world_file.empty() || !sdf::readFile(world_file, world_sdf)) {
411 fprintf(stderr, "NPS gz: could not load world '%s'\n", world_uri.c_str());
413 }
414 printf("NPS gz: vehicle %s, world %s\n", vehicle_file.c_str(), world_file.c_str());
415
416 sdf::ElementPtr world = world_sdf->Root()->GetElement("world");
417#if NPS_SIMULATE_VIDEO
418 gazebo_setup_cameras(vehicle_sdf->Root()->GetElement("model"));
419 // Cameras need the Sensors system. gz-sim only loads its default systems for
420 // a world without plugins, so add them as well.
421 if (!world->HasElement("plugin")) {
422 const char *plugins =
423 "<sdf version='1.6'><world name='plugins'>"
424 "<plugin filename='gz-sim-physics-system' name='gz::sim::systems::Physics'/>"
425 "<plugin filename='gz-sim-user-commands-system' name='gz::sim::systems::UserCommands'/>"
426 "<plugin filename='gz-sim-scene-broadcaster-system' name='gz::sim::systems::SceneBroadcaster'/>"
427 "<plugin filename='gz-sim-sensors-system' name='gz::sim::systems::Sensors'>"
428 "<render_engine>ogre2</render_engine></plugin>"
429 "</world></sdf>";
430 sdf::SDFPtr plugins_sdf(new sdf::SDF());
431 sdf::init(plugins_sdf);
432 sdf::readString(plugins, plugins_sdf);
433 for (sdf::ElementPtr plugin = plugins_sdf->Root()->GetElement("world")->GetElement("plugin"); plugin;
434 plugin = plugin->GetNextElement("plugin")) {
435 world->InsertElement(plugin->Clone());
436 }
437 } else {
438 fprintf(stderr, "NPS gz: the world has its own plugins, make sure it loads the Sensors system for the cameras\n");
439 }
440#endif
441 world->InsertElement(vehicle_sdf->Root()->GetFirstElement());
442 // One gz iteration per NPS step: use the NPS time step for the physics
443 world->GetElement("physics")->GetElement("max_step_size")->Set(fdm.init_dt);
444 std::string merged_file = pprz_home + "/var/gazebo.world";
445 world_sdf->Write(merged_file);
446
447 gz::sim::ServerConfig config;
448 config.SetSdfFile(merged_file);
449 // Render the cameras without a window when there is no display
450 config.SetHeadlessRendering(getenv("DISPLAY") == NULL);
451 server = std::make_unique<gz::sim::Server>(config);
452 pprz_system = std::make_shared<PaparazziSystem>();
453 if (!server->AddSystem(pprz_system).value_or(false)) {
454 fprintf(stderr, "NPS gz: could not add the paparazzi system\n");
456 }
457
458 // Run the first iteration paused: creates the entities and configures the system
459 server->RunOnce(true);
460 if (!pprz_system->Ready()) {
461 fprintf(stderr, "NPS gz: model '%s' not found in the world\n", NPS_GAZEBO_AC_NAME);
463 }
464
465 // gz-sim catches SIGINT and SIGTERM to stop its own run loop, which NPS does not use:
466 // restore the default handlers so Ctrl-C and the Paparazzi Center can stop the simulator
467 signal(SIGINT, SIG_DFL);
468 signal(SIGTERM, SIG_DFL);
469
470 // Motor directions as defined by the motor mixing
471#ifdef MOTOR_MIXING_YAW_COEF
472 const double yaw_coef[] = MOTOR_MIXING_YAW_COEF;
473 for (int i = 0; i < NPS_COMMANDS_NB; i++) {
474 if (yaw_coef[i] != 0.) {
475 double sign = yaw_coef[i] > 0. ? -1. : 1.;
478 }
479 }
480#endif
481
482#if NPS_SIMULATE_VIDEO
484#endif
485#if NPS_SIMULATE_LASER_RANGE_ARRAY
486 fprintf(stderr, "NPS gz: laser range array simulation is not supported yet\n");
487#endif
488}
489
496static void gazebo_read(void)
497{
498 static gz::math::Vector3d vel_prev;
499 static double time_prev = -1.;
500
501 if (!gz_state.valid) {
502 return;
503 }
504 const gz::math::Pose3d &pose = gz_state.pose;
505 const gz::math::Vector3d &vel = gz_state.linear_vel;
506 gz::math::Quaterniond to_enu = local_to_enu();
507
508 fdm.time = gz_state.sim_time;
509
510 // World acceleration from the velocity (as the Gazebo IMU does)
511 double dt = fdm.time - time_prev;
512 gz::math::Vector3d accel = (time_prev >= 0. && dt > 0.) ? (vel - vel_prev) / dt : gz::math::Vector3d::Zero;
513 vel_prev = vel;
515
516 // ltp definition in double for accuracy
517 struct LtpDef_d ltpdef_d;
524 for (int i = 0; i < 3 * 3; i++) {
525 ltpdef_d.ltp_of_ecef.m[i] = state.ned_origin_f.ltp_of_ecef.m[i];
526 }
528
529 /* position */
530 fdm.ltpprz_pos = ned_of_enu(to_enu.RotateVector(pose.Pos()));
538 fdm.agl = pose.Pos().Z(); // TODO sonar
539
540 /* velocity */
541 fdm.ltp_ecef_vel = ned_of_enu(to_enu.RotateVector(vel));
543 fdm.body_ecef_vel = frd_of_flu(pose.Rot().RotateVectorReverse(vel));
545
546 /* acceleration */
547 fdm.ltp_ecef_accel = ned_of_enu(to_enu.RotateVector(accel));
549 fdm.body_ecef_accel = frd_of_flu(pose.Rot().RotateVectorReverse(accel));
551 fdm.body_accel = frd_of_flu(pose.Rot().RotateVectorReverse(accel - gz_state.gravity));
553
554 /* attitude */
559
560 /* angular velocity */
561 fdm.body_ecef_rotvel = rates_frd_of_flu(pose.Rot().RotateVectorReverse(gz_state.angular_vel));
563
564 /* gravity and magnetic field */
565 fdm.ltp_g = vect_ned_of_enu(to_enu.RotateVector(-gz_state.gravity));
566 fdm.ltp_h = vect_ned_of_enu(to_enu.RotateVector(gz_state.magnetic_field));
567
568 /* atmosphere: not simulated */
569 fdm.wind = (struct DoubleVect3) {0, 0, 0};
571 fdm.airspeed = 0;
574 fdm.temperature = 20.0;
575 fdm.aoa = 0;
576 fdm.sideslip = 0;
577
578 /* flight controls and engines: unused */
579 fdm.elevator = 0;
580 fdm.flap = 0;
581 fdm.left_aileron = 0;
582 fdm.right_aileron = 0;
583 fdm.rudder = 0;
584 fdm.num_engines = 0;
585}
586
591static void gazebo_write(double act_commands[], int commands_nb)
592{
593 for (int i = 0; i < commands_nb && i < NPS_COMMANDS_NB; ++i) {
594 double sp = autopilot.motors_on ? act_commands[i] : 0.0;
595
596#ifdef NPS_ACTUATOR_TIME_CONSTANTS
598#else
599 double u = sp;
600#endif
601 double thrust = gazebo_actuators.thrusts[i] * u;
602 double torque = gazebo_actuators.torques[i] * u;
603
604#ifdef NPS_ACTUATOR_MAX_ANGULAR_MOMENTUM
606 torque += gazebo_actuators.max_ang_momentum[i] / (2.0 * sqrt(u > 0.05 ? u : 0.05)) * udot;
607#endif
608
609 gz_cmd.thrusts[i] = thrust;
610 gz_cmd.torques[i] = torque;
611 }
612}
613
614#if NPS_SIMULATE_VIDEO
616static std::string camera_topic(const char *dev_name)
617{
618 return std::string("/nps/camera/") + dev_name;
619}
620
630static void gazebo_setup_cameras(sdf::ElementPtr model)
631{
632 for (sdf::ElementPtr link = model->GetElement("link"); link; link = link->GetNextElement("link")) {
633 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
634 if (link->Get<std::string>("name") != cameras[i]->dev_name || !link->HasElement("sensor")) {
635 continue;
636 }
637 sdf::ElementPtr sensor = link->GetElement("sensor");
638 std::string name = sensor->Get<std::string>("name");
639 sensor->GetElement("topic")->Set(camera_topic(cameras[i]->dev_name));
640 sensor->GetElement("always_on")->Set(true);
641 sdf::ElementPtr camera = sensor->GetElement("camera");
642 if (name == "mt9f002") {
644 sdf::ElementPtr image = camera->GetElement("image");
645 image->GetElement("width")->Set(image->Get<int>("width") / NPS_MT9F002_SENSOR_RES_DIVIDER);
646 image->GetElement("height")->Set(image->Get<int>("height") / NPS_MT9F002_SENSOR_RES_DIVIDER);
647 if (camera->HasElement("lens") && camera->GetElement("lens")->HasElement("env_texture_size")) {
648 sdf::ElementPtr env = camera->GetElement("lens")->GetElement("env_texture_size");
649 env->Set(env->Get<int>() / NPS_MT9F002_SENSOR_RES_DIVIDER);
650 }
651 }
652 if (MT9F002_TARGET_FPS) {
653 sensor->GetElement("update_rate")->Set(Min(MT9F002_TARGET_FPS, sensor->Get<double>("update_rate")));
654 }
655 } else if (name == "mt9v117") {
656 if (MT9V117_TARGET_FPS) {
657 sensor->GetElement("update_rate")->Set(Min(MT9V117_TARGET_FPS, sensor->Get<double>("update_rate")));
658 }
659 }
660 sdf::ElementPtr image = camera->GetElement("image");
661 int w = image->Get<int>("width");
662 int h = image->Get<int>("height");
663
664 // Video config, as with Gazebo Classic
665 struct video_config_t *cam = cameras[i];
666 cam->output_size.w = w;
667 cam->output_size.h = h;
668 cam->sensor_size.w = w;
669 cam->sensor_size.h = h;
670 cam->crop.w = w;
671 cam->crop.h = h;
672 cam->fps = 0;
673 cam->camera_intrinsics.focal_x = w / 2.0f;
674 cam->camera_intrinsics.center_x = w / 2.0f;
675 cam->camera_intrinsics.focal_y = h / 2.0f;
676 cam->camera_intrinsics.center_y = h / 2.0f;
677 if (name == "mt9f002") {
678 // See boards/bebop/mt9f002.c
679 cam->output_size.w = MT9F002_OUTPUT_WIDTH;
680 cam->output_size.h = MT9F002_OUTPUT_HEIGHT;
681 cam->sensor_size.w = MT9F002_OUTPUT_WIDTH;
682 cam->sensor_size.h = MT9F002_OUTPUT_HEIGHT;
683 cam->crop.w = MT9F002_OUTPUT_WIDTH;
684 cam->crop.h = MT9F002_OUTPUT_HEIGHT;
685 cam->fps = MT9F002_TARGET_FPS;
686 cam->camera_intrinsics = {
687 .focal_x = MT9F002_FOCAL_X,
688 .focal_y = MT9F002_FOCAL_Y,
689 .center_x = MT9F002_CENTER_X,
690 .center_y = MT9F002_CENTER_Y,
691 .Dhane_k = MT9F002_DHANE_K
692 };
693 } else if (name == "mt9v117") {
694 // See boards/bebop/mt9v117.h
695 cam->fps = MT9V117_TARGET_FPS;
696 cam->camera_intrinsics = {
697 .focal_x = MT9V117_FOCAL_X,
698 .focal_y = MT9V117_FOCAL_Y,
699 .center_x = MT9V117_CENTER_X,
700 .center_y = MT9V117_CENTER_Y,
701 .Dhane_k = MT9V117_DHANE_K
702 };
703 }
704 gazebo_cams[i].sensor = name;
705 printf("NPS gz: camera %s (%s) %dx%d on %s\n", cam->dev_name, name.c_str(), w, h,
706 camera_topic(cam->dev_name).c_str());
707 }
708 }
709 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
710 if (gazebo_cams[i].sensor.empty()) {
711 fprintf(stderr, "NPS gz: no camera sensor in link '%s' of model '%s'\n", cameras[i]->dev_name, NPS_GAZEBO_AC_NAME);
712 }
713 }
714}
715
717static void gazebo_init_video(void)
718{
719 gz_node = std::make_unique<gz::transport::Node>();
720 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
721 if (gazebo_cams[i].sensor.empty()) {
722 continue;
723 }
724 struct gazebo_cam_t *gcam = &gazebo_cams[i];
725 std::function<void(const gz::msgs::Image &)> cb = [gcam](const gz::msgs::Image &msg) {
726 std::lock_guard<std::mutex> lock(gcam->mutex);
727 gcam->msg = msg;
728 gcam->fresh = true;
729 };
730 if (!gz_node->Subscribe(camera_topic(cameras[i]->dev_name), cb)) {
731 fprintf(stderr, "NPS gz: could not subscribe to %s\n", camera_topic(cameras[i]->dev_name).c_str());
732 }
733 }
734}
735
741static void read_image(struct image_t *img, const gz::msgs::Image &msg, bool is_mt9f002)
742{
743 const int w = msg.width();
744 const int h = msg.height();
745 const int step = msg.step() ? msg.step() : 3 * w;
746 if (is_mt9f002) {
748 } else {
750 }
751 const uint8_t *data_rgb = (const uint8_t *)msg.data().data();
752 uint8_t *data_yuv = (uint8_t *)(img->buf);
753 for (int y_yuv = 0; y_yuv < img->h; ++y_yuv) {
754 for (int x_yuv = 0; x_yuv < img->w; ++x_yuv) {
755 int x_rgb = x_yuv;
756 int y_rgb = y_yuv;
757 if (is_mt9f002) {
762 }
763 x_rgb = Clip(x_rgb, 0, w - 1);
764 y_rgb = Clip(y_rgb, 0, h - 1);
765 const uint8_t *rgb = data_rgb + step * y_rgb + 3 * x_rgb;
766 int idx_yuv = 2 * (img->w * y_yuv + x_yuv);
767 if (x_yuv % 2 == 0) {
768 data_yuv[idx_yuv] = - 0.148 * rgb[0] - 0.291 * rgb[1] + 0.439 * rgb[2] + 128; // U
769 } else {
770 data_yuv[idx_yuv] = 0.439 * rgb[0] - 0.368 * rgb[1] - 0.071 * rgb[2] + 128; // V
771 }
772 data_yuv[idx_yuv + 1] = 0.257 * rgb[0] + 0.504 * rgb[1] + 0.098 * rgb[2] + 16; // Y
773 }
774 }
775 img->ts.tv_sec = msg.header().stamp().sec();
776 img->ts.tv_usec = msg.header().stamp().nsec() / 1000;
777 img->pprz_ts = msg.header().stamp().sec() * 1000000 + msg.header().stamp().nsec() / 1000;
778 img->buf_idx = 0; // unused
779}
780
782static void gazebo_read_video(void)
783{
784 for (int i = 0; i < VIDEO_THREAD_MAX_CAMERAS && cameras[i] != NULL; ++i) {
785 struct gazebo_cam_t *gcam = &gazebo_cams[i];
786 gz::msgs::Image msg;
787 {
788 std::lock_guard<std::mutex> lock(gcam->mutex);
789 if (!gcam->fresh) {
790 continue;
791 }
792 msg.Swap(&gcam->msg);
793 gcam->fresh = false;
794 }
795 if (msg.pixel_format_type() != gz::msgs::PixelFormatType::RGB_INT8) {
796 fprintf(stderr, "NPS gz: camera %s: unsupported pixel format %d\n", cameras[i]->dev_name, msg.pixel_format_type());
797 continue;
798 }
799 struct image_t img;
800 read_image(&img, msg, gcam->sensor == "mt9f002");
802 image_free(&img);
803 }
804}
805#endif
static void h(const real32_T x[7], const real32_T q[4], real32_T y[6])
struct pprz_autopilot autopilot
Global autopilot structure.
Definition autopilot.c:50
Core autopilot interface common to all firmwares.
bool motors_on
motor status
Definition autopilot.h:68
gz-sim System that connects NPS with the simulation.
void PostUpdate(const gz::sim::UpdateInfo &info, const gz::sim::EntityComponentManager &ecm) override
bool Ready() const
void PreUpdate(const gz::sim::UpdateInfo &info, gz::sim::EntityComponentManager &ecm) override
gz::sim::Entity body_link_
gz::sim::Entity world_
void FindModel(gz::sim::EntityComponentManager &ecm)
gz::sim::Entity motor_links_[NPS_COMMANDS_NB]
gz::sim::Entity model_
void Configure(const gz::sim::Entity &entity, const std::shared_ptr< const sdf::Element > &, gz::sim::EntityComponentManager &ecm, gz::sim::EventManager &) override
void cv_run_device(struct video_config_t *device, struct image_t *img)
Definition cv.c:178
Computer vision framework for onboard processing.
static pthread_mutex_t mutex
static float sim_time
#define Min(x, y)
Definition esc_dshot.c:110
double psi
in radians
double theta
in radians
double phi
in radians
void double_quat_of_eulers(struct DoubleQuat *q, struct DoubleEulers *e)
angular rates
float m[3 *3]
double x
in meters
struct EcefCoor_d ecef
origin of local frame in ECEF
double z
in meters
double lat
in radians
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)
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
#define PPRZ_ISA_SEA_LEVEL_PRESSURE
ISA sea level standard atmospheric pressure in Pascal.
Definition pprz_isa.h:50
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.
uint8_t msg[10]
Buffer used for general comunication over SPI (out buffer)
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...
@ IMAGE_YUV422
UYVY format (uint16 per pixel)
Definition image.h:36
uint16_t w
The width.
Definition image.h:87
static const Vector3f gravity(0.f, 0.f, 9.81f)
Simple first order low pass filter with bilinear transform.
static void init_first_order_low_pass(struct FirstOrderLowPass *filter, float tau, const float sample_time, float value)
Init first order low pass filter.
static float update_first_order_low_pass(struct FirstOrderLowPass *filter, const float value)
Update first order low pass filter state with a new value.
First order low pass filter structure.
uint16_t foo
Definition main_demo5.c:58
uint8_t step
Definition max7456.c:140
static const int32_t yaw_coef[MOTOR_MIXING_NB_MOTOR]
#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
static float sign(float x)
sign function
Definition nav_fish.c:232
#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
static gazebo::physics::ModelPtr model
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)
NPS FDM rover init.
static struct DoubleVect3 vect_ned_of_enu(const gz::math::Vector3d &enu)
static std::unique_ptr< gz::sim::Server > server
static struct gazebo_actuators_t gazebo_actuators
void nps_fdm_run_step(bool launch, double *act_commands, int commands_nb)
Minimum complexity flight dynamic model In legacy Paparazzi simulator, was implemented in OCaml and c...
struct FirstOrderHighPass highpass[NPS_COMMANDS_NB]
double thrusts[NPS_COMMANDS_NB]
static void gazebo_write(double act_commands[], int commands_nb)
Compute the motor thrusts and torques from the normalized commands [0, 1], with NPS_ACTUATOR_THRUSTS ...
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.
static struct @367 gz_state
Simulation state, written in PaparazziSystem::PostUpdate (gz local frame, FLU body)
static void gazebo_read(void)
Fill the fdm struct from the gz state.
static struct DoubleRates rates_frd_of_flu(const gz::math::Vector3d &flu)
static struct @368 gz_cmd
Actuator commands in the body frame of each motor link, read in PaparazziSystem::PreUpdate.
static gz::math::Quaterniond local_to_enu(void)
double torques[NPS_COMMANDS_NB]
void nps_fdm_set_turbulence(double wind_speed, int turbulence_severity)
static void init_gazebo(void)
Start an embedded gz-sim server.
static struct DoubleEulers eulers_ned_of_enu(const gz::math::Quaterniond &q_enu)
NED to FRD euler angles from the ENU to FLU attitude.
#define NPS_GAZEBO_AC_NAME
static struct NedCoor_d ned_of_enu(const gz::math::Vector3d &enu)
void nps_fdm_set_wind(double speed, double dir)
void nps_fdm_run_init_step(void)
struct NpsFdm fdm
Holds all necessary NPS FDM state information.
static std::shared_ptr< PaparazziSystem > pprz_system
static struct DoubleVect3 frd_of_flu(const gz::math::Vector3d &flu)
Paparazzi double precision floating point algebra.
Paparazzi double-precision 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.
struct img_size_t output_size
Output image size.
char * dev_name
path to device
V4L2 device settings.
#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 char uint8_t
Typedef defining 8 bit unsigned char type.