September 2, 2018

Работа с CAM-профилями

Есть ли возможность корректировать CAM-профиль (таблицу кулачкового взаимодействия) на лету? Например, когда ПЛК-программа подставляет новые точки в профиль или изменяет уже имеющийся профиль прямо в процессе отработки.

Да, можно. В процессе отработки профиля, можно изменять не только координаты точек траектории, но и скорость, ускорение и рывок (jerk). Подставлять новые или удалять уже имеющиеся точки — нельзя, но можно исключить имеющиеся точки из траектории. В дальнейшем система будет игнорировать их, что будет выглядеть, как будто мы их удалили.
MC_MotionFunctionPoint .PointType : MC_MotionPointType := MOTIONPOINTTYPE_IGNORE
По мере необходимости, скрытые точки можно реактивировать заново, что можно расценивать, как добавление точек в траекторию. Необходимо заранее предусмотреть максимально необходимое число точек.

А что, если в ПЛК уже сидит какая-то сконфигурированная CAM-таблица, но мы хотим иметь возможность корректировать ее, то есть изменять профиль, причем, не залезая в конфигуратор и не реактивируя конфиг? Например, необходимо организовать полный доступ к CAM-таблице через интерфейс пользователя.

Новые профили (CAM-таблицы) можно создавать из ПЛК-программы в любое время, в том числе и во время исполнения текущей CAM-таблицы. Можно создавать таблицы в редакторе профилей, встраивать их как образцы в конфигурацию, а затем в рантайме редактировать их из ПЛК-программы. Также можно "на лету" переключаться между профилями/таблицами или запускать их друг за другом, выстраивая сложную траекторию из отдельных кусков простых.

Вообще, система не различает таблицы созданные в редакторе и созданные вручную из ПЛК-программы. Система различает таблицы по их номеру CamTableID : MC_CAM_ID. По сути, этот номер просто синоним для числа типа UDINT, который отражает номер таблицы среди всех доступных таблиц. Системе всё равно, где была создана таблица.

TYPE
    MC_CAM_ID : UDINT;
END_TYPE


Возникает вопрос, а нужен ли вообще редактор Cam Design Tools или можно как-нить обойтись и сэкономить? Как-нить можно, но ругают его в основном за стоимость.

Редактор крут и наворочен. Позволяет одним махом нарисовать и отредактировать не только кривую траекторию, но и задать скорости, ускорения, рывки в узловых точках, по сути доступен полный набор производных движения. Траекторию можно сглаживать всякими полиномами пятой степени и загружать их одним кликом, вместе с остальным проектом. Стоит это все 4,5К евро, что для многих ставит жирный крест на рисовании траекторий кулачковых механизмов, отправляя их в пешее путешествии за таблицами в Экселе.

Если нет лицензии на редактор, то таблицы из CAM-дезигнера будут доступны только до первого закрытия студии или PLC Control'а. После повторного открытия конфигурации или проекта, этих таблиц в проекте уже не будет — они пропадут. Аналогично, без лицензии эти таблицы не захотят выгружаться в контроллер. Рисовать можно, грабить караваны координат и тащить их в казематы Экселя — можно, сохранять проект для потомков или в ПЛК-задачу — нельзя.

Подытожим, если супер редактор — это дорого, то смотрите на создание таблиц вручную из ПЛК-задачи. Вся библиотека (и дизайнер) хорошо описаны в документации TwinCAT MC Camming. Поэтому дальше будем вытаскивать только нюансы.


Создание CAM-таблиц


Если вы создаете CAM-таблицу вручную, то вам необходима функция MC_CamTableSelect. Данные, предоставленные вами в функцию, передаются в NC, где создается новая CAM-таблица. Эта функция не нужна, если вы создали таблицу в дизайнере: можно сразу переходить к MC_CamIn.

