40
40
* Coriolis g Corporation - January 2019
41
41
*/
42
42
43
+ #include " aero.hpp"
43
44
#include " sih.hpp"
44
45
45
46
#include < px4_platform_common/getopt.h>
@@ -67,8 +68,10 @@ Sih::Sih() :
67
68
const hrt_abstime task_start = hrt_absolute_time ();
68
69
_last_run = task_start;
69
70
_gps_time = task_start;
71
+ _airspeed_time = task_start;
70
72
_gt_time = task_start;
71
73
_dist_snsr_time = task_start;
74
+ _vehicle = (VehicleType)constrain (_sih_vtype.get (), 0 , 1 );
72
75
}
73
76
74
77
Sih::~Sih ()
@@ -149,6 +152,11 @@ void Sih::Run()
149
152
send_gps ();
150
153
}
151
154
155
+ if (_vehicle == VehicleType::FW && _now - _airspeed_time >= 50_ms) {
156
+ _airspeed_time = _now;
157
+ send_airspeed ();
158
+ }
159
+
152
160
// distance sensor published at 50 Hz
153
161
if (_now - _dist_snsr_time >= 20_ms
154
162
&& fabs (_distance_snsr_override) < 10000 ) {
@@ -254,24 +262,53 @@ void Sih::read_motors()
254
262
{
255
263
actuator_outputs_s actuators_out;
256
264
265
+ float pwm_middle = 0 .5f * (PWM_DEFAULT_MIN + PWM_DEFAULT_MAX);
266
+
257
267
if (_actuator_out_sub.update (&actuators_out)) {
258
268
for (int i = 0 ; i < NB_MOTORS; i++) { // saturate the motor signals
259
- float u_sp = constrain ((actuators_out.output [i] - PWM_DEFAULT_MIN) / (PWM_DEFAULT_MAX - PWM_DEFAULT_MIN), 0 .0f , 1 .0f );
260
- _u[i] = _u[i] + _dt / _T_TAU * (u_sp - _u[i]); // first order transfer function with time constant tau
269
+ if (_vehicle == VehicleType::FW && i < 3 ) { // control surfaces in range [-1,1]
270
+ _u[i] = constrain (2 .0f * (actuators_out.output [i] - pwm_middle) / (PWM_DEFAULT_MAX - PWM_DEFAULT_MIN), -1 .0f , 1 .0f );
271
+
272
+ } else { // throttle signals in range [0,1]
273
+ float u_sp = constrain ((actuators_out.output [i] - PWM_DEFAULT_MIN) / (PWM_DEFAULT_MAX - PWM_DEFAULT_MIN), 0 .0f , 1 .0f );
274
+ _u[i] = _u[i] + _dt / _T_TAU * (u_sp - _u[i]); // first order transfer function with time constant tau
275
+ }
261
276
}
262
277
}
263
278
}
264
279
265
280
// generate the motors thrust and torque in the body frame
266
281
void Sih::generate_force_and_torques ()
267
282
{
268
- _T_B = Vector3f (0 .0f , 0 .0f , -_T_MAX * (+_u[0 ] + _u[1 ] + _u[2 ] + _u[3 ]));
269
- _Mt_B = Vector3f (_L_ROLL * _T_MAX * (-_u[0 ] + _u[1 ] + _u[2 ] - _u[3 ]),
270
- _L_PITCH * _T_MAX * (+_u[0 ] - _u[1 ] + _u[2 ] - _u[3 ]),
271
- _Q_MAX * (+_u[0 ] + _u[1 ] - _u[2 ] - _u[3 ]));
283
+ if (_vehicle == VehicleType::MC) {
284
+ _T_B = Vector3f (0 .0f , 0 .0f , -_T_MAX * (+_u[0 ] + _u[1 ] + _u[2 ] + _u[3 ]));
285
+ _Mt_B = Vector3f (_L_ROLL * _T_MAX * (-_u[0 ] + _u[1 ] + _u[2 ] - _u[3 ]),
286
+ _L_PITCH * _T_MAX * (+_u[0 ] - _u[1 ] + _u[2 ] - _u[3 ]),
287
+ _Q_MAX * (+_u[0 ] + _u[1 ] - _u[2 ] - _u[3 ]));
288
+ _Fa_I = -_KDV * _v_I; // first order drag to slow down the aircraft
289
+ _Ma_B = -_KDW * _w_B; // first order angular damper
290
+
291
+ } else if (_vehicle == VehicleType::FW) {
292
+ _T_B = Vector3f (_T_MAX * _u[3 ], 0 .0f , 0 .0f ); // forward thruster
293
+ // _Mt_B = Vector3f(_Q_MAX*_u[3], 0.0f,0.0f); // thruster torque
294
+ _Mt_B = Vector3f ();
295
+ generate_aerodynamics ();
296
+ }
297
+
298
+ }
272
299
273
- _Fa_I = -_KDV * _v_I; // first order drag to slow down the aircraft
274
- _Ma_B = -_KDW * _w_B; // first order angular damper
300
+ void Sih::generate_aerodynamics ()
301
+ {
302
+ _v_B = _C_IB.transpose () * _v_I; // velocity in body frame [m/s]
303
+ float altitude = _H0 - _p_I (2 );
304
+ _wing_l.update_aero (_v_B, _w_B, altitude, _u[0 ]*FLAP_MAX);
305
+ _wing_r.update_aero (_v_B, _w_B, altitude, -_u[0 ]*FLAP_MAX);
306
+ _tailplane.update_aero (_v_B, _w_B, altitude, _u[1 ]*FLAP_MAX, _T_MAX * _u[3 ]);
307
+ _fin.update_aero (_v_B, _w_B, altitude, _u[2 ]*FLAP_MAX, _T_MAX * _u[3 ]);
308
+ _Fa_I = _C_IB * (_wing_l.get_Fa () + _wing_r.get_Fa () + _tailplane.get_Fa () + _fin.get_Fa ()) - _KDV *
309
+ _v_I; // sum of aerodynamic forces
310
+ // _Ma_B = wing_l.Ma + wing_r.Ma + tailplane.Ma + fin.Ma + flap_moments() -_KDW * _w_B; // aerodynamic moments
311
+ _Ma_B = _wing_l.get_Ma () + _wing_r.get_Ma () + _tailplane.get_Ma () + _fin.get_Ma () - _KDW * _w_B; // aerodynamic moments
275
312
}
276
313
277
314
// apply the equations of motion of a rigid body and integrate one step
@@ -282,40 +319,79 @@ void Sih::equations_of_motion()
282
319
// Equations of motion of a rigid body
283
320
_p_I_dot = _v_I; // position differential
284
321
_v_I_dot = (_W_I + _Fa_I + _C_IB * _T_B) / _MASS; // conservation of linear momentum
285
- _q_dot = _q.derivative1 (_w_B); // attitude differential
322
+ // _q_dot = _q.derivative1(_w_B); // attitude differential
323
+ _dq = expq (0 .5f * _dt * _w_B);
286
324
_w_B_dot = _Im1 * (_Mt_B + _Ma_B - _w_B.cross (_I * _w_B)); // conservation of angular momentum
287
325
288
326
// fake ground, avoid free fall
289
327
if (_p_I (2 ) > 0 .0f && (_v_I_dot (2 ) > 0 .0f || _v_I (2 ) > 0 .0f )) {
290
- if (!_grounded) { // if we just hit the floor
291
- // for the accelerometer, compute the acceleration that will stop the vehicle in one time step
292
- _v_I_dot = -_v_I / _dt;
293
-
294
- } else {
295
- _v_I_dot.setZero ();
328
+ if (_vehicle == VehicleType::MC) {
329
+ if (!_grounded) { // if we just hit the floor
330
+ // for the accelerometer, compute the acceleration that will stop the vehicle in one time step
331
+ _v_I_dot = -_v_I / _dt;
332
+
333
+ } else {
334
+ _v_I_dot.setZero ();
335
+ }
336
+
337
+ _v_I.setZero ();
338
+ _w_B.setZero ();
339
+ _grounded = true ;
340
+
341
+ } else if (_vehicle == VehicleType::FW) {
342
+ if (!_grounded) { // if we just hit the floor
343
+ // for the accelerometer, compute the acceleration that will stop the vehicle in one time step
344
+ _v_I_dot (2 ) = -_v_I (2 ) / _dt;
345
+
346
+ } else {
347
+ // we only allow negative acceleration in order to takeoff
348
+ _v_I_dot (2 ) = fminf (_v_I_dot (2 ), 0 .0f );
349
+ }
350
+
351
+ // integration: Euler forward
352
+ _p_I = _p_I + _p_I_dot * _dt;
353
+ _v_I = _v_I + _v_I_dot * _dt;
354
+ Eulerf RPY = Eulerf (_q);
355
+ RPY (0 ) = 0 .0f ; // no roll
356
+ RPY (1 ) = radians (0 .0f ); // pitch slightly up to get some lift
357
+ _q = Quatf (RPY);
358
+ _w_B.setZero ();
359
+ _grounded = true ;
296
360
}
297
361
298
- _v_I.setZero ();
299
- _w_B.setZero ();
300
- _grounded = true ;
301
-
302
362
} else {
303
363
// integration: Euler forward
304
364
_p_I = _p_I + _p_I_dot * _dt;
305
365
_v_I = _v_I + _v_I_dot * _dt;
306
- _q = _q + _q_dot * _dt ; // as given in attitude_estimator_q_main.cpp
366
+ _q = _q * _dq ; // as given in attitude_estimator_q_main.cpp
307
367
_q.normalize ();
308
- _w_B = _w_B + _w_B_dot * _dt;
368
+ // integration Runge-Kutta 4
369
+ // rk4_update(_p_I, _v_I, _q, _w_B);
370
+ _w_B = constrain (_w_B + _w_B_dot * _dt, -6 .0f * M_PI_F, 6 .0f * M_PI_F);
309
371
_grounded = false ;
310
372
}
311
373
}
312
374
375
+ // Sih::States Sih::eom_f(States x) // equations of motion f: x'=f(x)
376
+ // {
377
+ // States x_dot{}; // dx/dt
378
+
379
+ // Dcmf C_IB = matrix::Dcm<float>(x.q); // body to inertial transformation
380
+ // // Equations of motion of a rigid body
381
+ // x_dot.p_I = x.v_I; // position differential
382
+ // x_dot.v_I = (_W_I + _Fa_I + C_IB * _T_B) / _MASS; // conservation of linear momentum
383
+ // x_dot.q = x.q.derivative1(x.w_B); // attitude differential
384
+ // x_dot.w_B = _Im1 * (_Mt_B + _Ma_B - x.w_B.cross(_I * x.w_B)); // conservation of angular momentum
385
+
386
+ // return x_dot;
387
+ // }
388
+
313
389
// reconstruct the noisy sensor signals
314
390
void Sih::reconstruct_sensors_signals ()
315
391
{
316
- // The sensor signals reconstruction and noise levels are from
317
- // Bulka, Eitan, and Meyer Nahon. "Autonomous fixed-wing aerobatics: from theory to flight."
318
- // In 2018 IEEE International Conference on Robotics and Automation (ICRA), pp. 6573-6580. IEEE, 2018.
392
+ // The sensor signals reconstruction and noise levels are from [1]
393
+ // [1] Bulka, Eitan, and Meyer Nahon. "Autonomous fixed-wing aerobatics: from theory to flight."
394
+ // In 2018 IEEE International Conference on Robotics and Automation (ICRA), pp. 6573-6580. IEEE, 2018.
319
395
320
396
// IMU
321
397
_acc = _C_IB.transpose () * (_v_I_dot - Vector3f (0 .0f , 0 .0f , CONSTANTS_ONE_G)) + noiseGauss3f (0 .5f , 1 .7f , 1 .4f );
@@ -376,6 +452,17 @@ void Sih::send_gps()
376
452
_sensor_gps_pub.publish (_sensor_gps);
377
453
}
378
454
455
+ void Sih::send_airspeed ()
456
+ {
457
+ airspeed_s airspeed{};
458
+ airspeed.timestamp = _now;
459
+ airspeed.true_airspeed_m_s = fmaxf (0 .1f , _v_B (0 ) + generate_wgn () * 0 .2f );
460
+ airspeed.indicated_airspeed_m_s = airspeed.true_airspeed_m_s * sqrtf (_wing_l.get_rho () / RHO);
461
+ airspeed.air_temperature_celsius = _baro_temp_c;
462
+ airspeed.confidence = 0 .7f ;
463
+ _airspeed_pub.publish (airspeed);
464
+ }
465
+
379
466
void Sih::send_dist_snsr ()
380
467
{
381
468
_distance_snsr.timestamp = _now;
@@ -430,6 +517,22 @@ void Sih::publish_sih()
430
517
_gpos_gt_pub.publish (_gpos_gt);
431
518
}
432
519
520
+ // quaternion exponential as defined in [3]
521
+ Quatf Sih::expq (const matrix::Vector3f &u)
522
+ {
523
+ float u_norm = u.norm ();
524
+ Vector3f v;
525
+
526
+ if (u_norm < 1 .0e-6f ) { // error will be smaller than 1e-18
527
+ v = (1 .0f - u_norm * u_norm / 6 .0f ) * u; // first taylor serie term of sin(x)/x
528
+
529
+ } else {
530
+ v = sinf (u_norm) / u_norm * u;
531
+ }
532
+
533
+ return Quatf (cosf (u_norm), v (0 ), v (1 ), v (2 ));
534
+ }
535
+
433
536
float Sih::generate_wgn () // generate white Gaussian noise sample with std=1
434
537
{
435
538
// algorithm 1:
@@ -465,6 +568,37 @@ Vector3f Sih::noiseGauss3f(float stdx, float stdy, float stdz)
465
568
return Vector3f (generate_wgn () * stdx, generate_wgn () * stdy, generate_wgn () * stdz);
466
569
}
467
570
571
+ int Sih::print_status ()
572
+ {
573
+ if (_vehicle == VehicleType::MC) {
574
+ PX4_INFO (" Running MC" );
575
+
576
+ } else {
577
+ PX4_INFO (" Running FW" );
578
+ }
579
+
580
+ PX4_INFO (" vehicle landed: %d" , _grounded);
581
+ PX4_INFO (" dt [us]: %d" , (int )(_dt * 1e6f));
582
+ PX4_INFO (" inertial position NED (m)" );
583
+ _p_I.print ();
584
+ PX4_INFO (" inertial velocity NED (m/s)" );
585
+ _v_I.print ();
586
+ PX4_INFO (" attitude roll-pitch-yaw (deg)" );
587
+ (Eulerf (_q) * 180 .0f / M_PI_F).print ();
588
+ PX4_INFO (" angular acceleration roll-pitch-yaw (deg/s)" );
589
+ (_w_B * 180 .0f / M_PI_F).print ();
590
+ PX4_INFO (" actuator signals" );
591
+ Vector<float , 8 > u = Vector<float , 8 >(_u);
592
+ u.transpose ().print ();
593
+ PX4_INFO (" Aerodynamic forces NED inertial (N)" );
594
+ _Fa_I.print ();
595
+ PX4_INFO (" Aerodynamic moments body frame (Nm)" );
596
+ _Ma_B.print ();
597
+ PX4_INFO (" v_I.z: %f" , (double )_v_I (2 ));
598
+ PX4_INFO (" v_I_dot.z: %f" , (double )_v_I_dot (2 ));
599
+ return 0 ;
600
+ }
601
+
468
602
int Sih::task_spawn (int argc, char *argv[])
469
603
{
470
604
Sih *instance = new Sih ();
0 commit comments