Skip to content

Commit 2acf37e

Browse files
committed
kinematics: answer which joints point the tool somewhere
The frames say where the tool points for a set of joint values. The question a tilted work plane actually asks is the other way round: which joint values point it at the orientation the program wants. Today that is answered outside the kinematics, in per-machine trigonometry in the TWP remap, once per machine and once per pair of rotary letters, which is how the copy in the tree came to check the primary angle against the secondary joint's limits. kinematicsToolFrameInverse() asks the module instead. It reports every set of joint values that reaches the orientation, and nothing else: no joint limits and no preference between the answers, because the caller knows the limits and knows whether the operator asked for the shortest move or for one direction of rotation only. A module that reports its frames needs to supply nothing, the generic search in kins_util.c drives them; a module with a closed form registers it and that is used instead. The request is a tool axis and optionally a tool x as well, since a five axis machine cannot pin down the spin about the tool and asking it to would be asking for the impossible. Where a request leaves the machine free, at a singular pose or on a machine with a third orientation joint, one point of the family is reported, the one nearest the seed, with the number of free directions alongside. Returning samples of a curve as though they were alternatives would be worse than saying so. Three details in the search are not incidental. The damping is adaptive, which is what keeps a rank deficient pose from turning finite difference noise into a step of thousands of degrees. The Jacobian is central differenced, because a one sided error is first order in the step and shows up as a spurious singular value, which is exactly what the rank test must not see. And the joint unit is discovered by adding a whole turn and asking whether the frame came back, because every module in the tree takes degrees but the interface does not say so. The unit test drives it through a table rotary machine, a nutating head and a machine with both, and checks the nutating answers against the closed form the remap uses.
1 parent 46ef1dd commit 2acf37e

6 files changed

Lines changed: 988 additions & 0 deletions

File tree

docs/src/motion/kinematics-conventions.adoc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ are tilted work plane handling, tool length compensation along a tilted axis,
264264
previews and simulation models, and probing routines that have to say which way
265265
the stylus faces.
266266

267+
The joint values that reach a requested orientation, through
268+
`kinematicsToolFrameInverse()`, which is the inverse of the pair above and the
269+
question a tilted work plane asks when it has to orient the machine.
270+
<<sec:orientation-inverse,The Orientation Inverse>> says what it answers.
271+
267272
The Jacobian, relating commanded velocity to joint velocity at a given pose, so
268273
that a feed can be checked against the joint velocity, acceleration and limit
269274
values it will actually demand, and so that proximity to a singularity is a
@@ -276,6 +281,61 @@ None needs state carried between calls, and none needs the module to be running
276281
in a realtime thread to be useful: the interesting callers, a limit check
277282
before a move and a preview before a program runs, are not in the servo loop.
278283

284+
[[sec:orientation-inverse]]
285+
== The Orientation Inverse
286+
287+
Pointing the tool somewhere is the question a tilted work plane asks on every
288+
orienting move, and the question a program written as tool vectors asks on
289+
every block. It is the inverse of the tool frame: not where the tool points
290+
for these joints, but which joints point it there.
291+
292+
It has more than one answer. A five-axis machine reaches a given tool axis two
293+
ways, one with the secondary rotary positive and one with it negative, and
294+
which of the two is wanted depends on the joint limits and on what the operator
295+
asked for, shortest move or one direction of rotation only. So the module
296+
reports every set of joint values that reaches the orientation and stops there.
297+
It does not apply the joint limits and it does not rank the answers, because
298+
neither is geometry: a module that picked for the caller would be picking with
299+
less to go on than the caller has.
300+
301+
The request is a tool axis, and optionally a tool x as well. Leaving tool x out
302+
is the normal case on a five-axis machine, whose two rotaries cannot pin down
303+
the spin about the tool anyway, and it is what <<sec:tool-frame,The Tool
304+
Frame>> deals with through the virtual rotation. Asking for both is what a
305+
machine with a third orientation joint needs, and what makes its answer a list
306+
rather than a family.
307+
308+
Some requests do not pin the machine down. A five-axis machine asked to point
309+
the tool along the axis its primary rotary turns about can hold any primary
310+
angle; a machine with three orientation joints asked only for a tool axis has a
311+
whole curve of solutions. In both cases the answer is a continuum, so one point
312+
of it is reported, the one nearest where the machine already is, along with the
313+
number of directions left free. A caller that hands back a list of samples from
314+
a curve as though they were alternatives is telling the operator something
315+
false.
316+
317+
=== What a module has to supply
318+
319+
Nothing, if it already reports its frames. The shared code answers the question
320+
by searching: it finds which joints move `transpose(work) * tool`, and solves
321+
for them. That is the whole reason the frames are worth reporting. A module
322+
that supplies them gets the inverse without deriving anything.
323+
324+
A module with a closed form registers it and that is used instead. It is faster
325+
than a search and it knows its own degenerate poses without having to discover
326+
them. The nutating heads are the case in point: for those,
327+
328+
cos(secondary) = (Kzz - Cv^2) / (1 - Cv^2)
329+
330+
with `Cv = cos(v)` for a nutation angle `v` and `Kzz` the z component of the
331+
requested tool axis, and the primary follows from the other two components.
332+
Both roots of the arc cosine are solutions, which is where the pair of answers
333+
comes from.
334+
335+
The search is not a realtime routine. How long it takes depends on the machine
336+
and on the request, and the callers that want it, orienting a tilted work plane
337+
and previewing a program, are not in the servo loop.
338+
279339
[[sec:writing-a-module]]
280340
== Writing a Module
281341

