LinuxCNC Documentation

1. Вступ

Ряд кінематичних модулів підтримують перемикання кінематичних розрахунків. Ці модулі підтримують стандартний кінематичний метод (тип 0), другий вбудований метод (тип 1) і (опціонально) кінематичний метод, наданий користувачем (тип 2). Для методу типу 1 зазвичай використовується ідентична кінематика.

Функціональність switchkins може використовуватися для верстатів, де під час налаштування необхідне управління суглобами після повернення в початкове положення або для уникнення руху поблизу сингулярностей з G-коду. Такі верстати використовують специфічні кінематичні розрахунки для більшості операцій, але можуть перемикатися на ідентичну кінематику для управління окремими суглобами після повернення в початкове положення.

The kinematics type is selected with G12.1 P- and G13.1, from a G-code program or by interactive MDI commands. Buttons on a virtual panel (PyVCP, GladeVCP, etc.) or on hardware controls select a kinematics type through the halui provisions for activating MDI commands.

Changing the kinematics type requires the interpreter and motion parts of LinuxCNC to be synchronized, which G12.1 and G13.1 do themselves.

A deprecated HAL pin, motion.switchkins-type, selects a kinematics type as well. It is described under Usage below, because existing configurations use it.

2. Перемикані кінематичні модулі

Наступні кінематичні модулі підтримують перемикану кінематику:

  1. xyzac-trt-kins (type0:xyzac-trt-kins type1:identity)

  2. xyzbc-trt-kins (type0:xyzbc-trt-kins type1:identity)

  3. genhexkins (type0:genhexkins type1:identity)

  4. genserkins (type0:genserkins type1:identity) (puma560 example)

  5. pumakins (type0:pumakins type1:identity)

  6. three21kins (type0:three21kins type1:identity)

  7. scarakins (type0:scarakins type1:identity)

  8. 5axiskins (type0:5axiskins type1:identity) (bridgemill)

  9. millturn (type0:identity type1:turn)

  10. xyzab_tdr_kins (type0:identity type1:tcp)

  11. xyzacb_trsrn (type0:identity type1:tcp type2:tool)

  12. xyzbca_trsrn (type0:identity type1:tcp type2:tool)

Every module listed above uses its own kinematics for type0 and identity kinematics for type1. Each accepts the module string parameter sparm to swap the two, so that the machine starts in identity kinematics and the module kinematics are selected on demand:

[KINS]
KINEMATICS = xyzac-trt-kins sparm=identityfirst
# ...

Starting in identity kinematics leaves a way out of poses the module kinematics cannot solve. A module kinematics failure (near a singularity, for instance) reports an error and disables the machine; type0 is then reached without running the failing kinematics again.

Note
The sparm setting exchanges type0 and type1, so any G-code or HAL logic that selects a kinematics type by number must match.

Kinematics that solve the forward direction iteratively, genhexkins among them, need a pose to start from. While another type is running they take the estimate the caller supplies, which motion seeds from the [TRAJ]HOME world home, so they are ready to be switched to.

2.1. Призначення ідентифікаційних листів

Під час використання кінематики типу identity параметр модуля coordinates може бути використаний для призначення літер суглобам у довільному порядку з набору дозволених літер координат. Приклади:

[KINS]
JOINTS = 6

# звичайне впорядкування тотожності: joint0==x, joint1==y, ...
KINEMATICS = genhexkins coordinates=xyzabc

# замовлення індивідуальних ідентифікаційних даних: joint0==c, joint1==b, ...
# KINEMATICS = genhexkins coordinates=cbazyx
Note
Якщо параметр coordinates= пропущено, призначення ідентифікаторів літер за замовчуванням буде joint0==x,joint1=y,…​

Спільні завдання, передбачені для кінематики identity при використанні параметра координат, ідентичні тим, що передбачені для модуля trivkins. Однак дублювання літер осей для призначення декількох з’єднань для літери координати, як правило, не застосовується для послідовної або паралельної кінематики (такої як genserkins, pumakins, genhexkins тощо), де немає простого взаємозв’язку між з’єднаннями та координатами.

