Сегодня я поведаю вам про работу с таким контроллером BLDC/PMSM моторов как Odrive. По данной теме практически отсутствует материал в русскоязычном интернете, поэтому я спешу это исправить.
Я рассмотрю работу с данным драйвером на примере моего двигателя - Leadshine ACM601V36-T-2500.
Также в сети можно найти его характеристики. Далее нас будут интересовать в основном численные параметры двигателя для настройки драйвера:
Либо их можно посмотреть на стикере наклеенном на мотор:
Суть взаимодействия с Odrive заключается в том что мы последовательно задаем параметры в python приложении подключив драйвер к пк, тем самым настраивая драйвер для работы с конкретным двигателем. После завершения настройки, останется лишь подать параметры которые должен будет отработать мотор в зависимости от выбранного типа управления. Для задания управляющего сигнала доступны разные интерфейсы - CAN, UART, либо их можно задавать с ПК используя python. Далее рассмотрим последовательность действий необходимую для работы с выбранным двигателем.
Документацию на английском можно найти тут. Я же опишу свою последовательность действий слегка отличную от предлагаемой авторами.
Установка необходимого софта
Для управления данным драйвером существует библиотека на языке пайтон, которая позволяет управлять моторами в командном режиме. Ее использование представляет из себя поочередный ввод и исполнение команд и интерпретаторе Python 3
. Для ее установки нужно сделать следующее, установить питон с pip3
если их нет в системе:
sudo apt install python3 python3-pip
Далее доставим нужные библиотеки:
sudo pip3 install --upgrade odrive
По идее после этого правила udev должны прописаться автоматически, но если этого не произошло нужно сделать следующее:
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d[0-9][0-9]", MODE="0666"' | sudo tee /etc/udev/rules.d/91-odrive.rules
sudo udevadm control --reload-rules
sudo udevadm trigger
При вводе команды можно использовать кнопку TAB
и среда будет подсказывать возможные завершения команд, перемещаться по ним можно с помощью стрелок:
Так-же в ходе работы могут возникнуть ошибки, они работают по как флаги - если произошло какое-то событие, например превышение лимита по току, то установится специальный флаг и система не будет работать пока он не будет сброшен (перед сбросом флагов необходимо устранить причину возникновения). Для этого есть 2 команды:
dump_errors(odrv0)
odrv0.clear_errors()
Тут я столкнулся с проблемой, на одном компьютере odrivetool
падал с кучей ошибок при нажатии на табуляцию, для решения этой проблемы необходимо до установить пакет который необходим для работы подсказок:
pip3 install jedi==0.17.2
Подключение мотора
Далее необходимо подключить драйвер к питанию для моторов и компьютеру. Тут есть очень важный момент, нужно сначала подключить двигатель к плате и плату к блоку питания, и только ПОТОМ включать блок питания. Если сделать наоборот то включенный блок питания очень быстро передаст энергию с выходных конденсаторов на паразитные емкости в драйвере и драйвер сгорит. Если же включать блок после соединения всей схемы то плавно нарастающее напряжение на блоке питания, будет также плавно заряжать емкости в самом драйвере, и бросков тока не будет.
Если очень нужно включить сначала блок питания и только потом сам драйвер, то между
После подключения драйвера по USB к компьютеру (рекомендуется подключать через гальваническую развязку) запускаем приложение для управления двигателем:
odrivetool
После чего, для теста корректности работы, запросим напряжение на драйвере:
In [1]: odrv0.vbus_voltage
Out[1]: 47.85161209106445
После чего получаем напряжение на БП. Если все так, отлично, идем дальше.
После этого можно сбросить параметры к заводским, если драйвер использовался кем-то до вас. Делается это командой - odrv0.erase_configuration()
.
Далее необходимо пояснение. Выбранный мотор подключен к портам M0, поэтому все параметры будут забиваться для объекта odrv0.axis0
, но все эти действия справедливы и для мотора под номером 1.
Настройка лимитов.
Первым делом необходимо установить лимиты, для того чтобы система не вышла из строя.
1) Лимиты по току.
odrv0.axis0.motor.config.current_lim = 5 # [A].
Их можно узнать из документации на мотор, что представленны выше, или равнятся лимиту блока питания.
2) Максимальная скорость.
odrv0.axis0.controller.config.vel_limit = 50 # [оборот / с].
Тут нужно смотреть по документации и из соображения безопасности. Если вдруг скорость двигателя привысит это значение то двигатель автоматически будет отключен, с ошибкой ERROR_OVERSPEED
(ошибка контроллера).
3) Колибровачный ток.
odrv0.axis0.motor.config.calibration_current = 2 # [A]
По идее должен быть меньше чем максимальный ток. Если поставить слишком большой то мотор покажет ошибку ERROR_PHASE_RESISTANCE_OUT_OF_RANGE
, тоесть контроллер не сможет корректно померить характеристики обмоток.
Задание физических характеристик движка
1) Задание тормозного резистора.
odrv0.config.enable_brake_resistor = True
odrv0.config.brake_resistance = 2 # [Ом]
В принципе можно запускать контроллер и без тормозного резистора, но это крайне не советуется в случае работы от блока питания. Идея тут в следующем: при торможении двигателя образуется электрическая энергия, которую нужно куда-то девать. Если мы питаем мотор и драйвер от аккумулятора, то эта энергия вернется в аккумулятор, но если мы писаемся от блока питания, то он не сможет её усвоить, что приведет к поломке. Потому данную энергию можно рассеять на резисторе, чье сопротивление мы и указываем. Если вы не подключили тормозной резистор, то в параметре odrv0.config.dc_max_negative_current
вы сможете указать максимальное значение тока который может поглотить ваш БП (указывается во Float со знаком "-"). Превышение этого значения вызовет ошибку ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT
т.е. нужно настроить систему так, чтобы обратный ток никогда не превышал данное значение. Также это функцию можно отключить если вместо float указать -INFINITY
.
Далее необходимо сохранить параметры контроллера иначе включение резистора не применится:
odrv0.save_configuration()
2) Количество пар полюсов.
odrv0.axis0.motor.config.pole_pairs = 4
В данном параметре необходимо указать количество полюсов магнита в роторе, деленное на два. Для определения их количества можно подключить одну из фаз к БП с ограничением по току на 1-2А, и посчитать сколько раз ротор будет "фиксироваться" в процессе прокручивания его вами на один оборот. Это и будет количество полюсов.
3) Крутящий момент двигателя.
odrv0.axis0.motor.config.torque_constant = 0.0712
Это соотношение крутящего момента двигателя, на ампер тока, подаваемого на двигатель. Это 8.27 / (motor KV) или значение Torque Const(N.m/A) из документации к движку.
4) Тип двигателя.
odrv0.axis0.motor.config.motor_type=MOTOR_TYPE_HIGH_CURRENT
Здесь возможно 2 варианта:
MOTOR_TYPE_HIGH_CURRENT
если обмотки стартера находятся снаружи от ротора с постоянными магнитами.MOTOR_TYPE_GIMBAL
если ротор с постоянными магнитами находится с наружи стартера.
5) Калибровка мотора.
Далее необходимо произвести калибровку мотора чтобы он померил недостающее параметры. При этом вал должен быть ничем не нагружен и свободно вращаться.
odrv0.axis0.requested_state = AXIS_STATE_MOTOR_CALIBRATION
После чего мотор должен издать писк продолжительностью примерно в секунду. Если этого не произойдет, то можно попробовать изменить параметры, например уменьшить калибровочный ток. Также проверить успешность калибровки можно при помощи команды считывания ошибок - dump_errors(odrv0)
:
In [25]: dump_errors(odrv0)
system: not found
axis0
axis: no error
motor: no error
sensorless_estimator: no error
encoder: no error
controller: no error
axis1
axis: no error
motor: no error
sensorless_estimator: no error
encoder: no error
controller: no error
Если ее вывод такой же как выше, значит ошибок нет. После этого можно сохранить калибровку и параметры:
odrv0.axis0.motor.config.pre_calibrated = True
odrv0.save_configuration()
odrv0.reboot()
Энкодер
После настройки параметров двигателя можно приступить к настройкам энкодра. Тут возможны разные варианты, в качестве обратной связи по положению может использоваться не только непосредственно энкодер, но еще датчики холла, SPI магнитный сенсор или вообще обойтись без него. В данной статье мы рассмотрим работу с энкоденом, включающим в себя индексный (Z) сигнал.
1) Разрешение энкодера
odrv0.axis0.encoder.config.cpr = 10000 # Счетчик энкодера на оборот [CPR]
В данную переменную нужно написать CRP
энкодера или PPR * 4
. Это можно глянуть в характеристиках энкодера, или мотора.
2) Включаем использование индекса:
odrv0.axis0.encoder.config.use_index = True
3) Делаем поиск индекса и после ищем разницу между полюспми мотора и энкодером. После вкдючения отладки мотор сделает несколько оборотов так что вал должен иметь возможность свободно вращатся.
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
4) Далее проверяем оштбки. Если они будут, занчить ошибки в параметрах, также значение ошибок можно посмотреть тут.
dump_errors(odrv0)
5) После этого сохраняем калибровку и включаем поск индекса при старте. К сожалению для работы драйвера при каждом старте он будет искать индекс, работать без этого не будет.
odrv0.axis0.encoder.config.pre_calibrated = True
odrv0.axis0.config.startup_encoder_index_search = True
odrv0.axis0.motor.config.pre_calibrated = True
6) Сохраняем конфигурацию.
odrv0.save_configuration()
7) Для того что-бы узнать значения какого-то парметра достатчно просто ввести его название в консоль:
In [12]: odrv0.axis0.encoder.config.cpr
Out[12]: 10000
Включение мотора
Для начала попробуем включить замкнутый контур (по умолчанию замкнется по положению) и посмотрим как будет работать двигатель. Для этого нужно включить следующее:
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
Для того чтобы его разорвать (выключить мотор, в случае если он ведет себя неожиданным образом) нужно использовать следующую команду:
odrv0.axis0.requested_state = AXIS_STATE_IDLE
По идее после этого двигатель должен поддерживать свое положение, однако почти никогда так не бывает. Данный двигатель начал сильно вибрировать. С другим мотором была другая ситуация - если отклонить вал то он не будет сопротивляться, но через некоторое время попробует вернутся назад, но с сильным пере регулированием. Система будет идти в разнос до тех пор, пока не появится ошибка MOTOR_ERROR_CONTROL_DEADLINE_MISSED
. В вашем случае поведение может выглядеть иначе, но в любом случае оно не адекватно. Для того чтобы это исправить нужно подобрать коэффициенты регуляторов.
Настройка пидов осуществляется с помощью следующих 3х параметров:
odrv0.axis0.controller.config.pos_gain = 20 #[(turn/s) / turn]
odrv0.axis0.controller.config.vel_gain = 0.01 #[Nm/(turn/s)]
odrv0.axis0.controller.config.vel_integrator_gain = 1 #[Nm/((turn/s) * s)]
Для настройке управления по положению необходимо использовать плоттер, чтобы увидеть пере регулирование:
start_liveplotter(lambda:[odrv0.axis0.encoder.pos_estimate, odrv0.axis0.controller.pos_setpoint])
Для выставления позиции можно использовать следующую команду, также перед настройкой необходимо включить замкнутый контур управления:
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis0.controller.input_pos = 1
Создатели одрайва предлагают следующий алгоритм:
- Выставите
vel_integrator_gain
в 0. - Добейтесь стабильности системы (отсутствия вибраций), уменьшая все усилители (
pos_gain
,vel_gain
), лучше уменьшить с запасом. - Увеличивайте
vel_gain
на 30% за раз до появления вибраций. - Понижайте
vel_gain
на 50% от значения полученного на прошлом этапе. - Увеличивайте
pos_gain
до появления пере регулирования или мотор будет падать с ошибкойMOTOR_ERROR_CONTROL_DEADLINE_MISSED
. Чтобы её сбросить перезапустите контроллер. - Уменьшите
pos_gain
до того момента пока пере регулирование не пропадет. - Далее подбираем
vel_integrator_gain
так чтобы избавится от статической ошибки. - После получения рабочей системы можно незначительно подправить все коэффициенты для получения лучших характеристик.
Данные коэффициенты очень зависят от лимита по скорости, поэтому если вы хотите получить более резкую реакцию системы его необходимо увеличить. Это позволит повысить pos_gain
без появления ошибки MOTOR_ERROR_CONTROL_DEADLINE_MISSED
(на самом деле это ERROR_OVERSPEED
).
odrv0.axis0.controller.config.vel_limit = 20
После чего мы можем задать положение для управления по положению:
odrv0.axis0.controller.input_pos = 1
Другие способы управления и сохранение состояния.
Если ваша цель это управление по положению, то все что осталось сделать это включение Замкнутого контура сразу после включения контроллера после поиска индекса:
odrv0.axis0.config.startup_closed_loop_control = True
odrv0.save_configuration()
odrv0.reboot()
После этого можно будет задавать положение сразу после перезагрузки контроллера. Также положение (как и другие параметры) можно будет задавать через другие интерфейсы, например через юарт. Для этого цепляемся юартом за пины 1 -> TX и 2 -> RX, ставим скорость 115200, после чего задаем положение следующей строкой:
w axis0.controller.input_pos 1
Таким образом можно записывать (через w) и читать (через r) любые параметры доступные через python оболочку. Также доступны короткие версии этих команд посмотреть их можно тут.
Для того чтобы перейти в другой режим работы, например управление по скорости, необходимо сделать следующее:
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
После чего можно задавать скорость через команду:
odrv0.axis0.controller.input_vel = 10
Или для управления моментом:
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL
odrv0.axis0.controller.input_torque = 0.1
Подробнее про режимы тут.