Daily MaverickTroubled port: Transnet and foreign operator face scrutiny over backlog at Durban Gateway TerminalPunchWhy Ronaldo didn’t fit into my Man United plans — Ten HagThe Jerusalem PostJordan and Syria tout close ties, but border smuggling and strikes tell another story – analysisZDF heuteEntdecken Sie das ZDF-NachrichtenstudiotazJunge Kulturszene in Jerewan: Wo das kulturelle Magma brodeltThe Sydney Morning HeraldMexican F1 GP LIVE: Kimi Antonelli set to clinch championship; Ocon denies violence allegationsVariety‘Nobody Wants This’ Star Tovah Feldshuh Says ‘Tensions Are Not Rising’ Amid Disagreement With Adam Brody over Israel-Palestine ConflictABC News (Australia)How the AI 'cow-gorithm' is helping this dairy couple get the job doneسكاي نيوز عربيةمكالمة "صعبة" بشأن إيران.. تقرير يكشف خلاف ترامب ونتنياهوTagesschauAusschreitungen nach Protesten: Unterwanderung oder spontane Eskalation?SudinfoUne nouvelle phase litigieuse : Charleroi devait-il bénéficier de ce penalty contre la RAAL La Louvière ?Il Fatto QuotidianoAxios: “Netanyahu fa pressione su Trump perché gli Usa tornino a colpire l’Iran dopo le elezioni di mid-term”
The Daily Newsstand · Free, Always
Sunday, November 1, 2026

Продолжение полёта

Translate

В прошлых статьях я рассказывал о портировании AM32 на отечественный микроконтроллер К1946ВК035 и о том, как принимать DShot, когда нужный нам модуль захвата сигнала не соединён с DMA. С шестишаговой коммутацией тогда разобрались, мотор закрутился. Но на этом, как вы уже понимаете, останавливаться было бы неинтересно.

На этот раз попробуем векторное управление, оно же FOC. Микроконтроллер оставим тот же, а за математикой отправимся в открытый проект https://github.com/vedderb/bldc Бенджамина Веддера.

Для тех, кому интереснее сразу посмотреть результат, — https://github.com/RRegulo/K1946VK035_VESC

Этот мотор ещё не знает, сколько срывов синхронизации, перепутанных знаков и перезапусков ждёт его во время отладки. Пока просто крутится и верит в автора.

Этот мотор ещё не знает, сколько срывов синхронизации, перепутанных знаков и перезапусков ждёт его во время отладки. Пока просто крутится и верит в автора.

Ну а для всех остальных — прошу под кат. Здесь будут три отдельных ШИМ-блока, немного математики и АЦП, который успел сообщить о готовности раньше, чем положил в буфер все результаты.

Что именно переносим

Сначала немного о разнице с прошлым проектом.

В AM32 мы использовали шестишаговую коммутацию. В каждый момент две фазы участвовали в управлении, а по свободной фазе можно было следить за обратной ЭДС и определять момент следующего переключения. Для этого я использовал внешние компараторы. Подробности остались в https://habr.com/ru/articles/938128/

Теперь нам нужно управлять током относительно положения ротора. Измеряем фазные токи, переводим их в систему координат, связанную с ротором, и получаем две составляющие: Id и Iq. В обычном режиме управления моментом для нашего мотора задаём Id = 0, а момент регулируем через Iq. На выходе токового регулятора получаем вектор напряжения, который ещё предстоит превратить в переключения транзисторов.

Вот здесь и пригодится VESC. Из него перенесены наблюдатель положения ротора, PLL, токовый регулятор и пространственно-векторная ШИМ — SVPWM. Последняя рассчитывает время включения фаз так, чтобы за период ШИМ получить нужный средний вектор напряжения.

Не всё здесь взято из VESC. Преобразования Clarke/Park и обратное Park, фильтр, PID и задатчик плавного изменения задания адаптированы из MotorControlDemo, разработанного ООО «НПФ Вектор» по заказу НИИЭТ. В моём порте эти блоки переведены на float и собраны в FOC_Math.c. Так что НПФ Вектор тоже спасибо: базовую математику для управления мотором писать с нуля не пришлось.

При этом мы не переносим на ВК035 всю прошивку VESC вместе с её операционной системой и всеми интерфейсами. Настройку периферии, запуск, хранение параметров и обмен с конфигуратором пришлось делать под нашу плату.

На чём будем запускать

В этот раз я использую плату BLDC на К1946ВК035 от TDMLab https://github.com/TDMLab/K1946VK035_BLDC_BOARD. Сразу отмечу: разработчик платы — TDMLab. Схему и материалы по ней смотрим в исходном репозитории, авторство железа себе не приписываем.