Дублювання літер координат осей підтримується в кінематичних модулях xyzac-trt-kins, xyzbc-trt-kins та 5axiskins (bridgemill). Типовими застосуваннями для дублювання координат є портальні верстати, де два двигуни (з’єднання) використовуються для поперечної осі.

2.2. Зворотна сумісність

Перемикається кінематика ініціалізується з motion.switchkins-type==0, реалізуючи однойменний метод кінематики. Якщо контакт motion.switchkins-type не підключений, як у старих конфігураціях, доступний тільки тип кінематики за замовчуванням.

3. Піни HAL

Перемикання кінематики контролюється вхідним контактом HAL модуля руху motion.switchkins-type. Значення контакту з плаваючою комою обрізається до цілого числа і використовується для вибору одного з наданих типів кінематики. Нульове значення запуску вибирає тип кінематики за замовчуванням type0.

Note
Вхідний контакт motion.switchkins-type є плаваючим, щоб полегшити підключення до вихідних контактів модуля руху, таких як motion.analog-out-0n, які можна керувати за допомогою стандартних M-кодів (зазвичай M68EnL0).

Вихідні контакти HAL призначені для інформування графічних інтерфейсів користувача про поточний тип кінематики. Ці контакти також можуть бути підключені до цифрових входів, які зчитуються програмами G-коду, щоб увімкнути або вимкнути поведінку програми відповідно до активного типу кінематики.

3.1. Зведення про піни HAL

  1. motion.switchkins-type Input (real)

  2. motion.kins-type Output (real)

  3. kinstype.is-0 Output (bool)

  4. kinstype.is-1 Output (bool)

  5. kinstype.is-2 Output (bool)

A module providing more than three kinematics types has one kinstype.is-N pin per type.

4. Застосування

4.1. З’єднання HAL

G12.1 and G13.1 ask motion for a kinstype directly and need no HAL connection at all.

A kinstype can also be selected by writing the pin motion.switchkins-type, which is sourced from an analog output pin like motion.analog-out-03 so that it can be set by M68 commands:

net :kinstype-select <= motion.analog-out-03
net :kinstype-select => motion.switchkins-type
Warning
Selecting the kinstype from HAL is deprecated and motion says so, once, the first time the pin is used to change it. The interpreter does not see the pin, so a program is read, its limits checked and its path looked ahead in whatever kinematics the interpreter last knew about, which is not necessarily the one that will run it. Use G12.1 and G13.1. The pin is in a grace period: it keeps working for now, but is meant to be removed in the future.

4.2. G-code commands

G12.1 P- selects a kinstype and G13.1 cancels back to identity kinematics. Which kinstype that is, the module declares (see Code Notes), not fixed to a number; on a module whose slides do not line up with its frame it is the machine frame type the module declares instead:

...
G12.1 P1  ;select kinstype 1
...
...       ;user G-code
...
G13.1     ;back to identity kinematics
...

These codes ask motion for the kinstype directly and synchronize task and motion themselves, so no HAL connection and no separate sync command are needed. The G-code words and the motion.switchkins-type pin are both acted on when they change, so whichever asked most recently is the one in force. motion.kins-type reports what is currently selected.

The pin is deprecated, see the warning under HAL Connections.

The kinstype in force is readable in G-code as #<_kins_type>, which lets a subroutine restore whatever its caller had selected:

#<saved> = #<_kins_type>
G12.1 P2
( ... )
G12.1 P#<saved>

Selection is not cancelled by the end of a program or by an abort, so that the kinstype continues to match the position readout. A program that should leave the machine in identity kinematics ends with G13.1.

A module that declares no identity or machine frame kinstype refuses G13.1 with an error and can still be driven by number with G12.1; see Code Notes for how a module declares its types.

For tool length work there are spellings that name the kinematics by what it is rather than by number: G43.4 applies the tool length offset and switches to the kinstype the module declares its working transform, and the G49 that cancels it switches back to identity. See the G-code documentation for G43.4 and G49, and for G12.1 and G13.1, for the full description.

4.3. M-code commands

