LINUX.ORG.RU

PID регулятор

 ,


1

2

Накидал простенький ПИД-регулятор для того чтобы поддерживать заданную скорость робота с двумя ведущими колесами. Формулы брал тут. Он вроде бы и работает, но я не могу его никак подобрать ему коэффициенты, постоянно то с места сдвинуться не может, то едет рывками, то разгоняется за несколько секунд до максимальных оборотов. Гляньте пожалуйста, мб я где нибудь глупую ошибку допустил в коде.

// main.c
#include "asuro.h"
#include "def.h"
#include "pid.h"

extern uint16_t left_speed = 16;
extern uint16_t right_speed = 16;
extern uint8_t pid_enable = 1;

int main(void)
{
    Init();
    
    EncoderInit();
    EncoderStart();
    
    // main loop
	while(1) {
		process_pid();
	}
    
    return 0;
}
// def.h
#ifndef DEF_H
#define DEF_H

// PID regulator coefficients
#define kP 		5
#define kI		0
#define kD		0

// PID regulator settings
#define PID_INTERVAL 		100
#define MOTOR_MAX_SPEED 	255
#define MOTOR_MIN_SPEED 	0

#endif // DEF_H
// pid.h
#ifndef PID_H
#define PID_H

#include "asuro.h"
#include "def.h"

uint16_t left_speed = 0;
uint16_t right_speed = 0;

uint8_t pid_enable = 0;
static uint32_t prev_pid_time = 0;

static uint16_t left_prev_error = 1;
static uint16_t right_prev_error = 1;
static float left_prev_i = 1;
static float right_prev_i = 1;

void process_pid(void);
static uint8_t pid_compute(int current, int must_be, uint16_t *prev_error, float *prev_i);

#endif	// PID_H
// pid.c
#include "pid.h"

void process_pid(void)
{
	if(!pid_enable) return;
	
	if(Gettime() > prev_pid_time){
		if(Gettime() - prev_pid_time >= PID_INTERVAL){
			uint8_t l_pwm = pid_compute(encoder[LEFT], left_speed, &left_prev_error, &left_prev_i);
			uint8_t r_pwm = pid_compute(encoder[RIGHT], right_speed, &right_prev_error, &right_prev_i);
			MotorSpeed(l_pwm, r_pwm);
			encoder[LEFT] = 0;
			encoder[RIGHT] = 0;
			prev_pid_time = Gettime();
		}
	} else {
		if(0xFF - prev_pid_time + Gettime() + 1 >= PID_INTERVAL){
			uint8_t l_pwm = pid_compute(encoder[LEFT], left_speed, &left_prev_error, &left_prev_i);
			uint8_t r_pwm = pid_compute(encoder[RIGHT], right_speed, &right_prev_error, &right_prev_i);
			MotorSpeed(l_pwm, r_pwm);
			encoder[LEFT] = 0;
			encoder[RIGHT] = 0;
			prev_pid_time = Gettime();
		}
	}
}

static uint8_t pid_compute(int current, int must_be, uint16_t *prev_error, float *prev_i)
{
	float p, i, d;
	uint8_t output;
	int16_t error = must_be - current;
	
	p = kP * error;
	
	i = *prev_i + (kI * error);
	
	d = kD * (current - *prev_error);
	
	output = p + i + d;
	
	if(output > MOTOR_MAX_SPEED) output = MOTOR_MAX_SPEED;
	if(output < MOTOR_MIN_SPEED) output = MOTOR_MIN_SPEED;
	
	*prev_i = i;
	*prev_error = error;
	
	return output;
}
★★★★
Ответ на: комментарий от anonymous

вижу, проблема не в этом, я скопипастил не ту версию файла. на самом деле я пробую так

d = kD * (error - *prev_error);

а может быть правильно так?

d = kD * error - *prev_error;

WRG ★★★★
() автор топика
Последнее исправление: WRG (всего исправлений: 1)

См. http://en.wikipedia.org/wiki/PID_controller#Pseudocode

У тебя неправильно численное дифференцирование. Коэффициенты можно подобрать по методу Николса. Надо понимать, что пид-контроллер - линейная система и не может эффективно использоваться для систем с динамикой выше второго порядка, как например, в данном случае.

