Skip to content

Commit 0f1aa81

Browse files
authored
Merge pull request #4702 from grandixximo/kins-params
kinematics: modules as functions of a parameter block, callable outside realtime
2 parents e2d0d65 + b0adf38 commit 0f1aa81

90 files changed

Lines changed: 7824 additions & 3540 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎configs/sim/axis/vismach/5axis/bridgemill/5axisgui.hal‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ net :gui-pivot-len <= 5axisgui.pivot_len
1717
net :gui-pivot-len => 5axiskins.pivot-length
1818

1919
net :tool-len <= motion.tooloffset.z
20-
net :tool-len => 5axiskins.tool-length
2120
net :tool-len => 5axisgui.tool_length
2221

2322
net :tool-diam <= halui.tool.diameter

‎configs/sim/axis/vismach/5axis/max5/max5kins.hal‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ loadusr -W ./max5gui.py
1616

1717
# set a visible tool
1818
setp max5gui.tool-radius 3
19-
# the tool length is applied along the tool, not along Z, so the tip stays
20-
# on the programmed point as B tilts
21-
net tool-len motion.tooloffset.z max5gui.tool-length maxkins.tool-length
19+
net tool-len motion.tooloffset.z max5gui.tool-length
2220

2321
# add motion controller functions to servo thread
2422
addf motion-command-handler servo-thread

‎debian/linuxcnc.install.in‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ usr/bin/hy_vfd
3636
usr/bin/image-to-gcode
3737
usr/bin/inivalue
3838
usr/bin/inivar
39+
usr/bin/kinslimits
3940
usr/bin/latency-histogram
4041
usr/bin/latency-plot
4142
usr/bin/latency-test

‎docs/src/code/code-notes.adoc‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,8 +1312,7 @@ settings.tool_offset::
13121312
+
13131313
* Used to compute position in various places.
13141314
* Sent to Motion via the +EMCMOT_SET_OFFSET+ message.
1315-
All motion does with the offsets is export them to the HAL pins +motion.0.tooloffset.[xyzabcuvw]+.
1316-
FIXME: export these from someplace closer to the tool table (io or interp, probably) and remove the EMCMOT_SET_OFFSET message.
1315+
Motion exports the offsets to the HAL pins +motion.0.tooloffset.[xyzabcuvw]+ and hands them to the kinematics module through +kinematicsSetTool()+, for a module whose maths needs the tool length.
13171316

