|
57 | 57 | using namespace math;
|
58 | 58 | using namespace matrix;
|
59 | 59 |
|
60 |
| -int Sih::print_usage(const char *reason) |
61 |
| -{ |
62 |
| - if (reason) { |
63 |
| - PX4_WARN("%s\n", reason); |
64 |
| - } |
65 |
| - |
66 |
| - PRINT_MODULE_DESCRIPTION( |
67 |
| - R"DESCR_STR( |
68 |
| -### Description |
69 |
| -This module provide a simulator for quadrotors running fully |
70 |
| -inside the hardware autopilot. |
71 |
| -
|
72 |
| -This simulator subscribes to "actuator_outputs" which are the actuator pwm |
73 |
| -signals given by the mixer. |
74 |
| -
|
75 |
| -This simulator publishes the sensors signals corrupted with realistic noise |
76 |
| -in order to incorporate the state estimator in the loop. |
77 |
| -
|
78 |
| -### Implementation |
79 |
| -The simulator implements the equations of motion using matrix algebra. |
80 |
| -Quaternion representation is used for the attitude. |
81 |
| -Forward Euler is used for integration. |
82 |
| -Most of the variables are declared global in the .hpp file to avoid stack overflow. |
83 |
| -
|
84 |
| -
|
85 |
| -)DESCR_STR"); |
86 |
| - |
87 |
| - PRINT_MODULE_USAGE_NAME("sih", "simulation"); |
88 |
| - PRINT_MODULE_USAGE_COMMAND("start"); |
89 |
| - PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); |
90 |
| - |
91 |
| - return 0; |
92 |
| -} |
93 |
| - |
94 | 60 | int Sih::print_status()
|
95 | 61 | {
|
96 |
| - PX4_INFO("Running"); |
97 |
| - return 0; |
| 62 | + PX4_INFO("Running"); |
| 63 | + return 0; |
98 | 64 | }
|
99 | 65 |
|
100 | 66 | int Sih::custom_command(int argc, char *argv[])
|
101 | 67 | {
|
102 |
| - return print_usage("unknown command"); |
| 68 | + return print_usage("unknown command"); |
103 | 69 | }
|
104 | 70 |
|
105 | 71 |
|
106 | 72 | int Sih::task_spawn(int argc, char *argv[])
|
107 | 73 | {
|
108 |
| - _task_id = px4_task_spawn_cmd("sih", |
109 |
| - SCHED_DEFAULT, |
110 |
| - SCHED_PRIORITY_MAX, |
111 |
| - 1024, |
112 |
| - (px4_main_t)&run_trampoline, |
113 |
| - (char *const *)argv); |
114 |
| - |
115 |
| - if (_task_id < 0) { |
116 |
| - _task_id = -1; |
117 |
| - return -errno; |
118 |
| - } |
| 74 | + _task_id = px4_task_spawn_cmd("sih", |
| 75 | + SCHED_DEFAULT, |
| 76 | + SCHED_PRIORITY_MAX, |
| 77 | + 1024, |
| 78 | + (px4_main_t)&run_trampoline, |
| 79 | + (char *const *)argv); |
| 80 | + |
| 81 | + if (_task_id < 0) { |
| 82 | + _task_id = -1; |
| 83 | + return -errno; |
| 84 | + } |
119 | 85 |
|
120 |
| - return 0; |
| 86 | + return 0; |
121 | 87 | }
|
122 | 88 |
|
123 | 89 | Sih *Sih::instantiate(int argc, char *argv[])
|
124 | 90 | {
|
125 |
| - Sih *instance = new Sih(); |
| 91 | + Sih *instance = new Sih(); |
126 | 92 |
|
127 |
| - if (instance == nullptr) { |
128 |
| - PX4_ERR("alloc failed"); |
129 |
| - } |
| 93 | + if (instance == nullptr) { |
| 94 | + PX4_ERR("alloc failed"); |
| 95 | + } |
130 | 96 |
|
131 |
| - return instance; |
| 97 | + return instance; |
132 | 98 | }
|
133 | 99 |
|
134 |
| -Sih::Sih() |
135 |
| - : ModuleParams(nullptr), |
136 |
| - _loop_perf(perf_alloc(PC_ELAPSED, "sih_execution")), |
137 |
| - _sampling_perf(perf_alloc(PC_ELAPSED, "sih_sampling")) |
| 100 | +Sih::Sih() : |
| 101 | + ModuleParams(nullptr), |
| 102 | + _loop_perf(perf_alloc(PC_ELAPSED, "sih_execution")), |
| 103 | + _sampling_perf(perf_alloc(PC_ELAPSED, "sih_sampling")) |
138 | 104 | {
|
139 | 105 | }
|
140 | 106 |
|
141 | 107 | void Sih::run()
|
142 | 108 | {
|
| 109 | + // to subscribe to (read) the actuators_out pwm |
| 110 | + _actuator_out_sub = orb_subscribe(ORB_ID(actuator_outputs)); |
143 | 111 |
|
144 |
| - // to subscribe to (read) the actuators_out pwm |
145 |
| - _actuator_out_sub = orb_subscribe(ORB_ID(actuator_outputs)); |
146 |
| - |
147 |
| - // initialize parameters |
148 |
| - _parameter_update_sub = orb_subscribe(ORB_ID(parameter_update)); |
149 |
| - parameters_update_poll(); |
150 |
| - |
151 |
| - init_variables(); |
152 |
| - init_sensors(); |
| 112 | + // initialize parameters |
| 113 | + _parameter_update_sub = orb_subscribe(ORB_ID(parameter_update)); |
| 114 | + parameters_update_poll(); |
153 | 115 |
|
154 |
| - const hrt_abstime task_start = hrt_absolute_time(); |
155 |
| - _last_run = task_start; |
156 |
| - _gps_time = task_start; |
157 |
| - _serial_time = task_start; |
| 116 | + init_variables(); |
| 117 | + init_sensors(); |
158 | 118 |
|
159 |
| - px4_sem_init(&_data_semaphore, 0, 0); |
| 119 | + const hrt_abstime task_start = hrt_absolute_time(); |
| 120 | + _last_run = task_start; |
| 121 | + _gps_time = task_start; |
| 122 | + _serial_time = task_start; |
160 | 123 |
|
161 |
| - hrt_call_every(&_timer_call, LOOP_INTERVAL, LOOP_INTERVAL, timer_callback, &_data_semaphore); |
| 124 | + px4_sem_init(&_data_semaphore, 0, 0); |
162 | 125 |
|
163 |
| - perf_begin(_sampling_perf); |
| 126 | + hrt_call_every(&_timer_call, LOOP_INTERVAL, LOOP_INTERVAL, timer_callback, &_data_semaphore); |
164 | 127 |
|
165 |
| - while (!should_exit()) |
166 |
| - { |
167 |
| - px4_sem_wait(&_data_semaphore); // periodic real time wakeup |
| 128 | + perf_begin(_sampling_perf); |
168 | 129 |
|
169 |
| - perf_end(_sampling_perf); |
170 |
| - perf_begin(_sampling_perf); |
| 130 | + while (!should_exit()) { |
| 131 | + px4_sem_wait(&_data_semaphore); // periodic real time wakeup |
171 | 132 |
|
172 |
| - perf_begin(_loop_perf); |
| 133 | + perf_end(_sampling_perf); |
| 134 | + perf_begin(_sampling_perf); |
173 | 135 |
|
174 |
| - inner_loop(); // main execution function |
| 136 | + perf_begin(_loop_perf); |
175 | 137 |
|
176 |
| - perf_end(_loop_perf); |
177 |
| - } |
| 138 | + inner_loop(); // main execution function |
178 | 139 |
|
179 |
| - hrt_cancel(&_timer_call); // close the periodic timer interruption |
180 |
| - px4_sem_destroy(&_data_semaphore); |
181 |
| - orb_unsubscribe(_actuator_out_sub); |
182 |
| - orb_unsubscribe(_parameter_update_sub); |
| 140 | + perf_end(_loop_perf); |
| 141 | + } |
183 | 142 |
|
| 143 | + hrt_cancel(&_timer_call); // close the periodic timer interruption |
| 144 | + px4_sem_destroy(&_data_semaphore); |
| 145 | + orb_unsubscribe(_actuator_out_sub); |
| 146 | + orb_unsubscribe(_parameter_update_sub); |
184 | 147 | }
|
185 | 148 |
|
186 | 149 | // timer_callback() is used as a real time callback to post the semaphore
|
187 | 150 | void Sih::timer_callback(void *sem)
|
188 | 151 | {
|
189 |
| - px4_sem_post((px4_sem_t *)sem); |
| 152 | + px4_sem_post((px4_sem_t *)sem); |
190 | 153 | }
|
191 | 154 |
|
192 | 155 | // this is the main execution waken up periodically by the semaphore
|
193 | 156 | void Sih::inner_loop()
|
194 | 157 | {
|
195 |
| - _now = hrt_absolute_time(); |
196 |
| - _dt = (_now - _last_run) * 1e-6f; |
197 |
| - _last_run = _now; |
| 158 | + _now = hrt_absolute_time(); |
| 159 | + _dt = (_now - _last_run) * 1e-6f; |
| 160 | + _last_run = _now; |
198 | 161 |
|
199 |
| - read_motors(); |
| 162 | + read_motors(); |
200 | 163 |
|
201 |
| - generate_force_and_torques(); |
| 164 | + generate_force_and_torques(); |
202 | 165 |
|
203 |
| - equations_of_motion(); |
| 166 | + equations_of_motion(); |
204 | 167 |
|
205 |
| - reconstruct_sensors_signals(); |
| 168 | + reconstruct_sensors_signals(); |
206 | 169 |
|
207 |
| - send_IMU(); |
| 170 | + send_IMU(); |
208 | 171 |
|
209 |
| - if (_now - _gps_time >= 50000) // gps published at 20Hz |
210 |
| - { |
211 |
| - _gps_time=_now; |
212 |
| - send_gps(); |
213 |
| - } |
| 172 | + if (_now - _gps_time >= 50000) { // gps published at 20Hz |
| 173 | + _gps_time = _now; |
| 174 | + send_gps(); |
| 175 | + } |
214 | 176 |
|
215 |
| - // send uart message every 40 ms |
216 |
| - if (_now - _serial_time >= 40000) |
217 |
| - { |
218 |
| - _serial_time=_now; |
| 177 | + // send uart message every 40 ms |
| 178 | + if (_now - _serial_time >= 40000) { |
| 179 | + _serial_time = _now; |
219 | 180 |
|
220 |
| - publish_sih(); // publish _sih message for debug purpose |
| 181 | + publish_sih(); // publish _sih message for debug purpose |
221 | 182 |
|
222 |
| - parameters_update_poll(); // update the parameters if needed |
223 |
| - } |
| 183 | + parameters_update_poll(); // update the parameters if needed |
| 184 | + } |
224 | 185 | }
|
225 | 186 |
|
226 | 187 | void Sih::parameters_update_poll()
|
227 | 188 | {
|
228 |
| - bool updated; |
229 |
| - struct parameter_update_s param_upd; |
| 189 | + bool updated = false; |
| 190 | + struct parameter_update_s param_upd; |
230 | 191 |
|
231 |
| - orb_check(_parameter_update_sub, &updated); |
| 192 | + orb_check(_parameter_update_sub, &updated); |
232 | 193 |
|
233 |
| - if (updated) { |
234 |
| - orb_copy(ORB_ID(parameter_update), _parameter_update_sub, ¶m_upd); |
235 |
| - updateParams(); |
236 |
| - parameters_updated(); |
237 |
| - } |
| 194 | + if (updated) { |
| 195 | + orb_copy(ORB_ID(parameter_update), _parameter_update_sub, ¶m_upd); |
| 196 | + updateParams(); |
| 197 | + parameters_updated(); |
| 198 | + } |
238 | 199 | }
|
239 | 200 |
|
240 | 201 | // store the parameters in a more convenient form
|
241 | 202 | void Sih::parameters_updated()
|
242 | 203 | {
|
| 204 | + _T_MAX = _sih_t_max.get(); |
| 205 | + _Q_MAX = _sih_q_max.get(); |
| 206 | + _L_ROLL = _sih_l_roll.get(); |
| 207 | + _L_PITCH = _sih_l_pitch.get(); |
| 208 | + _KDV = _sih_kdv.get(); |
| 209 | + _KDW = _sih_kdw.get(); |
| 210 | + _H0 = _sih_h0.get(); |
243 | 211 |
|
244 |
| - _T_MAX = _sih_t_max.get(); |
245 |
| - _Q_MAX = _sih_q_max.get(); |
246 |
| - _L_ROLL = _sih_l_roll.get(); |
247 |
| - _L_PITCH = _sih_l_pitch.get(); |
248 |
| - _KDV = _sih_kdv.get(); |
249 |
| - _KDW = _sih_kdw.get(); |
250 |
| - _H0 = _sih_h0.get(); |
251 |
| - |
252 |
| - _LAT0 = (double)_sih_lat0.get()*1.0e-7; |
253 |
| - _LON0 = (double)_sih_lon0.get()*1.0e-7; |
254 |
| - _COS_LAT0=cosl(radians(_LAT0)); |
| 212 | + _LAT0 = (double)_sih_lat0.get() * 1.0e-7; |
| 213 | + _LON0 = (double)_sih_lon0.get() * 1.0e-7; |
| 214 | + _COS_LAT0 = cosl(radians(_LAT0)); |
255 | 215 |
|
256 |
| - _MASS=_sih_mass.get(); |
| 216 | + _MASS = _sih_mass.get(); |
257 | 217 |
|
258 |
| - _W_I=Vector3f(0.0f,0.0f,_MASS*CONSTANTS_ONE_G); |
| 218 | + _W_I = Vector3f(0.0f, 0.0f, _MASS * CONSTANTS_ONE_G); |
259 | 219 |
|
260 |
| - _I=diag(Vector3f(_sih_ixx.get(),_sih_iyy.get(),_sih_izz.get())); |
261 |
| - _I(0,1)=_I(1,0)=_sih_ixy.get(); |
262 |
| - _I(0,2)=_I(2,0)=_sih_ixz.get(); |
263 |
| - _I(1,2)=_I(2,1)=_sih_iyz.get(); |
| 220 | + _I = diag(Vector3f(_sih_ixx.get(), _sih_iyy.get(), _sih_izz.get())); |
| 221 | + _I(0, 1) = _I(1, 0) = _sih_ixy.get(); |
| 222 | + _I(0, 2) = _I(2, 0) = _sih_ixz.get(); |
| 223 | + _I(1, 2) = _I(2, 1) = _sih_iyz.get(); |
264 | 224 |
|
265 |
| - _Im1=inv(_I); |
266 |
| - |
267 |
| - _mu_I=Vector3f(_sih_mu_x.get(), _sih_mu_y.get(), _sih_mu_z.get()); |
| 225 | + _Im1 = inv(_I); |
268 | 226 |
|
| 227 | + _mu_I = Vector3f(_sih_mu_x.get(), _sih_mu_y.get(), _sih_mu_z.get()); |
269 | 228 | }
|
270 | 229 |
|
271 | 230 | // initialization of the variables for the simulator
|
272 | 231 | void Sih::init_variables()
|
273 | 232 | {
|
274 |
| - srand(1234); // initialize the random seed once before calling generate_wgn() |
275 |
| - |
276 |
| - _p_I=Vector3f(0.0f,0.0f,0.0f); |
277 |
| - _v_I=Vector3f(0.0f,0.0f,0.0f); |
278 |
| - _q=Quatf(1.0f,0.0f,0.0f,0.0f); |
279 |
| - _w_B=Vector3f(0.0f,0.0f,0.0f); |
| 233 | + srand(1234); // initialize the random seed once before calling generate_wgn() |
280 | 234 |
|
281 |
| - _u[0]=_u[1]=_u[2]=_u[3]=0.0f; |
| 235 | + _p_I = Vector3f(0.0f, 0.0f, 0.0f); |
| 236 | + _v_I = Vector3f(0.0f, 0.0f, 0.0f); |
| 237 | + _q = Quatf(1.0f, 0.0f, 0.0f, 0.0f); |
| 238 | + _w_B = Vector3f(0.0f, 0.0f, 0.0f); |
282 | 239 |
|
| 240 | + _u[0] = _u[1] = _u[2] = _u[3] = 0.0f; |
283 | 241 | }
|
284 | 242 |
|
285 | 243 | void Sih::init_sensors()
|
286 | 244 | {
|
287 |
| - _vehicle_gps_pos.fix_type=3; // 3D fix |
288 |
| - _vehicle_gps_pos.satellites_used=8; |
289 |
| - _vehicle_gps_pos.heading=NAN; |
290 |
| - _vehicle_gps_pos.heading_offset=NAN; |
291 |
| - _vehicle_gps_pos.s_variance_m_s = 0.5f; |
292 |
| - _vehicle_gps_pos.c_variance_rad = 0.1f; |
293 |
| - _vehicle_gps_pos.eph = 0.9f; |
294 |
| - _vehicle_gps_pos.epv = 1.78f; |
295 |
| - _vehicle_gps_pos.hdop = 0.7f; |
296 |
| - _vehicle_gps_pos.vdop = 1.1f; |
| 245 | + _vehicle_gps_pos.fix_type = 3; // 3D fix |
| 246 | + _vehicle_gps_pos.satellites_used = 8; |
| 247 | + _vehicle_gps_pos.heading = NAN; |
| 248 | + _vehicle_gps_pos.heading_offset = NAN; |
| 249 | + _vehicle_gps_pos.s_variance_m_s = 0.5f; |
| 250 | + _vehicle_gps_pos.c_variance_rad = 0.1f; |
| 251 | + _vehicle_gps_pos.eph = 0.9f; |
| 252 | + _vehicle_gps_pos.epv = 1.78f; |
| 253 | + _vehicle_gps_pos.hdop = 0.7f; |
| 254 | + _vehicle_gps_pos.vdop = 1.1f; |
297 | 255 | }
|
298 | 256 |
|
299 | 257 | // read the motor signals outputted from the mixer
|
300 | 258 | void Sih::read_motors()
|
301 | 259 | {
|
302 |
| - struct actuator_outputs_s actuators_out {}; |
| 260 | + actuator_outputs_s actuators_out {}; |
| 261 | + |
| 262 | + // read the actuator outputs |
| 263 | + bool updated = false; |
| 264 | + orb_check(_actuator_out_sub, &updated); |
303 | 265 |
|
304 |
| - // read the actuator outputs |
305 |
| - bool updated; |
306 |
| - orb_check(_actuator_out_sub, &updated); |
| 266 | + if (updated) { |
| 267 | + orb_copy(ORB_ID(actuator_outputs), _actuator_out_sub, &actuators_out); |
307 | 268 |
|
308 |
| - if (updated) { |
309 |
| - orb_copy(ORB_ID(actuator_outputs), _actuator_out_sub, &actuators_out); |
310 |
| - for (int i=0; i<NB_MOTORS; i++) // saturate the motor signals |
311 |
| - _u[i]=constrain((actuators_out.output[i]-PWM_DEFAULT_MIN)/(PWM_DEFAULT_MAX-PWM_DEFAULT_MIN),0.0f, 1.0f); |
312 |
| - } |
| 269 | + for (int i = 0; i < NB_MOTORS; i++) { // saturate the motor signals |
| 270 | + _u[i] = constrain((actuators_out.output[i] - PWM_DEFAULT_MIN) / (PWM_DEFAULT_MAX - PWM_DEFAULT_MIN), 0.0f, 1.0f); |
| 271 | + } |
| 272 | + } |
313 | 273 | }
|
314 | 274 |
|
315 | 275 | // generate the motors thrust and torque in the body frame
|
316 | 276 | void Sih::generate_force_and_torques()
|
317 | 277 | {
|
318 |
| - _T_B=Vector3f(0.0f,0.0f,-_T_MAX*(+_u[0]+_u[1]+_u[2]+_u[3])); |
319 |
| - _Mt_B=Vector3f( _L_ROLL*_T_MAX* (-_u[0]+_u[1]+_u[2]-_u[3]), |
320 |
| - _L_PITCH*_T_MAX*(+_u[0]-_u[1]+_u[2]-_u[3]), |
321 |
| - _Q_MAX * (+_u[0]+_u[1]-_u[2]-_u[3])); |
| 278 | + _T_B = Vector3f(0.0f, 0.0f, -_T_MAX * (+_u[0] + _u[1] + _u[2] + _u[3])); |
| 279 | + _Mt_B = Vector3f(_L_ROLL * _T_MAX * (-_u[0] + _u[1] + _u[2] - _u[3]), |
| 280 | + _L_PITCH * _T_MAX * (+_u[0] - _u[1] + _u[2] - _u[3]), |
| 281 | + _Q_MAX * (+_u[0] + _u[1] - _u[2] - _u[3])); |
322 | 282 |
|
323 |
| - _Fa_I=-_KDV*_v_I; // first order drag to slow down the aircraft |
324 |
| - _Ma_B=-_KDW*_w_B; // first order angular damper |
| 283 | + _Fa_I = -_KDV * _v_I; // first order drag to slow down the aircraft |
| 284 | + _Ma_B = -_KDW * _w_B; // first order angular damper |
325 | 285 | }
|
326 | 286 |
|
327 | 287 | // apply the equations of motion of a rigid body and integrate one step
|
328 | 288 | void Sih::equations_of_motion()
|
329 | 289 | {
|
330 |
| - _C_IB=_q.to_dcm(); // body to inertial transformation |
331 |
| - |
332 |
| - // Equations of motion of a rigid body |
333 |
| - _p_I_dot=_v_I; // position differential |
334 |
| - _v_I_dot=(_W_I+_Fa_I+_C_IB*_T_B)/_MASS; // conservation of linear momentum |
335 |
| - _q_dot=_q.derivative1(_w_B); // attitude differential |
336 |
| - _w_B_dot=_Im1*(_Mt_B+_Ma_B-_w_B.cross(_I*_w_B)); // conservation of angular momentum |
337 |
| - |
338 |
| - // fake ground, avoid free fall |
339 |
| - if(_p_I(2)>0.0f && (_v_I_dot(2)>0.0f || _v_I(2)>0.0f)) { |
340 |
| - if (!_grounded) { // if we just hit the floor |
341 |
| - // for the accelerometer, compute the acceleration that will stop the vehicle in one time step |
342 |
| - _v_I_dot=-_v_I/_dt; |
343 |
| - } else { |
344 |
| - _v_I_dot.setZero(); |
345 |
| - } |
346 |
| - _v_I.setZero(); |
347 |
| - _w_B.setZero(); |
348 |
| - _grounded = true; |
349 |
| - } else { |
350 |
| - // integration: Euler forward |
351 |
| - _p_I = _p_I + _p_I_dot*_dt; |
352 |
| - _v_I = _v_I + _v_I_dot*_dt; |
353 |
| - _q = _q+_q_dot*_dt; // as given in attitude_estimator_q_main.cpp |
354 |
| - _q.normalize(); |
355 |
| - _w_B = _w_B + _w_B_dot*_dt; |
356 |
| - _grounded = false; |
357 |
| - } |
| 290 | + _C_IB = _q.to_dcm(); // body to inertial transformation |
| 291 | + |
| 292 | + // Equations of motion of a rigid body |
| 293 | + _p_I_dot = _v_I; // position differential |
| 294 | + _v_I_dot = (_W_I + _Fa_I + _C_IB * _T_B) / _MASS; // conservation of linear momentum |
| 295 | + _q_dot = _q.derivative1(_w_B); // attitude differential |
| 296 | + _w_B_dot = _Im1 * (_Mt_B + _Ma_B - _w_B.cross(_I * _w_B)); // conservation of angular momentum |
| 297 | + |
| 298 | + // fake ground, avoid free fall |
| 299 | + if (_p_I(2) > 0.0f && (_v_I_dot(2) > 0.0f || _v_I(2) > 0.0f)) { |
| 300 | + if (!_grounded) { // if we just hit the floor |
| 301 | + // for the accelerometer, compute the acceleration that will stop the vehicle in one time step |
| 302 | + _v_I_dot = -_v_I / _dt; |
| 303 | + |
| 304 | + } else { |
| 305 | + _v_I_dot.setZero(); |
| 306 | + } |
| 307 | + |
| 308 | + _v_I.setZero(); |
| 309 | + _w_B.setZero(); |
| 310 | + _grounded = true; |
| 311 | + |
| 312 | + } else { |
| 313 | + // integration: Euler forward |
| 314 | + _p_I = _p_I + _p_I_dot * _dt; |
| 315 | + _v_I = _v_I + _v_I_dot * _dt; |
| 316 | + _q = _q + _q_dot * _dt; // as given in attitude_estimator_q_main.cpp |
| 317 | + _q.normalize(); |
| 318 | + _w_B = _w_B + _w_B_dot * _dt; |
| 319 | + _grounded = false; |
| 320 | + } |
358 | 321 | }
|
359 | 322 |
|
360 | 323 | // reconstruct the noisy sensor signals
|
361 | 324 | void Sih::reconstruct_sensors_signals()
|
362 | 325 | {
|
363 |
| - |
364 |
| - // The sensor signals reconstruction and noise levels are from |
365 |
| - // Bulka, Eitan, and Meyer Nahon. "Autonomous fixed-wing aerobatics: from theory to flight." |
366 |
| - // In 2018 IEEE International Conference on Robotics and Automation (ICRA), pp. 6573-6580. IEEE, 2018. |
367 |
| - |
368 |
| - // IMU |
369 |
| - _acc=_C_IB.transpose()*(_v_I_dot-Vector3f(0.0f,0.0f,CONSTANTS_ONE_G))+noiseGauss3f(0.5f,1.7f,1.4f); |
370 |
| - _gyro=_w_B+noiseGauss3f(0.14f,0.07f,0.03f); |
371 |
| - _mag=_C_IB.transpose()*_mu_I+noiseGauss3f(0.02f,0.02f,0.03f); |
372 |
| - |
373 |
| - // barometer |
374 |
| - float altitude=(_H0-_p_I(2))+generate_wgn()*0.14f; // altitude with noise |
375 |
| - _baro_p_mBar=CONSTANTS_STD_PRESSURE_MBAR* // reconstructed pressure in mBar |
376 |
| - powf((1.0f+altitude*TEMP_GRADIENT/T1_K),-CONSTANTS_ONE_G/(TEMP_GRADIENT*CONSTANTS_AIR_GAS_CONST)); |
377 |
| - _baro_temp_c=T1_K+CONSTANTS_ABSOLUTE_NULL_CELSIUS+TEMP_GRADIENT*altitude; // reconstructed temperture in celcius |
378 |
| - |
379 |
| - // GPS |
380 |
| - _gps_lat_noiseless=_LAT0+degrees((double)_p_I(0)/CONSTANTS_RADIUS_OF_EARTH); |
381 |
| - _gps_lon_noiseless=_LON0+degrees((double)_p_I(1)/CONSTANTS_RADIUS_OF_EARTH)/_COS_LAT0; |
382 |
| - _gps_alt_noiseless=_H0-_p_I(2); |
383 |
| - |
384 |
| - _gps_lat=_gps_lat_noiseless+(double)(generate_wgn()*7.2e-6f); // latitude in degrees |
385 |
| - _gps_lon=_gps_lon_noiseless+(double)(generate_wgn()*1.75e-5f); // longitude in degrees |
386 |
| - _gps_alt=_gps_alt_noiseless+generate_wgn()*1.78f; |
387 |
| - _gps_vel=_v_I+noiseGauss3f(0.06f,0.077f,0.158f); |
| 326 | + // The sensor signals reconstruction and noise levels are from |
| 327 | + // Bulka, Eitan, and Meyer Nahon. "Autonomous fixed-wing aerobatics: from theory to flight." |
| 328 | + // In 2018 IEEE International Conference on Robotics and Automation (ICRA), pp. 6573-6580. IEEE, 2018. |
| 329 | + |
| 330 | + // IMU |
| 331 | + _acc = _C_IB.transpose() * (_v_I_dot - Vector3f(0.0f, 0.0f, CONSTANTS_ONE_G)) + noiseGauss3f(0.5f, 1.7f, 1.4f); |
| 332 | + _gyro = _w_B + noiseGauss3f(0.14f, 0.07f, 0.03f); |
| 333 | + _mag = _C_IB.transpose() * _mu_I + noiseGauss3f(0.02f, 0.02f, 0.03f); |
| 334 | + |
| 335 | + // barometer |
| 336 | + float altitude = (_H0 - _p_I(2)) + generate_wgn() * 0.14f; // altitude with noise |
| 337 | + _baro_p_mBar = CONSTANTS_STD_PRESSURE_MBAR * // reconstructed pressure in mBar |
| 338 | + powf((1.0f + altitude * TEMP_GRADIENT / T1_K), -CONSTANTS_ONE_G / (TEMP_GRADIENT * CONSTANTS_AIR_GAS_CONST)); |
| 339 | + _baro_temp_c = T1_K + CONSTANTS_ABSOLUTE_NULL_CELSIUS + TEMP_GRADIENT * altitude; // reconstructed temperture in celcius |
| 340 | + |
| 341 | + // GPS |
| 342 | + _gps_lat_noiseless = _LAT0 + degrees((double)_p_I(0) / CONSTANTS_RADIUS_OF_EARTH); |
| 343 | + _gps_lon_noiseless = _LON0 + degrees((double)_p_I(1) / CONSTANTS_RADIUS_OF_EARTH) / _COS_LAT0; |
| 344 | + _gps_alt_noiseless = _H0 - _p_I(2); |
| 345 | + |
| 346 | + _gps_lat = _gps_lat_noiseless + (double)(generate_wgn() * 7.2e-6f); // latitude in degrees |
| 347 | + _gps_lon = _gps_lon_noiseless + (double)(generate_wgn() * 1.75e-5f); // longitude in degrees |
| 348 | + _gps_alt = _gps_alt_noiseless + generate_wgn() * 1.78f; |
| 349 | + _gps_vel = _v_I + noiseGauss3f(0.06f, 0.077f, 0.158f); |
388 | 350 | }
|
389 | 351 |
|
390 | 352 | void Sih::send_IMU()
|
@@ -422,93 +384,133 @@ void Sih::send_IMU()
|
422 | 384 |
|
423 | 385 | void Sih::send_gps()
|
424 | 386 | {
|
425 |
| - _vehicle_gps_pos.timestamp=_now; |
426 |
| - _vehicle_gps_pos.lat=(int32_t)(_gps_lat*1e7); // Latitude in 1E-7 degrees |
427 |
| - _vehicle_gps_pos.lon=(int32_t)(_gps_lon*1e7); // Longitude in 1E-7 degrees |
428 |
| - _vehicle_gps_pos.alt=(int32_t)(_gps_alt*1000.0f); // Altitude in 1E-3 meters above MSL, (millimetres) |
429 |
| - _vehicle_gps_pos.alt_ellipsoid = (int32_t)(_gps_alt*1000); // Altitude in 1E-3 meters bove Ellipsoid, (millimetres) |
430 |
| - _vehicle_gps_pos.vel_ned_valid=true; // True if NED velocity is valid |
431 |
| - _vehicle_gps_pos.vel_m_s=sqrtf(_gps_vel(0)*_gps_vel(0)+_gps_vel(1)*_gps_vel(1)); // GPS ground speed, (metres/sec) |
432 |
| - _vehicle_gps_pos.vel_n_m_s=_gps_vel(0); // GPS North velocity, (metres/sec) |
433 |
| - _vehicle_gps_pos.vel_e_m_s=_gps_vel(1); // GPS East velocity, (metres/sec) |
434 |
| - _vehicle_gps_pos.vel_d_m_s=_gps_vel(2); // GPS Down velocity, (metres/sec) |
435 |
| - _vehicle_gps_pos.cog_rad=atan2(_gps_vel(1),_gps_vel(0)); // Course over ground (NOT heading, but direction of movement), -PI..PI, (radians) |
436 |
| - if (_vehicle_gps_pos_pub != nullptr) { |
437 |
| - orb_publish(ORB_ID(vehicle_gps_position), _vehicle_gps_pos_pub, &_vehicle_gps_pos); |
438 |
| - } else { |
439 |
| - _vehicle_gps_pos_pub = orb_advertise(ORB_ID(vehicle_gps_position), &_vehicle_gps_pos); |
440 |
| - } |
| 387 | + _vehicle_gps_pos.timestamp = _now; |
| 388 | + _vehicle_gps_pos.lat = (int32_t)(_gps_lat * 1e7); // Latitude in 1E-7 degrees |
| 389 | + _vehicle_gps_pos.lon = (int32_t)(_gps_lon * 1e7); // Longitude in 1E-7 degrees |
| 390 | + _vehicle_gps_pos.alt = (int32_t)(_gps_alt * 1000.0f); // Altitude in 1E-3 meters above MSL, (millimetres) |
| 391 | + _vehicle_gps_pos.alt_ellipsoid = (int32_t)(_gps_alt * 1000); // Altitude in 1E-3 meters bove Ellipsoid, (millimetres) |
| 392 | + _vehicle_gps_pos.vel_ned_valid = true; // True if NED velocity is valid |
| 393 | + _vehicle_gps_pos.vel_m_s = sqrtf(_gps_vel(0) * _gps_vel(0) + _gps_vel(1) * _gps_vel( |
| 394 | + 1)); // GPS ground speed, (metres/sec) |
| 395 | + _vehicle_gps_pos.vel_n_m_s = _gps_vel(0); // GPS North velocity, (metres/sec) |
| 396 | + _vehicle_gps_pos.vel_e_m_s = _gps_vel(1); // GPS East velocity, (metres/sec) |
| 397 | + _vehicle_gps_pos.vel_d_m_s = _gps_vel(2); // GPS Down velocity, (metres/sec) |
| 398 | + _vehicle_gps_pos.cog_rad = atan2(_gps_vel(1), |
| 399 | + _gps_vel(0)); // Course over ground (NOT heading, but direction of movement), -PI..PI, (radians) |
| 400 | + |
| 401 | + if (_vehicle_gps_pos_pub != nullptr) { |
| 402 | + orb_publish(ORB_ID(vehicle_gps_position), _vehicle_gps_pos_pub, &_vehicle_gps_pos); |
| 403 | + |
| 404 | + } else { |
| 405 | + _vehicle_gps_pos_pub = orb_advertise(ORB_ID(vehicle_gps_position), &_vehicle_gps_pos); |
| 406 | + } |
441 | 407 | }
|
442 | 408 |
|
443 | 409 | void Sih::publish_sih()
|
444 | 410 | {
|
| 411 | + _gpos_gt.timestamp = hrt_absolute_time(); |
| 412 | + _gpos_gt.lat = _gps_lat_noiseless; |
| 413 | + _gpos_gt.lon = _gps_lon_noiseless; |
| 414 | + _gpos_gt.alt = _gps_alt_noiseless; |
| 415 | + _gpos_gt.vel_n = _v_I(0); |
| 416 | + _gpos_gt.vel_e = _v_I(1); |
| 417 | + _gpos_gt.vel_d = _v_I(2); |
| 418 | + |
| 419 | + if (_gpos_gt_sub != nullptr) { |
| 420 | + orb_publish(ORB_ID(vehicle_global_position_groundtruth), _gpos_gt_sub, &_gpos_gt); |
| 421 | + |
| 422 | + } else { |
| 423 | + _gpos_gt_sub = orb_advertise(ORB_ID(vehicle_global_position_groundtruth), &_gpos_gt); |
| 424 | + } |
445 | 425 |
|
446 |
| - _gpos_gt.timestamp=hrt_absolute_time(); |
447 |
| - _gpos_gt.lat=_gps_lat_noiseless; |
448 |
| - _gpos_gt.lon=_gps_lon_noiseless; |
449 |
| - _gpos_gt.alt=_gps_alt_noiseless; |
450 |
| - _gpos_gt.vel_n=_v_I(0); |
451 |
| - _gpos_gt.vel_e=_v_I(1); |
452 |
| - _gpos_gt.vel_d=_v_I(2); |
453 |
| - |
454 |
| - if (_gpos_gt_sub != nullptr) { |
455 |
| - orb_publish(ORB_ID(vehicle_global_position_groundtruth), _gpos_gt_sub, &_gpos_gt); |
456 |
| - } else { |
457 |
| - _gpos_gt_sub = orb_advertise(ORB_ID(vehicle_global_position_groundtruth), &_gpos_gt); |
458 |
| - } |
459 |
| - |
460 |
| - // publish attitude groundtruth |
461 |
| - _att_gt.timestamp=hrt_absolute_time(); |
462 |
| - _att_gt.q[0]=_q(0); |
463 |
| - _att_gt.q[1]=_q(1); |
464 |
| - _att_gt.q[2]=_q(2); |
465 |
| - _att_gt.q[3]=_q(3); |
466 |
| - _att_gt.rollspeed=_w_B(0); |
467 |
| - _att_gt.pitchspeed=_w_B(1); |
468 |
| - _att_gt.yawspeed=_w_B(2); |
469 |
| - if (_att_gt_sub != nullptr) { |
470 |
| - orb_publish(ORB_ID(vehicle_attitude_groundtruth), _att_gt_sub, &_att_gt); |
471 |
| - } else { |
472 |
| - _att_gt_sub = orb_advertise(ORB_ID(vehicle_attitude_groundtruth), &_att_gt); |
473 |
| - } |
| 426 | + // publish attitude groundtruth |
| 427 | + _att_gt.timestamp = hrt_absolute_time(); |
| 428 | + _att_gt.q[0] = _q(0); |
| 429 | + _att_gt.q[1] = _q(1); |
| 430 | + _att_gt.q[2] = _q(2); |
| 431 | + _att_gt.q[3] = _q(3); |
| 432 | + _att_gt.rollspeed = _w_B(0); |
| 433 | + _att_gt.pitchspeed = _w_B(1); |
| 434 | + _att_gt.yawspeed = _w_B(2); |
| 435 | + |
| 436 | + if (_att_gt_sub != nullptr) { |
| 437 | + orb_publish(ORB_ID(vehicle_attitude_groundtruth), _att_gt_sub, &_att_gt); |
| 438 | + |
| 439 | + } else { |
| 440 | + _att_gt_sub = orb_advertise(ORB_ID(vehicle_attitude_groundtruth), &_att_gt); |
| 441 | + } |
474 | 442 | }
|
475 | 443 |
|
476 | 444 | float Sih::generate_wgn() // generate white Gaussian noise sample with std=1
|
477 | 445 | {
|
478 |
| - // algorithm 1: |
479 |
| - // float temp=((float)(rand()+1))/(((float)RAND_MAX+1.0f)); |
480 |
| - // return sqrtf(-2.0f*logf(temp))*cosf(2.0f*M_PI_F*rand()/RAND_MAX); |
481 |
| - // algorithm 2: from BlockRandGauss.hpp |
482 |
| - static float V1, V2, S; |
483 |
| - static bool phase = true; |
484 |
| - float X; |
485 |
| - |
486 |
| - if (phase) { |
487 |
| - do { |
488 |
| - float U1 = (float)rand() / RAND_MAX; |
489 |
| - float U2 = (float)rand() / RAND_MAX; |
490 |
| - V1 = 2.0f * U1 - 1.0f; |
491 |
| - V2 = 2.0f * U2 - 1.0f; |
492 |
| - S = V1 * V1 + V2 * V2; |
493 |
| - } while (S >= 1.0f || fabsf(S) < 1e-8f); |
494 |
| - |
495 |
| - X = V1 * float(sqrtf(-2.0f * float(logf(S)) / S)); |
496 |
| - |
497 |
| - } else { |
498 |
| - X = V2 * float(sqrtf(-2.0f * float(logf(S)) / S)); |
499 |
| - } |
500 |
| - |
501 |
| - phase = !phase; |
502 |
| - return X; |
| 446 | + // algorithm 1: |
| 447 | + // float temp=((float)(rand()+1))/(((float)RAND_MAX+1.0f)); |
| 448 | + // return sqrtf(-2.0f*logf(temp))*cosf(2.0f*M_PI_F*rand()/RAND_MAX); |
| 449 | + // algorithm 2: from BlockRandGauss.hpp |
| 450 | + static float V1, V2, S; |
| 451 | + static bool phase = true; |
| 452 | + float X; |
| 453 | + |
| 454 | + if (phase) { |
| 455 | + do { |
| 456 | + float U1 = (float)rand() / RAND_MAX; |
| 457 | + float U2 = (float)rand() / RAND_MAX; |
| 458 | + V1 = 2.0f * U1 - 1.0f; |
| 459 | + V2 = 2.0f * U2 - 1.0f; |
| 460 | + S = V1 * V1 + V2 * V2; |
| 461 | + } while (S >= 1.0f || fabsf(S) < 1e-8f); |
| 462 | + |
| 463 | + X = V1 * float(sqrtf(-2.0f * float(logf(S)) / S)); |
| 464 | + |
| 465 | + } else { |
| 466 | + X = V2 * float(sqrtf(-2.0f * float(logf(S)) / S)); |
| 467 | + } |
| 468 | + |
| 469 | + phase = !phase; |
| 470 | + return X; |
503 | 471 | }
|
504 | 472 |
|
505 | 473 | // generate white Gaussian noise sample vector with specified std
|
506 |
| -Vector3f Sih::noiseGauss3f(float stdx,float stdy, float stdz) |
| 474 | +Vector3f Sih::noiseGauss3f(float stdx, float stdy, float stdz) |
507 | 475 | {
|
508 |
| - return Vector3f(generate_wgn()*stdx,generate_wgn()*stdy,generate_wgn()*stdz); |
| 476 | + return Vector3f(generate_wgn() * stdx, generate_wgn() * stdy, generate_wgn() * stdz); |
509 | 477 | }
|
510 | 478 |
|
511 | 479 | int sih_main(int argc, char *argv[])
|
512 | 480 | {
|
513 |
| - return Sih::main(argc, argv); |
| 481 | + return Sih::main(argc, argv); |
| 482 | +} |
| 483 | + |
| 484 | +int Sih::print_usage(const char *reason) |
| 485 | +{ |
| 486 | + if (reason) { |
| 487 | + PX4_WARN("%s\n", reason); |
| 488 | + } |
| 489 | + |
| 490 | + PRINT_MODULE_DESCRIPTION( |
| 491 | + R"DESCR_STR( |
| 492 | +### Description |
| 493 | +This module provide a simulator for quadrotors running fully |
| 494 | +inside the hardware autopilot. |
| 495 | +
|
| 496 | +This simulator subscribes to "actuator_outputs" which are the actuator pwm |
| 497 | +signals given by the mixer. |
| 498 | +
|
| 499 | +This simulator publishes the sensors signals corrupted with realistic noise |
| 500 | +in order to incorporate the state estimator in the loop. |
| 501 | +
|
| 502 | +### Implementation |
| 503 | +The simulator implements the equations of motion using matrix algebra. |
| 504 | +Quaternion representation is used for the attitude. |
| 505 | +Forward Euler is used for integration. |
| 506 | +Most of the variables are declared global in the .hpp file to avoid stack overflow. |
| 507 | +
|
| 508 | +
|
| 509 | +)DESCR_STR"); |
| 510 | + |
| 511 | + PRINT_MODULE_USAGE_NAME("sih", "simulation"); |
| 512 | + PRINT_MODULE_USAGE_COMMAND("start"); |
| 513 | + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); |
| 514 | + |
| 515 | + return 0; |
514 | 516 | }
|
0 commit comments