Чего нет в документации, так это фразы, что функция CamTableSelect по сути создает новую таблицу. Если же таблица с таким номером уже существует, то старая таблица будет удалена и на ее месте создана новая. Вот кусок, отвечающий за этот процесс (находится в библиотеке TcMC2_Camming). Для удобства, я выделил ключевые слова:

(* delete old table (possibly existing)*)
STATE_INTERNAL_DELETE :
    fbAdsWrite( NETID   := TcMcGlobal.NCNETID_TCMC_CAM,
                PORT    := TcMcGlobal.NCPORT_TCNCCAMMING_TABLEFUNCTION,
                IDXGRP  := TcMcGlobal.Table.Functions.IDXGRP + CamTableID,
                IDXOFFS := TcMcGlobal.Table.Functions.IDXOFFS.DELETETAB,
                LEN     := 0,
                SRCADDR := 0,
                WRITE   := TRUE,
                TMOUT   := TcMcGlobal.tADSTimeOut);

    IF NOT fbAdsWrite.BUSY THEN
        IF NOT fbAdsWrite.ERR THEN
            (*next step*)
            iStateInternal := STATE_INTERNAL_CREATE;
        ELSE

Видно, что библиотека пытается сначала удалить таблицу с заданным CamTableID и только затем переходит к созданию новой таблицы. Точки из старой таблицы пропадают в никуда.


Активация профиля и наоборот


MC_CamIn — занимается сцеплением подчиненной оси с мастер-осью, а прослойкой между ними служит таблица с индексом CamTableID. Можно рассматривать эту таблицу как электронный редуктор с программным коэффициентом передачи, который по ходу работы будет извлекаться из данной таблицы.

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

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

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


Последовательность выполнения функций