Deleted
()
Ответ на: комментарий от Deleted

систем с динамикой выше второго порядка

Кинь пожалуйста ссылку на учебник с формальным определением что такое «порядок динамики».

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 2)

сначала ты должен прочитать кучу толстых книжек по теории автоматического управления, нефиг читерить тут, формулы он взял :)

Harald ★★★★★
()
Ответ на: комментарий от Deleted

спасибо, перепилил по образцу псевдокода. щас попробую снова настроить. уже который раз убеждаюсь в том, что читать английскую википедию бывает полезнее.

WRG ★★★★
() автор топика
Ответ на: комментарий от Manhunt

Порядок линейной динамической системы - это порядок дифф уравнения, описывающего эту систему или что то же самое, размерность ее пространства состояний.

см. Андриевский - Фрадков. Избранные главы теории автоматического управления.

ПИД контроллер сам имеет второй порядок. А более сложная система, например 3-го порядка, может к примеру включать в себя ситему, обратную пид контроллеру, то есть, полностью нейтрализующую его действие. Тогда управление будет невозможно.

Deleted
()

кстати, я вызываю функцию process_pid() раз в 100 миллисекунд, т.е. 10 раз в секунду. может стоит вызывать ее чаще?

WRG ★★★★
() автор топика
Ответ на: комментарий от Deleted

а можно пример из жизни? а то слабо представляю, где это может быть такая система, обратная пид-регулятору.

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

прослушал

Кхм. Это почти как «проспал»? Очень ёмкая формулировка :D

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от WRG

Такую систему легко построить. Например, ПФ ПИД = W(s). Объекта управления - H(s), петли обратной связи: -1. Значит общая ПФ W(s)*H(s) / (1 + W(s)*H(s)); (если не вру). Приравняем последнее выражение к единице, и решим уравнение относительно H(s), вместо W подставив ПФ отсюда: http://en.wikipedia.org/wiki/PID_controller#Laplace_form_of_the_PID_controller

Получится вероятно неустойчивая система, с физической реализацией могут быть проблемы. Но задачу нейтрализации пид-контроллера такая система будет решать успешно.

Deleted
()

а движки-то какие у колёс? если у них не хватает момента при старте, то никакой ПИД не поможет. например, если это шаговики с делением шага и малым напряжением, то как раз будет похоже на то, что ты описываешь: то вообще ничего, то резкие скачки. для таких случаев либо напряжение повышают, если можно, либо уменьшают значение делителя шага, либо строят некий профиль для разгона, исходя из момента и мощности движка.

Iron_Bug ★★★★★
()
Ответ на: комментарий от WRG

нет, там я не спал, предмет был интересный, да и препод тоже. скучать не приходилось)

WRG ★★★★
() автор топика

можно еще попробовать инкрементальную форму pid. Числа с плавающей точкой плохо подходят для pid, потому что они не гаратнируют увеличение накопленного значения (целые числа - гарантируют).

ihanick
()
Ответ на: комментарий от Iron_Bug

моторчики такие вот, как в обычных китайских игрушках. если просто врубить их на максимум, то робот очень неплохо рвет с места, т.е. момента через такой редуктор должно хватать. у меня есть еще один подобной конструкции, правда весом он где то 3кг, моторчики такие же, но редуктора стоят 1:120, он с ПИД регулятором нормально трогается с места, даже на ковре с густым ворсом. а вот тут настроить что-то не получается.

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

еще столкнулся с таким явлением. когда движки управляются ПИД регулятором, если пытаться их остановить, регулятор увеличивает свое выходное значение почти до максимума (у меня до 255), а вот если застопорить совсем, выход тут же падает примерно до 150, хотя теоретически должен быть максимальным, т.к. моторчик не вращается, а должен бы. причем это значение 150 зависит от пропорционального коэффициента, если настроить его так, чтобы при блокировке на выходе был максимум (т.е. 255) весь регулятор работает рывками почему то.

WRG ★★★★
() автор топика