Warning
This is the deprecated route described under HAL Connections above. It is documented because existing configurations use it. New ones should use G12.1 and G13.1.

Writing motion.switchkins-type through an analog output pin needs the HAL connection shown above. Kinstype selection is then managed using G-code sequences like:

...
M68 E3 Q1 ;оновлення analog-out-03 для вибору kinstype 1
M66 E0 L0 ;синхронізація interp-motion...
...
       ;G-код користувача...

M68 E3 Q0 ;оновлення analog-out-03 для вибору kinstype 0
M66 E0 L0 ;синхронізація interp-motion
...
Note
Команда M66 «wait-on-input» оновлює змінну #5399. Якщо поточне значення цієї змінної потрібне для подальших цілей, його слід скопіювати до додаткової змінної перед викликом M66.

Ці послідовності команд G-коду зазвичай реалізуються в підпрограмах G-коду як перепризначені M-коди або за допомогою звичайних скриптів M-коду.

Рекомендовані коди (як використовуються в конфігураціях SIM-карти):

Звичайні M-коди користувача:

  1. M128 Виберіть тип кінематики 0 (запуск за замовчуванням)

  2. M129 Виберіть тип кінематики 1 (зазвичай ідентична кінематика)

  3. M130 Виберіть тип кінематики 2 (кінематика, задана користувачем)

Перепризначені M-коди:

  1. M428 Виберіть кінематику 0 (кінематика запуску за замовчуванням)

  2. M429 Виберіть тип кінематики 1 (зазвичай ідентична кінематика)

  3. M430 Виберіть тип кінематики 2 (кінематика, задана користувачем)

Note
Звичайні M-коди користувача (в діапазоні M100-M199) знаходяться в модальній групі 10. Перепризначені M-коди (в діапазоні M200-M999) можуть вказувати модальну групу. Додаткову інформацію див. в документації щодо перепризначення.

4.4. Налаштування обмежень для INI-файлів

Планування траєкторії LinuxCNC використовує обмеження для положення (min, max), швидкості та прискорення для кожної відповідної літери координати, зазначеної в конфігураційному файлі INI. Приклад для літери L (у наборі «XYZABCUVW»):

[AXIS_L]
MIN_LIMIT =
MAX_LIMIT =
MAX_VELOCITY =
MIN_ACCELERATION =

Вказані обмеження файлу INI застосовуються до типу кінематики за замовчуванням типу 0, який активується під час запуску. Ці обмеження можуть не застосовуватися при переході на альтернативну кінематику. Однак, оскільки при переході на іншу кінематику необхідна синхронізація інтерпретатора та руху, для налаштування обмежень для очікуваного типу кінематики можна використовувати контакти INI-HAL.

Note
Виводи INI-HAL зазвичай не розпізнаються під час виконання G-коду, якщо не видано команду синхронізації (вимкнення черги). Див. сторінку довідки milltask для отримання додаткової інформації ($ man milltask).

Відповідні контакти INI-HAL для номера з’єднання (N):

ini.N.min_limit
ini.N.max_limit
ini.N.max_acceleration
ini.N.max_velocity

Відповідні контакти INI-HAL для координати осі (L):

ini.L.min_limit
ini.L.max_limit
ini.L.max_velocity
ini.L.max_acceleration
Note
Загалом, між номерами суглобів і літерами координат осей немає фіксованих відповідників. Для деяких кінематичних модулів, особливо тих, що реалізують ідентичну кінематику (trivkins), можуть існувати специфічні відповідники. Докладнішу інформацію див. на сторінці довідки kins ($ man kins).

M-код, наданий користувачем, може змінювати будь-які або всі межі координат осей перед зміною руху.switchkins-type pin і синхронізацією інтерпретатора та частин руху LinuxCNC. Наприклад, скрипт bash, що викликає halcmd, може бути «жорстко закодований» для встановлення будь-якої кількості контактів HAL:

#!/bin/bash
halcmd -f <<EOF
setp ini.x.min_limit -100
setp ini.x.max_limit  100
# ... повторіть для інших граничних параметрів
EOF