Сам микроконтроллер нам уже знаком по AM32: Cortex-M4F, аппаратный FPU одинарной точности, три блока ШИМ и АЦП с секвенсорами. В этой прошивке ядро работает на 100 МГц.

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

Три блока ШИМ вместо одного таймера

В прошлом проекте мы уже сталкивались с тем, что привычный по STM32 продвинутый таймер здесь заменён тремя отдельными ШИМ-модулями. Возможности есть, но настраивать их нужно согласованно.

Задаём счёт вверх-вниз, период 2000 тактов и синхронизацию между блоками. При частоте TBCLK 100 МГц получаем:

100 000 000 / (2 × 2000) = 25 000 Гц

Мёртвое время задаём отдельно. В текущей настройке это 40 тактов, то есть 0,4 мкс, на соответствующем фронте:

PWM0->TBPRD = PWM_TIM1_AUTORELOAD;

PWM1->TBPRD = PWM_TIM1_AUTORELOAD;

PWM2->TBPRD = PWM_TIM1_AUTORELOAD;

PWM0->DBRED = PWM_DEAD_TIME;

PWM0->DBFED = PWM_DEAD_TIME;

Настройка комплементарных выходов и задержек повторяется для всех трёх фаз. Полный код находится в PWM_Control.c.

Переключение сигналов плеча и dead-time.

Переключение сигналов плеча и dead-time.

На осциллограмме смотрим на взаимное положение фронтов: один сигнал переключается раньше, чем включается комплементарный. Между ними должна оставаться пауза, чтобы ключи одного плеча не оказались открыты одновременно. В прошивке задано 0,4 мкс; для точного измерения этой паузы нужен увеличенный участок фронта и временные курсоры, а не линейка, приложенная к картинке. Сквозной ток, к сожалению, не интересуется тем, насколько аккуратно мы написали константу PWM_DEAD_TIME.

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

В этом проекте времена из SVPWM переводятся в CMPA с инверсией:

g_pwm.GammaA = g_pwm.k_pwm - tA;

g_pwm.GammaB = g_pwm.k_pwm - tB;

g_pwm.GammaC = g_pwm.k_pwm - tC;

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

Напряжения на трёх фазах при вращении мотора

Напряжения на трёх фазах при вращении мотора

Здесь видны импульсы напряжения на фазах при вращении. Между повторяющимися фронтами около двух делений, то есть 40 мкс — наш период ШИМ 25 кГц. Уровень импульсов соответствует примерно напряжению питания стенда. Небольшое различие положения фронтов каналов связано с разным временем включения фаз.

Красивую гладкую синусоиду фазного напряжения на таком масштабе ждать не стоит: мост переключает напряжение импульсами, а рассчитанный вектор получается в среднем за период. Фазные токи после измерительных усилителей посмотрим отдельно ниже.

25 кГц ШИМ — это ещё не 25 кГц управления

Тут стоит сразу разделить две частоты, чтобы дальше не путаться.

ШИМ работает на 25 кГц, один период занимает 40 мкс. А обычный контур FOC в открытом проекте выполняется раз в четыре периода — на 6,25 кГц. На один расчёт у нас есть 160 мкс.

Запуск АЦП привязан к событию SOCA от PWM0:

PWM0->ETSEL_bit.SOCAEN = 1;

PWM0->ETSEL_bit.SOCASEL = ET_CTR_ZERO;

ADC->EMUX_bit.EM0 = ADC_EMUX_EM0_PWM012A;

PWM0->ETPS_bit.SOCAPRD = ET_3RD;

В используемой настройке значение поля SOCAPRD равно 3, событие получается раз в четыре периода. Соответственно, шаг алгоритмов задаём так:

#define FOC_TS (1.0f / 6250.0f)


Это шаг интеграторов, наблюдателя и токового контура. Просто оставить здесь 25 кГц, а вызывать код в четыре раза реже нельзя: формулы станут описывать другое течение времени.

При проверке на плате частота вызовов получилась около 6,25 кГц. Считаем сами вызовы и сверяемся со счётчиком тактов DWT, а не делаем вывод только по тому, какую константу записали в заголовке.

Сейчас обычный контур работает на 6,25 кГц. Работа над повышением частоты управления до 25 кГц продолжается, но в текущей версии она ещё не завершена.

Токи, напряжения и наблюдатель должны договориться

В бессенсорном FOC отдельного датчика положения ротора у нас нет. Наблюдатель получает токи, напряжения и параметры мотора, по ним оценивает магнитное состояние и угол. PLL помогает отслеживать угол и оценивать скорость.