Не стоит забывать про время в PID. Реальный период далеко не всегда будет 100 мс Период интегрирования нужно подобрать так чтобы в него попадало достаточное количество энкодерных импульсов.

mike666
()
Ответ на: комментарий от WRG

да вроде должно хватать. а ты вообще укладываешься в этот PID_INTERVAL? поставь его побольше, посмотри, что будет. может, он у тебя мал, и значение энкодера тупо болтается около нуля и поэтому разброс велик и постоянные колебания то в плюс, то в минус?

Iron_Bug ★★★★★
()
Ответ на: комментарий от mike666

ну в моем случае за 100мс у меня с энкодеров от 5 до 25 импульсов в зависимости от скорости. этого достаточно, или лучше больше?

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

что то я окончательно запутался. у меня почему то при одном значении ШИМ, (т.е. выходном значении пид регулятора), которое я мониторю через serial, движки постепенно все разгоняются все больше и больше.

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

Более-менее. Больше - конечно лучшее, но тогда будет большое запаздывание и как следствие большая колебательность. Можно попробовать такую штуку: За каждый такт обратной связи разрешить менять шим не более чем на N единиц.

mike666
()
Ответ на: комментарий от anonymous
uint8_t pid_compute(int measured, int setpoint, uint16_t *prev_error, float *integral)
{
	int16_t error = setpoint - measured;
	uint8_t output;
	float derivate;
	
	*integral = *integral + (error * kI);
	derivate = (error - *prev_error) / (float)PID_INTERVAL;
	output = (kP * error) + (kI * (*integral)) + (kD * derivate);
	*prev_error = error;
	
	if(output > MOTOR_MAX_SPEED) output = MOTOR_MAX_SPEED;
	if(output < MOTOR_MIN_SPEED) output = MOTOR_MIN_SPEED;
	
	return output;
}
WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

может такое быть, что настроить требуется только пропорциональный коэффициент, а остальные оставить равными нулю? или требуются все коэффициенты?

WRG ★★★★
() автор топика
Ответ на: комментарий от anonymous

попробовал. только пока не распарсил, как тут все работает. c uint_