Такі скрипти можна викликати як M-код користувача і використовувати до M-коду перемикання kins, який оновлює HAL-контакт motion.switchkins-type і примусово виконує синхронізацію interp-motion. Зазвичай для кожного типу kinstype (0,1,2) використовуються окремі скрипти.

Коли кінематика ідентичності надається як засіб управління окремими шарнірами, може бути зручно встановити або відновити обмеження, як зазначено в системному файлі INI. Наприклад, робот починає роботу зі складною (неідентичною) кінематикою (тип 0) після повернення в початкове положення. Система налаштована таким чином, що її можна переключити на ідентичну кінематику (тип 1) для маніпулювання окремими суглобами за допомогою звичайних літер з набору «XYZABCUVW». Налаштування файлу INI ([AXIS_L]) не застосовуються при роботі з ідентичною кінематикою (тип 1). Для вирішення цього випадку використання скрипти M-коду користувача можна розробити наступним чином:

M129 (Switch to identity type1)

  1. читати та аналізувати INI-файл

  2. HAL: встановити граничні контакти INI-HAL для кожної літери осі ([AXIS_L]) відповідно до налаштування INI-файлу номера з’єднання, на яке посилаються з ідентифікатором ([JOINT_N])

  3. HAL: setp motion.switchkins-type 1

  4. MDI: виконати синхронізуючий G-код (M66E0L0)

M128 (restore robot default kinematics type 0)

  1. читати та аналізувати INI-файл

  2. HAL: встановити граничні контакти INI-HAL для кожної літери осі ([AXIS_L]) згідно з відповідним налаштуванням INI-файлу ([AXIS_L])

  3. HAL: setp motion.switchkins-type 0

  4. MDI: виконати синхронізуючий G-код (M66E0L0)

Note
Конфігурації симуляції vismach для робота puma демонструють сценарії M-коду (M128, M129, M130) для цього прикладу використання.

4.5. Міркування щодо зміщення системи координат

Як і налаштування обмежень файлу INI, зміщення координатної системи (G92, G10L2, G10L20, G43 тощо) зазвичай застосовуються лише для типу кінематики за замовчуванням 0. При перемиканні типів кінематики може бути важливо або скинути всі зміщення перед перемиканням, або оновити зміщення відповідно до вимог конкретної системи.

4.6. Міркування щодо зовнішнього зміщення

Зовнішні зміщення (встановлені на вісь (L) через axis.L.eoffset-request) зберігаються під час перемикання кінематики. Коли зміщення активне на осі перед перемиканням (видиме в axis.L.eoffset), планувальник траєкторії зберігає те саме зміщення після перемикання, подібно до того, як він зберігає задане положення з G-коду. Це забезпечує стабільну роботу машини незалежно від активної кінематики.

Якщо підтримка зміщення буде проблемою через зміни обмежень осей або інші проблеми, обов’язково очистіть і, можливо, вимкніть eoffset, перш ніж перемикати кінематику.

5. Конфігурації симуляції

Конфігурації симуляції (що не потребують апаратного забезпечення) надаються з ілюстративними дисплеями vismach у підкаталогах configs/sim/axis/vismach/.

  1. 5axis/table-rotary-tilting/xyzac-trt.ini (xyzac-trt-kins)

  2. 5axis/table-rotary-tilting/xyzbc-trt.ini (xyzac-trt-kins)

  3. 5axis/bridgemill/5axis.ini (5axiskins)

  4. scara/scara.ini (scarakins)

  5. puma/puma560.ini (genserkins)

  6. puma/puma.ini (pumakins)

  7. hexapod-sim/hexapod.ini (genhexkins)

  8. millturn/millturn.ini (millturn)

  9. 5axis/table-dual-rotary/xyzab-tdr.ini (xyzab_tdr_kins)

  10. 5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini (xyzacb_trsrn)

  11. 5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini (xyzbca_trsrn)

6. Положення щодо кінематики користувача

There are two ways to supply custom kinematics. Adding a kinstype to a module that is already in the tree is the smaller job; building a module of your own gives you every kinstype it provides.

