Взгляд в мир PID-регулирования математика, расчеты и примеры реализации

Введение
В области автоматического управления, где точность и стабильность имеют решающее значение, PID-регулирование является надежным защитником. Эта технология, основанная на пропорциональном, интегральном и дифференциальном управлении, является ключом к успешному управлению различными системами.
PID не просто сокращение, а мощный инструмент, обеспечивающий автоматическое управление переменными процессами. Пропорциональная, интегральная и дифференциальная составляющие взаимодействуют, формируя умный алгоритм регулирования.
От промышленных систем управления до робототехники, PID является надежным решением для поддержания желаемого состояния системы. Его универсальность и эффективность делают его незаменимым компонентом в разнообразных областях.
Основы PID-регулирования
ПИД-регулятор (Пропорционально-Интегрально-Дифференциального) — это устройство в управляющем контуре с обратной связью, которое используется в системах автоматического управления для формирования управляющего сигнала с целью получения необходимых точности и качества переходного процесса.
ПИД-регулятор состоит из трех компонентов: пропорционального, интегрального и дифференциального, которые в совокупности позволяют поддерживать выходную величину системы на заданном уровне, компенсируя внешние возмущения и внутренние нелинейности.
Пропорциональный компонент (P)
Пропорциональный компонент (P) в PID-регуляторе отвечает за создание управляющего воздействия, пропорционального величине текущей ошибки, то есть разнице между заданным значением (уставкой) и фактическим значением выходного сигнала системы. Этот компонент является основным и самым простым элементом PID-регулятора, определяющим реакцию системы на отклонения от заданного значения.
Формально пропорциональный компонент выражается как произведение коэффициента пропорциональности (Kp и величины ошибки e(t):
U_p(t) = K_p \cdot e(t)
Здесь Up(t) — управляющее воздействие, создаваемое пропорциональным компонентом в момент времени t , e(t) — текущая ошибка, а Kp — коэффициент пропорциональности, который определяет, насколько сильно система реагирует на эту ошибку.
Если коэффициент Kp увеличивается, пропорциональный компонент будет усиливать реакцию системы на отклонение, делая её более чувствительной к изменениям. Это может помочь системе быстрее достигать уставки, но также может привести к увеличению колебаний и потенциальной нестабильности, если Kp слишком велик.
С другой стороны, если Kp слишком мал, реакция системы будет слишком слабой, что приведет к медленному возврату к уставке и возможному сохранению значительной ошибки. В такой ситуации система может стать недостаточно точной, особенно в условиях изменяющихся внешних воздействий.
Интегральный компонент (I)
Интегральный компонент (I) в PID-регуляторе отвечает за учет накопленной ошибки во времени, что позволяет системе компенсировать постоянные отклонения от заданного значения, которые не могут быть устранены только с помощью пропорционального компонента. Этот компонент важен для устранения остаточной ошибки, которая может сохраняться в системе при использовании только пропорционального управления.
Интегральный компонент рассчитывается как интеграл от ошибки во времени:
I_{\text{out}} = K_i \int_{0}^{t} e(\tau) \, d\tau
Где:
- Iout — выходной сигнал интегрального компонента.
- Ki — коэффициент интегрального действия, который определяет скорость, с которой накапливается интегральное воздействие.
- e(τ) — ошибка в момент времени (τ).
- t — текущее время.
Интегральный компонент накапливает ошибку во времени и усиливает управляющее воздействие в зависимости от того, насколько долго ошибка существует. Если ошибка продолжает существовать на протяжении длительного времени, интегральный компонент будет увеличивать свое воздействие, пока ошибка не будет устранена.
Основное преимущество интегрального компонента заключается в его способности устранять статическую ошибку — постоянное отклонение, которое остается после применения только пропорционального управления. Однако, если коэффициент Ki выбран слишком высоким, интегральный компонент может привести к перерегулированию и вызвать колебания системы. Это происходит из-за того, что интегральное воздействие накапливается даже при небольших отклонениях, что может привести к чрезмерной коррекции.
Если коэффициент Ki слишком мал, система может оставаться с незначительной, но постоянной ошибкой, так как интегральное воздействие будет недостаточным для её устранения.
Дифференциальный компонент (D)
Дифференциальный компонент (D) в PID-регуляторе играет важную роль в предсказании будущего поведения ошибки и сглаживании отклика системы. В отличие от пропорционального и интегрального компонентов, которые реагируют на текущее значение ошибки и накопленную ошибку соответственно, дифференциальный компонент учитывает скорость изменения ошибки, то есть, насколько быстро ошибка растет или уменьшается. Это позволяет регулятору заранее корректировать управляющее воздействие, уменьшая вероятность перерегулирования и осцилляций.
Дифференциальный компонент рассчитывается как производная ошибки по времени:
D_{\text{out}} = K_d \cdot \frac{d e(t)}{d t}
Где:
- Dout — выходной сигнал дифференциального компонента.
- Kd — коэффициент дифференциального действия, определяющий вес, с которым учитывается скорость изменения ошибки.
- e(t) — текущая ошибка.
- de(t)/dt — скорость изменения ошибки по времени.
Дифференциальный компонент усиливает управляющее воздействие, когда ошибка начинает резко меняться. Это особенно важно в системах с быстрыми динамическими процессами, где изменение ошибки может произойти очень быстро, и требуется быстрая реакция, чтобы избежать слишком сильного отклонения от заданного значения.
Основная задача дифференциального компонента — уменьшить колебания и сгладить реакцию системы. Например, если ошибка начинает резко увеличиваться, дифференциальный компонент может предсказать, что система движется в неправильном направлении, и сразу же применить противоположное управляющее воздействие, чтобы замедлить или предотвратить это изменение.
Однако дифференциальный компонент также может увеличить чувствительность системы к шуму, особенно если коэффициент Kd слишком велик. Это связано с тем, что дифференциальное действие реагирует на любые изменения ошибки, включая небольшие флуктуации, вызванные шумом. Поэтому важно выбрать значение Kd таким образом, чтобы улучшить устойчивость и качество отклика системы, но не усилить влияние шума.
Применение PID в практике
Применение PID (Пропорционально-Интегрально-Дифференциального) регулятора в практике включает в себя настройку коэффициентов Kp, Ki и Kd.
Настройка коэффициентов PID
Методика выбора оптимальных значений Kp,Ki,Kd:
Метод Зиглера-Никольса:
Метод Зиглера-Никольса — один из самых известных и широко используемых эмпирических методов настройки PID-регуляторов, разработанный Джоном Зиглером и Натаном Николсом в 1940-х годах. Этот метод стал популярен благодаря своей простоте и эффективности, позволяя быстро и относительно легко настроить PID-регулятор для достижения устойчивого и достаточно быстрого отклика системы.
В основе метода Зиглера-Никольса лежат два подхода. Первый из них — это настройка PID-регулятора по отклику системы на ступенчатое воздействие. В этом подходе на вход системы подается ступенчатый сигнал (например, скачкообразное изменение входного сигнала), и затем записывается временная характеристика отклика системы. На основе полученной временной характеристики определяются ключевые параметры, такие как время запаздывания L и постоянная времени T . Эти параметры используются для расчета коэффициентов PID-регулятора по следующим эмпирическим формулам: пропорциональный коэффициент
K_p = \frac{1.2 \cdot T}{L}
интегральное время
T_i = 2L
и дифференциальное время
T_d = 0.5L
Например, если время запаздывания L = 2 секунды, а постоянная времени T = 10 секунд, коэффициенты PID-регулятора будут: Kp = 6 , Ti = 4 секунды, Td = 1 секунда.
Второй подход, известный как метод предельного цикла, применяется в тех случаях, когда провести эксперимент с откликом на ступенчатое воздействие затруднительно. В этом методе используется только пропорциональное управление, а коэффициенты интегрального и дифференциального звеньев устанавливаются равными нулю. Пропорциональный коэффициент Kp постепенно увеличивается до тех пор, пока система не начнет показывать устойчивые колебания. В этот момент фиксируются критический коэффициент усиления Ku и период колебаний Tu. Эти значения используются для расчета коэффициентов PID-регулятора. Например, для P-регулятора:
K_p = 0.5 \cdot K_u
для PI-регулятора
K_p = 0.45 \cdot K_u
и
T_i = 0.83 \cdot T_u
для PID-регулятора:
K_p = 0.6 \cdot K_u , \\ T_i = 0.5 \cdot T_u , \\ T_d = 0.125 \cdot T_u
Если критический коэффициент усиления Ku = 8 , а период колебаний Tu = 6 секунд, то для PID-регулятора получаем: Kp = 4.8 , Ti = 3 секунды, Td = 0.75 секунды.
Метод Зиглера-Никольса привлекает своей простотой и доступностью, поскольку не требует сложных вычислений и позволяет быстро провести настройку непосредственно на объекте. Однако, поскольку метод основывается на эмпирических данных, он не всегда дает оптимальные результаты, особенно для сложных или сильно нелинейных систем. Кроме того, после начальной настройки могут возникать колебания в установившемся режиме, особенно если система склонна к нестабильности, что требует дополнительных корректировок и уточнений.
Метод частотной реакции:
Метод частотной реакции — это один из подходов к настройке коэффициентов PID-регулятора, основанный на анализе отклика системы на синусоидальный входной сигнал. Этот метод позволяет определить оптимальные значения пропорционального (P), интегрального (I) и дифференциального (D) коэффициентов PID-регулятора, чтобы добиться желаемой устойчивости и динамики системы.
Основные шаги настройки PID-регулятора методом частотной реакции заключаются в следующем: сначала необходимо получить частотную характеристику системы, проведя экспериментальный или аналитический анализ. Затем определяются критические частота и усиление, при которых система находится на границе устойчивости. После этого выбираются параметры регулятора (P, I, D) на основе частотных характеристик, чтобы обеспечить нужные запасы устойчивости и заданные характеристики переходного процесса. В завершение проводится проверка настроек на объекте и, при необходимости, корректировка параметров для достижения оптимального результата.
Значение коэффициентов в зависимости от характеристик системы
Пропорциональный коэффициент (Kp):
Пропорциональный коэффициент Kp является одним из ключевых параметров в PID-регуляторе, определяющим, насколько сильно система реагирует на ошибку между заданным значением и текущим выходом. Увеличение значения Kp приводит к более агрессивному отклику системы на отклонения, что может существенно сократить время переходного процесса и уменьшить статическую ошибку. Однако, если значение Kp становится слишком высоким, это может вызвать нестабильность системы, проявляющуюся в виде колебаний или даже полной неустойчивости, когда система не достигает установившегося режима.
Наоборот, слишком низкое значение Kp приводит к тому, что система реагирует на отклонения слабо, что замедляет переходный процесс. В результате система может оставаться стабильной, но при этом возникает более длительное время отклика и сохраняется статическая ошибка, которая может быть значительной.
Оптимальное значение Kp должно быть выбрано таким образом, чтобы обеспечить быстрый и эффективный отклик на ошибки, но при этом не вызывать чрезмерные колебания или нестабильность. Это требует тщательной балансировки, поскольку каждое увеличение Kp может улучшить один аспект работы системы (например, сократить время отклика), но ухудшить другой (например, увеличить вероятность колебаний). Таким образом, настройка Kp осуществляется с учетом динамических характеристик конкретной системы, а также требований к ее устойчивости и точности управления. Обычно это делается в процессе пошагового увеличения значения Kp, с одновременным наблюдением за динамикой системы, чтобы найти оптимальное значение.
Интегральный коэффициент (Ki):
Интегральный коэффициент (Ki) в PID-регуляторе отвечает за устранение постоянной ошибки, которая может возникнуть в системе при использовании только пропорционального управления. Этот коэффициент определяет, насколько сильно будет учитываться накопленная ошибка во времени, что позволяет системе корректировать отклонения от заданного значения, даже если они малы и не устраняются пропорциональным звеном.
Если значение Ki увеличивается, регулятор будет быстрее реагировать на накопленную ошибку, что помогает устранить устойчивую ошибку быстрее. Это особенно полезно в ситуациях, где важно точное соответствие заданному значению, например, в системах, требующих высокой точности поддержания параметров (температура, давление и т.д.). Однако чрезмерно высокое значение Ki может привести к перерегулированию и вызвать колебания, поскольку система может начать реагировать слишком активно на накопленную ошибку, даже когда она незначительна.
С другой стороны, если значение Ki слишком мало, регулятор может не успевать корректировать ошибку, что приведет к медленному снижению постоянной ошибки или даже к её сохранению. Это сделает систему менее точной, особенно в установившемся режиме, где требуется, чтобы отклонения от уставки были минимальными.
Оптимальный выбор значения Ki зависит от конкретных характеристик системы и требований к её работе. Оно должно быть достаточно высоким, чтобы эффективно устранять постоянную ошибку, но не настолько высоким, чтобы вызвать излишнюю чувствительность и колебания. Обычно настройка этого коэффициента проводится в комбинации с другими коэффициентами (пропорциональным и дифференциальным) для достижения наилучшего баланса между скоростью реакции, точностью и устойчивостью системы.
Расчет времени дискретизации
Время дискретизации (или шаг дискретизации) определяет, с какой частотой система будет считывать входные данные и обновлять свои выходные значения. Правильный выбор времени дискретизации влияет на точность и устойчивость системы.
Частота обновления (fs):
Частота дискретизации обычно выражается в герцах (Hz) и определяется как обратное значение времени дискретизации (T). Формула для расчета частоты дискретизации следующая:
f = \frac{1}{T}
Время дискретизации (Td):
Время дискретизации — это интервал времени между двумя последовательными образцами в дискретном сигнале. Оно может быть рассчитано как обратное значение частоты дискретизации:
T = \frac{1}{f}
Это интервал времени между последовательными измерениями и выдачей управляющего сигнала.
Теорема Котельникова:
Согласно теореме Котельникова, частота дискретизации должна быть как минимум в два раза выше максимальной частоты сигнала. Если максимальная частота сигнала равна f_max, то частота дискретизации должна быть:
f \geq 2 \cdot f_{max}
Кратность времени дискретизации:
Этот параметр выражается соотношением между временной константой системы и временем дискретизации. Оптимальная кратность позволяет системе точно отслеживать изменения в управляемом объекте, обеспечивая быстрое и точное реагирование на любые отклонения.
Если кратность времени дискретизации выбирается слишком маленькой, то система может не успевать адекватно реагировать на изменения, что приведет к задержкам, неточностям в отслеживании сигнала и повышенному риску нестабильности. В таком случае система может работать с задержкой, теряя важные изменения в объекте управления, что негативно скажется на общем качестве управления.
С другой стороны, слишком большая кратность дискретизации, при которой время дискретизации намного меньше временной константы системы, может привести к избыточной вычислительной нагрузке и повышению уровня шума в системе. Несмотря на это, более высокая кратность обычно приводит к более точному управлению, но её чрезмерное увеличение может быть нецелесообразным с точки зрения затрат ресурсов и сложности реализации.
Кратность времени дискретизации должна быть выбрана таким образом, чтобы обеспечить баланс между точностью управления и доступными вычислительными ресурсами. Обычно кратность в пределах от 10 до 20 раз считается оптимальной для большинства практических приложений, так как она позволяет достичь необходимой точности и устойчивости системы управления без чрезмерной нагрузки на процессор.
Учет динамики системы:
Динамика системы описывает, как объект управления реагирует на изменения входных сигналов, и включает в себя такие характеристики, как инерционность, задержки, резонансные эффекты и нелинейности.
Понимание и учет динамики системы позволяют проектировщику выбрать правильные параметры управления и обеспечить адекватный отклик системы на внешние воздействия. Если динамика системы учтена неправильно, это может привести к нестабильности, колебаниям или медленному отклику, что негативно скажется на качестве управления.
Например, системы с высокой инерционностью, такие как крупные промышленные механизмы или термодинамические процессы, требуют более осторожного выбора времени дискретизации и параметров PID-регулятора. В таких системах необходимо учитывать длительные задержки и плавные изменения, что диктует использование меньших значений коэффициентов пропорционального и интегрального управления, чтобы избежать перерегулирования и колебаний.
В системах с быстрой динамикой, таких как электронные схемы или системы управления полетом, важна высокая частота дискретизации и более агрессивные настройки регулятора, поскольку такие системы требуют быстрого и точного отклика на изменения входных сигналов. Здесь даже небольшие ошибки в учете динамики могут привести к значительным отклонениям от желаемого поведения системы.
Экспериментальный подход:
Проведите эксперименты с различными значениями времени дискретизации и наблюдайте за реакцией системы. Это может включать в себя анализ стабильности и производительности системы при различных уровнях дискретизации.
Реальные примеры и расчеты
Пример 1: Температурный контроль
ПИД-регуляторы часто используются для регулировки температуры. В качестве примера можно рассмотреть ПИД-регулятор температуры на микроконтроллерах STM32 или Avr.
double Kp = 2.0; // Пропорциональный коэффициент
double Ki = 0.1; // Интегральный коэффициент
double Kd = 1.0; // Дифференциальный коэффициент
double error = 0.0; // Текущая ошибка
double integral = 0.0; // Интегральная сумма ошибок
double derivative = 0.0; // Производная ошибки
double previous_error = 0.0; // Предыдущая ошибка
double target_temperature = 25.0; // Целевая температура
while (true) {
double current_temperature = readTemperature(); // Чтение температуры
error = target_temperature - current_temperature; // Расчет ошибки
// Расчет интегральной суммы
integral += error * delta_time;
// Расчет производной
derivative = (error - previous_error) / delta_time;
// Расчет управляющего сигнала
double output = Kp * error + Ki * integral + Kd * derivative;
// Ограничение выхода (если требуется)
output = clamp(output, min_output, max_output);
// Управление нагревателем
controlHeater(output);
// Обновление предыдущей ошибки
previous_error = error;
// Задержка для обеспечения времени цикла (delta_time)
delay(delta_time);
}
Пример 2: Позиционирование двигателя и робототехника
ПИД-регуляторы широко применяются в робототехнике для точного позиционирования робота. Вот пример кода для регулирования скорости двигателя:
int avgSpeed = 150; // Средняя скорость моторов
int kP = 10; // Коэффициент пропорциональной обратной связи
int maxCorrection = 100; // Максимальная коррекция скорости
int minSpeed = 0; // Минимальная скорость моторов
int maxSpeed = 255; // Максимальная скорость моторов
while (true) {
int error = bot_position(); // Получение текущего положения робота
// Расчет коррекции
int correction = kP * error;
// Ограничение коррекции
correction = constrain(correction, -maxCorrection, maxCorrection);
// Расчет скорости моторов
int motor1Speed = constrain(avgSpeed + correction, minSpeed, maxSpeed);
int motor2Speed = constrain(avgSpeed - correction, minSpeed, maxSpeed);
// Управление моторами
motor1.move(motor1Speed);
motor2.move(motor2Speed);
// Задержка (если требуется для цикла)
delay(10);
}
Пример 3: ПИД-регулятор на Arduino
В этом примере используется Arduino для реализации ПИД-регулятора. В коде используются три составляющие ПИД-регулятора: пропорциональная, интегральная и дифференциальная.
double Kp = 2.0, Ki = 0.5, Kd = 1.0; // Коэффициенты PID-регулятора
double error = 0, previous_error = 0, integral = 0, derivative = 0;
double target = 512; // Целевое значение (например, середина диапазона 0–1023)
double actual = 0;
double output = 0;
unsigned long previous_time = 0; // Для учета времени
unsigned long current_time = 0;
double delta_time = 0;
void loop() {
// Чтение текущего значения
actual = analogRead(A0);
// Расчет времени между циклами
current_time = millis();
delta_time = (current_time - previous_time) / 1000.0; // В секундах
previous_time = current_time;
// Расчет ошибки
error = target - actual;
// Расчет интегральной составляющей с ограничением
integral += error * delta_time;
integral = constrain(integral, -1000, 1000); // Ограничение интегральной составляющей
// Расчет дифференциальной составляющей
derivative = (error - previous_error) / delta_time;
// Расчет выходного значения
output = Kp * error + Ki * integral + Kd * derivative;
// Ограничение выходного значения
output = constrain(output, 0, 255);
// Сохранение текущей ошибки
previous_error = error;
// Управление устройством
analogWrite(9, output);
// Задержка для стабилизации системы
delay(100);
}
Пример 4: ПИД-регулятор на Python
В этом примере используется Python для реализации ПИД-регулятора. В коде используются три составляющие ПИД-регулятора: пропорциональная, интегральная и дифференциальная.
class PID:
def __init__(self, Kp, Ki, Kd, integral_limit=None):
"""
Инициализация PID-регулятора.
:param Kp: Пропорциональный коэффициент
:param Ki: Интегральный коэффициент
:param Kd: Дифференциальный коэффициент
:param integral_limit: Максимальное значение интегральной составляющей (для предотвращения насыщения)
"""
self.Kp = Kp # Пропорциональный коэффициент
self.Ki = Ki # Интегральный коэффициент
self.Kd = Kd # Дифференциальный коэффициент
self.error = 0.0 # Текущая ошибка
self.integral = 0.0 # Интегральная сумма ошибок
self.integral_limit = integral_limit # Ограничение интегральной составляющей (если задано)
def update(self, error, delta_time):
"""
Обновление PID-регулятора для нового значения ошибки и интервала времени.
:param error: Текущая ошибка (разница между целевым и текущим значением)
:param delta_time: Интервал времени с момента последнего вызова (в секундах)
:return: Выходное значение PID-регулятора
"""
if delta_time <= 0: # Проверка на корректность времени
raise ValueError("delta_time must be greater than 0")
# Вычисление производной ошибки
derivative = (error - self.error) / delta_time
# Обновление интегральной составляющей
self.integral += error * delta_time
# Ограничение интегральной составляющей, если задано
if self.integral_limit is not None:
self.integral = max(min(self.integral, self.integral_limit), -self.integral_limit)
# Сохранение текущей ошибки для использования в следующем цикле
self.error = error
# Вычисление выходного значения PID-регулятора
output = self.Kp * self.error + self.Ki * self.integral + self.Kd * derivative
return output
def reset(self):
"""
Сброс состояния PID-регулятора.
Устанавливает ошибки и интеграл в нулевые значения.
"""
self.error = 0.0
self.integral = 0.0
Пример использования класса.
pid = PID(Kp=2.0, Ki=0.5, Kd=1.0, integral_limit=100)
# Пример обновления PID-регулятора
error = 10
delta_time = 0.1 # Время между циклами
output = pid.update(error, delta_time)
print("Output:", output)
# Сброс регулятора
pid.reset()
Пример 5: ПИД-регулятор на Java
В этом примере ПИД-регулятор реализован на языке Java. Код представляет собой простую реализацию ПИД-регулятора, который выполняет каждые 20 мс.
public class PIDController {
private double Kp, Ki, Kd; // Коэффициенты PID-регулятора
private double errorSum, lastError; // Интегральная сумма ошибок и последняя ошибка
private double integralLimit = Double.MAX_VALUE; // Лимит интегральной составляющей
public PIDController(double Kp, double Ki, double Kd) {
this.Kp = Kp;
this.Ki = Ki;
this.Kd = Kd;
}
public double calculate(double setpoint, double actual, double deltaTime) {
if (deltaTime <= 0) {
throw new IllegalArgumentException("deltaTime must be greater than 0");
}
// Расчет ошибки
double error = setpoint - actual;
// Обновление интегральной составляющей с учетом лимита
errorSum += error * deltaTime;
errorSum = Math.max(Math.min(errorSum, integralLimit), -integralLimit);
// Расчет дифференциальной составляющей
double dError = (error - lastError) / deltaTime;
// Обновление последней ошибки
lastError = error;
// Вычисление выходного значения PID-регулятора
return Kp * error + Ki * errorSum + Kd * dError;
}
public void setIntegralLimit(double limit) {
this.integralLimit = limit;
}
public void setKp(double Kp) {
this.Kp = Kp;
}
public void setKi(double Ki) {
this.Ki = Ki;
}
public void setKd(double Kd) {
this.Kd = Kd;
}
public void reset() {
errorSum = 0;
lastError = 0;
}
}
Пример 6: ПИД-регулятор на языке C для микроконтроллеров avr
Пример кода PID-регулятора для микроконтроллеров AVR на языке C. Этот код написан для использования в среде разработки AVR (например, с использованием Atmel Studio).
#include
#include
// Параметры PID-регулятора
float kp = 2.0;
float ki = 0.5;
float kd = 1.0;
volatile int16_t setpoint = 512; // Целевое значение (например, середина диапазона АЦП 0-1023)
volatile int16_t current_value = 0; // Текущее значение (чтение с АЦП)
volatile int16_t previous_error = 0; // Предыдущая ошибка
volatile int32_t integral = 0; // Интегральная составляющая
// Ограничения для PID
#define MAX_INTEGRAL 1000
#define MIN_INTEGRAL -1000
#define MAX_OUTPUT 255
#define MIN_OUTPUT 0
void adc_init() {
// Инициализация АЦП
ADMUX = (1 << REFS0); // Опорное напряжение AVCC
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Предделитель 128
}
uint16_t adc_read(uint8_t channel) {
// Чтение значения с АЦП
ADMUX = (ADMUX & 0xF0) | (channel & 0x0F); // Выбор канала АЦП
ADCSRA |= (1 << ADSC); // Запуск преобразования
while (ADCSRA & (1 << ADSC)); // Ожидание завершения
return ADC; // Возвращаем результат
}
void pwm_init() {
// Инициализация PWM на таймере 0 (OCR0A)
TCCR0A |= (1 << WGM00) | (1 << WGM01) | (1 << COM0A1); // Fast PWM, Non-Inverting
TCCR0B |= (1 << CS01); // Предделитель 8
DDRD |= (1 << PD6); // Устанавливаем PD6 (OCR0A) как выход
}
void timer1_init() {
// Настройка таймера 1 для прерывания каждые 10 мс (100 Гц)
TCCR1B |= (1 << WGM12); // CTC режим
TIMSK1 |= (1 << OCIE1A); // Разрешить прерывание по совпадению
OCR1A = 2499; // Для 10 мс при F_CPU = 16 МГц и предделителе 64
TCCR1B |= (1 << CS11) | (1 << CS10); // Предделитель 64
}
ISR(TIMER1_COMPA_vect) {
// Прерывание таймера 1 для выполнения PID
current_value = adc_read(0); // Чтение значения с АЦП (канал 0)
int16_t error = setpoint - current_value; // Расчёт ошибки
// Интегральная составляющая с ограничением
integral += error;
if (integral > MAX_INTEGRAL) integral = MAX_INTEGRAL;
if (integral < MIN_INTEGRAL) integral = MIN_INTEGRAL;
// Дифференциальная составляющая
int16_t derivative = error - previous_error;
// Расчёт PID
int16_t output = (int16_t)(kp * error + ki * integral + kd * derivative);
// Ограничение выходного значения
if (output > MAX_OUTPUT) output = MAX_OUTPUT;
if (output < MIN_OUTPUT) output = MIN_OUTPUT;
// Управление ШИМ
OCR0A = (uint8_t)output;
// Обновление предыдущей ошибки
previous_error = error;
}
int main(void) {
adc_init(); // Инициализация АЦП
pwm_init(); // Инициализация PWM
timer1_init(); // Инициализация таймера 1
sei(); // Включение глобальных прерываний
while (1) {
// Основной цикл пуст, так как управление выполняется в прерывании
}
}
Пример 7: ПИД-регулятор на языке C для микроконтроллеров STM
Пример кода PID-регулятора для микроконтроллеров STM32 на языке C. Этот пример написан с использованием HAL-библиотеки, которая часто используется в среде разработки STM32CubeIDE. В данном примере используется таймер для вызова функции PID-контроля с определенной частотой.
#include "main.h"
// PID коэффициенты
float kp = 1.0f, ki = 0.1f, kd = 0.05f; // Пропорциональный, интегральный, дифференциальный коэффициенты
// Переменные PID
float setpoint = 100.0f, current_value = 0.0f, previous_error = 0.0f, integral = 0.0f;
// Таймеры и ADC
TIM_HandleTypeDef htim2; // Управление PWM
ADC_HandleTypeDef hadc1; // АЦП для чтения данных сенсора
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_ADC1_Init(void);
// Функция PID-регулятора
void pid_control() {
float error = setpoint - current_value; // Вычисление ошибки
integral += error; // Интегральная составляющая
if (integral > 100.0f) integral = 100.0f; // Ограничение интеграла
if (integral < 0.0f) integral = 0.0f;
float derivative = error - previous_error; // Дифференциальная составляющая
float output = (kp * error) + (ki * integral) + (kd * derivative); // Расчет PID
if (output > 100.0f) output = 100.0f; // Ограничение выхода
if (output < 0.0f) output = 0.0f;
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (uint32_t)output); // Управление PWM
previous_error = error; // Сохранение ошибки
}
// Функция чтения значения с АЦП
float read_sensor() {
HAL_ADC_Start(&hadc1); // Запуск АЦП
if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK) {
uint32_t adc_value = HAL_ADC_GetValue(&hadc1); // Получение значения
return (float)adc_value * 100.0f / 4095.0f; // Преобразование в диапазон 0-100
}
return 0.0f;
}
int main(void) {
HAL_Init(); // Инициализация HAL
SystemClock_Config(); // Настройка тактирования
MX_GPIO_Init(); // Инициализация GPIO
MX_TIM2_Init(); // Настройка таймера для PWM
MX_ADC1_Init(); // Инициализация АЦП
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Запуск PWM
while (1) {
current_value = read_sensor(); // Считывание текущего значения
pid_control(); // Вычисление и применение PID
HAL_Delay(100); // Задержка 100 мс
}
}
// Настройка таймера 2 для PWM
void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 79; // Предделитель для 1 МГц
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 100; // Период для PWM (0-100%)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // Начальное значение PWM
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
HAL_TIM_MspPostInit(&htim2);
}
// Настройка АЦП для чтения данных с сенсора
void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
// Настройка GPIO для вывода PWM
void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // Включение тактирования GPIOA
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5; // Настройка пина PA5 (PWM-вывод)
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // Альтернативная функция
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// Настройка системного тактирования
void SystemClock_Config(void) {
// Настройка тактирования (например, 80 МГц)
}
// Обработка ошибок
void Error_Handler(void) {
while (1) {
// Бесконечный цикл при ошибке
}
}
Сфера применения PID
PID-регулирование находит применение в самых разнообразных областях благодаря своей способности точно управлять динамическими системами, минимизируя отклонения от заданных параметров. Этот метод широко используется в промышленности, где требуется поддержание стабильности процессов: например, в химических реакторах для контроля температуры и давления, на производственных линиях для регулирования скорости конвейеров или в энергетике для управления генераторами и стабилизации параметров электросетей. В сфере автоматизации зданий PID-алгоритмы обеспечивают комфортный микроклимат, управляя системами отопления, вентиляции и кондиционирования, а в транспортной отрасли они лежат в основе работы автопилотов, круиз-контроля и систем стабилизации дронов. Робототехника активно задействует PID для точного позиционирования манипуляторов и движения автономных устройств, а в бытовой технике — от мультиварок до стиральных машин — алгоритмы помогают поддерживать заданные режимы работы. Даже в медицине, например в инфузионных насосах, PID-регулирование обеспечивает дозированную подачу препаратов.