13181317
settings.pockets_max::
13191318
Used interchangeably with +CANON_POCKETS_MAX+ (a #defined constant, set to 1000 as of April 2020).

‎docs/src/man/man9/kins.9.adoc‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,10 @@ replacing it. Put a given length in one column or the other, not both.
252252

253253
*maxkins.tool-length*::
254254
Tool length, applied along the tool rather than along Z, so that the tip
255-
stays on the programmed point as B tilts. Net it from *motion.tooloffset.z*.
256-
Left unconnected, the tool length stays where canon put it, along machine
257-
Z, which is only correct at B0.
255+
stays on the programmed point as B tilts. Motion hands the module the
256+
offset in effect (G43, G49), so the pin needs no connection; it is read
257+
only until motion has sent anything. To avoid a joint jump, change the
258+
tool offset only when B is 0.
258259

259260
=== pentakins - Pentapod Kinematics
260261

@@ -413,12 +414,13 @@ expected by it (XYZBCW `->` joints 0..5)
413414

414415
*5axiskins.tool-length*::
415416
Tool length, applied along the tool rather than along Z, so that the tip
416-
stays on the programmed point as B and C move. Net it from
417-
*motion.tooloffset.z*. Left unconnected, the tool length stays where canon
418-
put it, along machine Z, which is only correct at B0. A tool length in the
419-
W column of the tool table reaches the same place, once a block commands W,
420-
and adds to this pin rather than replacing it. Put a given length in one
421-
column or the other, not both.
417+
stays on the programmed point as B and C move. Motion hands the module the
418+
offset in effect (G43, G49), so the pin needs no connection; it is read
419+
only until motion has sent anything. To avoid a joint jump, change the
420+
tool offset only when B is 0. A tool length in the W column of the tool
421+
table reaches the same place, once a block commands W, and adds to this
422+
one rather than replacing it. Put a given length in one column or the
423+
other, not both.
422424

423425
== SEE ALSO
424426

‎docs/src/motion/5-axis-kinematics.adoc‎

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -317,23 +317,17 @@ See the simulation INI files for details of the HAL connections used for the vis
317317

318318
=== Tool-Length Compensation
319319

320-
In order to use tools from a tool table sequentially with tool-length compensation applied automatically, a further Z-offset is required. For a tool that is longer than the "master" tool, which typically has a tool length of zero, LinuxCNC has a variable called "motion.tooloffset.z". If this variable is passed on to the kinematic component (and vismach python script), then the necessary additional Z-offset for a new tool can be accounted for by adding the component statement, for example:
320+
In order to use tools from a tool table sequentially with tool-length compensation applied automatically, a further Z-offset is required. For a tool that is longer than the "master" tool, which typically has a tool length of zero, the kinematics accounts for the tool length in effect, for example:
321321

322322
image::5-axis-figures/equation__38.png[align="center"]
323323

324-
The required HAL connection (for xyzac-trt) is:
324+
Motion hands the tool offset in effect (G43, G49) to the kinematics module directly, so the module sees the tool from the tool table with no HAL connection. The module's tool-offset pin (xyzac-trt-kins.tool-offset) remains for a configuration that connects it, and is read only until motion has sent an offset; a value set on it that disagrees with the tool table is reported once and not used.
325325

326-
[source,hal]
327-
----
328-
net :tool-offset motion.tooloffset.z xyzac-trt-kins.tool-offset
329-
----
330-
331-
where:
326+
Motion also publishes the offset on the HAL pin "motion.tooloffset.z", which is what a vismach python script reads to draw the tool:
332327

328+
[source,hal]
333329
----
334-
:tool-offset ---------------- signal name
335-
motion.tooloffset.z --------- output HAL pin from LinuxCNC motion module
336-
xyzac-trt-kins.tool-offset -- input HAL pin to xyzac-trt-kins
330+
net :tool-offset motion.tooloffset.z xyzac-trt-gui.tool-offset
337331
----
338332

339333
== Custom Kinematics Components
@@ -383,17 +377,18 @@ KINEMATICS = kinsname
383377

384378
where "kinsname" is the name of your kins program.
385379
Additional HAL pins may be created by the module for variable configuration items
386-
such as the D~x~, D~y~, D~z~, tool-offset used in the xyzac-trt kinematics module.
380+
such as the D~x~, D~y~, D~z~ used in the xyzac-trt kinematics module.
387381
These pins can be connected to a signal for dynamic control or set once with HAL connections like:
388382

389383
[source,hal]
390384
----
391385
# set offset parameters
392-
net :tool-offset motion.tooloffset.z xyzac-trt-kins.tool-offset
393386
setp xyzac-trt-kins.y-offset 0
394387
setp xyzac-trt-kins.z-offset 20
395388
----
396389

390+
The tool length is not among them: motion hands it to the module from the tool table.
391+
397392
== Figures
398393

399394
.Table tilting/rotating configuration

‎docs/src/motion/kinematics-conventions.adoc‎

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ half turn about one of the two transverse axes, and which one is chosen decides
178178
where tool X lands.
179179

180180
Because it is a rotation in its own right, a module declares it rather than
181-
applying it by hand, as the last argument of `switchkinsRegisterFrames()`.
181+
applying it by hand, in the `native` field of its ops table.
182182
Shared code applies it and checks once, at load, that it is orthonormal with
183183
determinant +1. `TOOL_FRAME_SPINDLE` is the identity, for a module whose maths
184184
is already in the convention; `TOOL_FRAME_FLANGE` is the half turn a
@@ -289,6 +289,8 @@ All of these are functions of the joint values and the module's own geometry.
289289
None needs state carried between calls, and none needs the module to be running
290290
in a realtime thread to be useful: the interesting callers, a limit check
291291
before a move and a preview before a program runs, are not in the servo loop.
292+
<<sec:parameters,The Parameter Block>> is how a module is written so that they
293+
can call it.
292294

293295
[[sec:orientation-inverse]]
294296
== The Orientation Inverse
@@ -438,28 +440,28 @@ every consumer would otherwise guess it.
438440

439441
=== What a module has to supply
440442

441-
Nothing. If a module registers no Jacobian, the shared code computes one by
443+
Nothing. If a module supplies no Jacobian, the shared code computes one by
442444
central differences: it calls the module's inverse eighteen times, stepping
443445
each pose coordinate a small amount to either side on the solution branch
444446
the inverse flags select, and differences the results. The answer is as
445447
precise as the inverse itself; behind an inverse that iterates, that is the
446-
iteration's convergence tolerance divided by the step. Modules built on `switchkins.c` answer this way for every type that
447-
registers nothing; an identity type answers exactly.
448+
iteration's convergence tolerance divided by the step. Modules built on `switchkins.c` answer this way for every type
449+
whose ops table has no Jacobian; an identity type answers exactly.
448450

449451
This cost matters because `kinematicsJacobian()` is meant to run in the
450452
servo thread, where checking a feed limit calls it once per cycle: each call
451453
then costs eighteen inverses. On the closed-form inverses in the tree that
452454
is under two microseconds. `genserkins`, the one module whose inverse
453455
iterates, would cost near eighty microseconds per call this way, against
454-
under two for the geometric Jacobian it registers instead.
456+
under two for the geometric Jacobian it supplies instead.
455457

456-
So a module whose inverse iterates should register a closed-form Jacobian,
457-
as below. Nothing enforces this. A module that registers nothing still gets
458+
So a module whose inverse iterates should supply a closed-form Jacobian,
459+
as below. Nothing enforces this. A module that supplies none still gets
458460
a correct Jacobian, just one that costs eighteen inverses and carries the
459461
iteration's precision, which behind an iterating inverse is too slow for the
460462
servo thread.
461463

462-
A module with a closed form registers it with `switchkinsRegisterJacobian()`.
464+
A module with a closed form puts it in the `jacobian` field of its ops table.
463465
It is exact, it costs what the inverse costs, and it knows its own singular
464466
poses rather than discovering them as an inverse that fails a step away from
465467
the pose. Every module in the tree supplies one. The two arms whose inverse is
@@ -476,6 +478,100 @@ rather than from the pose, which the nutating heads do, has an inverse whose
476478
derivative about the pose is not the coupling the machine has. Such a module
477479
supplies the closed form, taken against the pose.
478480

481+
[[sec:parameters]]
482+
== The Parameter Block
483+
484+
Everything above is a function of the joint values, the tool and the machine's
485+
geometry. A module written the old way reads its geometry from HAL pins it
486+
created, keeps its kinematics type and its iteration scratch in statics, and so
487+
can only answer for the machine as it is now, from inside the realtime thread.
488+
Anything else that needs the same maths, a planner evaluating poses the machine
489+
has not reached, task checking a program at load, a tool asking what if, had to
490+
carry a second copy of it, and the two copies drift.
491+
492+
A module is written instead as functions of two blocks the caller supplies.
493+
`kins_params` describes the machine: the kinematics type, the joint map from
494+
`coordinates=`, the tool offset, and the geometry as an array of doubles. One
495+
copy may be shared by any number of callers, since nothing writes it during a
496+
call. `kins_scratch` is what one caller carries between its own calls: the pose
497+
an iterating forward last found, which seeds the next, and what the module
498+
reports about the call it just made. It is never shared between callers, so
499+
motion and a planner evaluating the same module cannot disturb each other.
500+
501+
=== The table
502+
503+
A module declares its geometry as a table of named entries, one per value it
504+
reads. The name is the pin name the config already uses, less the module
505+
prefix, so nothing in a config changes.
506+
507+
[source,c]
508+
----
509+
static const kins_param_desc fiveaxis_params[] = {
510+
{ "pivot-length", KINS_PARAM_FLOAT, KINS_IN, 0, DEFAULT_PIVOT_LENGTH },
511+
};
512+
enum { P_PIVOT_LENGTH };
513+
----
514+
515+
An entry is an input, an output, or an input that can be poked (`KINS_IO`, a
516+
`HAL_IO` pin). The maths reads `p->geometry[P_PIVOT_LENGTH]` where it read a
517+
pin, and writes an output into `s->out[]` at the same index. An entry flagged
518+
as the tool is the tool length along the tool axis; the shared code puts its
519+
value in `p->tool.tran.z` as well, which is what the maths reads, so that a
520+
caller outside realtime can supply the tool from the tool table without there
521+
being a pin.
522+
523+
=== The ops table
524+
525+
The maths of one kinematics type is a `kins_ops` table: the forward and inverse,
526+
the optional work and tool frames with the native rotation that relates the
527+
tool frame to the convention, and the optional Jacobian. A type whose forward
528+
iterates from the pose it is handed says so, and the shared code seeds it with
529+
the last answer after a switch. A type also says what it IS: `identity` marks
530+
the no-transform type `G13.1` cancels to, `primary` the working transform
531+
`G43.4` switches to (see the Switchable Kinematics chapter). A module with
532+
several types has one geometry table and one ops table per type, registered
533+
with `switchkinsRegisterOps()`; a module with one type describes itself in a
534+
`kins_module` and links `kins_single.c`.
535+
536+
=== What the shared code does
537+
538+
In realtime it makes one HAL pin per table entry, copies the pins into the
539+
block before every call and the outputs back after it, and supplies the classic
540+
entry points, `kinematicsForward()` and the rest, so that motion sees no
541+
difference. It also exports `kinematicsSetTool()`, through which motion hands
542+
the module the tool offset in effect whenever that changes, so the tool comes
543+
from the tool table and not from a net the config had to remember. A table
544+
entry flagged as the tool is read from its pin only until motion has sent
545+
anything; after that the pin is overwritten with motion's value, and the shared
546+
code says once if the pin is left disagreeing with it, which is a config
547+
setting a tool length where the tool table should. Motion references the call
548+
weakly, so a module written before it still loads and keeps its pin.
549+
550+
Outside realtime a module exports `kinsDescribe()`, which hands a caller its
551+
table and the ops of each type; the caller fills a block from wherever it likes
552+
and asks the same functions through `kinsOpsForward()`, `kinsOpsInverse()`,
553+
`kinsOpsJacobian()` and the frame calls, with the same defaults applied, so
554+
both sides get the same answers. The non-realtime loader in
555+
`kinematics_userspace/` binds the pins of the running module by the table's
556+
names. Its tool is the caller's where the caller gives one through
557+
`kinematicsUserSetTool()`, since a planner knows what a segment runs under
558+
better than the machine does; otherwise it is motion's, from motion's own
559+
offset pins, and the loader says once when the module's tool pin disagrees with
560+
them. `kinslimits` is built on it.
561+
562+
A module that does not provide the form keeps working as it did. It just cannot
563+
be evaluated outside realtime, which the loader reports.
564+
565+
=== What stays outside the block
566+
567+
The kinematics type is in the block, so a caller evaluating a program that
568+
switches type puts the type each block will run under in its own block, and
569+
nothing is switched globally. The tool is in the block, from motion in realtime
570+
and from the caller outside it. The joint map is in the block, from
571+
`coordinates=`. Nothing else the maths needs exists,
572+
and a module that finds it needs something else has found a parameter it should
573+
declare.
574+
479575
[[sec:writing-a-module]]
480576
== Writing a Module
481577

@@ -516,6 +612,13 @@ Geometry stays in the module::
516612
the module. A consumer that restates it has taken a copy that nothing keeps
517613
in step, which is the situation this chapter exists to end.
518614

615+
No state::
616+
Write the maths as functions of the parameter block and the scratch, as
617+
<<sec:parameters,The Parameter Block>> describes: geometry in the table,
618+
the kinematics type and the tool from the block, and anything carried
619+
between calls in the scratch. A static in a module is a second machine
620+
that only the realtime thread can see.
621+
519622
Mount orientation is not this::
520623
A tool or holder mount orientation is a different quantity: a right-angle
521624
head, a tool held at an angle, an end effector clocked on its flange. Those

‎docs/src/motion/kinematics.adoc‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,12 @@ See <<cha:switchable-kinematics,Switchable Kinematitcs>>.
320320

321321
[NOTE]
322322
The majority of provided kinematics modules support a single
323-
kinematics type and use the directive "*KINS_NOT_SWITCHABLE*" to
324-
supply defaults for the required kinematicsSwitchable() and
325-
kinematicsSwitch() functions.
323+
kinematics type; they are written on the parameter block and link
324+
'kins_single.c', which supplies these entry points and answers "not
325+
switchable" for them. A single-type module written without the
326+
parameter block can use the directive "*KINS_NOT_SWITCHABLE*" to
327+
supply defaults for kinematicsSwitchable(), kinematicsSwitch() and
328+
kinematicsTypeFlags().
326329

327330
----
328331
int kinematicsHome(EmcPose *world, double *joint,

0 commit comments

Comments
 (0)