6.1. Adding a kinstype to an in-tree module

Custom kinematics can be coded and tested on Run-In-Place (RIP) builds. A template file src/emc/kinematics/userkfuncs.c is provided in the distribution. This file can be copied/renamed to a user directory and edited to supply custom kinematics with kinstype==2: the in-tree modules register its USERK_OPS as that kinstype, so the forward and inverse in the copy are what runs.

Файл користувацької кінематики можна скомпілювати з позадеревних джерел для реалізацій rt-preempt або шляхом заміни шаблонного файлу в дереві (src/emc/kinematics/userkfuncs.c) для систем rtai.

Приклад створення Preempt-rt:

$ userkfuncs=/home/myname/kins/mykins.c make && sudo make setuid

6.2. Building a switchkins module of your own

A complete kinematics module can be built out-of-tree with halcompile using the same switchkins implementation the in-tree modules use, so it gets the kinematics switching, the kinstype.is-N pins, the coordinates= identity mapping and the G-code and HAL controls without reimplementing any of them.

The implementation is the switchkins_core module. It provides no kinstypes of its own; it exports switchkinsInit() and the rest of the switchkins.h interface, and the identity kinematics and helpers kins_module.h declares. A module of your own includes switchkins.h, supplies switchkinsSetup() and a kinsDescribe() that replays it through switchkinsDescribeWith(), and calls switchkinsRunSetupWith() and switchkinsInit() from EXTRA_SETUP(), which halcompile runs after hal_init() and before hal_ready(). See Code Notes for the calls.

The template is src/hal/components/switchkinscomp.comp. Copy and rename it (both the file and the component name) and replace the example kinstype with the real kinematics. Start from the template, not from an in-tree component such as millturn.comp: those link the switchkins objects into the module through hal/components/Submakefile, which a halcompile build outside the tree does not do.

$ halcompile --install user_switchkins.comp
[KINS]
KINEMATICS = user_switchkins
JOINTS = 3

The HAL file loads switchkins_core ahead of the module:

loadrt switchkins_core
loadrt [KINS]KINEMATICS

A module loaded without switchkins_core ahead of it fails to load, and the error names the first switchkins or kinematics function it could not find:

user_switchkins: dlopen: .../rtlib/user_switchkins.so: undefined symbol: ...

7. Попередження

Неочікувана поведінка може виникнути, якщо програму G-коду випадково запустити з несумісним типом кінематики. Небажаної поведінки в програмах G-коду можна уникнути шляхом:

  1. Підключення відповідних контактів HAL kintype.is.N до контактів цифрового входу (наприклад, motion.digital-in-0m).

  2. Зчитування цифрового вхідного виводу (M66 E0 Pm) на початку програми G-коду

  3. Переривання (M2) програми G-коду з повідомленням (DEBUG, problem_message), якщо тип роду не підходить.

Під час інтерактивного використання засобів бігової роботи або команд MDI оператору потрібна обережність. GuiDesign повинен містити індикатори для відображення поточного типу кінематики.

Note
Зміна кінематики може спричинити істотні операційні зміни, що вимагають ретельного проектування, тестування та навчання для впровадження. Управління зміщеннями координат, компенсацією інструменту та обмеженнями файлів INI може вимагати складних і нестандартних операційних протоколів.

8. Примітки до коду

Kinematic modules providing switchkins functionality are linked to the switchkins.o object (switchkins.c). It provides kinematicsForward(), kinematicsInverse(), kinematicsSwitch() and the rest of the kinematics interface, dispatching each call to the kinstype currently selected, and it creates the HAL pins common to all switchkins modules. It does not provide the module main program, so a module can get that from wherever suits it.

A kinstype is supplied by calling switchkinsRegisterOps(), once per kinstype, with the maths of that type written as functions of the parameter block (see the Kinematics Conventions chapter):

int switchkinsRegisterOps(int ktype, const kins_ops *ops);

The geometry of the whole module is one table, named in the kparms fields params and nparams; every kinstype reads it from the block. The older form, switchkinsRegister() with a setup, forward and inverse routine per kinstype that read pins of their own, is still accepted:

