Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
nps_fdm_pybullet.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2021 Fabien-B <fabien-b@github.com>
3  *
4  * This file is part of paparazzi. See LICENCE file.
5  */
6 
7 #include "nps_fdm.h"
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <Python.h>
12 
13 #include "math/pprz_geodetic.h"
16 #include "math/pprz_algebra.h"
18 #include "math/pprz_isa.h"
19 
20 #include "generated/airframe.h"
21 #include "generated/flight_plan.h"
22 
23 #include "state.h"
24 
25 #define _WIDEN(x) L ## x
26 #define WIDEN(x) _WIDEN(x)
27 
28 #ifndef PYTHON_EXEC
29 #define PYTHON_EXEC "python3"
30 #endif
31 MESSAGE("PyBullet using" VALUE(PYTHON_EXEC))
32 
33 #ifndef PYBULLET_GUI
34 #define PYBULLET_GUI TRUE
35 #endif
36 
37 #ifdef NPS_ACTUATORS_ORDER
38  int actuators_order[ACTUATORS_NB] = NPS_ACTUATORS_ORDER;
39 #else
40  #error "[PyBullet] missing NPS_ACTUATORS_ORDER define!"
41 #endif
42 
43 #ifndef NPS_PYBULLET_MODULE
44 #define NPS_PYBULLET_MODULE "simple_quad_sim"
45 MESSAGE("NPS_PYBULLET_MODULE not defined, take 'simple_quad_sim' as default value.")
46 #endif
47 
48 
49 #ifndef NPS_PYBULLET_URDF
50 #define NPS_PYBULLET_URDF "robobee.urdf"
51 MESSAGE("NPS_PYBULLET_URDF not defined, take 'robobee.urdf' as default value.")
52 #endif
53 
54 
55 // NpsFdm structure
56 struct NpsFdm fdm;
57 
58 // Reference point
59 static struct LtpDef_d ltpdef;
60 
61 //rotation from pybullet to pprz
62 struct DoubleQuat quat_to_pprz = {
63  1/sqrt(2),
64  0,
65  0,
66  1/sqrt(2),
67 };
68 
69 // python object declarations
70 PyObject *fdm_module = NULL;
71 PyObject *bullet_fdm = NULL;
72 
73 // static python related function declaration
74 static void py_check_status(PyConfig* config, PyStatus* status);
75 static void py_check(bool exit_on_error, int line_nb);
76 static void python_init(double dt);
77 
78 // Static functions declaration
79 static void init_ltp(void);
80 
81 static void get_pos(PyObject* ppos);
82 static void get_vel(PyObject* pvel);
83 static void get_acc(PyObject* pacc);
84 
85 static void get_orient(PyObject* porient);
86 static void get_ang_vel(PyObject* pang_vel);
87 static void get_ang_acc(PyObject* pang_acc);
88 
89 void nps_fdm_init(double dt)
90 {
91  python_init(dt);
92 
93  fdm.init_dt = dt; // (1 / simulation freq)
94  fdm.curr_dt = dt;
95  fdm.time = dt;
96 
97  fdm.on_ground = TRUE;
98 
99  fdm.nan_count = 0;
100  fdm.pressure = -1;
102  fdm.total_pressure = -1;
103  fdm.dynamic_pressure = -1;
104  fdm.temperature = -1;
105 
107  init_ltp();
108 
109  // run a first step to initialize all fdm fields
110  double dummy_commands[] = {1, 2, 3, 4};
111  nps_fdm_run_step(false, dummy_commands, 4);
112 }
113 
114 void nps_fdm_run_step(bool launch __attribute__((unused)), double *commands, int commands_nb __attribute__((unused)))
115 {
116  // TODO create a np.array instead ?
117  PyObject* pcmd = PyList_New(commands_nb);
118 
119  for(int i=0; i<commands_nb; i++) {
120  int j = actuators_order[i];
121  PyList_SetItem(pcmd, i, PyFloat_FromDouble(commands[j]));
122  }
123 
124  if(commands[0] < 0) {
125  return;
126  }
127 
128  PyObject* ret = PyObject_CallMethod(bullet_fdm, "step", "(O)", pcmd);
129  py_check(true, __LINE__);
130 
131  // borrowed references
132  PyObject* ppos = PyDict_GetItemString(ret, "pos");
133  PyObject* pvel = PyDict_GetItemString(ret, "vel");
134  PyObject* pacc = PyDict_GetItemString(ret, "accel");
135 
136  PyObject* pquat = PyDict_GetItemString(ret, "quat");
137  PyObject* pang_v = PyDict_GetItemString(ret, "ang_v");
138  PyObject* pang_acc = PyDict_GetItemString(ret, "ang_accel");
139  //PyObject* prpy = PyDict_GetItemString(ret, "rpy");
140  py_check(true, __LINE__);
141 
142 
143  get_pos(ppos);
144  get_orient(pquat);
145  get_vel(pvel);
146  get_acc(pacc);
147  get_ang_vel(pang_v);
148  get_ang_acc(pang_acc);
149 
150  Py_XDECREF(pcmd);
151  Py_XDECREF(ret);
152 }
153 
154 static void get_pos(PyObject* ppos) {
155  // get position from pybullet, in ENU, in the local frame
156  struct EnuCoor_d enu_pos;
157  enu_pos.x = PyFloat_AsDouble(PyTuple_GetItem(ppos, 0));
158  enu_pos.y = PyFloat_AsDouble(PyTuple_GetItem(ppos, 1));
159  enu_pos.z = PyFloat_AsDouble(PyTuple_GetItem(ppos, 2));
160  py_check(true, __LINE__);
161 
162  /*********** positions *************/
164  ecef_of_enu_point_d(&fdm.ecef_pos, &ltpdef, &enu_pos);
166  fdm.hmsl = -fdm.ltpprz_pos.z; //FIXME
167  fdm.agl = fdm.hmsl; //FIXME
168 }
169 
170 static void get_vel(PyObject* pvel) {
171  // get velocity from pybullet, in ENU wrt local frame (fixed/world)
172  struct EnuCoor_d enu_vel;
173  enu_vel.x = PyFloat_AsDouble(PyTuple_GetItem(pvel, 0));
174  enu_vel.y = PyFloat_AsDouble(PyTuple_GetItem(pvel, 1));
175  enu_vel.z = PyFloat_AsDouble(PyTuple_GetItem(pvel, 2));
176  py_check(true, __LINE__);
177 
178  /*********** velocities *************/
183  // /** velocity in body frame, wrt ECEF frame */
184  // struct DoubleVect3 body_ecef_vel; /* aka UVW */
185  // /** velocity in ltppprz frame, wrt ECEF frame */
187 }
188 
189 static void get_acc(PyObject* pacc) {
190 
191  struct EnuCoor_d enu_accel = {
192  PyFloat_AsDouble(PyTuple_GetItem(pacc, 0)),
193  PyFloat_AsDouble(PyTuple_GetItem(pacc, 1)),
194  PyFloat_AsDouble(PyTuple_GetItem(pacc, 2)),
195  };
196 
201 
202 
204 
206  struct DoubleVect3 tmp = {
209  fdm.ltp_ecef_accel.z - 10,
210  };
212 
213  // /** acceleration in body frame, wrt ECEF frame */
214  // fdm.body_ecef_accel;
215  // /** acceleration in body frame, wrt ECI inertial frame */
216  // fdm.body_inertial_accel;
217 
218 
219 }
220 
221 static void get_orient(PyObject* pquat) {
222  // /* attitude */
223 
224  struct DoubleQuat enu_quat = {
225  PyFloat_AsDouble(PyTuple_GetItem(pquat, 3)),
226  PyFloat_AsDouble(PyTuple_GetItem(pquat, 1)),
227  PyFloat_AsDouble(PyTuple_GetItem(pquat, 0)),
228  -PyFloat_AsDouble(PyTuple_GetItem(pquat, 2)),
229  };
230 
235 }
236 
237 static void get_ang_vel(PyObject* pang_vel) {
238 
239 
240  struct DoubleVect3 sim_rates, pprz_rates;
241  sim_rates.x = PyFloat_AsDouble(PyTuple_GetItem(pang_vel, 1));
242  sim_rates.y = PyFloat_AsDouble(PyTuple_GetItem(pang_vel, 0));
243  sim_rates.z = -PyFloat_AsDouble(PyTuple_GetItem(pang_vel, 2));
244 
245  double_quat_vmult(&pprz_rates, &fdm.ltp_to_body_quat, &sim_rates);
246 
247  fdm.body_inertial_rotvel.p = pprz_rates.x;
248  fdm.body_inertial_rotvel.q = pprz_rates.y;
249  fdm.body_inertial_rotvel.r = pprz_rates.z;
251 
252 
253 }
254 
255 static void get_ang_acc(PyObject* pang_acc) {
256 
257  struct DoubleVect3 sim_rotaccel, pprz_rotaccel;
258  sim_rotaccel.x = PyFloat_AsDouble(PyTuple_GetItem(pang_acc, 1));
259  sim_rotaccel.y = PyFloat_AsDouble(PyTuple_GetItem(pang_acc, 0));
260  sim_rotaccel.z = -PyFloat_AsDouble(PyTuple_GetItem(pang_acc, 2));
261 
262  double_quat_vmult(&pprz_rotaccel, &fdm.ltp_to_body_quat, &sim_rotaccel);
263 
264  fdm.body_inertial_rotaccel.p = pprz_rotaccel.x;
265  fdm.body_inertial_rotaccel.q = pprz_rotaccel.y;
266  fdm.body_inertial_rotaccel.r = pprz_rotaccel.z;
268 }
269 
270 
271 
272 /**************************
273  ** Generating LTP plane **
274  **************************/
275 
276 static void init_ltp(void)
277 {
278 
279  struct LlaCoor_d llh_nav0; /* Height above the ellipsoid */
280  llh_nav0.lat = RadOfDeg((double)NAV_LAT0 / 1e7);
281  llh_nav0.lon = RadOfDeg((double)NAV_LON0 / 1e7);
282  llh_nav0.alt = NAV_ALT0 / 1000.0;
283 
284  struct EcefCoor_d ecef_nav0;
285 
286  ecef_of_lla_d(&ecef_nav0, &llh_nav0);
287 
288  ltp_def_from_ecef_d(&ltpdef, &ecef_nav0);
289  fdm.ecef_pos = ecef_nav0;
290 
291 
292 // TODO vérifier tout ça
293  fdm.ltp_g.x = 0.;
294  fdm.ltp_g.y = 0.;
295  fdm.ltp_g.z = 0.; // accel data are already with the correct format
296 
297 #ifdef AHRS_H_X
298  fdm.ltp_h.x = AHRS_H_X;
299  fdm.ltp_h.y = AHRS_H_Y;
300  fdm.ltp_h.z = AHRS_H_Z;
301  PRINT_CONFIG_MSG("Using magnetic field as defined in airframe file.")
302 #else
303  fdm.ltp_h.x = 0.4912;
304  fdm.ltp_h.y = 0.1225;
305  fdm.ltp_h.z = 0.8624;
306 #endif
307 
308 }
309 
310 
311 /*****************************************************/
312 // Atmosphere function (we don't need that features) //
313 void nps_fdm_set_wind(double speed __attribute__((unused)),
314  double dir __attribute__((unused)))
315 {
316 }
317 
318 void nps_fdm_set_wind_ned(double wind_north __attribute__((unused)),
319  double wind_east __attribute__((unused)),
320  double wind_down __attribute__((unused)))
321 {
322 }
323 
324 void nps_fdm_set_turbulence(double wind_speed __attribute__((unused)),
325  int turbulence_severity __attribute__((unused)))
326 {
327 }
328 
329 void nps_fdm_set_temperature(double temp __attribute__((unused)),
330  double h __attribute__((unused)))
331 {
332 }
333 
334 static void python_init(double dt) {
335  PyStatus status;
336  PyConfig config;
337  PyConfig_InitPythonConfig(&config);
338 
339  // needed for the prints
340  config.configure_c_stdio = 1;
341  config.buffered_stdio = 0;
342 
343  status = PyConfig_SetString(&config, &config.program_name, WIDEN(PYTHON_EXEC));
345  status = Py_InitializeFromConfig(&config);
347 
348  // add path to sys.path
349  PyObject *sys_path = PySys_GetObject("path");
350  PyObject *module_path = PyUnicode_FromString( PAPARAZZI_SRC "/sw/simulator/nps/pybullet");
351  PyList_Append(sys_path, module_path);
352  Py_DECREF(module_path);
353 
354  fdm_module = PyImport_ImportModule(NPS_PYBULLET_MODULE);
355  py_check(true, __LINE__);
356 
357  PyObject* bullet_fdm_class = PyObject_GetAttrString(fdm_module, "BulletFDM");
358  py_check(true, __LINE__);
359 
360  PyObject* fdm_ctor_args = Py_BuildValue("(d)", dt);
361  PyObject* fdm_ctor_kwargs = Py_BuildValue("{siss}", "GUI", PYBULLET_GUI, "urdf", NPS_PYBULLET_URDF);
362  bullet_fdm = PyObject_Call(bullet_fdm_class, fdm_ctor_args, fdm_ctor_kwargs);
363 
364  py_check(true, __LINE__);
365  Py_DECREF(fdm_ctor_args);
366  Py_DECREF(bullet_fdm_class);
367 }
368 
369 static void py_check_status(PyConfig* config, PyStatus* status) {
370  if (PyStatus_Exception(*status)) {
371  printf("error\n");
372  PyConfig_Clear(config);
373  Py_ExitStatusException(*status);
374  exit(1);
375  }
376 }
377 
378 
379 static void py_check(bool exit_on_error, int line_nb) {
380  if(PyErr_Occurred()) {
381  printf("Error at line %d\n", line_nb);
382  PyErr_Print();
383  if(exit_on_error) {
384  exit(1);
385  }
386  }
387 }
static void h(const real32_T x[7], const real32_T q[4], real32_T y[6])
pprz_t commands[COMMANDS_NB]
Definition: commands.c:30
double psi
in radians
double p
in rad/s^2
double q
in rad/s^2
double r
in rad/s^2
void double_eulers_of_quat(struct DoubleEulers *e, struct DoubleQuat *q)
void double_quat_vmult(struct DoubleVect3 *v_out, struct DoubleQuat *q, struct DoubleVect3 *v_in)
void double_quat_comp(struct DoubleQuat *a2c, struct DoubleQuat *a2b, struct DoubleQuat *b2c)
Composition (multiplication) of two quaternions.
Roation quaternion.
#define EULERS_COPY(_a, _b)
Definition: pprz_algebra.h:268
#define QUAT_COPY(_qo, _qi)
Definition: pprz_algebra.h:596
#define VECT3_COPY(_a, _b)
Definition: pprz_algebra.h:140
double y
in meters
double z
in meters
double z
in meters
double y
in meters
double x
in meters
double lat
in radians
double alt
in meters above WGS84 reference ellipsoid
double lon
in radians
double x
in meters
void ltp_def_from_ecef_d(struct LtpDef_d *def, struct EcefCoor_d *ecef)
void ecef_of_enu_vect_d(struct EcefCoor_d *ecef, struct LtpDef_d *def, struct EnuCoor_d *enu)
void lla_of_ecef_d(struct LlaCoor_d *lla, struct EcefCoor_d *ecef)
void ecef_of_lla_d(struct EcefCoor_d *ecef, struct LlaCoor_d *lla)
void ecef_of_enu_point_d(struct EcefCoor_d *ecef, struct LtpDef_d *def, struct EnuCoor_d *enu)
vector in EarthCenteredEarthFixed coordinates
vector in East North Up coordinates Units: meters
vector in Latitude, Longitude and Altitude
definition of the local (flat earth) coordinate system
#define VECT3_NED_OF_ENU(_o, _i)
#define PPRZ_ISA_SEA_LEVEL_PRESSURE
ISA sea level standard atmospheric pressure in Pascal.
Definition: pprz_isa.h:50
PRINT_CONFIG_MSG("USE_INS_NAV_INIT defaulting to TRUE")
double time
Definition: nps_fdm.h:46
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
struct DoubleQuat ltpprz_to_body_quat
Definition: nps_fdm.h:93
double init_dt
Definition: nps_fdm.h:47
double total_pressure
total atmospheric pressure in Pascal
Definition: nps_fdm.h:111
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 DoubleRates body_inertial_rotvel
Definition: nps_fdm.h:101
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
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 DoubleRates body_inertial_rotaccel
Definition: nps_fdm.h:102
double temperature
current temperature in degrees Celcius
Definition: nps_fdm.h:113
struct DoubleRates body_ecef_rotaccel
Definition: nps_fdm.h:98
struct DoubleEulers ltpprz_to_body_eulers
Definition: nps_fdm.h:94
struct LlaCoor_d lla_pos
Definition: nps_fdm.h:55
struct DoubleVect3 body_accel
acceleration in body frame as measured by an accelerometer (incl.
Definition: nps_fdm.h:87
bool on_ground
Definition: nps_fdm.h:49
int nan_count
Definition: nps_fdm.h:50
struct NedCoor_d ltp_ecef_accel
acceleration in LTP frame, wrt ECEF frame
Definition: nps_fdm.h:76
Definition: nps_fdm.h:44
void nps_fdm_set_wind_ned(double wind_north, double wind_east, double wind_down)
static void python_init(double dt)
void nps_fdm_init(double dt)
NPS FDM rover init.
static void init_ltp(void)
struct DoubleQuat quat_to_pprz
#define PYTHON_EXEC
void nps_fdm_run_step(bool launch, double *commands, int commands_nb)
Minimum complexity flight dynamic model In legacy Paparazzi simulator, was implemented in OCaml and c...
PyObject * bullet_fdm
PyObject * fdm_module
#define NPS_PYBULLET_URDF
void nps_fdm_set_temperature(double temp, double h)
Set temperature in degrees Celcius at given height h above MSL.
#define WIDEN(x)
static void get_vel(PyObject *pvel)
static void get_ang_acc(PyObject *pang_acc)
static void get_orient(PyObject *porient)
static void get_ang_vel(PyObject *pang_vel)
void nps_fdm_set_turbulence(double wind_speed, int turbulence_severity)
#define PYBULLET_GUI
static void py_check_status(PyConfig *config, PyStatus *status)
#define NPS_PYBULLET_MODULE
static void get_pos(PyObject *ppos)
void nps_fdm_set_wind(double speed, double dir)
static void get_acc(PyObject *pacc)
struct NpsFdm fdm
Holds all necessary NPS FDM state information.
static void py_check(bool exit_on_error, int line_nb)
static struct LtpDef_d ltpdef
uint8_t status
Paparazzi generic algebra macros.
Paparazzi floating point algebra.
Paparazzi generic macros for geodetic calculations.
Paparazzi double-precision floating point math for geodetic calculations.
Paparazzi floating point math for geodetic calculations.
Paparazzi atmospheric pressure conversion utilities.
static const float dir[]
API to get/set the generic vehicle states.
#define TRUE
Definition: std.h:4
static const struct usb_config_descriptor config
Definition: usb_ser_hw.c:200