x=0, p=10, i=1.000000, d=10.000000
x=2, p=8, i=1.800000, d=-2.000000
x=2, p=8, i=2.600000, d=0.000000
x=3, p=7, i=3.300000, d=-1.000000
x=3, p=7, i=4.000000, d=0.000000
x=4, p=6, i=4.600000, d=-1.000000
x=4, p=6, i=5.200000, d=0.000000
x=5, p=5, i=5.700000, d=-1.000000
x=5, p=5, i=6.200000, d=0.000000
x=6, p=4, i=6.600000, d=-1.000000
x=6, p=4, i=7.000000, d=0.000000
x=7, p=3, i=7.300000, d=-1.000000
x=7, p=3, i=7.600000, d=0.000000
x=8, p=2, i=7.800000, d=-1.000000
x=8, p=2, i=8.000000, d=0.000000
x=9, p=1, i=8.100000, d=-1.000000
x=9, p=1, i=8.200001, d=0.000000
x=9, p=1, i=8.300001, d=0.000000
x=9, p=1, i=8.400002, d=0.000000
x=9, p=1, i=8.500002, d=0.000000
x=9, p=1, i=8.600002, d=0.000000
x=9, p=1, i=8.700003, d=0.000000
x=9, p=1, i=8.800003, d=0.000000
x=9, p=1, i=8.900003, d=0.000000
x=9, p=1, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=-1.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=-10, i=8.000004, d=-10.000000
x=110, p=-110, i=-2.999996, d=-65636.000000
x=192, p=-192, i=-22.199997, d=-65618.000000
x=265, p=-265, i=-48.699997, d=-65609.000000
x=329, p=-329, i=-81.599998, d=-65600.000000
x=384, p=-384, i=-120.000000, d=-65591.000000
x=431, p=-431, i=-163.100006, d=-65583.000000
x=470, p=-470, i=-210.100006, d=-65575.000000
x=501, p=-501, i=-260.200012, d=-65567.000000
x=525, p=-525, i=-312.700012, d=-65560.000000
x=542, p=-542, i=-366.900024, d=-65553.000000
x=552, p=-552, i=-422.100037, d=-65546.000000
x=556, p=-556, i=-477.700043, d=-65540.000000
x=656, p=-656, i=-543.300049, d=-65636.000000
x=756, p=-756, i=-618.900024, d=-65636.000000
x=856, p=-856, i=-704.500000, d=-65636.000000
x=956, p=-956, i=-800.099976, d=-65636.000000
x=1056, p=-1056, i=-905.699951, d=-65636.000000
x=1156, p=-1156, i=-1021.299927, d=-65636.000000
x=1256, p=-1256, i=-1146.899902, d=-65636.000000
x=1356, p=-1356, i=-1282.499878, d=-65636.000000
x=1441, p=-1441, i=-1426.599854, d=-65621.000000
x=1505, p=-1505, i=-1577.099854, d=-65600.000000
x=1549, p=-1549, i=-1731.999878, d=-65580.000000
x=1575, p=-1575, i=-1889.499878, d=-65562.000000
x=1585, p=-1585, i=-2047.999878, d=-65546.000000
x=1685, p=-1685, i=-2216.500000, d=-65636.000000
x=1785, p=-1785, i=-2395.000000, d=-65636.000000
x=1885, p=-1885, i=-2583.500000, d=-65636.000000
x=1985, p=-1985, i=-2782.000000, d=-65636.000000
x=2085, p=-2085, i=-2990.500000, d=-65636.000000
x=2182, p=-2182, i=-3208.699951, d=-65633.000000
x=2248, p=-2248, i=-3433.500000, d=-65602.000000
x=2288, p=-2288, i=-3662.300049, d=-65576.000000
x=2304, p=-2304, i=-3892.699951, d=-65552.000000
x=2404, p=-2404, i=-4133.100098, d=-65636.000000
x=2504, p=-2504, i=-4383.500000, d=-65636.000000
x=2604, p=-2604, i=-4643.899902, d=-65636.000000
x=2704, p=-2704, i=-4914.299805, d=-65636.000000
x=2803, p=-2803, i=-5194.599609, d=-65635.000000
x=2864, p=-2864, i=-5480.999512, d=-65597.000000
x=2894, p=-2894, i=-5770.399414, d=-65566.000000
x=2895, p=-2895, i=-6059.899414, d=-65537.000000
x=2995, p=-2995, i=-6359.399414, d=-65636.000000
x=3095, p=-3095, i=-6668.899414, d=-65636.000000
x=3195, p=-3195, i=-6988.399414, d=-65636.000000
x=3294, p=-3294, i=-7317.799316, d=-65635.000000
x=3350, p=-3350, i=-7652.799316, d=-65592.000000
x=3371, p=-3371, i=-7989.899414, d=-65557.000000
x=3471, p=-3471, i=-8336.999023, d=-65636.000000

c int_