int switchkinsRegister(int ktype, KS kset, KF kfwd, KI kinv);

ktype runs from 0 to SWITCHKINS_MAX_TYPES-1 (defined in kinematics.h). Registering a kinstype twice, by either route, is an error, and so is leaving a gap below the highest kinstype provided. Either mistake fails the module load and says which kinstype is at fault.

Each kinstype gets its own kinstype.is-N pin, so a module providing the usual three keeps the pin names it always had.

A module also declares what each kinstype IS, with flags from kinematics.h:

  1. KINSTYPE_IDENTITY no transform: the joints are the world

  2. KINSTYPE_PRIMARY the module’s working transform

  3. KINSTYPE_MACHINE the machine frame: the pivot in machine coordinates, the rotaries as joints

A kinstype registered with switchkinsRegisterOps() carries its flag in the ops table itself, as the identity, primary or machine field; a kinstype registered the older way gets it from a call, again from within switchkinsSetup():

int switchkinsDeclare(int ktype, int flags);

G-code reads these declarations: G13.1 and G49 cancel to the kinstype declared KINSTYPE_MACHINE, and G43.4 switches to the kinstype declared KINSTYPE_PRIMARY, whatever their numbers, so a module whose kinematics are not in the conventional order still gets working spellings. The machine frame type is the machine with the orientation left out, XYZ the pivot in machine coordinates and the rotary letters the rotary joints; on a machine whose slides line up with its frame that is the identity type, so a module that declares no machine frame kinstype has its identity kinstype stand in, and every shipped module is of that kind. A module whose carriage does not line up, a slanted slide or a composite Y on a mill-turn, declares its machine frame kinstype separately and keeps KINSTYPE_IDENTITY for a kinstype whose joints really are the axes, since motion and the planner skip the maths on that flag alone. At most one kinstype may be declared identity, at most one primary and at most one machine frame, and declaring a kinstype the module does not provide fails the module load. A module that declares nothing keeps working exactly as before for G12.1 P- and G49, but G13.1 and G43.4 are an error, since the numbers of the identity and primary kinematics are then a guess.

A module that implements kinematicsTypeFlags() itself, without switchkins.c, returns the flags of every kinstype it can switch to, 0 when neither applies, and -1 for any other: G12.1 P- refuses a kinstype that returns -1.

When every kinstype is registered, the module calls:

int switchkinsInit(const int comp_id, kparms* kp, const char* coordinates);

which checks the supplied parameters, creates the HAL pins, the table’s among them, selects kinstype 0, and then invokes the setup routine of each kinstype registered the older way. The caller owns the HAL component: it does hal_init() before switchkinsInit() and hal_ready() after it.

A module built this way also exports kinsDescribe(), through which a copy of it loaded outside realtime learns its table and the maths of each kinstype; the non-realtime loader and kinslimits use it. It exports kinematicsSetTool() as well, through which motion hands it the tool offset in effect whenever that changes. A table entry flagged as the tool is overwritten with it, and the entry’s pin only matters until motion has sent anything, so a config need not net motion.tooloffset.z to the module. A kinstype registered the older way reads its own pins and is not affected.

8.1. Module main program

A module written as a plain C file links switchkins_main.o (switchkins_main.c) for its rtapi_app_main(). That main program reads the (optional) module command-line parameters (coordinates, sparm) and passes them to the module-provided function switchkinsSetup():

int switchkinsSetup(kparms* kp,
                    KS* kset0, KS* kset1, KS* kset2,
                    KF* kfwd0, KF* kfwd1, KF* kfwd2,
                    KI* kinv0, KI* kinv1, KI* kinv2);

which sets the configuration settings, names the geometry table and registers the kinstypes with switchkinsRegisterOps(). The pointer arguments are the older route for kinstypes 0,1,2; a module using them leaves the rest alone. switchkinsRunSetup() in switchkins_setup.c is what runs switchkinsSetup() and registers what it returned, for the main program and for kinsDescribe() alike.

