Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

32-разрядные микроконтроллеры разработки АО "НИИЭТ"

Модераторы: ea, dav, bkolbov, Alis, pip, _sva_

petrovitch
Сообщения: 107
Зарегистрирован: 15 фев 2017, 19:07

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение petrovitch »

Настраиваю регуляторы тока согласно описанию на ПО (п. 6.1.2.).
Заданный ток удержания 1А.
Хотя параметры ПИД-регулятора и влияют на результат, но при всех
настройках осциллограммы токов фаз В и С имеют форму как на картинке:
Oscill_Ifabc.jpg
Oscill_Ifabc.jpg (137.6 КБ) 3219 просмотров
В чём может быть проблема?
Аватара пользователя
Лашкевич
Сообщения: 373
Зарегистрирован: 13 май 2015, 13:10
Предприятие: ООО "НПФ Вектор"
Откуда: Москва
Контактная информация:

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение Лашкевич »

Да, есть такой эффект, связан, насколько я помню, с влиянием мёртвого времени при попытке приложить околонулевое напряжение вдоль одной из осей. Попробуйте сменить тип ШИМ на другой. Также попробуйте поменять частоту ШИМ - попробуйте 10кГц, 15кГц, посмотрите как влияет.
С уважением,
Лашкевич Максим.
Инженер-программист ООО "НПФ Вектор", Москва.
http://motorcontrol.ru/
petrovitch
Сообщения: 107
Зарегистрирован: 15 фев 2017, 19:07

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение petrovitch »

В структуре TDrvParams базовая скорость хранится в явном виде в формате int16.
Рассчитанная скорость получается в формате int32 (_iq). Эта скорость в относительных единицах?
Если да, то где производится обратный пересчёт из относительной скорости?

Объясните смысл констант в строке в файле V_DPR_eCAP.c

Код: Выделить всё

p->TsNom = ((CORE_CLK / (drv_params.speed_nom * drv_params.p)) * 15*2);
и в строке

Код: Выделить всё

p->TsNomMilsec = (60.0*1000 / (6*drv_params.speed_nom * drv_params.p));
petrovitch
Сообщения: 107
Зарегистрирован: 15 фев 2017, 19:07

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение petrovitch »

И ещё вопрос о запуске управления по двум каналам.

Как поступить?
Фактически код управления помимо дополнительных настроек портов должен быть продублирован.
А как поступать со словарём объектов в целевой программе и в UniCon-е.
Аватара пользователя
Лашкевич
Сообщения: 373
Зарегистрирован: 13 май 2015, 13:10
Предприятие: ООО "НПФ Вектор"
Откуда: Москва
Контактная информация:

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение Лашкевич »

petrovitch писал(а): 22 окт 2020, 23:14 В структуре TDrvParams базовая скорость хранится в явном виде в формате int16.
Рассчитанная скорость получается в формате int32 (_iq). Эта скорость в относительных единицах?
Да, в проекте всё в относительных единицах.
Обратный пересчёт из относительной скорости внутри программы не производится нигде, а для пользователя пересчёт производит программа UniCON, пользуясь выведенной в словарь объектов базовой скоростью (масштабирующий коэффициент 14, co2_vars.co_scaleNum14 = drv_params.speed_nom;).

Предпосчитанная константа TsNom нужна для быстрого расчёта частоты вращения в функции SpeedCalc. Она показывает количество тактов микроконтроллера между двумя фронтами одного канала датчика холла при номинальной скорости
Здесь speed_nom в об/мин, например, 3000, p число пар полюсов.
1/(speed_nom/60*p) даст время одного периода датчика холла, а так как скорость рассчитывается два раза на периоде (между каждыми двумя фронтами), то константа в два раза меньше (умножение на 30 вместо 60). CORE_CLK переводит время в процессорные такты