x=0, p=10, i=1.000000, d=10.000000
x=2, p=8, i=1.800000, d=-2.000000
x=2, p=8, i=2.600000, d=0.000000
x=3, p=7, i=3.300000, d=-1.000000
x=3, p=7, i=4.000000, d=0.000000
x=4, p=6, i=4.600000, d=-1.000000
x=4, p=6, i=5.200000, d=0.000000
x=5, p=5, i=5.700000, d=-1.000000
x=5, p=5, i=6.200000, d=0.000000
x=6, p=4, i=6.600000, d=-1.000000
x=6, p=4, i=7.000000, d=0.000000
x=7, p=3, i=7.300000, d=-1.000000
x=7, p=3, i=7.600000, d=0.000000
x=8, p=2, i=7.800000, d=-1.000000
x=8, p=2, i=8.000000, d=0.000000
x=9, p=1, i=8.100000, d=-1.000000
x=9, p=1, i=8.200001, d=0.000000
x=9, p=1, i=8.300001, d=0.000000
x=9, p=1, i=8.400002, d=0.000000
x=9, p=1, i=8.500002, d=0.000000
x=9, p=1, i=8.600002, d=0.000000
x=9, p=1, i=8.700003, d=0.000000
x=9, p=1, i=8.800003, d=0.000000
x=9, p=1, i=8.900003, d=0.000000
x=9, p=1, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=-1.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=0, i=9.000004, d=0.000000
x=10, p=-10, i=8.000004, d=-10.000000
x=10, p=-10, i=7.000004, d=0.000000
x=10, p=-10, i=6.000004, d=0.000000
x=10, p=-10, i=5.000004, d=0.000000
x=10, p=-10, i=4.000004, d=0.000000
x=10, p=-10, i=3.000004, d=0.000000
x=10, p=-10, i=2.000004, d=0.000000
x=10, p=-10, i=1.000004, d=0.000000
x=10, p=-10, i=0.000004, d=0.000000
x=10, p=-10, i=-0.999996, d=0.000000
x=10, p=-10, i=-1.999996, d=0.000000
x=10, p=-10, i=-2.999996, d=0.000000
x=10, p=-10, i=-3.999996, d=0.000000
x=10, p=-10, i=-4.999996, d=0.000000
x=10, p=-10, i=-5.999996, d=0.000000
x=10, p=-10, i=-6.999996, d=0.000000
x=10, p=-10, i=-7.999996, d=0.000000
x=10, p=-10, i=-8.999996, d=0.000000
x=10, p=-10, i=-9.999996, d=0.000000
x=10, p=-10, i=-10.999996, d=0.000000
x=10, p=-10, i=-11.999996, d=0.000000
x=10, p=-10, i=-12.999996, d=0.000000
x=10, p=-10, i=-13.999996, d=0.000000
x=10, p=-10, i=-14.999996, d=0.000000
x=10, p=-10, i=-15.999996, d=0.000000
x=10, p=-10, i=-16.999996, d=0.000000
x=10, p=-10, i=-17.999996, d=0.000000
x=10, p=-10, i=-18.999996, d=0.000000
x=10, p=-10, i=-19.999996, d=0.000000
x=10, p=-10, i=-20.999996, d=0.000000
x=10, p=-10, i=-21.999996, d=0.000000
x=10, p=-10, i=-22.999996, d=0.000000
x=10, p=-10, i=-23.999996, d=0.000000
x=10, p=-10, i=-24.999996, d=0.000000
x=10, p=-10, i=-25.999996, d=0.000000
x=10, p=-10, i=-26.999996, d=0.000000
x=10, p=-10, i=-27.999996, d=0.000000
x=10, p=-10, i=-28.999996, d=0.000000
x=10, p=-10, i=-29.999996, d=0.000000
x=10, p=-10, i=-30.999996, d=0.000000
x=10, p=-10, i=-31.999996, d=0.000000
x=10, p=-10, i=-32.999996, d=0.000000
x=10, p=-10, i=-33.999996, d=0.000000
x=10, p=-10, i=-34.999996, d=0.000000
x=10, p=-10, i=-35.999996, d=0.000000
x=10, p=-10, i=-36.999996, d=0.000000
x=10, p=-10, i=-37.999996, d=0.000000
x=10, p=-10, i=-38.999996, d=0.000000
x=10, p=-10, i=-39.999996, d=0.000000
x=10, p=-10, i=-40.999996, d=0.000000

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

а, точно, забыл про скорости упомянуть, их ограничения исправь на +100/-100.

anonymous
()

uint8_t output;
output = p + i + d;

Вот здесь у тебя происходит переполнение, инфа 100% (и все последующие проверки уже как мёртвому припарки).

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

о, точно. сейчас снова попробуем.

WRG ★★★★
() автор топика
Ответ на: комментарий от prischeyadro

последний вариант фукнции вот такой