A module written as a halcompile component gets rtapi_app_main() from halcompile instead. It supplies the same switchkinsSetup() and a kinsDescribe() that replays it, and from its EXTRA_SETUP() routine, which halcompile runs after hal_init() and before hal_ready(), runs switchkinsSetup() and then switchkinsInit():

int kinsDescribe(const char *coords, const char *sparm,
                 kins_module_info *info)
{
    return switchkinsDescribeWith(switchkinsSetup, coords, sparm, info);
}

EXPORT_SYMBOL(kinsDescribe);

EXTRA_SETUP() {
    kparms kp;
    (void)__comp_inst; (void)prefix; (void)extra_arg;

    if (switchkinsRunSetupWith(switchkinsSetup, &kp, NULL)) { return -1; }
    return switchkinsInit(comp_id, &kp, coordinates);
}

switchkinsRunSetupWith() and switchkinsDescribeWith() take the module’s switchkinsSetup() as an argument, so they work from switchkins_core. switchkinsRunSetup() and the kinsDescribe() in switchkins_setup.c call the switchkinsSetup() they were linked with, and only a module built in tree links them.

Where the switchkins code comes from depends on where the component is built. Out of tree, the switchkins_core module provides it, loaded ahead of the component (see Building a switchkins module of your own). In tree, the component links the objects into itself instead, named in hal/components/Submakefile:

millturn-extra-objs := emc/kinematics/switchkins.o emc/kinematics/switchkins_setup.o emc/kinematics/kins_util.o

8.2. Outline

A switchkinsSetup() with the kinematics itself left out: the table, the ops table of the machine’s own kinstype, and the shared identity and userk ops for the other two:

static const kins_param_desc my_params[] = {
    { "pivot-length", KINS_PARAM_FLOAT, KINS_IN, 0, 100.0 },
    { "tool-offset",  KINS_PARAM_FLOAT, KINS_IN, 1, 0.0 },   // the tool length
};
enum { P_PIVOT_LENGTH, P_TOOL_OFFSET };

static int my_forward(const kins_params *p, kins_scratch *s,
                      const double *joint, EmcPose *pos,
                      const KINEMATICS_FORWARD_FLAGS *fflags,
                      KINEMATICS_INVERSE_FLAGS *iflags)
{
    double pivot = p->geometry[P_PIVOT_LENGTH];   // where a pin was read
    double tool  = p->tool.tran.z;                // the tool, from wherever the caller has it
    // ...
}

static int my_inverse(const kins_params *p, kins_scratch *s,
                      const EmcPose *pos, double *joint,
                      const KINEMATICS_INVERSE_FLAGS *iflags,
                      KINEMATICS_FORWARD_FLAGS *fflags);

static const kins_ops my_ops = {
    .forward = my_forward,
    .inverse = my_inverse,
    // .work, .tool, .native and .jacobian are optional, see kins_module.h
};

int switchkinsSetup(kparms* kp,
                    KS* kset0, KS* kset1, KS* kset2,
                    KF* kfwd0, KF* kfwd1, KF* kfwd2,
                    KI* kinv0, KI* kinv1, KI* kinv2
                   )
{
    kp->kinsname             = "mykins"; // must agree with the filename
    kp->halprefix            = "mykins"; // hal pin names
    kp->required_coordinates = "xyzab";
    kp->max_joints           = strlen(kp->required_coordinates);
    kp->params               = my_params;
    kp->nparams              = sizeof(my_params)/sizeof(my_params[0]);
    // remaining kparms fields

    switchkinsRegisterOps(0, &my_ops);              // kinstype 0 is the startup default
    switchkinsRegisterOps(1, &KINS_IDENTITY_OPS);
    switchkinsRegisterOps(2, &USERK_OPS);

    // any further kinstype is registered the same way, and the
    // numbering carries on with no gaps
    return 0;
} // switchkinsSetup()

For the surrounding shape, the in-tree switchkinsSetup() routines are in src/emc/kinematics: 5axiskins.c, xyzac-trt-kins.c, genserkins.c, scarakins.c and the others listed at the top of this document. None of them registers a fourth kinstype yet, so a call for one has no in-tree example to copy.