@@ -300,6 +360,11 @@ Tool X::
300360
Derive the value from the module's own rotation matrices, and write those
301361
matrices down in the module.
302362

363+
Orientation inverse::
364+
A module that reports its frames gets it from the shared search and needs to
365+
do nothing. Register a closed form only where one exists, and where it does,
366+
say which poses it treats as degenerate.
367+
303368
Geometry stays in the module::
304369
Whatever a consumer needs to know about the machine's shape is answered by
305370
the module. A consumer that restates it has taken a copy that nothing keeps

src/emc/kinematics/kinematics.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,68 @@ extern int toolFrameInWork(const PmRotationMatrix *work,
239239
actually hold. Used to check a declared rotation once, at load. */
240240
extern int toolFrameIsProper(const PmRotationMatrix *m);
241241

242+
/* The inverse of kinematicsToolFrame(): which joint values point the tool
243+
along a requested direction. This is the question a tilted work plane asks
244+
when it has to orient the machine, and the one vector format G-code asks
245+
for every block.
246+
247+
axis_in_work is the wanted tool axis and x_in_work the wanted tool x, both
248+
in workpiece coordinates, both in the sense of transpose(work) * tool.
249+
x_in_work may be NULL, which leaves the spin about the tool free and is the
250+
normal case on a five axis machine, whose two rotaries cannot pin it down.
251+
252+
seed is a full set of joint values, normally where the machine is now. The
253+
joints that do not affect the tool orientation are copied from it, and it
254+
breaks the tie where a machine has more orientation joints than the request
255+
constrains.
256+
257+
solutions receives max_solutions complete sets of joint values, one after
258+
another, each num_joints long. free_directions, if not NULL, receives one
259+
entry per solution: 0 where the joints are pinned down, and n where the
260+
solution is one point of an n dimensional family, which happens at a
261+
singular pose and on a machine with a spare orientation joint. In that case
262+
one representative is reported, the one nearest the seed, because the answer
263+
is a continuum and a list of samples from it would be arbitrary.
264+
265+
Joint limits are not applied and no solution is preferred over another: the
266+
module answers what the geometry permits, and the caller picks by whatever
267+
rule it works to, shortest move or positive rotation only or whatever else.
268+
269+
Returns the number of solutions, 0 if the orientation cannot be reached, or
270+
-1 if the module cannot answer.
271+
272+
This is not a realtime routine. It searches, and how long it takes depends
273+
on the machine and the request. */
274+
#define TOOL_FRAME_MAX_SOLUTIONS 8
275+
#define TOOL_FRAME_MAX_FREE 4
276+
277+
extern int kinematicsToolFrameInverse(const PmCartesian *axis_in_work,
278+
const PmCartesian *x_in_work,
279+
const double *seed,
280+
double *solutions,
281+
int max_solutions,
282+
int *free_directions);
283+
284+
/* The generic implementation of the above, driven by a module's own frame
285+
functions, so that a module gets it for free once it supplies them. A
286+
module with a closed form registers that instead: it is faster, and it
287+
knows its own degenerate poses without having to find them.
288+
289+
num_joints is the length of seed and of each row of solutions. */
290+
typedef int (*kinsFrameFunc)(const double *joint,
291+
PmRotationMatrix *rot,
292+
const KINEMATICS_FORWARD_FLAGS *fflags);
293+
294+
extern int toolFrameSolve(kinsFrameFunc work,
295+
kinsFrameFunc tool,
296+
int num_joints,
297+
const PmCartesian *axis_in_work,
298+
const PmCartesian *x_in_work,
299+
const double *seed,
300+
double *solutions,
301+
int max_solutions,
302+
int *free_directions);
303+
242304
extern int kinematicsSwitchable(void);
243305
extern int kinematicsSwitch(int switchkins_type);
244306
//NOTE: switchable kinematics may require Interp::Synch

0 commit comments

Comments
 (0)