На словах всё выглядит вполне понятно. Но у измерительного усилителя есть своя полярность, у фаз — своё подключение, а у программы — свои представления о направлении вращения. Если эти трое не договорились, мотор очень быстро объяснит это механическим способом.

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

Применение параметра выглядит так:

g_foc_obs_vraw = (g_params.s.flags & FOC_PARAM_FLAG_OBS_V_RAW) ? 1u : 0u;

А при передаче напряжения наблюдателю учитываются оба флага:

float v_inv = (g_foc_inv_i && !g_foc_obs_vraw) ? -1.0f : 1.0f;

float v_alpha = g_vesc_state.v_alpha * v_inv;

float v_beta = g_vesc_state.v_beta * v_inv;


Тут, думаю, понятно, почему мотор может дёргаться даже тогда, когда преобразования координат написаны без ошибок. Если ток и напряжение относятся к разным системам координат, наблюдатель будет добросовестно оценивать совсем не то, что происходит на валу.

Сигналы фазных токов после измерительных усилителей

Сигналы фазных токов после измерительных усилителей

Сигналы с выходов усилителей измерения фазных токов. Это напряжения измерительной цепи, соответствующие токам, а не напряжения на силовых фазах.

Здесь уже видны изменения огибающих трёх сигналов на фоне высокочастотной пульсации от ШИМ. По ним удобно сравнивать форму и взаимное положение фазных токов. Чтобы получить из показаний вольтметра амперы, нужно учитывать нулевое смещение и коэффициент усиления цепи. Само по себе число в вольтах на экране осциллографа током ещё не является.

Коэффициенты под мотор, а не под прошлый эксперимент

Коэффициенты токового регулятора должны соответствовать мотору. Если сопротивление, индуктивность и постоянную времени контура меняем, а Kp и Ki оставляем от предыдущего эксперимента, рассчитывать на такой регулятор было бы слишком оптимистично.

Для одного мотора это могло выглядеть приемлемо. При подключении другого, особенно с небольшой индуктивностью, результат уже гораздо менее приятный: резкий толчок, просадка питания и UV fault.

Коэффициенты рассчитываем так же, как в VESC. Сначала записываем cc_tc_us, затем применяем R/L мотора и пересчитываем регулятор:

float bw = 1.0f / (g_foc_cc_tc_us * 1e-6f);

g_vesc_cfg.foc_current_kp = p->L * bw;

g_vesc_cfg.foc_current_ki = p->R * bw;

Здесь R — в омах, L — в генри, постоянная времени — в микросекундах. Для наблюдателя усиление тоже рассчитывается из параметров мотора, а не остаётся одной константой на все случаи.

В текущей версии мотор вращается, задание оборотов через конфигуратор работает. Но правильные формулы не отменяют необходимости проверить введённые параметры и ограничения тока под конкретную силовую часть.

А теперь измерим R и L

В прошивке есть отдельная процедура определения параметров двигателя. Для сопротивления задаём два уровня тока при фиксированном положении вектора и усредняем полученные токи и напряжения:

R = (Vd2 − Vd1) / (Id2 − Id1)

Два уровня нужны, чтобы вычесть общее смещение напряжения. Это полезно, когда падение на силовых ключах и влияние dead-time уже сравнимы с напряжением, которое подаём на обмотку.

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

(i3 − i2) / (i2 − i1) = exp(−dt / tau)

L = R × tau

В коде расчёт достаточно короткий:

float dt = (float)n1 * DETECT_L_SAMPLE_DT;

float tau = -dt / logf(d2 / d1);

g_detect_l = s_l_rr * tau;

Для этого на время импульса выполняется короткая серия из четырёх выборок АЦП с шагом 40 мкс. Усреднение отключается, а нужные точки сохраняются непосредственно в прерывании. По окончании импульса силовые выходы закрываются и восстанавливается обычный режим измерений с шагом 160 мкс.

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

uint32_t guard = 2000u;

while(ADC->SEQ[0].SFLOAD_bit.VAL < 4u && guard != 0u)

    guard--;

Если полный пакет не появился, остаток FIFO очищается, а расчёт по этой выборке пропускается. Неполный буфер не должен превращаться в четыре вполне убедительных, но неправильных измерения.

Настройки во Flash

Про работу с Flash я уже рассказывал в статье про AM32: стираем страницу целиком, читаем и записываем данные группами по 8 байт.

Здесь блок параметров занимает 128 байт, содержит версию формата и CRC16. Страница настроек начинается по адресу 0xF000, а линкер ограничивает программу первыми 60 КиБ. Так при росте прошивки код не попадёт в область, которую мы потом будем стирать ради настроек.

На время стирания и записи соответствующие функции выполняются из RAM. Это нужно для самой операции с Flash. Контур FOC в RAM ради ускорения не переносится.