Для примера, простая последовательность действий выглядит как-то так:
  1. MC_CamTableSelect( CamTableID
  2. MC_SetCamOnlineChangeMode
  3. MC_Power (TRUE, Master
  4. MC_Power (TRUE, Slave
  5. MC_CamIn
  6. MC_MoveVelocity( Master
  7. ...технологический процесс
  8. MC_CamOut
  9. MC_Halt( Master
  10. MC_Halt( Slave
  11. MC_Power (FALSE, Master
  12. MC_Power (FALSE, Slave


Изменение параметров движения


Прежде, чем изменять параметры движения, то есть вносить изменения в CAM-таблицу, нужно договориться с системой — как и когда вы будете это делать. Для переговоров используется функция MC_SetCamOnlineChangeMode. Достаточно выполнить ее один раз с необходимым набором параметров. Например, задать ActivationMode = MC_CAMACTIVATION_ASSOONASPOSSIBLE и данные будут применяться на лету, как можно быстрее и в самый безопасный момент движения.

Перед изменением можно прочитать существующий параметры (если они уже загружены) с помощью функции MC_ReadMotionFunction. Функция прочитает все данные таблицы, либо только заданное количество, а если вы зададите больше, чем есть на самом деле, то не страшно — функция прочитает только то, что будет доступно, ошибок не будет.

Для доступа к данным отдельной конкретной точки, используется функция MC_ReadMotionFunctionPoint. Данные содержат не только координаты, но и много другой полезной информации. Начнем с PointIndex — это порядковый номер (индекс) точки в траектории. Индексы положительны, начинаются с 1 и далее идут без разрывов, с единичным шагом: 1, 2, 3, 4, 5, ... и никаких нулей для стартового индекса.

Индексы последовательны, но порядок обхода точек — произвольный. За это отвечает поле RelIndexNextPoint — это относительный индекс следующей вычисляемой точки траектории. По умолчанию, он равен 1, что означает — выполнять по порядку следования точек. Для последней точки в траектории относительный индекс = 0. Изменять относительный индекс "на лету" нельзя, так как этот параметр важен для предварительного обсчета траектории.

И последнее, но не менее важное поле, FunctionType. Оно задает функцию движения для формирования траектории и расчета промежуточных точек. По умолчанию, используется MOTIONFUNCTYPE_POLYNOM5_MM — гладкая траектория с интерполяцией полиномами 5-й степени. В дизайнере этому соответствует функция Automatic. Типу же Synchron соответствуют сплайны первой степени MOTIONFUNCTYPE_POLYNOM1.

Учитывайте, что при использовании MOTIONFUNCTYPE_POLYNOM5_MM (или Automatic) необходимы как минимум три активные точки в куске траектории, то есть нужна сама точка и по одной точке с каждой стороны от нее. Итого, три точки в куске. Точка считается активной, если она не игнорируется PointType <> MOTIONPOINTTYPE_IGNORE. Это важно, если вы используете механизм, описанный в самом начале поста.


Практикум


Я создал замкнутый, повторяющийся каждые 360° профиль. Для простоты, траектория разбита пятью точками на четыре кусочно-линейные интервала движения. Точка №5 совпадает с точкой №1. Не забывайте, вы ставите точки, а траекторию между ними рассчитывает система.



На самом деле, я сначала нарисовал график в дизайнере, а затем вручную перетащил данные в ПЛК-программу (см. Приложение ниже). В итоге, для подчиненной оси получился следующий график:


До позиции 21000 подчиненный отрабатывает заданный в CAM-таблице график. Я отрезал на картинке "пики" позиций, чтобы не загромождать иллюстрацию.

После 21000 я программно, с помощью MC_WriteMotionFunctionPoint, ставлю в игнор точку 2: следите как изменился график от 21000 до 23500. Начиная с 23500 (вторая зеленая линия), я ставлю в игнор точку 3: с 23500 по 25000 работают только точки 1, 4, 5.
Учтите, что непосредственно точки я не удаляю, а всего-лишь изменяю их тип на PointType := MOTIONPOINTTYPE_IGNORE.
После позиции 25000 я возвращаю сначала точку 2, а где-то ближе к 28000 и точку 3. Затем все повторяется. Прогоните в голове эту последовательность несколько раз и всё встанет на свои места.


Приложение


PROGRAM MAIN
VAR
    AxMaster         : AXIS_REF;
    AxSlave          : AXIS_REF;
    camTable2_Id     : MC_CAM_ID := 2;
    camTable2        : MC_CAM_REF;
    camTable2_Points : ARRAY[1..10000] OF MC_MotionFunctionPoint;
    
[...]

camTable2.ArraySize   := SIZEOF(camTable2_Points);
camTable2.pArray      := ADR(camTable2_Points);
camTable2.TableType   := MC_TABLETYPE_MOTIONFUNCTION;
camTable2.NoOfColumns := 1;
camTable2.NoOfRows    := pointsNumber;

FOR i := 1 TO PointsNumber DO
    camTable2_Points[i].PointIndex := i;
END_FOR

camTable2_Points[1].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[1].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; // 5_MM;
camTable2_Points[1].RelIndexNextPoint := 1;
camTable2_Points[1].MasterPos         := 0.0;
camTable2_Points[1].SlavePos          := 0.0;

camTable2_Points[2].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[2].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[2].RelIndexNextPoint := 1;
camTable2_Points[2].MasterPos         := 100.0;
camTable2_Points[2].SlavePos          := 10.0;

camTable2_Points[3].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[3].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[3].RelIndexNextPoint := 1;
camTable2_Points[3].MasterPos         := 150.0;
camTable2_Points[3].SlavePos          := 100.0;

camTable2_Points[4].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[4].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[4].RelIndexNextPoint := 1;
camTable2_Points[4].MasterPos         := 250.0;
camTable2_Points[4].SlavePos          := 10.0;

camTable2_Points[5].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[5].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[5].RelIndexNextPoint := 0; // последняя точка в траектории
camTable2_Points[5].MasterPos         := 360.0;
camTable2_Points[5].SlavePos          := 0.0;


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