#include "systemid.h" #if AP_PLANE_SYSTEMID_ENABLED #include #include "Plane.h" /* handle systemid via an auxiliary switch */ const AP_Param::GroupInfo AP_SystemID::var_info[] = { // @Param: _AXIS // @DisplayName: System identification axis // @Description: Controls which axis are being excited. Set to non-zero to see more parameters // @User: Standard // @Values: 0:None, 1:Input Roll Angle, 2:Input Pitch Angle, 3:Input Yaw Angle, 4:Recovery Roll Angle, 5:Recovery Pitch Angle, 6:Recovery Yaw Angle, 7:Rate Roll, 8:Rate Pitch, 9:Rate Yaw, 10:Mixer Roll, 11:Mixer Pitch, 12:Mixer Yaw, 13:Mixer Thrust AP_GROUPINFO_FLAGS("_AXIS", 1, AP_SystemID, axis, 0, AP_PARAM_FLAG_ENABLE), // @Param: _MAGNITUDE // @DisplayName: System identification Chirp Magnitude // @Description: Magnitude of sweep in deg, deg/s and 0-1 for mixer outputs. // @User: Standard AP_GROUPINFO("_MAGNITUDE", 2, AP_SystemID, waveform_magnitude, 5), // @Param: _F_START_HZ // @DisplayName: System identification Start Frequency // @Description: Frequency at the start of the sweep // @Range: 0.01 100 // @Units: Hz // @User: Standard AP_GROUPINFO("_F_START_HZ", 3, AP_SystemID, frequency_start, 0.5f), // @Param: _F_STOP_HZ // @DisplayName: System identification Stop Frequency // @Description: Frequency at the end of the sweep // @Range: 0.01 100 // @Units: Hz // @User: Standard AP_GROUPINFO("_F_STOP_HZ", 4, AP_SystemID, frequency_stop, 15), // @Param: _T_FADE_IN // @DisplayName: System identification Fade in time // @Description: Time to reach maximum amplitude of sweep // @Range: 0 20 // @Units: s // @User: Standard AP_GROUPINFO("_T_FADE_IN", 5, AP_SystemID, time_fade_in, 5), // @Param: _T_REC // @DisplayName: System identification Total Sweep length // @Description: Time taken to complete the sweep // @Range: 0 255 // @Units: s // @User: Standard AP_GROUPINFO("_T_REC", 6, AP_SystemID, time_record, 70), // @Param: _T_FADE_OUT // @DisplayName: System identification Fade out time // @Description: Time to reach zero amplitude at the end of the sweep // @Range: 0 5 // @Units: s // @User: Standard AP_GROUPINFO("_T_FADE_OUT", 7, AP_SystemID, time_fade_out, 1), // @Param: _XY_CTRL_MUL // @DisplayName: System identification XY control multiplier // @Description: A multiplier for the XY velocity and position controller when using systemID in VTOL modes that do horizontal position and velocity control // @Range: 0.05 1.0 // @User: Standard AP_GROUPINFO("_XY_CTRL_MUL", 8, AP_SystemID, xy_control_mul, 0.1), AP_GROUPEND }; AP_SystemID::AP_SystemID(void) { AP_Param::setup_object_defaults(this, var_info); } // @LoggerMessage: SIDS // @Description: System ID settings // @Field: TimeUS: Time since system startup // @Field: Ax: The axis which is being excited // @Field: Mag: Magnitude of the chirp waveform // @Field: FSt: Frequency at the start of chirp // @Field: FSp: Frequency at the end of chirp // @Field: TFin: Time to reach maximum amplitude of chirp // @Field: TC: Time at constant frequency before chirp starts // @Field: TR: Time taken to complete chirp waveform // @Field: TFout: Time to reach zero amplitude after chirp finishes /* start systemid */ void AP_SystemID::start() { start_axis = axis; // check if enabled if (start_axis == AxisType::NONE) { gcs().send_text(MAV_SEVERITY_WARNING, "SystemID: No axis selected"); return; } if (!plane.quadplane.enabled()) { gcs().send_text(MAV_SEVERITY_WARNING, "SystemID: only for quadplane"); return; } if (!plane.control_mode->supports_systemid()) { gcs().send_text(MAV_SEVERITY_WARNING, "SystemID: Not supported in mode %s", plane.control_mode->name()); return; } if (!hal.util->get_soft_armed()) { gcs().send_text(MAV_SEVERITY_WARNING, "SystemID: must be armed"); return; } attitude_offset_deg.zero(); throttle_offset = 0; restore.att_bf_feedforward = plane.quadplane.attitude_control->get_bf_feedforward(); waveform_time = 0; time_const_freq = 2.0 / frequency_start; // Two full cycles at the starting frequency chirp_input.init(time_record, frequency_start, frequency_stop, time_fade_in, time_fade_out, time_const_freq); gcs().send_text(MAV_SEVERITY_INFO, "SystemID Starting: axis=%d", (unsigned)axis); #if HAL_LOGGING_ENABLED AP::logger().WriteStreaming("SIDS", "TimeUS,Ax,Mag,FSt,FSp,TFin,TC,TR,TFout", "s--ssssss", "F--------", "QBfffffff", AP_HAL::micros64(), uint8_t(start_axis), waveform_magnitude.get(), frequency_start.get(), frequency_stop.get(), time_fade_in.get(), time_const_freq, time_record.get(), time_fade_out.get()); #endif // HAL_LOGGING_ENABLED running = true; } /* stop systemid */ void AP_SystemID::stop() { if (running) { running = false; attitude_offset_deg.zero(); throttle_offset = 0; auto *attitude_control = plane.quadplane.attitude_control; attitude_control->bf_feedforward(restore.att_bf_feedforward); attitude_control->rate_bf_roll_sysid_rads(0); attitude_control->rate_bf_pitch_sysid_rads(0); attitude_control->rate_bf_yaw_sysid_rads(0); plane.quadplane.pos_control->set_NE_control_scale_factor(1); // re-initialise the XY controller so we take current position as target plane.quadplane.pos_control->init_NE_controller(); gcs().send_text(MAV_SEVERITY_INFO, "SystemID stopped"); } } /* update systemid - needs to be called at main loop rate */ void AP_SystemID::update() { if (!running) { return; } if (chirp_input.completed()) { stop(); return; } float const last_loop_time_s = AP::scheduler().get_last_loop_time_s(); waveform_time += last_loop_time_s; waveform_sample = chirp_input.update(waveform_time, waveform_magnitude); waveform_freq_rads = chirp_input.get_frequency_rads(); auto *attitude_control = plane.quadplane.attitude_control; switch (start_axis) { case AxisType::NONE: // not possible, see start() break; case AxisType::INPUT_ROLL: attitude_offset_deg.x = waveform_sample; break; case AxisType::INPUT_PITCH: attitude_offset_deg.y = waveform_sample; break; case AxisType::INPUT_YAW: attitude_offset_deg.z = waveform_sample; break; case AxisType::RECOVER_ROLL: attitude_offset_deg.x = waveform_sample; attitude_control->bf_feedforward(false); break; case AxisType::RECOVER_PITCH: attitude_offset_deg.y = waveform_sample; attitude_control->bf_feedforward(false); break; case AxisType::RECOVER_YAW: attitude_offset_deg.z = waveform_sample; attitude_control->bf_feedforward(false); break; case AxisType::RATE_ROLL: attitude_control->rate_bf_roll_sysid_rads(radians(waveform_sample)); break; case AxisType::RATE_PITCH: attitude_control->rate_bf_pitch_sysid_rads(radians(waveform_sample)); break; case AxisType::RATE_YAW: attitude_control->rate_bf_yaw_sysid_rads(radians(waveform_sample)); break; case AxisType::MIX_ROLL: attitude_control->actuator_roll_sysid(waveform_sample); break; case AxisType::MIX_PITCH: attitude_control->actuator_pitch_sysid(waveform_sample); break; case AxisType::MIX_YAW: attitude_control->actuator_yaw_sysid(waveform_sample); break; case AxisType::MIX_THROTTLE: throttle_offset = waveform_sample; break; } // reduce control in XY axis when in position controlled modes plane.quadplane.pos_control->set_NE_control_scale_factor(xy_control_mul); if (log_subsample <= 0) { log_data(); if (plane.should_log(MASK_LOG_ATTITUDE_FAST) && plane.should_log(MASK_LOG_ATTITUDE_MED)) { log_subsample = 1; } else if (plane.should_log(MASK_LOG_ATTITUDE_FAST)) { log_subsample = 2; } else if (plane.should_log(MASK_LOG_ATTITUDE_MED)) { log_subsample = 4; } else { log_subsample = 8; } } log_subsample -= 1; } // @LoggerMessage: SIDD // @Description: System ID data // @Field: TimeUS: Time since system startup // @Field: Time: Time reference for waveform // @Field: Targ: Current waveform sample // @Field: F: Instantaneous waveform frequency // @Field: Gx: Delta angle, X-Axis // @Field: Gy: Delta angle, Y-Axis // @Field: Gz: Delta angle, Z-Axis // @Field: Ax: Delta velocity, X-Axis // @Field: Ay: Delta velocity, Y-Axis // @Field: Az: Delta velocity, Z-Axis // log system id void AP_SystemID::log_data() const { #if HAL_LOGGING_ENABLED Vector3f delta_angle; float delta_angle_dt; plane.ins.get_delta_angle(delta_angle, delta_angle_dt); Vector3f delta_velocity; float delta_velocity_dt; plane.ins.get_delta_velocity(delta_velocity, delta_velocity_dt); if (is_positive(delta_angle_dt) && is_positive(delta_velocity_dt)) { const float dt_ang_inv = 1.0 / delta_angle_dt; const float dt_vel_inv = 1.0 / delta_velocity_dt; AP::logger().WriteStreaming("SIDD", "TimeUS,Time,Targ,F,Gx,Gy,Gz,Ax,Ay,Az", "ss-zkkkooo", "F---------", "Qfffffffff", AP_HAL::micros64(), waveform_time, waveform_sample, waveform_freq_rads / (2 * M_PI), degrees(delta_angle.x * dt_ang_inv), degrees(delta_angle.y * dt_ang_inv), degrees(delta_angle.z * dt_ang_inv), delta_velocity.x * dt_vel_inv, delta_velocity.y * dt_vel_inv, delta_velocity.z * dt_vel_inv); // log attitude controller at the same rate plane.quadplane.Log_Write_AttRate(); } #endif // HAL_LOGGING_ENABLED } #endif // AP_PLANE_SYSTEMID_ENABLED