int16_t pid_compute(int measured, int setpoint, uint16_t *prev_error, float *integral)
{
	int16_t error = setpoint - measured;
	int16_t output;
	float derivate;
	
	*integral = *integral + (error * kI);
	derivate = (error - *prev_error) / (float)PID_INTERVAL;
	output = (kP * error) + (kI * (*integral)) + (kD * derivate);
	*prev_error = error;
	
	if(output > MOTOR_MAX_SPEED) output = MOTOR_MAX_SPEED;
	if(output < MOTOR_MIN_SPEED) output = MOTOR_MIN_SPEED;
	
	return output;
}
пробовал настроить по методу Зиглера-Никольса, он либо секунд за 20 работы разгоняется до максимума при значениях Ku от 9 и выше, или едет заметно медленнее чем задано и даже не может тронуться с места, если Ku меньше 9. Уже не знаю как поступить даже. С одной стороны можно захардкодить несколько экспериментально определенных «скоростей», и как только обороты упали ниже определенных для данной скорости, то включить следующую скорость. Но с другой стороны это не Ъ, надо бы чтобы все автоматически работало.

WRG ★★★★
() автор топика
Последнее исправление: WRG (всего исправлений: 1)
Ответ на: комментарий от WRG

1) Сперва поэкспериментируй с одним лишь K_p, найди такой, при котором робот будет стабильно трогаться с места и поддерживать скорость. Его можно назначать побольше, но так, чтобы процесс троганья и езды не был дёрганым.
2) K_i сначала выбирай поменьше и постепенно увеличивай, найди такое его значение, чтобы оно было побольше, но при трогании и достижении нужной скорости не было колебаний скорости (переходный процесс не был колебательным).
3) Попробуй погонять робота так, чтобы он заезжал с ровной поверхности в горку. Подбери K_d, чтобы робот получше отрабатывал этот переход, но чтобы не возникало каких-нибудь рывков на ровной поверхности.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от WRG

Но с другой стороны это не Ъ, надо бы чтобы все автоматически работало.

Если хочешь Ъ, то надо снять переходную характеристику системы без регулятора (задаёшь сигнал на ШИМе и пишешь график скорости, задающий сигнал — ступенька, например 0 0 0 128 128 128 128 128 ...) по виду графика скорости строишь модель в Матлабе (там, скорее всего, астатическое звено 1-го порядка в грубом приближении), подбираешь коэффициенты ПИД в Матлабе.

prischeyadro ★★★☆☆
()
Последнее исправление: prischeyadro (всего исправлений: 2)
Ответ на: комментарий от prischeyadro

чтож, попробую еще раз. а на счет пробных замеров и моделей все осложняется тем, что робот общается по serial только через ИК приёмопередатчик, который работает только на расстоянии до 50см и должен всегда находиться над роботом, то есть далеко от компа не уеду.

WRG ★★★★
() автор топика
Ответ на: комментарий от prischeyadro

Сперва поэкспериментируй с одним лишь K_p

Да, здесь я имел в виду,что K_i и K_d равны нулю. Потом, когда будешь подбирать K_i, K_p оставляешь без изменений, K_d оставляешь нулевым. И в самом конце подбираешь оставшийся K_d.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

В общем, у меня получается более менее устойчиво, когда kP равен 10, а остальные коэффициенты равны нулю. Если устаноить kI хотябы 0.001 сразу начинает ехать рывками. Пробовал даже небольшие отрицательные значения, толку нету. Правда при настройке [10, 0, 0] его легко остановить, двигатели явно не работают в полную мощь если его задержать. Даже когда с ламината на ковер заезжает, может остановиться и больше не тронуться.

WRG ★★★★
() автор топика
Ответ на: комментарий от anonymous

Во блин, сколько можно глупых ошибок делать. Повнимательнее надо.

WRG ★★★★
() автор топика
Ответ на: комментарий от anonymous

теперь если поставить маленькое положительное значение kI то он медленно разгоняется до максимума, а если выбрать маленькое отрицательное значение, то он тормозится до нуля.

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

теперь если поставить маленькое положительное значение kI то он медленно разгоняется до максимума

Именно до максимума, а не до положенной скорости? Ведь если скорость переходит через заданное значение, то знак ошибки меняется, интеграл начинает убывать? У тебя меняется знак ошибки?

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

да, мой желаемый setpoint равен 13 импульсам энкодера за 100мс, а получается он постепенно разгоняется до ~22 импульса за 100мс, a это максимальные обороты, как будто я сделал MotorSpeed(255, 255)

WRG ★★★★
() автор топика
Ответ на: комментарий от WRG