Когда запускается АЦП

Запуск измерений относительно ШИМ 25 кГц. Жёлтый CH1 — ШИМ, голубой CH2 — контрольная отметка запуска измерений при стендовой проверке.

Запуск измерений относительно ШИМ 25 кГц. Жёлтый CH1 — ШИМ, голубой CH2 — контрольная отметка запуска измерений при стендовой проверке.

Жёлтый сигнал повторяется примерно через 40 мкс, голубые импульсы — через 160 мкс. Между соседними маркерами укладываются четыре периода ШИМ: на этой осциллограмме хорошо видно различие между частотой силовых переключений 25 кГц и частотой обычного запуска измерений 6,25 кГц.

Что получилось

На К1946ВК035 работает бессенсорное управление на основе алгоритмов VESC: мотор запускается, обороты задаются через конфигуратор, R и L измеряются. Есть UART-телеметрия и сохранение настроек во Flash. Несущая ШИМ — 25 кГц, обычный контур FOC — 6,25 кГц.

Проект открытый. Исходники прошивки доступны, их можно изучать и дорабатывать на условиях GPLv3. Авторство VESC, MESC, TDMLab и остальных исходных компонентов сохранено. Конфигуратор — отдельный проект, его исходники в этот репозиторий не входят.

Прошивка и описание распространяются без гарантий. Ограничения ответственности действуют в пределах применимого законодательства и условий лицензий. Это экспериментальный стенд, а не сертифицированный регулятор с завершённым набором защит.

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

Как пользоваться конфигуратором

Теперь о том, как всё это запустить, не пересобирая прошивку под каждый мотор.

Открываем https://regulo.ru/foc-configurator/ в браузере с поддержкой Web Serial. Плата подключается к компьютеру через USB–UART; нажимаем «Подключить (UART)» и выбираем порт платы. Скорость обмена — 115200 бод. Режим «Демо без платы» нужен только для знакомства с интерфейсом: настоящим мотором он не управляет.

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

1. Сначала параметры и лимиты. На вкладке «Параметры» задаём число пар полюсов, допустимые напряжения и «I cmd — потолок команды» под свой мотор и питание. Число пар полюсов — именно пар: для ротора с 14 полюсами вводим 7. От этого зависит пересчёт электрических оборотов в механические. Заданный в интерфейсе порог тока не заменяет аппаратную защиту и ограничение источника.

2. Измеряем R. В состоянии STOP переходим на вкладку «Измерение», выбираем подходящий ток замера и нажимаем «Измерить R». После успешного завершения нажимаем «→ параметры» в строке R. В таблице результат показан в мОм.

3. Измеряем L. Нажимаем «Измерить L», дожидаемся завершения и переносим результат кнопкой «→ параметры» в строке L. Здесь единицы — мкГн. В этой прошивке токовый уровень импульса L подбирает сама процедура; поле тока замера используется для R и λ.

4. Измеряем λ. После применения R и L нажимаем «Измерить λ (мотор вращается)». Прошивка выполняет разгон в разомкнутом режиме и оценивает потокосцепление. После завершения нажимаем «→ параметры (и KV)». Значение λ показано в мкВб; конфигуратор также пересчитывает KV с учётом числа пар полюсов.

5. Сохраняем настройки. Кнопки переноса результатов применяют значения в RAM платы. Чтобы они сохранились после отключения питания, на вкладке «Параметры» нажимаем «Сохранить во flash», пока мотор остановлен. Число в таблице результатов само по себе ещё не означает, что оно уже стало параметром регулятора.

6. Теперь можно крутить. Переходим на вкладку «Управление», в блоке «Скорость» задаём небольшие обороты в поле «Обороты, об/мин (мех.)» и нажимаем «Задать». Смотрим на токи, напряжение и поведение мотора. Для остановки — красная кнопка STOP.

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

Коротко последовательность такая: R → применить → L → применить → λ → применить → сохранить → задать обороты. Не наоборот. Мотор, конечно, может попробовать покрутиться и с параметрами от соседа, но хорошую работу ему тогда никто не обещал.

Полезные материалы и источники

  1. Предыдущие работы

  2. Реализации VESC и MESC FOC

    • VESC Firmware — Benjamin Vedder — в частности, motor/mcpwm_foc.c и motor/foc_math.c.

    • MESC Firmware — David Molony — источник происхождения наблюдателя MXLEMMING. Специальное уведомление об авторстве сохранено в исходном коде.

  3. Аппаратная платформа и документация

  4. Математические блоки и лицензирование

Ссылки на проект

Конфигуратор позволяет подключаться к плате через USB–UART с параметрами 115200 бод.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.