├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── actual ├── pid.c └── pid.h └── simulator ├── PID_Simulator.pro ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── pid.cpp ├── pid.h ├── transfunc.cpp └── transfunc.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *.user 35 | build-* 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 [Le_Seul] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PID_Simulator 2 | 3 | This is a PID Control Simulator build with Qt. 4 | 5 | In the./actual directory, it is a useful PID Coltrol source code. 6 | -------------------------------------------------------------------------------- /actual/pid.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // file name : pid.c 3 | // author : Wenliang Guan 4 | // version : 1.0 5 | // date : 2018/3/27 6 | // This is the implementation of a classic PID controller. 7 | //============================================================================= 8 | 9 | #include "pid.h" 10 | 11 | // PID initialization 12 | void pid_init(pid_control_t *pid, float kp, float ki, float kd, int scale) 13 | { 14 | pid->kp = (int)(kp * scale); 15 | pid->ki = (int)(ki * scale); 16 | pid->kd = (int)(kd * scale); 17 | pid->scale = scale; 18 | pid->sum = 0; 19 | pid->error = 0; 20 | pid->value = 0; 21 | } 22 | 23 | // PID setting the set value 24 | void pid_set_value(pid_control_t* pid, int value) 25 | { 26 | pid->value = value; 27 | } 28 | 29 | // PID setting parameters (floating-point) 30 | void pid_set_params(pid_control_t* pid, float kp, float ki, float kd) 31 | { 32 | pid->kp = (int)(kp * pid->scale); 33 | pid->ki = (int)(ki * pid->scale); 34 | pid->kd = (int)(kd * pid->scale); 35 | } 36 | 37 | // PID iteration calculation 38 | int pid_iteration(pid_control_t* pid, int measured) 39 | { 40 | int error, derror; 41 | 42 | error = pid->value - measured; // calculation of the current error 43 | derror = error - pid->error; // calculation error derivative 44 | pid->sum += error; // calculation error integral 45 | pid->error = error; // update error value 46 | 47 | /* calculation PID controller output value */ 48 | return (pid->kp * error // proportion control 49 | + pid->ki * pid->sum // integral control 50 | + pid->kd * derror) // derivative control 51 | / pid->scale; // integral point scaling 52 | } 53 | -------------------------------------------------------------------------------- /actual/pid.h: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // file name : pid.h 3 | // author : Wenliang Guan 4 | // version : 1.0 5 | // date : 2018/3/27 6 | // This is the implementation of a classic PID controller. 7 | //============================================================================= 8 | 9 | #ifndef __PID_H 10 | #define __PID_H 11 | 12 | #ifdef __cplusplus 13 | #if __cplusplus 14 | extern "C" { 15 | #endif 16 | #endif // __cplusplus 17 | 18 | typedef struct { 19 | int kp, ki, kd; // PID parameter 20 | int value; // set value 21 | int error; // last error 22 | int sum; // error integral 23 | int scale; // integral point scaling factor 24 | } pid_control_t; 25 | 26 | void pid_init(pid_control_t* pid, float kp, float ki, float kd, int scale); 27 | void pid_set_value(pid_control_t *pid, int value); 28 | void pid_set_params(pid_control_t* pid, float kp, float ki, float kd); 29 | int pid_iteration(pid_control_t* pid, int measured); 30 | 31 | #ifdef __cplusplus 32 | #if __cplusplus 33 | } 34 | #endif 35 | #endif // __cplusplus 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /simulator/PID_Simulator.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2018-03-23T14:42:12 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui charts 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = PID_Simulator 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += \ 27 | main.cpp \ 28 | mainwindow.cpp \ 29 | pid.cpp \ 30 | transfunc.cpp 31 | 32 | HEADERS += \ 33 | mainwindow.h \ 34 | pid.h \ 35 | transfunc.h 36 | 37 | FORMS += \ 38 | mainwindow.ui 39 | -------------------------------------------------------------------------------- /simulator/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /simulator/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "pid.h" 4 | #include "transfunc.h" 5 | #include 6 | 7 | MainWindow::MainWindow(QWidget *parent) : 8 | QMainWindow(parent), 9 | ui(new Ui::MainWindow) 10 | { 11 | ui->setupUi(this); 12 | 13 | m_chart = new QChart; 14 | ui->chartView->setChart(m_chart); 15 | ui->chartView->setRenderHint(QPainter::Antialiasing); 16 | 17 | addSeries("Set Value"); 18 | addSeries("Control Results"); 19 | addSeries("PID Output"); 20 | 21 | connectMarkers(); 22 | 23 | m_chart->createDefaultAxes(); 24 | m_chart->setMargins(QMargins(0, 0, 0, 0)); // set margins 25 | m_chart->setContentsMargins(-9, -9, -8, -8); 26 | m_chart->setBackgroundRoundness(4); 27 | 28 | connect(ui->spinBoxKp, SIGNAL(valueChanged(double)), this, SLOT(update())); 29 | connect(ui->spinBoxKi, SIGNAL(valueChanged(double)), this, SLOT(update())); 30 | connect(ui->spinBoxKd, SIGNAL(valueChanged(double)), this, SLOT(update())); 31 | connect(ui->spinBoxKp, SIGNAL(editingFinished()), this, SLOT(onSpinBoxEdited())); 32 | connect(ui->spinBoxKi, SIGNAL(editingFinished()), this, SLOT(onSpinBoxEdited())); 33 | connect(ui->spinBoxKd, SIGNAL(editingFinished()), this, SLOT(onSpinBoxEdited())); 34 | connect(ui->silderKp, SIGNAL(valueChanged(int)), this, SLOT(onSilderKpChanged(int))); 35 | connect(ui->silderKi, SIGNAL(valueChanged(int)), this, SLOT(onSilderKiChanged(int))); 36 | connect(ui->silderKd, SIGNAL(valueChanged(int)), this, SLOT(onSilderKdChanged(int))); 37 | 38 | update(); 39 | } 40 | 41 | MainWindow::~MainWindow() 42 | { 43 | delete ui; 44 | delete m_chart; 45 | } 46 | 47 | void MainWindow::onSilderKpChanged(int value) 48 | { 49 | ui->spinBoxKp->setValue(value / 200.0); 50 | } 51 | 52 | void MainWindow::onSilderKiChanged(int value) 53 | { 54 | ui->spinBoxKi->setValue(value / 1000.0); 55 | } 56 | 57 | void MainWindow::onSilderKdChanged(int value) 58 | { 59 | ui->spinBoxKd->setValue(value / 200.0); 60 | } 61 | 62 | void MainWindow::onSpinBoxEdited() 63 | { 64 | ui->silderKp->setValue((int)(ui->spinBoxKp->value() * 200)); 65 | ui->silderKi->setValue((int)(ui->spinBoxKi->value() * 1000)); 66 | ui->silderKd->setValue((int)(ui->spinBoxKd->value() * 200)); 67 | } 68 | 69 | void MainWindow::onMarkerClicked() 70 | { 71 | QLegendMarker* marker = qobject_cast (sender()); 72 | 73 | if (marker->type() == QLegendMarker::LegendMarkerTypeXY) { 74 | marker->series()->setVisible(!marker->series()->isVisible()); 75 | marker->setVisible(true); 76 | 77 | // Dim the marker, if series is not visible 78 | qreal alpha = marker->series()->isVisible() ? 1.0 : 0.5; 79 | 80 | QColor color; 81 | QBrush brush = marker->labelBrush(); 82 | color = brush.color(); 83 | color.setAlphaF(alpha); 84 | brush.setColor(color); 85 | marker->setLabelBrush(brush); 86 | 87 | brush = marker->brush(); 88 | color = brush.color(); 89 | color.setAlphaF(alpha); 90 | brush.setColor(color); 91 | marker->setBrush(brush); 92 | 93 | QPen pen = marker->pen(); 94 | color = pen.color(); 95 | color.setAlphaF(alpha); 96 | pen.setColor(color); 97 | marker->setPen(pen); 98 | } 99 | } 100 | 101 | void MainWindow::addSeries(const QString &name) 102 | { 103 | QLineSeries *series = new QLineSeries(); 104 | m_series.append(series); 105 | 106 | series->setName(name); 107 | m_chart->addSeries(series); 108 | series->setPen(QPen(series->pen().color(), 1.1)); 109 | 110 | m_data.append(QVector()); 111 | } 112 | 113 | void MainWindow::connectMarkers() 114 | { 115 | // Connect all markers to handler 116 | const auto markers = m_chart->legend()->markers(); 117 | for (QLegendMarker *marker : markers) { 118 | QObject::connect(marker, &QLegendMarker::clicked, this, &MainWindow::onMarkerClicked); 119 | } 120 | } 121 | 122 | void MainWindow::plotPoint(int index, double x, double y) 123 | { 124 | m_data[index].append(QPointF(x, y)); 125 | } 126 | 127 | void MainWindow::update() 128 | { 129 | int count = m_series.count(); 130 | for (int i = 0; i < count; ++i) { 131 | m_data[i].clear(); 132 | } 133 | 134 | // run simulater 135 | simulater(); 136 | 137 | for (int i = 0; i < count; ++i) { 138 | m_series[i]->replace(m_data[i]); 139 | } 140 | } 141 | 142 | void MainWindow::simulater() 143 | { 144 | double value = 0.0; 145 | PID pid(ui->spinBoxKp->value(), 146 | ui->spinBoxKi->value(), 147 | ui->spinBoxKd->value()); 148 | 149 | TransFunc tf; // init transfer function 150 | for (double t = 0; t < 1; t += 0.001) { 151 | 152 | double input = tf.drivingFunction(t); 153 | pid.setValue(input); 154 | 155 | double pidOut = pid.iteration(value); 156 | value = tf.iteration(pidOut); 157 | 158 | plotPoint(0, t, input); 159 | plotPoint(1, t, value); 160 | plotPoint(2, t, pidOut); 161 | } 162 | 163 | m_chart->axisX()->setRange(0, 1); 164 | m_chart->axisY()->setRange(-20, 100); 165 | } 166 | -------------------------------------------------------------------------------- /simulator/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | QT_CHARTS_USE_NAMESPACE 10 | 11 | namespace Ui { 12 | class MainWindow; 13 | } 14 | 15 | class MainWindow : public QMainWindow 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit MainWindow(QWidget *parent = 0); 21 | ~MainWindow(); 22 | 23 | private slots: 24 | void onSilderKpChanged(int value); 25 | void onSilderKiChanged(int value); 26 | void onSilderKdChanged(int value); 27 | void onSpinBoxEdited(); 28 | void onMarkerClicked(); 29 | void update(); 30 | 31 | private: 32 | void addSeries(const QString &name); 33 | void connectMarkers(); 34 | void plotPoint(int index, double x, double y); 35 | void simulater(); 36 | 37 | private: 38 | Ui::MainWindow *ui; 39 | QChart *m_chart; 40 | QList m_series; 41 | QList> m_data; 42 | }; 43 | 44 | #endif // MAINWINDOW_H 45 | -------------------------------------------------------------------------------- /simulator/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | PID Simulator 15 | 16 | 17 | 18 | 19 | 4 20 | 21 | 22 | 5 23 | 24 | 25 | 5 26 | 27 | 28 | 5 29 | 30 | 31 | 5 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | KI 49 | 50 | 51 | 52 | 53 | 54 | 55 | KP 56 | 57 | 58 | 59 | 60 | 61 | 62 | KD 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 0 71 | 0 72 | 73 | 74 | 75 | 1000 76 | 77 | 78 | 1 79 | 80 | 81 | Qt::Horizontal 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 0 91 | 92 | 93 | 94 | 1000 95 | 96 | 97 | 1 98 | 99 | 100 | Qt::Horizontal 101 | 102 | 103 | 104 | 105 | 106 | 107 | 3 108 | 109 | 110 | 1.000000000000000 111 | 112 | 113 | 114 | 115 | 116 | 117 | 3 118 | 119 | 120 | 5.000000000000000 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 0 129 | 0 130 | 131 | 132 | 133 | 1000 134 | 135 | 136 | 1 137 | 138 | 139 | Qt::Horizontal 140 | 141 | 142 | 143 | 144 | 145 | 146 | 3 147 | 148 | 149 | 5.000000000000000 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | QtCharts::QChartView 162 | QWidget 163 |
qtcharts/qchartview.h
164 | 1 165 |
166 |
167 | 168 | 169 |
170 | -------------------------------------------------------------------------------- /simulator/pid.cpp: -------------------------------------------------------------------------------- 1 | #include "pid.h" 2 | 3 | PID::PID(double kp, double ki, double kd) 4 | { 5 | setParams(kp, ki, kd); 6 | } 7 | 8 | void PID::setParams(double kp, double ki, double kd) 9 | { 10 | m_kp = kp; 11 | m_ki = ki; 12 | m_kd = kd; 13 | } 14 | 15 | void PID::setValue(double value) 16 | { 17 | m_setValue = value; 18 | } 19 | 20 | double PID::iteration(double measured) 21 | { 22 | double error = m_setValue - measured; 23 | double derror = error - m_error; 24 | 25 | m_sum += error; 26 | m_error = error; 27 | return m_kp * error + m_ki * m_sum + m_kd * derror; 28 | } 29 | -------------------------------------------------------------------------------- /simulator/pid.h: -------------------------------------------------------------------------------- 1 | #ifndef PID_H 2 | #define PID_H 3 | 4 | class PID 5 | { 6 | public: 7 | PID(double kp = 0, double ki = 0, double kd = 0); 8 | void setParams(double kp, double ki, double kd); 9 | void setValue(double value); 10 | double iteration(double measured); 11 | 12 | private: 13 | double m_kp, m_ki, m_kd; 14 | double m_setValue = 0, m_error = 0, m_sum = 0; 15 | }; 16 | 17 | #endif // PID_H 18 | -------------------------------------------------------------------------------- /simulator/transfunc.cpp: -------------------------------------------------------------------------------- 1 | #include "transfunc.h" 2 | #include 3 | 4 | // transfer function init 5 | TransFunc::TransFunc() 6 | { 7 | z[1] = z[0] = 0; 8 | } 9 | 10 | // generate input value 11 | double TransFunc::drivingFunction(double t) 12 | { 13 | if (t < 0.3) { 14 | return 30; 15 | } 16 | if (t < 0.6) { 17 | return t * 100; 18 | } 19 | return sin(t * 10 * M_PI) * 20 + 70; 20 | } 21 | 22 | //********************************************************* 23 | // IIR Filter: 24 | // b0 + b1 z^-1 + b2 z^-2 25 | // ------------------------------------- 26 | // 1 - a1 z^-1 - a2 z^-2 27 | //********************************************************* 28 | 29 | // The IIR Filter cut-off frequency of these parameters 30 | // is 40Hz, and the sampling rate is 1000Hz. 31 | #define a1 -1.64746 32 | #define a2 0.70090 33 | #define b0 0.01336 34 | #define b1 0.02672 35 | #define b2 0.01336 36 | 37 | // transfer function 38 | double TransFunc::iteration(double x) 39 | { 40 | double y; 41 | 42 | // IIR Filter 43 | x = x - a1 * z[0] - a2 * z[1]; 44 | y = b0 * x + b1 * z[0] + b2 * z[1]; 45 | z[1] = z[0]; 46 | z[0] = x; 47 | return y; 48 | } 49 | 50 | #undef a1 51 | #undef a2 52 | #undef b0 53 | #undef b1 54 | #undef b2 55 | -------------------------------------------------------------------------------- /simulator/transfunc.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSFUNC_H 2 | #define TRANSFUNC_H 3 | 4 | 5 | class TransFunc 6 | { 7 | public: 8 | TransFunc(); 9 | double drivingFunction(double t); 10 | double iteration(double x); 11 | 12 | private: 13 | double z[2]; 14 | }; 15 | 16 | #endif // TRANSFUNC_H 17 | --------------------------------------------------------------------------------