Выведи знак ошибки на какую-нибудь лампочку и мониторь. Определишь хотя бы, косяк ли у тебя в определении скорости или в алгоритме интегрирования.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

сейчас как раз этим и занимаюсь. вывожу ошибку на serial и смотрю через picocom. она почему-то всегда равна как раз 13, т.е. конечному результату, который я хочу получить. буду колупать дальше, почему не так работает.

накопал еще arduino pid library, там функция, аналогичная моей pid_compute() выглядит так

bool PID::Compute()
{
   if(!inAuto) return false;
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
	  double input = *myInput;
      double error = *mySetpoint - input;
      ITerm+= (ki * error);
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput;
      
	  if(output > outMax) output = outMax;
      else if(output < outMin) output = outMin;
	  *myOutput = output;
	  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
	  return true;
   }
   else return false;
}

WRG ★★★★
() автор топика
Последнее исправление: WRG (всего исправлений: 1)
Ответ на: комментарий от WRG
  *integral = *integral*0.9 + (er..

должно было делать это:

  ITerm+= (ki * error);
  if(ITerm > outMax) ITerm= outMax;
  else if(ITerm < outMin) ITerm= outMin;

anonymous
()
Ответ на: комментарий от anonymous

ну косяк я нашел. у меня в главном цикле постоянно дергается обработчик process_pid(), который проверяет, прошло ли сооотв. время после последнего запуска pid_compute(), и если прошло, то запустить снова, а время запуска записать. почему то если выводить из этого обработчика отладочную информацию, то она выводится как раз примерно с интервалом PID_INTERVAL, а если эту же информацию выводить из функции pid_compute(), то она выводится заметно чаще. вот только не пойму почему. Из-за этого срабатывания энкодера не успевают накопиться, и их счетчик равен нулю, поэтому ошибка всегда равна 13.

#include "pid.h"

void process_pid(void)
{
	if(!pid_enable) return;
		
	if(Gettime() > prev_pid_time){
        if(Gettime() - prev_pid_time >= PID_INTERVAL){
			int16_t l_pwm = pid_compute(encoder[LEFT], left_speed, &left_prev_error, &left_integral);
//          int16_t r_pwm = pid_compute(encoder[RIGHT], right_speed, &right_prev_error, &right_integral);
            MotorSpeed(l_pwm, 0);

            /* вот это выводится с правильным интервалом*/
//            // отладочный выхлоп
//            char str[10];
//            itoa(encoder[LEFT], str, 10);
//            SerPrint(str);
//            SerPrint("\n\r");

			encoder[LEFT] = 0;
			encoder[RIGHT] = 0;
			prev_pid_time = Gettime();
		}
	} else {
        if(0xFF - prev_pid_time + Gettime() + 1 >= PID_INTERVAL){
			int16_t l_pwm = pid_compute(encoder[LEFT], left_speed, &left_prev_error, &left_integral);
//			int16_t r_pwm = pid_compute(encoder[RIGHT], right_speed, &right_prev_error, &right_integral);
            MotorSpeed(l_pwm, 0);
			encoder[LEFT] = 0;
			encoder[RIGHT] = 0;
			prev_pid_time = Gettime();
		}
	}
}

int16_t pid_compute(int measured, int setpoint, int16_t *prev_error, float *integral)
{
	int16_t error = setpoint - measured;

    /* а вот это выводится очень часто, явно чаще чем надо */
    // отладочный вывод
    char msg[10];
    itoa(measured, msg, 10);
    SerPrint(Gettime());
    SerPrint("\n\r");

	int16_t output;
	float derivate;
	
	*integral += (error * PID_INTERVAL);
	derivate = (error - (*prev_error)) / (float)PID_INTERVAL;
	output = (kP * error) + (kI * (*integral)) + (kD * derivate);
	*prev_error = error;
	
	if(output > MOTOR_MAX_SPEED) output = MOTOR_MAX_SPEED;
	if(output < MOTOR_MIN_SPEED) output = MOTOR_MIN_SPEED;
	
	return output;
}

я что-то не пойму откуда она вызывается так часто

WRG ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.