Про TsNomMilsec есть комментарий в коде, коэффициент для пересчета времени между метками в мс в скорость в об/мин. 60 - об/мин, 1000 мс в секунде, 6 меток на эл. оборот. Нужен тоже для экономии процессорного времени, но вместо процессорных тактов показывает количество миллисекунд одного электрического периода.

petrovitch писал(а): 22 окт 2020, 23:24 И ещё вопрос о запуске управления по двум каналам.
Интересует одновременно два работающих модуля ДПР Холла? Так как модуль сильно завязан на использование конкретных регистров периферии, то проще всего просто скопировать и сделать второй модуль под другим именем (типа V_DPR_eCAP1.c), там переназначить ножки и модули CAP, сделать ещё три перывания в файле main.c, пройти поиском по проекту на DPReCAP и везде аналогично повторить всё для DPReCAP1. Затем в программе CoodEdit скопировать весь целиком индекс с параметрами про DPReCAP на свободное место (из индекса 5155 в 5156, например), заменить в скопированном имена переменных с DPReCAP на DPReCAP1. Также можно для второго датчика сделать отдельную группу параметров в перичеслении 15, чтобы не путаться, и выставить эту группу новому индексу параметров 5156. Тогда после перепрошивки и обновления словаря и экспорта базы текстов из CoodEdit в UniCon всё появится в юниконе. Рекомендуется пройти лабораторную работу "Добавление параметра CANopen в программе COODEdit" из руководства Описание структуры ПО MotorControlDemo_v23.pdf.
С уважением,
Лашкевич Максим.
Инженер-программист ООО "НПФ Вектор", Москва.
http://motorcontrol.ru/
petrovitch
Сообщения: 107
Зарегистрирован: 15 фев 2017, 19:07

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение petrovitch »

Спасибо за такой развёрнутый ответ.

Но по второму вопросу интересовал запуск двух двигателей с одного кристалла.
Аватара пользователя
Лашкевич
Сообщения: 373
Зарегистрирован: 13 май 2015, 13:10
Предприятие: ООО "НПФ Вектор"
Откуда: Москва
Контактная информация:

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение Лашкевич »

petrovitch писал(а): 23 окт 2020, 15:49 Но по второму вопросу интересовал запуск двух двигателей с одного кристалла.
Ну тогда надо мой ответ про модуль датчика положения расширить на все модули ПО, касающиеся управления двигателями.
С уважением,
Лашкевич Максим.
Инженер-программист ООО "НПФ Вектор", Москва.
http://motorcontrol.ru/
KVV
Сообщения: 4
Зарегистрирован: 02 фев 2021, 15:44
Предприятие: НПП Темп

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение KVV »

Здравствуйте!
Отлаживаясь на моделях, заметил проблему у себя: при моделировании СДПМ с количеством полюсов >1. виртуальный двигатель не выходит на рабочие обороты, будто слишком большой коэф.противоЭДС.
Начал копать по формулам и понял, что либо я что-то не понимаю, либо есть ошибка.

К сожалению, у меня пока нет платы с 1921, я отлаживаюсь на stm32f4, а для телеметрии, осциллографирования, настройки и прошивки использую свой "велосипед".
Однако, общая архитектура аналогична Вашей. Контур управления, регуляторы у меня аналогичны Вашим, а модель и обработчик энкодера используются непосредственно Ваши с минимальной адаптацией.
Берем первый мотор СДПМ из набора параметров. Видимо это Leadshine ACM601V36-T-2500. ЗПТ - 36В, амплитуды напряжений в модель доходят, весь контур векторного управления отлично замыкается, ток по оси d = 0, по оси q реагирует на момент. Но недобор скорости как раз раза в 4....
Я конечно могу подогнать действительное под желаемое, но хочется разобраться...

Код: Выделить всё

