From f6c0ce237877a06a174c20afce97472727dac158 Mon Sep 17 00:00:00 2001 From: Paul Beuchat <beuchatp@control.ee.ethz.ch> Date: Fri, 6 Dec 2019 09:17:15 +0100 Subject: [PATCH] Added first draft of simulation to CS1 GUI --- .../forms/csonecontrollertab.ui | 123 +++- .../include/csonecontrollertab.h | 44 +- .../flyingAgentGUI/src/csonecontrollertab.cpp | 640 ++++++++++++++---- 3 files changed, 667 insertions(+), 140 deletions(-) diff --git a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/forms/csonecontrollertab.ui b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/forms/csonecontrollertab.ui index 2e7d1709..b9db68b0 100644 --- a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/forms/csonecontrollertab.ui +++ b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/forms/csonecontrollertab.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>2417</width> + <width>3197</width> <height>1336</height> </rect> </property> @@ -421,6 +421,22 @@ <property name="rightMargin"> <number>0</number> </property> + <item row="0" column="8"> + <widget class="QLabel" name="label_simulation"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Simulation</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> <item row="0" column="2"> <widget class="QLabel" name="label_time_delay"> <property name="font"> @@ -437,7 +453,7 @@ </property> </widget> </item> - <item row="1" column="8"> + <item row="1" column="10"> <layout class="QGridLayout" name="gridLayout_8"> <item row="2" column="0"> <widget class="QPushButton" name="reset_integrator_button"> @@ -495,7 +511,7 @@ </item> </layout> </item> - <item row="0" column="7"> + <item row="0" column="9"> <spacer name="horizontalSpacer_7"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -543,7 +559,7 @@ </property> </widget> </item> - <item row="0" column="10"> + <item row="0" column="12"> <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -592,7 +608,7 @@ </font> </property> <property name="text"> - <string>0.1</string> + <string>0.10</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> @@ -641,7 +657,7 @@ </property> <property name="maximumSize"> <size> - <width>180</width> + <width>240</width> <height>60</height> </size> </property> @@ -651,7 +667,7 @@ </font> </property> <property name="text"> - <string>0.016</string> + <string>0.0160</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> @@ -922,7 +938,7 @@ </font> </property> <property name="text"> - <string>0.2</string> + <string>0.5</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> @@ -1028,7 +1044,7 @@ </property> </widget> </item> - <item row="0" column="8"> + <item row="0" column="10"> <widget class="QLabel" name="label_integrator"> <property name="font"> <font> @@ -1044,7 +1060,7 @@ </property> </widget> </item> - <item row="0" column="9"> + <item row="0" column="11"> <spacer name="horizontalSpacer_8"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -1123,6 +1139,80 @@ </item> </layout> </item> + <item row="0" column="7"> + <spacer name="horizontalSpacer_simulation"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>100</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="8"> + <layout class="QGridLayout" name="gridLayout_10"> + <item row="2" column="0"> + <widget class="QPushButton" name="clear_simulation_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>1000</width> + <height>60</height> + </size> + </property> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="simulate_step_response_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>1000</width> + <height>60</height> + </size> + </property> + <property name="text"> + <string>Perform</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_simulation_blank"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </item> </layout> </item> <item row="7" column="0"> @@ -1167,15 +1257,28 @@ <tabstop>lineEdit_k</tabstop> <tabstop>lineEdit_T</tabstop> <tabstop>lineEdit_alpha</tabstop> + <tabstop>lineEdit_time_delay</tabstop> <tabstop>lineEdit_kp</tabstop> <tabstop>lineEdit_kd</tabstop> <tabstop>lineEdit_step_size</tabstop> <tabstop>lineEdit_step_duration</tabstop> <tabstop>set_lead_compensator_parameters_button</tabstop> + <tabstop>set_time_delay_button</tabstop> <tabstop>set_pd_controller_parameters_button</tabstop> <tabstop>perform_step_button</tabstop> <tabstop>log_data_button</tabstop> + <tabstop>simulate_step_response_button</tabstop> + <tabstop>clear_simulation_button</tabstop> + <tabstop>toggle_integrator_button</tabstop> + <tabstop>reset_integrator_button</tabstop> + <tabstop>lineEdit_rise_time</tabstop> + <tabstop>lineEdit_settling_time</tabstop> + <tabstop>lineEdit_step_end</tabstop> + <tabstop>lineEdit_step_start</tabstop> <tabstop>chartView_for_x</tabstop> + <tabstop>chartView_for_pitch</tabstop> + <tabstop>lineEdit_overshoot_value</tabstop> + <tabstop>lineEdit_overshoot_percent</tabstop> </tabstops> <resources/> <connections/> diff --git a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/include/csonecontrollertab.h b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/include/csonecontrollertab.h index e45d064c..5ba4e055 100644 --- a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/include/csonecontrollertab.h +++ b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/include/csonecontrollertab.h @@ -59,6 +59,8 @@ // For the chart: #include <QChart> #include <QLineSeries> +#include <QList> +#include <QPointF> using namespace QtCharts; #ifdef CATKIN_MAKE @@ -118,6 +120,9 @@ public slots: private slots: + + float validate_and_get_value_from_lineEdit(QLineEdit * lineEdit, float min, float max, int decimals, float default_value); + void newDataForPerformingStepAndPlotting(float x, float pitch); void analyseStepResponse(); void updateStepAnalysisLineEdit(float value, QLineEdit * lineEdit, int num_dec_places); @@ -126,6 +131,9 @@ private slots: void on_perform_step_button_clicked(); void on_log_data_button_clicked(); + void on_simulate_step_response_button_clicked(); + void on_clear_simulation_button_clicked(); + void on_set_lead_compensator_parameters_button_clicked(); void on_set_time_delay_button_clicked(); @@ -142,10 +150,17 @@ private slots: void on_lineEdit_step_size_returnPressed(); void on_lineEdit_step_duration_returnPressed(); + void on_lineEdit_k_editingFinished(); + void on_lineEdit_T_editingFinished(); + void on_lineEdit_alpha_editingFinished(); + void on_lineEdit_step_size_editingFinished(); + void on_lineEdit_step_duration_editingFinished(); + void simulate_step_response(); + private: Ui::CsoneControllerTab *ui; @@ -171,6 +186,9 @@ private: float m_current_setpoint_z = 0.4; float m_current_setpoint_yaw = 0.0; + // > Mutex for serialising acess to the controller variables + QMutex m_controller_parameter_mutex; + // FOR PLOTTING DATA ON THE CHART // > Mutex for serialising acess to any charting variable QMutex m_chart_mutex; @@ -183,7 +201,7 @@ private: // > Time (as a float) for the horizontal axis float m_time_for_step = 0.0f; // > The duration for which to record the step - float m_step_response_data_recording_duration = 20.0f; + float m_step_response_data_recording_duration = 10.0f; // > Line Series for the x position data QLineSeries *m_lineSeries_for_setpoint_x; QLineSeries *m_lineSeries_for_measured_x; @@ -192,6 +210,28 @@ private: QLineSeries *m_lineSeries_for_measured_pitch; + // FOR SIMULATING THE CONTROLLER + // > Mutex for serialising acess to any simulation variables + QMutex m_simulation_mutex; + + // Flag for whether a simulation is under way + bool m_simulationIsInProgress = false; + + // The state space matrices of lead compensator controller + float m_lead_compensator_A=0.987578; + float m_lead_compensator_B=0.00496888; + float m_lead_compensator_C=-0.36; + float m_lead_compensator_D=0.16; + + // The state of lead compensator controller + float m_lead_compensator_state = 0.0; + + // Line Series for plotting the simulation results + QLineSeries *m_lineSeries_for_sim_setpoint_x; + QLineSeries *m_lineSeries_for_sim_measured_x; + QLineSeries *m_lineSeries_for_sim_measured_pitch; + + #ifdef CATKIN_MAKE // PUBLISHER @@ -248,6 +288,8 @@ private: void publishRequestForTimeDelayChange(int time_delay_to_publish); + void convertIntoDiscreteTimeParameters(float k, float T, float alpha); + }; #endif // CSONECONTROLLERTAB_H diff --git a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/src/csonecontrollertab.cpp b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/src/csonecontrollertab.cpp index a2114441..401093fb 100644 --- a/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/src/csonecontrollertab.cpp +++ b/dfall_ws/src/dfall_pkg/GUI_Qt/flyingAgentGUI/src/csonecontrollertab.cpp @@ -56,6 +56,21 @@ CsoneControllerTab::CsoneControllerTab(QWidget *parent) : ui->lineEdit_kd->hide(); ui->set_pd_controller_parameters_button->hide(); + + // ------------------------------------------------------------- // + // HIDE THE SIMULATION BUTTONS + //ui->label_simulation->hide(); + //ui->label_simulation_blank->hide(); + //ui->simulate_step_response_button->hide(); + //ui->clear_simulation_button->hide(); + + + // ------------------------------------------------------------- // + // SET VALIDATORS FOR THE LINE EDITS + // > NOT USED BECAUSE DO NOT AUTOMATICALLY CLIP TO THE MIN AND MAX + //ui->lineEdit_k->setValidator(new QDoubleValidator(-1.0,1.0,2,ui->lineEdit_k) ); + + // ------------------------------------------------------------- // // SETUP THE CHART VIEW FOR THE X POSITION // @@ -82,14 +97,26 @@ CsoneControllerTab::CsoneControllerTab(QWidget *parent) : m_lineSeries_for_setpoint_x = new QLineSeries(); m_lineSeries_for_measured_x = new QLineSeries(); + m_lineSeries_for_sim_setpoint_x = new QLineSeries(); + m_lineSeries_for_sim_measured_x = new QLineSeries(); + // Add the line series to the chart ui->chartView_for_x->chart()->addSeries(m_lineSeries_for_setpoint_x); ui->chartView_for_x->chart()->addSeries(m_lineSeries_for_measured_x); + ui->chartView_for_x->chart()->addSeries(m_lineSeries_for_sim_setpoint_x); + ui->chartView_for_x->chart()->addSeries(m_lineSeries_for_sim_measured_x); + // Set the style of the series m_lineSeries_for_setpoint_x->setPen(QPen( QBrush("blue") , 5.0 )); m_lineSeries_for_measured_x->setPen(QPen( QBrush("red") , 5.0 )); + m_lineSeries_for_sim_setpoint_x->setPen(QPen( QBrush("blue") , 5.0 , Qt::DotLine )); + m_lineSeries_for_sim_measured_x->setPen(QPen( QBrush("red") , 5.0 , Qt::DotLine )); + + // OPTIONS FOR THE LINE STYLE: + // { Qt::SolidLine , Qt::DashLine , Qt::DotLine , Qt::DashDotLine , Qt::DashDotDotLine } + // Set the initial axes limits ui->chartView_for_x->chart()->createDefaultAxes(); ui->chartView_for_x->chart()->axisX()->setMin(-1.0); @@ -117,20 +144,23 @@ CsoneControllerTab::CsoneControllerTab(QWidget *parent) : // Initialise the line series to be used for plotting //m_lineSeries_for_setpoint_pitch = new QLineSeries(); m_lineSeries_for_measured_pitch = new QLineSeries(); + m_lineSeries_for_sim_measured_pitch = new QLineSeries(); // Add the line series to the chart //ui->chartView_for_pitch->chart()->addSeries(m_lineSeries_for_setpoint_pitch); ui->chartView_for_pitch->chart()->addSeries(m_lineSeries_for_measured_pitch); + ui->chartView_for_pitch->chart()->addSeries(m_lineSeries_for_sim_measured_pitch); // Set the style of the series //m_lineSeries_for_setpoint_pitch->setPen(QPen( QBrush("blue") , 5.0 )); m_lineSeries_for_measured_pitch->setPen(QPen( QBrush("red") , 5.0 )); + m_lineSeries_for_sim_measured_pitch->setPen(QPen( QBrush("red") , 5.0 , Qt::DotLine )); // Set the initial axes limits ui->chartView_for_pitch->chart()->createDefaultAxes(); ui->chartView_for_pitch->chart()->axisX()->setMin(-1.0); ui->chartView_for_pitch->chart()->axisX()->setMax(m_step_response_data_recording_duration); - ui->chartView_for_pitch->chart()->axisY()->setMin(20.0); + ui->chartView_for_pitch->chart()->axisY()->setMin(-20.0); ui->chartView_for_pitch->chart()->axisY()->setMax(20.0); @@ -233,6 +263,37 @@ CsoneControllerTab::~CsoneControllerTab() + + +float CsoneControllerTab::validate_and_get_value_from_lineEdit(QLineEdit * lineEdit, float min, float max, int decimals, float default_value) +{ + // Initialise the value to the default + float return_value = default_value; + + // Update the duration from the field + if(!lineEdit->text().isEmpty()) + { + return_value = (lineEdit->text()).toFloat(); + // Ensure that it is in the range [2,60] + if (return_value < min) + return_value = min; + else if (return_value > max) + return_value = max; + } + + // Clip the value to the specified decimal places + + + // Put the value back into the line edit + lineEdit->setText(QString::number( return_value, 'f', decimals)); + + // Return the value + return return_value; +} + + + + // ---------------------------------------------------------------------------------- // BBBB U U TTTTT TTTTT OOO N N SSSS // B B U U T T O O NN N S @@ -262,29 +323,7 @@ void CsoneControllerTab::on_perform_step_button_clicked() ui->chartView_for_pitch->chart()->axisX()->setMin(m_time_for_step); // Update the duration from the field - if(!ui->lineEdit_step_duration->text().isEmpty()) - { - float temp_duration = (ui->lineEdit_step_duration->text()).toFloat(); - // Ensure that it is in the range [2,60] - if (temp_duration < 2.0) - { - temp_duration = 2.0; - ui->lineEdit_step_duration->setText(QString::number( temp_duration, 'f', 0)); - } - else if (temp_duration > 20.0) - { - temp_duration = 20.0; - ui->lineEdit_step_duration->setText(QString::number( temp_duration, 'f', 0)); - } - // Update the global variable - m_step_response_data_recording_duration = temp_duration; - } - else - { - // Default the duration to 10 - m_step_response_data_recording_duration = 10.0; - ui->lineEdit_step_duration->setText(QString::number( m_step_response_data_recording_duration, 'f', 0)); - } + m_step_response_data_recording_duration = validate_and_get_value_from_lineEdit(ui->lineEdit_step_duration,2.0,20.0,0,10.0); // Set the minimum of the x-axis to agree with the duration ui->chartView_for_x ->chart()->axisX()->setMax(m_step_response_data_recording_duration); @@ -338,29 +377,7 @@ void CsoneControllerTab::on_log_data_button_clicked() ui->chartView_for_pitch->chart()->axisX()->setMin(m_time_for_step); // Update the duration from the field - if(!ui->lineEdit_step_duration->text().isEmpty()) - { - float temp_duration = (ui->lineEdit_step_duration->text()).toFloat(); - // Ensure that it is in the range [2,60] - if (temp_duration < 2.0) - { - temp_duration = 2.0; - ui->lineEdit_step_duration->setText(QString::number( temp_duration, 'f', 0)); - } - else if (temp_duration > 60.0) - { - temp_duration = 60.0; - ui->lineEdit_step_duration->setText(QString::number( temp_duration, 'f', 0)); - } - // Update the global variable - m_step_response_data_recording_duration = temp_duration; - } - else - { - // Default the duration to 10 - m_step_response_data_recording_duration = 10.0; - ui->lineEdit_step_duration->setText(QString::number( m_step_response_data_recording_duration, 'f', 0)); - } + m_step_response_data_recording_duration = validate_and_get_value_from_lineEdit(ui->lineEdit_step_duration,2.0,20.0,0,10.0); // Set the minimum of the x-axis to agree with the duration ui->chartView_for_x ->chart()->axisX()->setMax(m_step_response_data_recording_duration); @@ -395,77 +412,99 @@ void CsoneControllerTab::on_log_data_button_clicked() } -void CsoneControllerTab::on_set_lead_compensator_parameters_button_clicked() -{ - // Initialise local variable for each of (x,y,z,yaw) - float k = 1.0f, T = 1.0f, alpha = 1.0f; - // Initialise a string variable for adding the "+" - QString qstr = ""; - // Take the new value if available, otherwise use default - // > For k - if(!ui->lineEdit_k->text().isEmpty()) +void CsoneControllerTab::on_simulate_step_response_button_clicked() +{ + // Initialise a flag for whether to start a simulation or not + bool shouldStartSimulation = true; + + // Set the flag that a simulation is being performed + m_simulation_mutex.lock(); + if (m_simulationIsInProgress) { - k = (ui->lineEdit_k->text()).toFloat(); - // Ensure that it is in the range [0,100] - if (k < 0.0) - { - k = 0.0; - ui->lineEdit_k->setText(qstr + QString::number( k, 'f', 4)); - } - else if (k > 100.0) - { - k = 100.0; - ui->lineEdit_k->setText(qstr + QString::number( k, 'f', 4)); - } + shouldStartSimulation = false; } else { - ui->lineEdit_k->setText(qstr + QString::number( k, 'f', 4)); + m_simulationIsInProgress = true; + // Set the button to not be avaialble + ui->simulate_step_response_button->setEnabled(false); + ui->clear_simulation_button->setEnabled(false); } + m_simulation_mutex.unlock(); - // > For T - if(!ui->lineEdit_T->text().isEmpty()) - { - T = (ui->lineEdit_T->text()).toFloat(); - // Ensure that it is in the range [0,100] - if (T < 0.0) - { - T = 0.0; - ui->lineEdit_T->setText(qstr + QString::number( T, 'f', 1)); - } - else if (T > 100.0) - { - T = 100.0; - ui->lineEdit_T->setText(qstr + QString::number( T, 'f', 1)); - } - } - else + // Call the function that performs the simulation + if (shouldStartSimulation) { - ui->lineEdit_T->setText(qstr + QString::number( T, 'f', 1)); + // Lock the mutex + m_chart_mutex.lock(); + + // Clear the line series + m_lineSeries_for_sim_setpoint_x->removePoints(0,m_lineSeries_for_sim_setpoint_x->count()); + m_lineSeries_for_sim_measured_x->removePoints(0,m_lineSeries_for_sim_measured_x->count()); + m_lineSeries_for_sim_measured_pitch->removePoints(0,m_lineSeries_for_sim_measured_pitch->count()); + + // > For x position + m_lineSeries_for_setpoint_x->removePoints(0,m_lineSeries_for_setpoint_x->count()); + m_lineSeries_for_measured_x->removePoints(0,m_lineSeries_for_measured_x->count()); + // > For pitch angles + //m_lineSeries_for_setpoint_pitch->removePoints(0,m_lineSeries_for_setpoint_pitch->count()); + m_lineSeries_for_measured_pitch->removePoints(0,m_lineSeries_for_measured_pitch->count()); + + // Unlock the mutex + m_chart_mutex.unlock(); + + simulate_step_response(); + //QTimer::singleShot(50, this, SLOT(simulate_step_response())); } +} + + +void CsoneControllerTab::on_clear_simulation_button_clicked() +{ + // Lock the mutex + m_chart_mutex.lock(); + + // First clear the line series + m_lineSeries_for_sim_setpoint_x->removePoints(0,m_lineSeries_for_sim_setpoint_x->count()); + m_lineSeries_for_sim_measured_x->removePoints(0,m_lineSeries_for_sim_measured_x->count()); + m_lineSeries_for_sim_measured_pitch->removePoints(0,m_lineSeries_for_sim_measured_pitch->count()); + + // > For x position + m_lineSeries_for_setpoint_x->removePoints(0,m_lineSeries_for_setpoint_x->count()); + m_lineSeries_for_measured_x->removePoints(0,m_lineSeries_for_measured_x->count()); + // > For pitch angles + //m_lineSeries_for_setpoint_pitch->removePoints(0,m_lineSeries_for_setpoint_pitch->count()); + m_lineSeries_for_measured_pitch->removePoints(0,m_lineSeries_for_measured_pitch->count()); + + // Unlock the mutex + m_chart_mutex.unlock(); +} + + + + +void CsoneControllerTab::on_set_lead_compensator_parameters_button_clicked() +{ + // Initialise local variable for each of (x,y,z,yaw) + float k = 1.0f, T = 1.0f, alpha = 1.0f; + + // Lock the mutex + m_controller_parameter_mutex.lock(); + + // Take the new value if available, otherwise use default + // > For k + k = validate_and_get_value_from_lineEdit(ui->lineEdit_k,-100.0,100.0,4,0.016); // > For T - if(!ui->lineEdit_alpha->text().isEmpty()) - { - alpha = (ui->lineEdit_alpha->text()).toFloat(); - // Ensure that it is in the range [0,100] - if (alpha < 0.0) - { - alpha = 0.0; - ui->lineEdit_alpha->setText(qstr + QString::number( alpha, 'f', 2)); - } - else if (T > 100.0) - { - T = 100.0; - ui->lineEdit_alpha->setText(qstr + QString::number( alpha, 'f', 2)); - } - } - else - { - ui->lineEdit_alpha->setText(qstr + QString::number( alpha, 'f', 2)); - } + T = validate_and_get_value_from_lineEdit(ui->lineEdit_T,0.0,100.0,1,4.0); + + // > For alpha + alpha = validate_and_get_value_from_lineEdit(ui->lineEdit_alpha,0.1,1.0,2,0.1); + + // Unlock the mutex + m_controller_parameter_mutex.unlock(); // Call the function to publish the controller parameters publishControllerParamters(k,T,alpha); @@ -475,28 +514,9 @@ void CsoneControllerTab::on_set_lead_compensator_parameters_button_clicked() void CsoneControllerTab::on_set_time_delay_button_clicked() { - // Initialise local variable for the time delay - float time_delay_int = 0; - // Take the new value if available, otherwise use default - if(!ui->lineEdit_time_delay->text().isEmpty()) - { - float time_delay_float = (ui->lineEdit_time_delay->text()).toFloat(); - // Ensure that it is in the range [0,1000] - if (time_delay_float < 0.0) - time_delay_int = 0; - else if (time_delay_float > 1000.0) - time_delay_int = 1000; - else - time_delay_int = int(time_delay_float); - } - else - { - time_delay_int = 0; - } - - // Put the value back into the line edit - ui->lineEdit_time_delay->setText( QString::number( time_delay_int, 'd', 0) ); + float time_delay_float = validate_and_get_value_from_lineEdit(ui->lineEdit_time_delay,0.0,1000.0,0,0.0); + float time_delay_int = int(time_delay_float); // Call the function to publish the time delay publishRequestForTimeDelayChange(time_delay_int); @@ -563,6 +583,46 @@ void CsoneControllerTab::on_lineEdit_step_duration_returnPressed() +void CsoneControllerTab::on_lineEdit_k_editingFinished() +{ + m_controller_parameter_mutex.lock(); + validate_and_get_value_from_lineEdit(ui->lineEdit_k,-100.0,100.0,4,0.016); + m_controller_parameter_mutex.unlock(); +} + +void CsoneControllerTab::on_lineEdit_T_editingFinished() +{ + m_controller_parameter_mutex.lock(); + validate_and_get_value_from_lineEdit(ui->lineEdit_T,0.0,100.0,1,4.0); + m_controller_parameter_mutex.unlock(); +} + +void CsoneControllerTab::on_lineEdit_alpha_editingFinished() +{ + m_controller_parameter_mutex.lock(); + validate_and_get_value_from_lineEdit(ui->lineEdit_alpha,0.1,1.0,2,0.1); + m_controller_parameter_mutex.unlock(); +} + + + +void CsoneControllerTab::on_lineEdit_step_size_editingFinished() +{ + m_chart_mutex.lock(); + validate_and_get_value_from_lineEdit(ui->lineEdit_step_size,-2.0,2.0,1,0.5); + m_chart_mutex.unlock(); +} + +void CsoneControllerTab::on_lineEdit_step_duration_editingFinished() +{ + m_chart_mutex.lock(); + m_step_response_data_recording_duration = validate_and_get_value_from_lineEdit(ui->lineEdit_step_duration,2.0,20.0,0,10.0); + m_chart_mutex.unlock(); +} + + + + // ---------------------------------------------------------------------------------- // PPPP OOO SSSS EEEEE DDDD A TTTTT A // P P O O S E D D A A T A A @@ -676,7 +736,7 @@ void CsoneControllerTab::newDataForPerformingStepAndPlotting(float x, float pitc m_shouldPerformStep = false; // Extract the current step size - float step_size = (ui->lineEdit_step_size->text()).toFloat(); + float step_size = validate_and_get_value_from_lineEdit(ui->lineEdit_step_size,-2.0,2.0,1,0.5); // Determine the new x setpoint float new_x = 0.0; @@ -1070,6 +1130,9 @@ void CsoneControllerTab::publishControllerParamters(float k, float T, float alph // TO ASSIST WITH DEBUGGING WHEN COMPILED AND RUN IN "QtCreator" QTextStream(stdout) << "[CSONE CONTROLLER GUI] would publish request for: [" << k << ", "<< T << ", "<< alpha << "]"; #endif + + // Call the function to convert this to a discrete-time state-space controller + convertIntoDiscreteTimeParameters(k, T, alpha); } @@ -1122,13 +1185,332 @@ void CsoneControllerTab::publishRequestForTimeDelayChange(int time_delay_to_publ ROS_INFO_STREAM("[CSONE CONTROLLER GUI] Published request for time delay of " << time_delay_to_publish << " milliseconds"); #else // TO ASSIST WITH DEBUGGING WHEN COMPILED AND RUN IN "QtCreator" - QTextStream(stdout) << "[CSONE CONTROLLER GUI] would publish request for time delay of " << time_delay_to_publish << " milliseconds"; + QTextStream(stdout) << "[CSONE CONTROLLER GUI] would publish request for time delay of " << time_delay_to_publish << " milliseconds" << "\n"; +#endif +} + + + + + + + +// ---------------------------------------------------------------------------------- +// +// +// +// +// +// ---------------------------------------------------------------------------------- + +// CHANGE CONTROLLER PARAMETERS INTO DISCRETE TIME FUNCTION +void CsoneControllerTab::convertIntoDiscreteTimeParameters(float k, float T, float alpha) +{ + float control_frequency = 200.0; + + if (alpha > 1){alpha = 1;} else if (alpha<0.1){alpha = 0.1;} + + if (T > 100){T = 100;} else if (T<0.1){T = 0.1;} + + // Lock the mutex + m_simulation_mutex.lock(); + + // Compute the A,B,C,D matrices + m_lead_compensator_A = pow(2.71828,(-1.0/(control_frequency*alpha*T))); + m_lead_compensator_B = -alpha*T*(m_lead_compensator_A-1.0); + m_lead_compensator_C = k/(alpha*T)*(1.0-1.0/alpha); + m_lead_compensator_D = k/alpha; + + // Reset the state of the lead compensator to zero + m_lead_compensator_state = 0.0; + +#ifdef CATKIN_MAKE +#else + // TO ASSIST WITH DEBUGGING WHEN COMPILED AND RUN IN "QtCreator" + QTextStream(stdout) << "[CSONE CONTROLLER GUI] Parameters changed to k=" << k << ", T=" << T << ", alpha=" << alpha << "\n"; + QTextStream(stdout) << "[CSONE CONTROLLER GUI] Matrices changed to A=" << m_lead_compensator_A << ", B=" << m_lead_compensator_B << ", C=" << m_lead_compensator_C << ", D=" << m_lead_compensator_D; #endif + + // Unlock the mutex + m_simulation_mutex.unlock(); + } +void CsoneControllerTab::simulate_step_response() +{ + // GET THE CONTROLLER PARAMETERS INTO LOCAL VARIABLES + + // Lock the mutex + m_simulation_mutex.lock(); + + float A = m_lead_compensator_A; + float B = m_lead_compensator_B; + float C = m_lead_compensator_C; + float D = m_lead_compensator_D; + + float controller_state = 0.0; + + // Unlock the mutex + m_simulation_mutex.unlock(); + + // GET THE STEP SPECIFICATIONS INTO LOCAL VARIABLES + + // Lock the mutex + m_chart_mutex.lock(); + + float current_setpoint_x = m_current_setpoint_x; + + float step_size = validate_and_get_value_from_lineEdit(ui->lineEdit_step_size,-2.0,2.0,1,0.5); + + m_step_response_data_recording_duration = validate_and_get_value_from_lineEdit(ui->lineEdit_step_duration,2.0,20.0,0,10.0); + float duration = m_step_response_data_recording_duration; + + // Unlock the mutex + m_chart_mutex.unlock(); + + + + // Take the new value if available, otherwise use default + float time_delay_float = validate_and_get_value_from_lineEdit(ui->lineEdit_time_delay,0.0,1000.0,0,0.0); + float time_delay_int = int(time_delay_float); + + // Compute the number of milliseconds per time step + float delta_T_in_milliseconds = 1000.0 / 200.0; + + // Convert the time delay to a number of time steps + int time_delay_in_steps = int( (float(time_delay_int) + 0.1) / delta_T_in_milliseconds ); + + // Wrap this value into the allowed limits + if (time_delay_in_steps<0) + time_delay_in_steps=0; + if(time_delay_in_steps>(300-1)) + time_delay_in_steps=300-1; +#ifdef CATKIN_MAKE +#else + // TO ASSIST WITH DEBUGGING WHEN COMPILED AND RUN IN "QtCreator" + QTextStream(stdout) << "[CSONE CONTROLLER GUI] time delay in steps = " << time_delay_in_steps << "\n"; +#endif + + + // PERFORM THE DURATION + + // Initialise things + float control_frequency = 200.0; + float control_deltaT = 0.005; + + float sim_time = 0.0; + + int num_time_steps = int( duration / control_deltaT ); + + int num_point_per_append = 10; + int next_index = 0; + + QVector<float> sim_traj_time (num_point_per_append,0); + + //std::vector<float> sim_traj_x (num_time_steps,0); + QVector<float> sim_traj_x (num_point_per_append,0); + //QList<QPointF> sim_traj_x; + + QVector<float> sim_traj_pitch (num_point_per_append,0); + + // The inertial x measurement during a time window of 300 measurements + QVector<float> x_buffer (300,0); + + float x_reference_start = current_setpoint_x; + // Determine the new x setpoint + float x_reference_end = 0.0; + if (x_reference_start < 0) + x_reference_end = 0.5 * step_size; + else + x_reference_end = -0.5 * step_size; + + // Initialise the current state + float current_x = x_reference_start; + float current_xdot = 0.0; + float current_pitch = 0.0; + + // Initialise variables for keeping track of the min and max + float min_x_value_sim = -0.01; + float max_x_value_sim = 0.01; + float min_pitch_value_sim = -0.01; + float max_pitch_value_sim = 0.01; + + // Update the min and max + min_x_value_sim = std::min( min_x_value_sim , current_x ); + max_x_value_sim = std::max( max_x_value_sim , current_x ); + min_pitch_value_sim = std::min( min_pitch_value_sim , current_pitch*float(RAD2DEG) ); + max_pitch_value_sim = std::max( max_pitch_value_sim , current_pitch*float(RAD2DEG) ); + + + // Put in the initial conditions + sim_traj_time[next_index] = sim_time; + + sim_traj_x[next_index] = current_x; + //sim_traj_x.append( QPointF(sim_time,current_x) ); + + sim_traj_pitch[next_index] = current_pitch; + + next_index++; + + + + + + // Lock the mutex + m_chart_mutex.lock(); + + // For the x reference + m_lineSeries_for_sim_setpoint_x->append(0.0, x_reference_start ); + m_lineSeries_for_sim_setpoint_x->append(0.0, x_reference_end ); + m_lineSeries_for_sim_setpoint_x->append(duration, x_reference_end ); + + // Unlock the mutex + m_chart_mutex.unlock(); + + + int write_index = 0; + int read_index = 0; + + + // Iterate over the duration + for ( int itime=1 ; itime<num_time_steps ; itime++ ) + { + // Increment the write index + write_index += 1; + // And wrap it back into range if necessary + if (write_index>=x_buffer.size()) + { + write_index = 0; + } + + // Compute the next read index based on the delay + read_index = write_index - time_delay_in_steps; + // And wrap it back into range if necessary + if (read_index<0) + { + read_index += x_buffer.size(); + } + + // Write the new data to the buffer + x_buffer[write_index] = current_x; + + // Read the data for this time step from the buffer + float x_for_this_time_step = x_buffer[read_index]; + + // Compute the error for this time step + float x_error = x_reference_end - x_for_this_time_step; + + // Compute the input to apply + // > FIRST: compute the new pitch reference + float pitch_ref = C*controller_state + D*x_error; + // > SECOND: evaluate the state update equation + controller_state = A*controller_state + B*x_error; + // > THIRD: perform the inner controller + float pitch_rate = 5.0*(pitch_ref-current_pitch); + +// A = +// x1 x2 x3 +// x1 1 0.004998 0.0001226 +// x2 0 0.9993 0.04903 +// x3 0 0 1 + +// B = +// u1 +// x1 2.043e-07 +// x2 0.0001226 +// x3 0.005 + + // Perform the evolution of the state + current_x = current_x + 0.004998*current_xdot + 0.0001226*current_pitch; + current_xdot = 0.9993*current_xdot + 0.04903*current_pitch + 0.0001226*pitch_rate; + current_pitch = current_pitch + 0.005*pitch_rate; + + // Update the min and max + min_x_value_sim = std::min( min_x_value_sim , current_x ); + max_x_value_sim = std::max( max_x_value_sim , current_x ); + min_pitch_value_sim = std::min( min_pitch_value_sim , current_pitch*float(RAD2DEG) ); + max_pitch_value_sim = std::max( max_pitch_value_sim , current_pitch*float(RAD2DEG) ); + + // Increment the simulation time + sim_time += control_deltaT; + + // Store the state and time + sim_traj_time[next_index] = sim_time; + + sim_traj_x[next_index] = current_x; + //sim_traj_x.append( QPointF(sim_time,current_x) ); + + sim_traj_pitch[next_index] = current_pitch; + + // Increment the next index pointer + next_index++; + + // Update the chart if required + if (next_index >= num_point_per_append) + { + // Lock the mutex + m_chart_mutex.lock(); + // Iterate through the point + for (int ipoint=0 ; ipoint<num_point_per_append ; ipoint++) + { + m_lineSeries_for_sim_measured_x->append( sim_traj_time[ipoint] , sim_traj_x[ipoint] ); + m_lineSeries_for_sim_measured_pitch->append( sim_traj_time[ipoint] , sim_traj_pitch[ipoint]*RAD2DEG ); + + //ui->chartView_for_x->repaint(); + } + next_index = 0; + // Unlock the mutex + m_chart_mutex.unlock(); + } + } + + + // PUT THE SIMULATION RESULTS INTO THE PLOTTED LINE SERIES + + // Lock the mutex + m_chart_mutex.lock(); + + // Update the chart with any remaining points + if (next_index > 0) + { + // Iterate through the point + for (int ipoint=0 ; ipoint<next_index ; ipoint++) + { + m_lineSeries_for_sim_measured_x->append( sim_traj_time[ipoint] , sim_traj_x[ipoint] ); + m_lineSeries_for_sim_measured_pitch->append( sim_traj_time[ipoint] , sim_traj_pitch[ipoint]*RAD2DEG ); + } + next_index = 0; + } + + // Rezoom the x position chart + float diff_of_x_values_sim = max_x_value_sim - min_x_value_sim; + ui->chartView_for_x->chart()->axisY()->setMin( min_x_value_sim - 0.1*diff_of_x_values_sim ); + ui->chartView_for_x->chart()->axisY()->setMax( max_x_value_sim + 0.1*diff_of_x_values_sim ); + // Rezoom the pitch angles chart + float diff_of_pitch_values_sim = max_pitch_value_sim - min_pitch_value_sim; + ui->chartView_for_pitch->chart()->axisY()->setMin( min_pitch_value_sim - 0.1*diff_of_pitch_values_sim ); + ui->chartView_for_pitch->chart()->axisY()->setMax( max_pitch_value_sim + 0.1*diff_of_pitch_values_sim ); + + // Unlock the mutex + m_chart_mutex.unlock(); + + + // Set the flag that a simulation is completed + m_simulation_mutex.lock(); + m_simulationIsInProgress = false; + // Set the button to not be avaialble + ui->simulate_step_response_button->setEnabled(true); + ui->clear_simulation_button->setEnabled(true); + m_simulation_mutex.unlock(); + + +} + + + // ---------------------------------------------------------------------------------- // A GGGG EEEEE N N TTTTT III DDDD SSSS // A A G E NN N T I D D S -- GitLab