Разбираемся с Odrive

August 20, 2021, 1:20 am
blog-header-image

Сегодня я поведаю вам про работу с таким контроллером BLDC/PMSM моторов как Odrive. По данной теме практически отсутствует материал в рускоязычном интеренете, поэтому я спешу это исправить.

Я рассмотрю работу с данным драйвером на примере моего двигателя - Leadshine ACM601V36-T-2500.

motor

Так-же в сети можно найти его характеристики. Далее нас будут интересовать в основном численные параметры двигателя для настройки драйвера:

motor_param

Либо их можно посмотреть на стикере наклеенном на мотор:

motor_sticker

Суть взаимодействия с 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 и среда будет подсказавать возможные завершения команд, перемещатся по ним можно с помощью стрелок:

odrv_console

Так-же в ходе работы могут возникнуть ошибки, они работают по как фоаги - если произошло какоето событие, например привышение лимита по току, то установится специальный флаг и система не будет работать пока он не будет сброшен (перед сбросом флагов необходимо устранить причину возникновения). Для этого есть 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) Калибровка мотора.

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

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

Создатели одрайва предлагают следующий алгоритм:

  1. Выставите vel_integrator_gain в 0.
  2. Добейтесь стабильности системы (отсутствия вибраций), уменьшая все уселители (pos_gain, vel_gain), лучше уменьшить с запасом.
  3. Увеличивайте vel_gain на 30% за раз до появляния вибраций.
  4. Снизте vel_gain на 50% от значения полученного на прошлом этапе.
  5. Увеличте pos_gain до появления перерегулирования или мотор будет падать с ошибкой MOTOR_ERROR_CONTROL_DEADLINE_MISSED. Чтобы её сбросить перезапустите контроллер.
  6. Уменьшете pos_gain до того момента пока переригулирование не пропадет.
  7. Далее подбираем vel_integrator_gain так чтобы избавится от статической ошибки.
  8. После получения рабочей системы можно незначительно подправить все каэффициэнты для получения лучших характеристик.

Данные коэффициэнты очень зависят от лимита по скорости, поэтому если вы хотите получить более резкую реакцию системы его необходимо увеличить. Это позволит повысить 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

Подробнее про режимы тут.

Previous Post