Tsm_param SMDATA[] = {\
//		|r_f	|l_f	|l_m	|If	|psi_pm		|r_s		|l_sd		|l_sq		|pp	|j          		|qep	   	|Power	|Speed	|Current
		{0,	0,	0,	0,	0.162056937,	0.38,	0.91,   	0.91,	4,   	0.00001032, 	2500,	0.1,		3000,	4},\
Screenshot_1.png
Screenshot_1.png (47.48 КБ) 1410 просмотров
Возьмем куски кода:

Инит:

Код: Выделить всё

if (MotorParametersValid){
	p->motorInternals.r_f = SMDATA[p->MotorParametersNum-1].r_f;			//сопротивление ОВ
	p->motorInternals.l_f = SMDATA[p->MotorParametersNum-1].l_f;			//индуктивность ОВ
	p->motorInternals.l_m = SMDATA[p->MotorParametersNum-1].l_m;			//коэффициент магнитного потока (взаимная индуктивность)
	p->motorInternals.rs = SMDATA[p->MotorParametersNum-1].r_s;				//сопротивление статора
	p->motorInternals.lsd = SMDATA[p->MotorParametersNum-1].l_sd;			//индуктивность статора
	p->motorInternals.lsq = SMDATA[p->MotorParametersNum-1].l_sq;			//индуктивность статора
	p->motorInternals.pp = SMDATA[p->MotorParametersNum-1].pp;				//число пар полюсов
	p->motorInternals.j = SMDATA[p->MotorParametersNum-1].j;				//момент инерции
	p->motorInternals.QEPResolution = SMDATA[p->MotorParametersNum-1].qep;	//разрешение энкодера
	p->motorInternals.m = SMDATA[p->MotorParametersNum-1].psi_pm;			//потокосцепление ротора равно потоку постоянных магнитов
	p->motorInternals.RatedPower = SMDATA[p->MotorParametersNum-1].RatedPower; //номинальная мощность (справочная величина, не используется в расчетах)
	p->motorInternals.RatedSpeed = SMDATA[p->MotorParametersNum-1].RatedSpeed; //номинальная скорость (справочная величина, не используется в расчетах)
	p->motorInternals.RatedCurrent = SMDATA[p->MotorParametersNum-1].RatedCurrent; //номинальный действующий ток
	p->motorInternals.RatedFluxCurrent = SMDATA[p->MotorParametersNum-1].RatedFluxCurrent; //номинальный ток ОВ
	p->motorInternals.MechLoss = p->motorInternals.j * 10; 		//механические потери
}
Тело:

Код: Выделить всё

// Если с постоянными магнитами, то поток ротора присвоился еще в ините и не меняется

// Расчет синуса и косинуса угла ротора
p->motorInternals.cosTetaR = _IQtoF(_IQcos(_IQ(p->motorInternals.tetaR)));//синус и косинус считаются с фиксированной точкой, т.к. флоатовские занимают тысячи тактов (видимо, какая-то медленная реализация, не использующая аппаратную поддержку).
p->motorInternals.sinTetaR = _IQtoF(_IQsin(_IQ(p->motorInternals.tetaR)));//если удастся найти быстрый плавающий синус и косинус, то их можно считать во флоате

// Поворот напряжений из осей альфа.бета в оси d,q
p->motorInternals.usd = p->motorInternals.usa * p->motorInternals.cosTetaR + p->motorInternals.usb * p->motorInternals.sinTetaR;
p->motorInternals.usq = -p->motorInternals.usa * p->motorInternals.sinTetaR + p->motorInternals.usb * p->motorInternals.cosTetaR;

// Расчет изменений потокосцеплений (предикторы)
p->motorInternals.dpsd = (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega);
p->motorInternals.dpsq = (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega);

// Расчет предикторов потокосцеплений
p->motorInternals.ppsd = p->motorInternals.psd + p->motorInternals.dpsd * p->motorInternals.t;
p->motorInternals.ppsq = p->motorInternals.psq + p->motorInternals.dpsq * p->motorInternals.t;

// Расчет токов для предикторного уравнения
p->motorInternals.isd = (p->motorInternals.ppsd - p->motorInternals.m) * p->motorInternals._1_lsd;
p->motorInternals.isq = p->motorInternals.ppsq * p->motorInternals._1_lsq;

// Расчет изменений потокосцеплений по Рунге-Кутта второго порядка
p->motorInternals.psd = p->motorInternals.psd + p->motorInternals.t2 * (p->motorInternals.dpsd + (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega));
p->motorInternals.psq = p->motorInternals.psq + p->motorInternals.t2 * (p->motorInternals.dpsq + (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega));

//если инвертор выключен и все токи пересекли ноль (колеблются около нуля, расфорсировка прошла)
//то выключаем расчет токов статора, чтобы не дергались (читерство, иначе сложный расчет будет)
if ((p->InvertorEna == 0) && (p->motorInternals.CurrentInvertedFlag == 7)) {
	p->motorInternals.psd = p->motorInternals.m;
	p->motorInternals.psq = 0;
}

// Расчет токов после уточнения изменения потокосцеплений
p->motorInternals.isd = (p->motorInternals.psd - p->motorInternals.m) * p->motorInternals._1_lsd;
p->motorInternals.isq = p->motorInternals.psq * p->motorInternals._1_lsq;

//поворот токов в оси альфа,бета (для вывода на АЦП)
p->motorInternals.isa = p->motorInternals.isd * p->motorInternals.cosTetaR - p->motorInternals.isq * p->motorInternals.sinTetaR;
p->motorInternals.isb = +p->motorInternals.isd * p->motorInternals.sinTetaR + p->motorInternals.isq * p->motorInternals.cosTetaR;

// Расчет момента
p->motorInternals.torque = 3.0 / 2.0 * p->motorInternals.pp * (p->motorInternals.psd * p->motorInternals.isq - p->motorInternals.psq * p->motorInternals.isd);

Собственно говоря, при инициализации

Код: Выделить всё

p->motorInternals.m = SMDATA[p->MotorParametersNum-1].psi_pm;			//потокосцепление ротора равно потоку постоянных магнитов
А далее главный вопрос...
Не ясен механизм учёта количества пар полюсов. Т.к. все расчёты до вычисления электромагнитного момента производятся основываясь на частоту вращения вала p->motorInternals.omega (НЕ электрическую) и потокосцепление p->motorInternals.m, которое вроде тоже является суммой для всех пар полюсов.
Но если это суммарное потокосцепление и частота вращения вала, то тогда почему в расчёте момента учитывается кол-во пар полюсов?

Код: Выделить всё

p->motorInternals.torque = 3.0 / 2.0 * p->motorInternals.pp * (p->motorInternals.psd * p->motorInternals.isq - p->motorInternals.psq * p->motorInternals.isd);
Буд-то это потокосцепления для одной пары полюсов до этого вычислялись...

У меня всё встало на свои места после того как в ините уменьшил присваеваемое потокосцепление от магнитов в кол-во пар полюсов раз и принял что расчёт идет для одной пары.
Типа так:

Код: Выделить всё

s->Internals.pp = SMDATA[s->MotorParametersNum-1].pp;	
s->Internals.m = SMDATA[s->MotorParametersNum-1].psi_pm/s->Internals.pp;
Хотя это кажется не верным, т.к. в расчётах продолжает использоваться частота вращения вала "s->Internals.omega"...
Почему мы, используя электрический угол для преобразований

Код: Выделить всё

p->motorInternals.cosTetaR = _IQtoF(_IQcos(_IQ(p->motorInternals.tetaR)));//синус и косинус считаются с фиксированной точкой, т.к. флоатовские занимают тысячи тактов (видимо, какая-то медленная реализация, не использующая аппаратную поддержку).
p->motorInternals.sinTetaR = _IQtoF(_IQsin(_IQ(p->motorInternals.tetaR)));//если удастся найти быстрый плавающий синус и косинус, то их можно считать во флоате
,используем механическую частоту вращения при расчёте предикторов и изменений потокосцеплений?

К сожалению описанные в файле "Описание имитационных моделей электродвигателей v8.pdf" модели не соответствуют буквально тем, которые реализованы в коде c переходом в d-q и расчетом предикторов.

Так же отдельный вопрос по самому двигателю ACM601V36. Как был получен psi_pm = 0.162056937? Как Вы связывали его с паспортными параметрами Се = 3.03 V/RPM и Cm = 0.0866 Hm/A.
Т.к. у меня этого двигателя в живую нет, то не совсем ясно, что такое в понимании производителя 3.03 V/RPM, если обычно Сm численно равен Сe(В/(рад/сек)), а тут связь какая-то странная. И если по Сm я логику вижу, то Сe какой-то странный....
Аватара пользователя
Disona
Сообщения: 81
Зарегистрирован: 06 дек 2016, 11:18
Предприятие: НПФ Вектор
Откуда: Москва
Контактная информация:

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение Disona »

Добрый день.

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

Вот здесь:

Код: Выделить всё

// Расчет изменений потокосцеплений (предикторы)
p->motorInternals.dpsd = (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega);
p->motorInternals.dpsq = (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega);
и вот здесь:

Код: Выделить всё

// Расчет изменений потокосцеплений по Рунге-Кутта второго порядка
p->motorInternals.psd = p->motorInternals.psd + p->motorInternals.t2 * (p->motorInternals.dpsd + (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega));
p->motorInternals.psq = p->motorInternals.psq + p->motorInternals.t2 * (p->motorInternals.dpsq + (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega));

следует домножить скорость "motorInternals.omega" на количество пар полюсов, чтобы получилось соответственно так:

Код: Выделить всё

	
	p->motorInternals.dpsd = (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega * p->motorInternals.pp);
	p->motorInternals.dpsq = (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega * p->motorInternals.pp);
	
и так:

Код: Выделить всё

	p->motorInternals.psd = p->motorInternals.psd + p->motorInternals.t2 * (p->motorInternals.dpsd + (p->motorInternals.usd - p->motorInternals.isd * p->motorInternals.rs + p->motorInternals.psq * p->motorInternals.omega * p->motorInternals.pp));
	p->motorInternals.psq = p->motorInternals.psq + p->motorInternals.t2 * (p->motorInternals.dpsq + (p->motorInternals.usq - p->motorInternals.isq * p->motorInternals.rs - p->motorInternals.psd * p->motorInternals.omega * p->motorInternals.pp));

А потокосцепление постоянных магнитов нужно вернуть на место.
Можете попробовать и рассказать о результатах?

На вопрос про величины Ce/Cm я пока ответить не могу, возможно коллеги позже ответят.
С уважением, Дмитрий Шпак
ООО "НПФ Вектор"
KVV
Сообщения: 4
Зарегистрирован: 02 фев 2021, 15:44
Предприятие: НПП Темп

Re: Проект MotorControlDemo для микроконтроллеров серии 1921ВК01

Сообщение KVV »

Disona писал(а): 19 фев 2021, 12:57 следует домножить скорость "motorInternals.omega" на количество пар полюсов
А потокосцепление постоянных магнитов нужно вернуть на место.
Можете попробовать и рассказать о результатах?
Попробовал... Результат предсказуем. Домножение скоростей на частоту хх не влияет никак, влияет на момент по всё видимости, с ним надо отдельно разбираться.
Screenshot_1.png
Screenshot_1.png (15.82 КБ) 1392 просмотра
Подозрение, что всё же потокосцепление от магнитов надо делить на количество пар полюсов + скорости домножить..... После обеда проанализирую коэф. момента двигателя при разных комбинациях "деления" и "умножения".
Ответить

Вернуться в «32-разрядные микроконтроллеры»