Repository navigation
Multiaxis kinematics and limits - #4374
grandixximo wants to merge 62 commits into
Conversation
9e1c9ed to
773c3e9
Compare
773c3e9 to
34ab07b
Compare
|
What is the migration path for people having their own out-of-tree kinematics switch component? |
Their module needs no source change. G12.1 and G13.1 reach a module through the same I checked rather than assumed. I built an out-of-tree component that deliberately does not use switchkins.h or switchkins.c: its own switchkins type, its own with no source change, and no deprecation warning, since the pin is never written. The config keeps working as well. The pin is deprecated, not gone, so an existing M68/M66 config runs as before with one warning per session. Moving off it is a config edit, not code: and the One group does have something to do, and it is not the one you asked about: anybody keeping a copy of switchkins.c in their own tree. Their current build is unaffected, but Announcing it is worth raising at a weekly meeting. Nothing is removed in this PR. |
|
Just a thought: |
|
That matches what switchkins.adoc already warns about: coordinate system offsets are described as generally applicable only to the type 0 startup kinematics, with advice to reset them or update them before switching. So the problem is real, and today it rests on the operator remembering. Can I ask what you switch between, and whether you cut in both types? I am trying to tell two situations apart. If both types leave the axis letters describing the same physical point, then your G54 values are still correct after the switch, and what is missing is having a different one become active. If one of the types is identity, the stored numbers stop describing anything, since X becomes joint 0 and may not even carry the same units, and then a different set does not help either; the values want clearing. Which of these is it for you: does the zero point of the part sit somewhere different once you switch, or is the part zero the same and you simply want a different one of G54 to G59.3 selected? On the mechanism, do you mean an association living in the INI, a coordinate system named per kinematics type? There is nothing like that today. Or do you mean it read at runtime? kins-type is a status pin, so it reports rather than commands, but you could net it into a motion analog input and branch on it with M66 in a sub, which would work now. I am unsure whether that belongs in the core or stays the sort of thing an integrator wires up, and your answer would tell me which problem I am actually looking at. |
Here are two examples, the tool may cut in all kinematic modes. Example1: Serial manipulator arm Example2: Tilted work plane Currently I have two ways of dealing with this:
Maybe a different set of G54...G59.3 values. One set for each kins-type. There are also some other things to think about when working in different kinematic modes ( eg G28 , G30, G53, G92/G52, M6) it might be useful to have way of blocking certain Gcodes / Axiswords in a given mode. |
|
Both examples are clear and they moved my view: I had been assuming identity was mostly a jogging mode, and you cut in all modes. I looked at how the commercial controls handle both cases, and in each of them the closest thing we could do in LinuxCNC turns out not to be an offset set. For the singularity case, the target stays in Cartesian coordinates and only the interpolation changes: a move that runs the inverse once at the endpoint and then interpolates the joints linearly to reach it. Coordinates and units never change, so the offsets keep applying and there is nothing to switch. In tree that is a new move type from canon into the trajectory planner, and the existing KINEMATICS_INVERSE_FLAGS already carry enough to pin which solution branch it lands on. G-code would need a way to ask for it. For the tilted plane, an extra stage in the offset chain rather than new values in it: a frame composed on top of the active G54, with its own cancel, so the blocks in between are programmed in the tilted plane while G54 itself is untouched. That is a stage in rotate_and_offset_pos() plus the modal state to go with it. Nothing is rewritten, so nothing needs restoring and on_abort_with_twp_reset.ngc has nothing left to do. Orienting the tool normal to the plane, your G53.1, does need the kinematics to say where the tool points and to solve for the rotaries, which is item 3 above. On having more of them: that part has plenty of precedent and I would be happy to look at it separately. Fanuc adds G54.1 P1 to P48, or to P300 on the 30i, and Siemens adds G505 to G599. What I cannot find anywhere is a control that ties a set to the active kinematics or transformation. Selection is always explicit in the program, or it belongs to a channel or path. So more coordinate systems yes, but I would rather not bind them to the kinstype. Which leaves me reading one set per kinstype as paying for the two absences above rather than as the thing you actually want. You have run this in production and I have not, so I would rather hear where that is wrong. Would a composing frame cover your case 2, or would it miss something? Your other point stands either way. Per-mode restrictions on G28, G30, G53, G92 and M6 look right to me, and refusing outright beats guessing. |
|
Nice that you have an overview of what other controllers are doing. Makes more sense adopting something that is established rather than coming up with something quirky that falls on our feet somewhere down the road.
That is how I started out, an offset built into the kinematics. The reason I moved away from that was that in it's current form LinuxCNC is totally unaware of what is going on in a custom kinematics. Basically what I'd like is to NOT have to tamper directly with the values in the offset table. |
|
That is the same conclusion from the other side, and it names the dependency. A composing frame is only worth having if motion knows the frame is there; otherwise G53 and G28 keep meaning whatever each module happens to do with them, and we are back to the operator remembering. That is items 3 and 4 above, which is why they come before anything in the offset chain. What "aware" has to mean concretely: the module answers where the tool points and how joint motion maps to world motion, both as plain functions of the joint values and a parameter block, with no state hidden in HAL pins. Once the control can call the kinematics rather than only drive it, G53 resolves in machine axis space, the per-mode restrictions you listed get checked rather than guessed, and a frame composes on top of G54 with nothing rewriting the table. So no per-kinstype offset sets, nothing tampering with the table in the background, and the tilted plane becomes a frame that cancels. Your two examples are the cases I will hold it to. |
|
Sounds great! |
ffd07b8 to
fea53f7
Compare
|
One more thing the tool round trip does not cover, found while giving Both modules add the W coordinate to the pivot to tip distance, so W is already a tool-length route: So on these two modules the tool length has three spellings: the W column with W commanded, the This is the same shape as the rest of items 3 and 4. Each module infers the tool from whatever it happens to be handed, every config picks a different one of the three, and nobody owns which is meant. Once the tool arrives as a parameter at the call site there is one answer instead of three. |
|
I have a set of kinematics that probably cover most 5 axis machine (tool side rotation, work side rotation and mixed) including nutating joints and tilted work plane. I have long wonder if I should add those to the collection: |
|
Yes, and I would take the kinematics first. The nutating ones are what items 3 and 4 need most. A nutating head is where "where does the tool point" stops being obvious, so a weak default every module answers is only worth having if something in tree can break it. Today the only nutating configs are the two On the TWP remap I would rather you decide with item 5 in front of you. I am proposing to move that mechanism into One request if the kins come in: could they take the tool length through a |
|
I'm not in hurry it's been sitting here for a while now. |
d32027c to
87c645b
Compare
|
I had a go at the split on the two trsrn configs in tree, to see how it lands: https://github.com/grandixximo/linuxcnc/tree/twp-remap-split Your generic I ran both configs through eight orientations under Three things I hit in your files:
The commit is authored by me at the moment and the code is yours, so I would set you as author before it goes anywhere. Happy either way: take the branch and send it yourself, or tell me it looks right and I will. |
|
Thanks for looking through this. I'll need to have a good look and report back. |
87c645b to
5bf0103
Compare
A machine with two tool heads, a saw blade and a waterjet say, has a working transform per head and selects the head with G12.1. The loader now accepts any number of types flagged primary; identity and machine frame stay unique, since G13.1 and G49 resolve a single answer from them. G43.4 and G43.5 stay on the type in force when it is primary, and from any other type switch to the lowest numbered primary, which is the single primary on every module that declares one. tests/ptp-machine-frame gains a second head on slantkins (type 3, the tip again, primary) and checks both rules.
#<_orient_a> #<_orient_b> #<_orient_c> become #<_orient_rot1> to #<_orient_rot3>: the angles of the axes #<_kins_orient_1> to #<_kins_orient_3> name, in that order, so a program reads them the same way whatever letters the machine gives its rotaries, as Fanuc and Siemens number the rotaries first and second. #5071-#5076 carry X Y Z and the three rotaries, #5077-#5079 are written 0. kinematicsUserOrientAxes() answers up to three axes. A type with three rotaries between tool and work, xyzacb_trsrn's A table and B C head, gives all three, tables first, each group in joint order, or the three letters its orient declares; a robot arm, whose joints all turn the tool, gives the A B C of its pose. #<_kins_orient_3> and #<_kins_orient_3_head> are new; a two rotary type reads -1 there, as before. G43.5 and the LINEAR check cover the third axis.
A head on X Y Z tilts the tool on B between a main spindle turning its part on C and a sub spindle facing it along Z and turning its own part on W, an ANGULAR axis. Type 0 works in the frame of the main spindle's part and orients the tool with C and B, type 1 in the frame of the sub spindle's part, the main one turned half a turn about X so its Z too points at the tool, and orients it with W and B; both are primary, so G12.1 picks the spindle and a program reads the same on either part. Type 2 is the identity and the machine frame. Written as pure functions of the parameter block, with work and tool frames and the orienting letters declared, so the interpreter solves the tilted work plane on either spindle. The joints read the gauge line with B at 0 and the tool length goes along the tool, as on 5axiskins. tests/kins-params checks both working types against realtime.
A vismach sim of twinspindlekins, the spindle line across the screen, with a demo program that drills a ring of tilted holes into the part in the main spindle and then into the part in the sub spindle through one subroutine. It names no rotary: G53.3 turns the ones the type in force orients with, and the report reads them as #<_kins_orient_1> and #<_kins_orient_2>, C and B on one spindle, W and B on the other, with their angles as #<_orient_rot1> and #<_orient_rot2>. tests/twinspindle checks the orienting axes of each type, that the same tilted hole solves and is reached on either spindle, the tip on the hole and the tool along its axis, and that the demo program runs.
Like the five axis sims: the INI sets MACHINE_MOVES_IN_MACHINE_FRAME with a comment, and the README says so, so G53 puts the slides in the same place on either spindle's type whatever the tool and the head angle. The demo program and tests/twinspindle (its own test.ini) use no G53, G28 or G30 off the machine frame type.
compute_jfwd() converts every link to a quaternion pose twice and builds the Jacobian through generic matrix calls: 0.9 us of the 1.2 us that genser_jacobian() takes. tpnext asks for up to about 60 Jacobians per move near a singular pose, and genser_inverse() takes one per iteration. genser_jfwd_frames() chains the DH frames as rotations and origins and takes each column as z_i x (o_n - o_i), z_i. It gives what compute_jfwd() gives to within 3e-10 relative over 20000 random poses, in 0.12 us. genser_jacobian() and genser_inverse() use it; genser_kin_jac_inv() and genser_kin_jac_fwd() keep compute_jfwd(). With the puma560 genser config, adding a move near the wrist singular pose falls from 153 to 71 us on average and from 207 to 93 us at most; other moves fall from 36 to 22 us.
The abort reset the planner onto its own position, which inside a joint interpolated segment is the chord between the world ends, and forgot the joints; the servo thread then inverted the chord point and the joints jumped there in one cycle (on 5axiskins, pivot 400, Y at 166670 mm/s). The joints the segment stopped on are now handed out as its end, and the planner takes the point the servo thread found from them, so the machine stays where it stopped.
The slider left joint interpolated segments alone, since they run in joint units. It now caps the tool's chord between the ends, measured as canon measures a straight move (X Y Z, else U V W, not at all for the rotaries alone): the segment takes at least as long as the chord would at the slider's speed. With the slider at 10 mm/s a G53.4 over a 100 mm chord takes 10.07 s instead of 1.04 s.
The conventions chapter said the two poses that reach one tool axis differ in the sign of the secondary rotary. That holds for two head rotaries only; on two table rotaries it is the one carrying the other, and on a head and a table it is the head, as G53.1 P now uses.
Motion checks the joints against [JOINT_n] MIN_LIMIT and MAX_LIMIT at the end of a move, so a move whose ends are both inside the travel can still pass a limit between them: a turn of a tilted head under the tool centre point swings the carriage round the tip, and an arc bulges past the travel its ends stay inside. Motion then stops on the soft limit partway through the move, the joint past its limit, to be jogged off it in joint mode. Rimas2200 bounded the joints along a move module by module in the first version of LinuxCNC#4619; this follows them on the sampling canon already does, for every module evaluated outside realtime. The identity samples nothing, as before: its joints are the axes, and its arcs are what LinuxCNC#4619 bounds. motion_planning/segment_cap.cc follows each joint against its travel on the samples segmentCap() takes for the joint velocity and acceleration cap: a sample past a limit, or between two samples the peak of the cubic through their positions and rates, read again through the inverse at that peak. A peak near the limit that the inverse does not confirm splits the stretch there, a bounded number of times. Only a point the inverse itself puts past a limit refuses the move. A side of the travel the joint starts the segment past is not followed, the move may be bringing it back, and neither is anything beyond a sample the module cannot invert or a joint moving between two points further than its rates allow, as where the inverse changes branch. The limits are the ones motion holds, ini pins included, on the joints that are homed. Canon drops the refused move and every move after it until task has taken the refusal, since a move sent after a dropped one would start from where that one began. Task reads it after the interpreter reads or executes a line and fails the line the way an interpreter error does, saying which joint would pass which limit, by how much and where along the move. A line stepped over on the way to the start line of a run lets its refusal go with the rest of its output: its moves are thrown away, and were sampled from wherever the machine stands. RIGID_TAP flushes the chained segments before it is sampled, so that the tap is read from where it starts. kinslimits takes --min and --max and prints where a joint leaves its travel. tests/kins-limits/travel swings a carriage 0.05 past its Y travel at a peak between two samples, 0.05 inside it and well past it, asks for the refused swing twice and runs moves inside the travel after it, refuses a move for its end, runs an arc under the travel and refuses it over the top, fails a program on the line of the swing with no joint past its limit, and runs the same program from the line after the swing, which only steps over it. Without the reading between samples the 0.05 swing runs onto the soft limit; without the exemption for stepped over lines the run from that line fails.
Motion evaluates the module's work and tool frames at the commanded joints every cycle and publishes transpose(work) * tool, the tool's axes seen from the work, as stat.tool_frame. An identity kinematics type turns neither frame whatever the rotaries do, so under one, the machine frame type G13.1 selects, motion takes the frames the primary type has at the same joints: switchkins gains kinematicsToolFrameOf() and kinematicsWorkFrameOf(), the frames of a type other than the one in force, and a display under G13.1 shows the tool against the part as the tool centre point kinematics has it. The frames are resolved weakly, as the Jacobian is, and with a module that exports none stat.tool_frame is None. This is where a GUI reads where the tool points instead of guessing it from [DISPLAY] GEOMETRY, and what a jog in the tool frame will need.
…ports Where status reports a tool frame the preview draws the tool marker along it and leaves GEOMETRY aside, so a machine whose rotaries GEOMETRY cannot describe, a nutating table among them, shows the tool as it stands against the work. GEOMETRY stays the fallback under an identity type and for modules without frames. Requested by Sigma1912.
A world jog of X, Y or Z moves along one of three frames the operator selects: the machine's axes, the axes of the active tilted work plane, or the tool's own axes as the kinematics module reports them. A program never changes the frame. It is selected by the new NML message EMC_TRAJ_SET_JOG_FRAME, in Python by jog_frame(), and reported as stat.jog_frame and motion.jog-frame. The machine frame is the machine's own X, Y and Z, which do not turn when a table does. Under a tool centre point type whose work frame turns with a joint, a rotary table or a work spindle, X, Y and Z are the part's, so a machine jog there runs through the frame planners turned by the transpose of the work frame, and a jog of X moves the X slide alone whatever the rotaries stand at, as on Heidenhain (M-CS) and Siemens (MCS/BCS). Whether a type turns the work is worked out once per type, by turning each joint a little. The part's axes stay one selection away: the plane frame with no plane in effect is the X, Y and Z of the kinematics in force. Task hands the plane to motion as it executes the plane's message, in program order, turned by the XY rotation of the coordinate system it was defined in. Only a change is sent, so a program without a plane sends motion nothing. Motion plans a frame jog with a planner of its own per frame axis. Each cycle its step, turned into the world, is added to the X, Y and Z teleop planners, so those keep holding where the machine is and a machine jog after a frame jog starts from there. One frame axis moves at a time, and a jog of another waits until the one under way has stopped: two at once would move each world axis by their sum, past what the limits along either allow, and run into the travel along neither one's way. The frame is taken when a jog starts and kept until it ends, and it cannot change while a jog is under way or slowing down. Every world axis keeps to its own [AXIS_L] velocity, acceleration and travel, and the joint cap reads the jog in world coordinates. The travel of an axis not homed is one range either way of where it stands when the jog starts, as for a plain jog of it. The tool frame is the one the kinematics reports whatever plane is in effect: jogging along the plane's axes is what the plane frame is for. Under an identity kinematics type, the machine frame type G13.1 selects, it is the primary type's at the same joints, so a tool jog moves along the tool as the head stands, the move it makes under the primary type: Fanuc's tool axis direction feed (0i-F operator's manual, 1.1.1), Heidenhain's Manual Operation Tool Axis and Siemens' JOG after a swivel move along the tool without their tool centre point either. tests/twp-jog-frame checks the plane and the tool frames on 5axiskins against status, that a plane leaves the tool frame alone, the speed and travel limits along a turned axis, a second frame axis waiting its turn, and the refused change of frame, and samples every cycle for the world axes' speed and acceleration, on the trapezoid and the S-curve planner; then on trivkins not homed, the travel a plane jog gets; then on a trunnion under the tool centre point with the table turned and tilted, that a machine jog moves one slide and a plane jog with no plane the part's axis. On the head machine a tool jog under the identity type moves the joints as it does under the primary type from the same joints. Requested by Sigma1912.
halui.jog-frame.machine, halui.jog-frame.plane and halui.jog-frame.tool select the frame X, Y and Z jog along in world mode, for a pendant, and halui.jog-frame.is-machine, -plane and -tool report the frame in force. tests/twp-jog-frame selects each frame through the pins.
The View menu lists the three jog frames after joint and world mode, % cycles through them as $ toggles the mode, and the status bar shows the frame next to the position type while it is not the machine's.
…inematics Under the machine frame kinematics, what G13.1 selects, a plane was in the machine frame and only head rotaries could orient the tool to it, so a table machine could not do 3+2 there: G53.1 refused a plane that needed the table. Sigma1912 asked for it on a mill-turn, the rotaries set once and the slides alone moving, without the TCP kinematics dragging the slides along on every rotary move. This is Fanuc's second feature coordinate system (0i-F 5.7.3.1). The interpreter takes a fixed map from the module's primary kinematics to the machine frame with the rotaries held, forward on the machine frame type of the primary type's inverse, read off at four points, and applies it after the whole offset chain. It carries the tool length along the tilted tool, which the primary type applies and the machine frame type does not. G68.2, G68.3 and G68.4 take it at the rotaries as they stand; G53.1, G53.3 and G53.6 orient the tool on the primary type's frames, so a table rotary helps, and take it where they end; G43 and G49 in the plane take it again with the new length, the slides staying, and the same offset given again keeps it; a kinematics switch in a plane takes it again or drops it. A rotary word keeps it, as Fanuc, Heidenhain and Siemens keep their plane: Fanuc's manual warns against turning a rotary in a tilted working plane and re-issues the plane after one. Canon gets the plane with the map folded in, so canon's maths and motion's are unchanged. Status keeps the two apart, as the standard controls do: g68_offset and g68_rotation stay the plane as defined under every kinematics type, as Fanuc's #_FCOORD and Heidenhain's FN 18 ID210 report it, and the map goes in the new g68_kins_offset and g68_kins_rotation, as Siemens keeps CYCLE800's workpiece reference frame apart from its table and tool reference frames, the identity where there is none. Task turns the plane jog frame by it, and the DROs of glcanon and halui and the preview's plane drawing compose it after the offset chain. SET_G68_FRAME carries both forms. Where a plane comes back from status, the preview restating the machine's plane and an abort with RETAIN_WORK_PLANE, the interpreter takes the map status has, so a rotary turned since counts; preview_helpers restates G43 ahead of the plane for it. tests/twp-static runs the same programs under the TCP kinematics and under G13.1 on a table machine and a head machine and checks the joints agree, then the rotary word, G68.4, G53, G69, a switch in the plane, the status split across a switch, the plane jog frame, an abort and the preview, after a rotary turn too.
Under the machine frame kinematics, which G13.1 selects, the preview drew the slides' coordinates. With the table or the head tilted, the program, the backplot and a tilted work plane were drawn away from where the tool cuts the part, while the tool marker leaned as GEOMETRY has it against the part. Sigma1912 reported it on the xyzab-tdr sim with 3+2: the plane lay flat after G53.1 had turned the table. Under the TCP kinematics the same program was drawn on the part, so a switch moved the whole picture. The standard controls keep the part fixed and move the tool: the Siemens simulation holds the workpiece fixed in space whatever the machine type (milling operating manual 10/2020, 7.1), and the Heidenhain graphic depicts the workpiece as it is machined, with the machine model a view of its own. The primary kinematics type knows where the tool stands against the part for any joints. switchkins exports kinematicsForwardOf(), the forward of a type other than the one in force, and motion reports the primary type's forward of the commanded joints as part_position in status: the commanded position itself under that type, so a switch moves nothing. The backplot draws it minus the tool offset, and the tool marker stands at its end. The preview's interpreter maps each drawn point the same way, outside realtime: the joints the type in force puts the machine in, through the primary type's forward. A rotary move is drawn as the curve the tool sweeps across the part, a move after a rotary word in a plane where it cuts the turned part, and the plane as defined. The extents the axis limits are checked against stay on the slides. The loader read every pin of the kinematics by name for each point, so kinematicsUserHoldPins() reads them once for a preview: 60000 moves preview in 0.10 s under G13.1, against 0.06 s under the TCP kinematics and 7.5 s without the hold. tests/twp-static draws the same 3+2 program under G13.1 and under the TCP kinematics and checks one plane in the same place and the same end of the cut. With no plane it turns a rotary under G13.1 at a tilt and checks the preview through the point the machine passes half way and where it ends, and that the live plot and part_position agree with the TCP kinematics at the same joints.
A switch moves no joint, but the point changes: on the machine the queue drains and task reads the new point from motion. A preview has no motion to ask, so its interpreter went on from the old type's coordinates under the new type. At a tilt the moves after a switch were drawn tens of millimetres from where they run: on the xyzac-trt sim at A30 C20, G91 G1 X5 after G12.1 P1 was drawn ending at X15 Y5 Z20 instead of X53.8 Y72.5 Z-20.9. In a tilted work plane taken again at the same joints the program's coordinates do not change, which is why a switch in the plane already previewed right. The preview's interpreter now takes the joints before the switch and reads the point from them under the new type, for G12.1, G13.1 and the switch G49 makes after G43.4. The renderer takes that point from the interpreter at the next thing it draws, so the drawing goes on from where the tool is, in the same numbers the next move starts from. Task does not read the point back: it gets it from motion, as before. tests/twp-static switches both ways at a tilt with no plane and checks that the move after the switch ends where the machine ends it.
The frame entry points take the joints alone. A parallel machine has its closed form in the inverse: the orientation of its platform is the A B C of the pose, and the joints give it only through an iterative forward that needs a seed, which the frame functions have no argument for. So genhexkins and pentakins could report no frames, and got none of what reads them: the status tool frame, the tool jog frame, G68.3, the G53.n solve. A type may now state its frames from the pose instead, as pose_work and pose_tool in its ops table, and declare the letters that orient the tool in orient. kinsOpsWorkFrame() and kinsOpsToolFrame() take the caller's scratch, read only. On such a type they use the pose of the last forward or inverse while the joints are still those, which is motion's case every cycle: the pose it just inverted, or the forward of the commanded joints in free mode. Otherwise they run a forward on a copy of the scratch, started from that pose, so the caller's seed does not move. The At forms take the pose a caller already holds. No forward runs for the status frame. This matches the controllers that drive these machines: PI, Siemens (the setpoint orientation, $AC_TOOLO_ACT), Turbo PMAC and the NIST hexapod all work from the commanded pose, with the orientation as virtual axes. switchkins records the pair for a forward of a type not in force too, so the primary type's frame under the machine frame type reads the pose motion found for it. kinematicsUserOrientAxes() answers the declared letters on such a type, all of them a head's.
genhexkins' tool frame is its platform frame, Rz(C) Ry(B) Rx(A) as its inverse builds it, the frame PI's hexapods call the tool frame and Fanuc's robots write as W P R. The tool length goes onto the platform joints' z, so platform z runs from the tip towards the holder, the convention's tool axis. The orienting letters are A B C. pentakins' is its effector frame, Ry(B) Rx(A). The ring joints leave the turn about the spindle free in the machine, so x is the chain's, machine x turned by Ry(B): the unit vector square to the spindle in the machine's Z/X plane, the choice Siemens' TOFRAME makes (MD21110). To every consumer the pentapod is a head with B carrying A, and orients with A B. Neither turns the work. On the hexapod sim the status tool frame now follows the platform: at A10 B-5 C15 it is the rotation those angles describe to 1e-16, and under M429, the identity type, it is still the genhexkins frame. tests/kins-frames checks both modules from poses over the sim configs' geometry and travel: the frame at the joints of a pose is the rotation its angles describe, the tool length goes along the reported axis in the module's own maths, and joints a cycle away from the last inverse get the frame of their own pose, with the last answer left alone. Composing the frame in the other order fails all three on both modules; using the last pose for any joints fails the third. tests/kins-params checks the letters, A B C and A B.
The X and Y of a tool offset turn with the tool, (I - R)(x, y, 0) added to the module's pose with R the tool frame in work coordinates. The shared code read R from the joints, and so passed over a type with pose frames. On such a type R is a function of the pose, which every caller holds, so the turn needs no joints. The inverse takes it off the pose it is asked for and calls the module once, where a type with joint frames has to iterate, the turn following the joints it is solving for. The forward adds it to the pose the module found. The Jacobian is the module's at the pose less the turn, times (I - D), D the change of the turn per pose coordinate, read from the pose frame by differences. A forward of the joints the last inverse or forward answered for now starts from that answer's pose on such a type, so it only confirms it, and the interpreter's forward of a solution it just inverted cannot settle on another assembly of the struts. tests/kins-params runs genhexkins, with the Jacobian closed and differenced, and pentakins with an offset across the tool: the tip, the joints back from the pose and the Jacobian against the inverse's differences. Taking the turn off the inverse's pose, adding it to the forward's, or applying (I - D) each fail it on both modules when left out.
The box is the machine frame envelope: the carriage, or a robot's flange, the frame that does not move when a head tilts or a tool is loaded. A module that names no machine frame type has its identity type stand in, which is the truth on a machine whose slides line up with its frame. A parallel machine has struts, not slides, and its identity type is the strut lengths read as X Y Z A B C. So on genhexkins the box was read on the struts, and every move from home was refused as past X's and Y's limits as soon as the axis travel was narrower than the struts' length; the hexapod sim only passed because its limits are 1000. The machine frame of a parallel machine is now its platform: the pose of the joints with the tool left out, as a robot's is its flange. kinsOpsMachinePose() runs the type's forward on a copy of the block with no tool, started from the pose the frames read, and switchkins and kins_single.c answer kinematicsMachineFrame() with it for a module with a type whose frames come from the pose. The box then limits where the platform goes whatever tool is loaded, as it limits the carriage of a head, and PI's soft limits on its hexapods are limits on the platform's pose. The struts keep their [JOINT_n] limits, the machine axes Siemens' software limit switches act on. The identity type keeps its flag, so G13.1 still selects it, which tests/kins-switch-unsolved does. tests/twp-parallel, in the next commit, runs the hexapod and the pentapod with their axis travel round the sims' workspace and a long tool on a tilted platform: a move whose tool pose is inside the box with the platform past it is refused, and one whose tool pose is past it with the platform inside is accepted. With the box on the tool's pose both fail on both machines.
G53.6, G53.3, G53.2 and G43.5 ask the tool frame inverse for joints that point the tool along a direction, and the shared search moves the joints that turn the frame. A parallel machine has none: its struts set the pose, and the pose's own angles orient the tool, A B C on genhexkins, A B on pentakins. On a type with pose frames the loader now runs the same search over the letters that orient, X Y Z held where the machine stands, and each answer goes to joints through the inverse. Angles that put the machine in the same place, the two sets of roll, pitch and yaw for one platform orientation or the pentapod's (180 - A, B + 180), are one answer, under the angles nearest where it stands. Tool x is left to the reported turn, as on a head, so the plane's X is the coordinate system's. On the hexapod the tool axis alone leaves the turn about it free, and the search's point on that curve is no policy (it moved C by up to 8.5 degrees from the start). The platform takes the shortest rotation from where it stands, with no turn about the tool. That is the hexapod treated as a five-axis machine, as the NIST Ingersoll hexapod was "programmed as if it were a 5-axis machine", and as Siemens makes the turn about the tool a word of its own, THETA. G53.1 is refused on such a machine. It turns the rotary joints and holds the linear ones, and with every joint a strut that is no move at all. Fanuc defines G53.1 for machines with rotary axes only (0i-F, 5.7.1), and Heidenhain's TURN positions the rotary axes alone while a machine without them has STAY only (TNC 640, 11.2). The message points at G53.6, G53.3 and G53.2. tests/twp-parallel runs the hexapod and the pentapod sims' geometry: G53.6 brings the tool axis to the plane's normal with the tool point still, the hexapod by the shortest rotation and the pentapod's tool x in the machine's Z/X plane; G53.1 is refused and moves nothing; G53.3 ends at the point in the plane on the normal; G68.3 reads the normal back off the tool. Solving for the whole frame of the turned platform left out, so the search picks its own point of the curve, fails the shortest rotation check; without the refusal G53.1 fails.
…orce has them kinematicsForwardOf() ran the bare forward of a type written on the parameter block, without what kinsOpsForward() adds around it: the offset across the tool turned with the tool, the seed of an iterating forward, the pose a type with pose frames reads its frames from. Under the machine frame type the part position in status is the primary type's forward of the commanded joints, so a tool with an X or Y offset put the point it reports off the tip the primary type places. It now runs kinsOpsForward(), the same call kinematicsForward() makes for the type in force. kinematicsInverseOf() is its inverse, through kinsOpsInverse(), seeded from the joints handed in, for a move that holds the tool tip on the part while the machine frame type stays in force: it follows the primary type's inverse. kinematicsInverse() goes through the same helper for the type in force. Exported by modules built on switchkins.c alone, as kinematicsForwardOf() is.
… type A joint interpolated segment can now be a line on a kinematics type other than the one in force: the pose runs straight from one end to the other as that type has them, and every servo cycle the joints are that type's inverse of the pose at the planner's progress, seeded from the last cycle's. The segment keeps the joints the servo thread solved, so the next cycle starts from them and an abort holds them where the machine stopped, through the path the joint segment already has for that; the ends are the joints given, exact. The position in status is the forward of the type in force, as for any joint segment, and nothing blends into or out of it. EMCMOT_SET_JOINT_LINE carries the type, the two poses, the length canon measured the line with and canon's velocity and acceleration for it, and EMC_TRAJ_JOINT_MOVE carries them from canon. Motion checks that the type's forward of the joints the queue ends on and of the joints given are the two poses, angles on the turns the interpreter gave, so that a rotary goes the way the program asked, and refuses the move otherwise: the queue does not end where the interpreter solved it, or the tool is not the one it solved with. Each joint's limits scaled onto the line's length still cap it, since a joint has at least its own distance to go over that length, below canon's caps. An inverse that fails part way stops the machine as one under the type in force does.
…ine frame kinematics Under the machine frame type G13.1 selects, G53.6 kept the tool tip at the ends of its move only: it went as a straight traverse in the machine frame, the slides in straight lines while the rotaries turned, and the tip left its point in between, by L(1 - cos(t/2)) for a tool L long turned through t, 3.4 mm for a 100 mm tool tilted 30 degrees. Sigma1912 reported it. Switching to the TCP type for the move and back after would hold the tip, but an abort or a feed hold part way leaves the machine on a type the program did not select, with nothing to put it back. Fanuc gives G53.6 in G49 mode and keeps the tool centre point throughout; Heidenhain's PLANE MOVE keeps the tool and the workpiece where they are relative to each other with a compensating move, without M128. Neither switches anything. So G53.6 under G13.1 is now the primary type's line from where the tip stands to the same tip at the orientation found, which motion follows with that type's inverse while G13.1 stays in force: the same move G53.6 makes under the TCP type, joint for joint. The solver leaves the linear joints where they were, so the joints at the end are the primary type's inverse of the tip at the new orientation. Under the TCP type G53.6 is the straight traverse it was. KINS_TYPE_TRAVERSE carries it to canon, which plans it as the type would plan the line in force: canon's measure, the axis limits, and the joint velocity, acceleration and travel along it sampled through that type's kinematics, from where the line starts rather than from canon's end point, which is on the type in force. The preview draws it as that line on the part: the renderer under G13.1 maps the points of a move onto the part through the primary type, and the slides' straight line between the ends would come out as the loop the tip no longer makes, so a part_traverse is drawn straight between its ends on the part, as under the TCP type. tests/twp-static: status part_position stays within 1e-3 of its point all along (4e-5 measured) G53.6 under G13.1 on both the table and the head machine, and the move ends on the joints the TCP type ends on, G13.1 still in force. An abort and a feed hold part way leave the tip on its point, and the machine goes on from there to the same end. A slide that peaks half way round, the tool turning from one side of the vertical to the other, refuses the move under G13.1 as under the TCP type when its limit is set between its ends and its peak. The preview of a G53.6 under G13.1 draws the same vertices as under the TCP type, to 1e-3 (identical as measured); drawn as a plain traverse it is 11.4 mm and 7.3 mm off. With the joints interpolated straight between the ends instead, the tip leaves its point by 11.4 mm on the table and 7.3 mm on the head and the test fails.
Under the kinematics G43.4 or G43.5 switches to, the current point is the tool tip, with the tool length along the tilted tool. G92 made that point the coordinates given and kept the offset after the G49, where the same numbers name another point: the spindle nose, or wherever the tilt put the length. G10 L20 did the same into a coordinate system. M6 changed the tool under the tip the program follows, so the point moved with the new length. A plain G43 changed the length and ended G43.4's hold on the kinematics, so G92 then passed with the machine still tilted on them and G49 no longer switched back. All of them ran without a word. Fanuc refuses G92 and G43 in tool centre point control (PS5421, not in its list of G codes allowed under G43.4/G43.5), Siemens refuses PRESETON on axes under a transformation (17600), and Heidenhain resets M128/TCPM before a TOOL CALL. G92, G92.1, G92.2, G92.3, G10 L20 and M6 are now refused while the switch G43.4 or G43.5 made is in effect, with a message to cancel it with G49 first; G43 is refused with a message to change the length with G43.4 H (G43.5 H under G43.5). G43.1 and G43.2 change the values of the offset in effect, the only way to give an offset across the tool, and keep the switch. G10 L2, G52 and G54 to G59.3 stay: they read no position, and Siemens and Heidenhain allow their frames and datum shifts under TCP (Fanuc refuses G52 and G54 to G59 too, tied to its own TCP coordinate handling). The rule follows the switch G43.4 made, not the kinematics type: once G12.1 or G13.1 has selected a kinematics the program has taken it over, and a hexapod or robot running G43 on its primary type keeps its codes; on a machine without switchable kinematics G43.4 is a plain G43 and nothing changes. Under a tilted work plane G92 and G10 were already refused and G52 shifts the plane. convert_g92_is_applied's result was stored and never checked, so every refusal of G92.1, G92.2 and G92.3 was dropped and the code did nothing without saying so: under cutter radius compensation and under a tilted work plane as well as here. It is checked now. tests/kins-switch: under G43.4, G92 X1, G92.1, G92.2, G92.3, T1 M6, G43 H1 and G10 L20 are each refused with the message; G43.4 H1, G10 L2, G52 and G55 pass, and after G43.1 or G43.2 G92 is still refused; under G43.5 G92, M6 and G43 are refused; after G49, and under G43 on a type G12.1 selected, G92, G92.1, M6 and G10 L20 pass. Each refusal aborts and the test's abort routine selects G13.1, so G43.4 is programmed again before each one. Without the change 12 checks fail; 12 of 12 runs pass and 12 of 12 fail as expected without it, at 6 copies in parallel with 4 busy loops, and 3 of 3 pass and fail likewise with 3 copies and a busy loop all on one CPU.
1243bd4 to
35e34b7
Compare
…sample them, for any caller Canon sampled each segment through kins_line_pose, kins_arc_pose and kins_samples, statics in emccanon.cc. They move to libkinslimits beside segmentCap() as segmentLinePose, segmentArcPose and segmentSamples, so that the preview can follow a move on the same samples task does. The arc carries the scale from its circle's units to the poses' units, which canon had in TO_EXT_LEN, and the sample count takes the angular axes as a mask. No change in what canon samples.
Task refuses a move whose joints leave their travel along it, but only when it reaches the move, partway through a program. The preview now follows the joints along every move the same way when a program is loaded, and AXIS lists the moves it finds in the warning it shows when Run is pressed, next to the axis extents, by line and joint; Run Anyway stays, and the refusal at run time stays behind it. None of Fanuc, Siemens and Heidenhain refuses a program at load: Fanuc and Siemens check each block before it moves, which canon does, and Heidenhain's simulation checks the software limits on request, which the preview and this warning are in LinuxCNC. gcodemodule follows each straight move, arc, joint move and G53.6 line under G13.1 through the interpreter's kinematics context, on the kinstype the move runs under and the tool in force, with segmentCap() on the samples canon takes, against [JOINT_n] MIN_LIMIT and MAX_LIMIT from the INI file. The joints a move ends on start the next, as a kinematics switch and a tool length change keep them, so a move starts where the machine stands; a joint move is checked at its end, where the joints go straight; the first move of a parse, a GUI's restating move to where the machine is, only places the joints from the machine's own and is checked at its end. A move a joint leaves its travel along calls the canon's joint_travel(line, joint, side, where, limit); GLCanon keeps them in joint_travel_left. On a machine whose kinematics is the identity nothing is loaded: each joint is the axis the coordinates= of [KINS] KINEMATICS maps it to, trivkins' XYZABCUVW where none is given, checked at the end of every move and, on an arc, at the angles where X, Y or Z turn back on the circle, which catches a gantry's second joint with less travel than its axis. A module that cannot be evaluated outside realtime is reported once with line -1, which AXIS leaves out of its dialog. The kinematics context, the length units, the identity question and the tool offset are read once a parse, or when the offset changes: asked on every move, the calls into the GUI's python cost more than the following. On 50000 five axis moves the parse takes 0.34 s instead of 0.17 s, the rest being the inverse and the Jacobian at each sample; on an identity machine no difference shows. tests/kins-limits/preview previews programs under halrun with 5axiskins and a 400 mm pivot: the swing of tests/kins-limits/travel past joint 1's travel both ways round, with its peak 0.05 past between two samples, and not 0.05 inside; a first move not followed from zero and one whose end is past; an arc over the travel one way round and not the other, in XY and in ZX; a joint move ending past; a move on the identity type; and on a trivkins gantry whose second Y joint has less travel than the axis, a line ending past it and an arc whose top is, not the arc the other way round. Without the report 9 of its cases fail.

Draft. Where the multiaxis kinematics work is going, open early so it can be argued with. As pieces are agreed I split them into small PRs, rebase this, and carry on, so nothing stalls behind it.
Status (2026-11-02)
Merged so far: #4372 (switchkins, more than three types), #4414 (
G12.1/G13.1), #4455 (a module reports where the tool points), #4546 (six kinematics fixes), #4548 (G43.4/G49over the switch), #4550 (the Jacobian entry point, every module), #4551 (the tilted work plane maths split per machine), #4581 ([AXIS_<letter>] TYPEin the interpreter, canon and motion), #4608 (the nutating sims' third kinematics type), #4600 (the tilted work plane,G68.2/G68.4/G69, the preview drawing it,G52in a plane,[RS274NGC] RETAIN_WORK_PLANE, and a work offset change that takes the plane along), #4595 (switchkins as one implementation for.c,.compand out of tree), #4594 (the GUIs and the preview byTYPE), #4586 (pumakinswrist), #4673 (the tool frame helpers checked in realtime), #4679 (the preview's first plane cut), #4702 (every module as a parameter block and pure functions, callable outside realtime), #4685 (a non-finite inverse no longer hangs the servo thread), #4676 (genserkinson posemath), #4724 (thegenhexkinsforward as a true Newton iteration), #4708 (pumakinsarm joints).Open now: #4730, item 1 below. This branch is master plus what follows.
Next, one PR at a time:
maxkins,xyzab_tdr_kinsandgenserkins, the point-to-point moves and the orientation codes,G53.1toG53.7andG68.3, then the tool offset under a tilt,G43.5,G28.5/G30.5and the machine frame (24 commits). Open as interp, motion: the point-to-point moves and the tool orientation codes, G53.1 to G53.7 and G68.3 #4730.maxkinsbox read on its carriage (7 commits).TYPE, several primary types andtwinspindlekins, thegenserkinsJacobian from its DH frames, and the planner fixes for joint-space segments (10 commits).genhexkinsandpentakins, read from the pose, with the tilted work plane on a parallel machine (5 commits).G53.6under the machine frame kinematics holding the tool tip all along its move (3 commits).G92,G10 L20,G43andM6held back whileG43.4is on, the rest of item 8 (1 commit).Then the load-time half of item 7, and item 9.
Problems found in the existing code on the way, and where they were fixed:
G53and theG28/G30return ignored the rotary tool offset on axes that are not wrapped, since 2009: rs274ngc: back the rotary tool offset out of machine coordinates #4416.pumakinslet joints 4 and 6 jump a full turn when the wrist crossed a half turn: pumakins: keep joints 4 and 6 within a half turn of the previous value #4586. Joints 1, 2 and 3 do the same, and can be a turn off anywhere: on the puma sim, the waist at 169 comes back as -191 when world mode is turned on: pumakins: keep every joint within a half turn of the previous value #4708.genhexkinsforward moved its angles by an angular velocity step rather than a Newton step: 12.6 iterations from a one-cycle seed and one solve in ten failing at a large tilt, against 3 and none: genhexkins: take the forward's Newton step in roll, pitch and yaw rates #4724.G92.1,G92.2andG92.3dropped their own errors, so one refused under cutter radius compensation or a tilted work plane did nothing without a word: fixed here, with item 8.genserkins' inverse took the whole Newton step and could converge on a pose whole turns away, which the joint limits then refuse; motion checked a move's joint limits from the joints the machine stands in rather than where the queue leaves them, so a short program was checked from its starting pose; and the preview never read its point back from the joints at a kinematics switch, so afterG12.1orG13.1with a rotary tilted it drew the rest of the program 40 to 70 mm away from where the machine goes.Plan, in order:
Switchkins: one implementation, used by
.comp,.c, in tree and out of tree. The registration half merged as switchkins: allow more than three kinematics types #4372; the rest is in this branch.Small fixes that stand on their own:
5axiskinsgets a.tool-offsetpin like every other module,maxkinsgets one or a doc note saying it has none, andG53stops ignoring the rotary tool offset on non-wrapped axes.The kinematics interface learns to answer two more questions: where the tool points, and how joint motion maps to world motion. One entry point each, both weak, both defaulting to today's behaviour, and a tool frame convention written down so every module answers the same way rather than each author choosing one.
Those answers, and forward and inverse alongside them, become pure functions of what the caller passes in: joint values, a parameter block carrying the tool and the kinematics type, and no state kept inside the module. Today the maths reads its geometry from HAL pins and keeps scratch in
haldata, so the only way to call it from anywhere else is to write a second copy of it. One implementation, parameters supplied at the call site.Two stages the offset chain was missing. Both came out of the discussion below, both wanted 3 and 4 first, and both are in this branch now.
A frame composed on top of the active coordinate system, with its own cancel, so the blocks in between are programmed in the tilted plane while G54 itself is untouched. Fanuc spells this
G68.2, and that is the spelling here:G68.2in its four forms,G68.3reading the plane off the tool,G68.4composing onto the active plane,G69cancelling. In tree it is a stage inrotate_and_offset_pos()plus modal group 9. Nothing is rewritten, so nothing needs restoring: an abort cancels it unless[RS274NGC] RETAIN_WORK_PLANEkeeps it, as Fanuc's D3R does, andon_abort_with_twp_reset.ngcis gone with the rest of the remap. As in the Fanuc manual,G52in a plane shifts it along its own axes, and with Fanuc's 3TW behaviour a work offset change takes the plane along.A move whose target is defined but whose path is left to the joints: run the inverse once at the endpoint, then interpolate the joints to reach it, the slowest setting the pace. That is how the commercial controls cross a singularity or flip a head without changing coordinate system. In tree it is one segment type in the planner,
JOINT_TRAVERSEandJOINT_FEEDin canon, and three non-modal codes onG0andG1, likeG53, each stripping one more layer of interpretation from the destination:G53.4to a program point, offsets and plane applied, the form a post writes;G53.5to the machine frame, the carriage or a robot's flange in machine coordinates with the tool left out, in program units and with no offset, the form for parking and tool change;G53.7to joint values,J2=-5sending joint 2 to -5 in the joint's own units with nothing converted, the form that works on every machine. Which kinematics type is the machine frame the module declares with a flag,KINSTYPE_MACHINE, next toKINSTYPE_IDENTITYandKINSTYPE_PRIMARY: the carriage on a mill, the flange in the base frame on a robot (ABBtool0, KUKA$NULLFRAME), wherepumakinsandgenserkinsflag their arm type.G13.1andG49cancel to it, so a robot stays on its arm kinematics and onlyG12.1 P1selects raw joints. A module that declares nothing has its identity type stand in, and there a letter carries a unit class and a joint does not, soG53.5is refused where the letters name joints of the other kind, which[JOINT_n] TYPEdeclares. WithG1the move takes the time the straight move would at F.G53.1,G53.3andG53.6orient the tool to the plane, andG53.2solves the same orientation without moving, publishing the pose on the read-only parameters#<_orient_x>to#<_orient_z>,#<_orient_rot1>to#<_orient_rot3>and#<_orient_valid>, mirrored on#5071to#5080, so a program can read the answer and use it in a move of its own.Ppicks the solution nearest first andQsays whether the joints that carry the work take part; the module's tool frame inverse answers, so no config carries the formula, and the twotrsrnconfigs run their demos with no remap left.Between them these are the reason not to add a set of offsets per kinematics type. Controls do let you add coordinate systems, Fanuc
G54.1 P1toP48, SiemensG505toG599, and none of them ties a set to the active kinematics or transformation: selection is explicit in the program, or it belongs to a channel. More coordinate systems is a fair request on its own; binding them to the kinstype is the workaround for these two absences.Limits taken from the joints instead of from static per-axis INI values. Joint limits are the physical fact. The region they imply in axis space depends on the tool and on where the rotaries are, and is not a box, which is why a box in the INI cannot describe it and config authors have to pick a conservative one and lose travel. Built now, in three parts, each with a test that was red before it.
Travel is the
[JOINT_n]limits, every move, every kinematics type. The[AXIS_L]box is the machine frame envelope: the carriage, or the flange, in machine coordinates, the frame that does not move when the head tilts or a tool is loaded, which is how Siemens and Fanuc read axis limits, machine-axis space after the transformation. Every move is checked against the box where its joints put the machine frame,G0andG53.5alike, so a tool centre point move whose tip lies past the box with the carriage inside it is accepted and aG53.5to a carriage position past the box is refused. No box on tip coordinates: the tip is not the carriage, and a tip fence would be a feature of its own. One box, one meaning, so nothing needs resetting at a switch, which retires thesetp ini.L.min_limitthe docs prescribed and the puma's M129 workaround. A module that declares no machine frame keeps the box on the world in force, as today. The one behaviour change for an existing switchkins config that declares only its identity: under TCP the box moves from the tip to the carriage.Velocity and acceleration come off the Jacobian. Canon samples every segment through the module's Jacobian ahead of motion, on the kinematics type and the tool offset the program is under, and lowers its velocity and acceleration to what the slowest joint can give within its own limits: the rate of each joint along the tangent, the share the changing rate takes at that speed (what a carriage on a circle feels as centripetal), and the bulge between samples so a peak between them is covered. Lines and arcs, traverse and feed, probe and rigid tap. A turn of a tilted head under the tool centre point, which swings the carriage through a circle the head's own limit never mentions, runs at the carriage's pace instead of tripping the carriage's following error; a full circle of the tip with C turning a revolution under it rides both. World jogs get the same in motion, every servo cycle, from the Jacobian at the joints the machine stands in, the planners capped so no joint is asked for more than its limits and the jog keeping its direction.
[AXIS_L] MAX_VELOCITYandMAX_ACCELERATIONkeep meaning the world of the type in force; the joint cap sits on top. A module that runs only in realtime, one without the parameter block form, caps nothing, as before. A pole, where a joint would have to move at any speed for the path to advance, gives an operator message and the move crawls rather than being dropped, since a dropped segment sends the next one along another path; refusing it ahead of time is item 7.The same query finishes a repair the tool orientation codes already needed. Solutions are now unwrapped onto the nearest turn of the current pose before ranking, so a C at 180 asked for a pose at C -90 goes to C 270, 90 degrees away, instead of turning 270 degrees the long way. The ranking also keeps each rotary inside its
[JOINT_n]travel, and inside the[AXIS_n]travel of the axis it turns unless that axis wraps: a pose goes onto the nearest turn that lies inside the limits, the long way round where the short way runs out, and a pose no turn brings inside is dropped before ranking. A program that winds a rotary, an incrementalG68.4plane in a loop withG53.6, is refused at plan time with that reason, where before motion refused it partway through.The joint limit check moves earlier and samples inside segments. It exists already, in
inRange(), but only at move endpoints and only once the block has reached motion, so a program that will overtravel says so partway into the cut rather than before it starts. With 4 in place the same check runs at load time, against the tool and the kinematics type each block will actually use. A verify button in a GUI is then a presentation of that, not a subsystem of its own.The run-time half is built. Canon follows every joint along each move on the samples the segment cap of 6 already reads: between two samples it looks for a joint peaking past its travel, from the joint positions and rates at both ends, and reads the inverse at the peak to confirm it. A move that would take a joint past
[JOINT_n] MIN_LIMITorMAX_LIMITanywhere along it, or end there, is refused before it reaches motion, with the line, the joint, where it would go and how far along the move; the move and everything after it are dropped and the program stops at that line. A tilted head turning under the tool centre point, whose carriage would pass its travel halfway round with both ends inside, is refused that way instead of being stopped by motion partway through the cut. Only a point the inverse itself puts past a limit refuses a move: a sample the module cannot answer, or a change of solution branch between samples, ends the check for that move, and a joint that starts the move past a limit is not refused on that side. Lines that run from line steps over are not refused.Two things this needs pinned before it is written. The interpreter has no seam for the query: all 66
GET_EXTERNAL_*accessors take at most an index, an axis, a spindle, a pocket, and never geometry, so one taking a segment is a new shape for that interface. And a load-time answer can go stale, since the interpreter runs ahead andmotion.switchkins-typeis a HAL_IN pin, so until 9 lands the type a block is checked against is not necessarily the type it runs under.Where a module cannot answer, cheaply or at all, the check skips rather than refuses. Refusing a reachable program leaves the user no recourse; missing an unreachable one is still caught by
inRange()at runtime. That asymmetry decides every ambiguous case in 6 and 7.Checking a program that switches type raises a question the tree currently leaves implicit: whether a type's coordinates are geometric. Most are, and for those a scan is one stream of world points with only the inverse changing at the switch.
identityis not, since world becomes the joints, so a datum in millimetres lands on a rotary joint in degrees; nothing notices, which is why switchkins.adoc asks the operator to clear the offsets before switching.The joint-space syntax that question wanted is
G53.7above: joint values by joint number, no offsets, the joint limits directly. Whether a type should also declare its space for the scan stays open.Per-mode restrictions. G28, G30, G53, G92, G52 and M6 do not mean the same thing in every kinematics type, and today each one quietly does whatever the module happens to leave it doing. Refusing outright beats guessing, and with 4 in place the control knows enough to decide rather than to be told. The first of them is in:
G53,G28andG30keep meaning the world of the kinematics in force, which on a table-side kinematics turns with the table, andG53.5,G28.5andG30.5are the machine frame forms; a configuration that wants the machine codes to mean that frame setsMACHINE_MOVES_IN_MACHINE_FRAME, and off the machine frame typeG53,G28andG30then run asG53.5,G28.5andG30.5. The rest is in too: whileG43.4orG43.5is on, the current point is the tool tip with the length along the tilted tool, soG92,G92.1,G92.2,G92.3,G10 L20andM6wait for theG49, and a new length isG43.4 Hrather than a plainG43;G52,G54toG59.3andG10 L2read no position and stay, as Siemens and Heidenhain allow their frames under TCP, andG43.1andG43.2change the offset in effect and keep the switch.Retire
motion.switchkins-type, once the grace period has run. Which release that is has still to be decided.The axes a machine orients with. The interpreter and canon took X Y Z U V W as lengths and A B C as angles by letter, and
[AXIS_<letter>] TYPEwas read by the AXIS GUI alone, so a V configuredANGULARon a mm machine moved 25.4 times too far underG20; and the orientation codes wrote their answer into A B C whatever axes turn the tool. NowTYPEis honored in the interpreter and canon,[TRAJ] FEED_AXESnames the axes F is measured along, and each kinematics type names the axes it orients the tool with, so a head that tilts on V is driven on V. Built. TheTYPEhalf is merged, Honor [AXIS_<letter>] TYPE in the interpreter, canon and motion #4581.G12.1 and G13.1 are #4414, split out of this branch and standing on their own.
On tool length specifically: the offset is a nine-axis pose from the interpreter, canon adds it into the coordinate, and each rotary kinematics takes the Z component back through a HAL pin and re-applies it along the real tool direction. That round trip is exact when the config wires the pin, and silently wrong otherwise: a missing
netcosts the full tool length, and an X or Y offset is applied in the rotating work frame while the tool is bolted to the machine. The mechanism works; it has no owner.Why 4 is worth the trouble: every other way of answering "will this program overtravel" builds a second model of the machine, in the verification package or in the post, and the two descriptions drift apart. If the kinematics module can be called with parameters the caller chooses, LinuxCNC can check a program against the same code that will run it, with no second model to keep in sync. That is not available to anyone whose kinematics is locked inside the control.
In this branch now
Items 5, 6, 8 and 10, the last of 3, and the run-time half of 7. Items 1, 4 (#4702) and most of 3 are merged, as is the
TYPEhalf of 10 (#4581); the orientation axes build on 4 and 5 and sit near the top:5axiskinsreports its frames since kinematics: modules as functions of a parameter block, callable outside realtime #4702.maxkins,xyzab_tdr_kinsandgenserkinsreport theirs here, themaxkinstilt following itsconventional-directionspin.genhexkinsandpentakinsreport theirs from the pose, below. Of the in-tree modules that tilt a tool or a part,scorbot-kinsstill reports none.G68.3, as above, with the interpreter evaluating the module ahead of motion. The inverse ahead of motion runs to a fixed point, because a module may read the joints it is handed and one pass from a stale seed answers for the wrong rotary angles. In motion a point-to-point segment keeps the last forward solution and clears a status flag when the forward fails inside it rather than parking the reported position at zero, and an external offset waits while a point-to-point segment is queued, since its joints were solved without it. Three repairs the work turned up:G68.3no longer leaks its axis words into a stray traverse, the python preview appliesSET_G68_FRAMEin program units like every other length canon call, and orientation answers from the module are unwrapped onto the nearest turn of the current pose before nearest-first ranking. The bridgemill sim runs its sphere demo five ways, one per method:G53.2with a fused orient-and-travel move,G53.6orient-then-move, real quill drilling throughG53.5andG53.7, aG12.1kinematics switch, and the historical W-as-tool-length program kept with the quill drawing locked, which reveals that the old trick only ever worked because the quill was not drawn.G43with the head tilted moves the point, not the machine, and the interpreter predicts the same through the loader. The X and Y of a tool offset turn with the tool frame the module reports, where every TCP module used to leave them on the work axes.G43.5gives the tool axis as a vector,I J KonG0andG1. The machine frame is a declared kinematics type,G53.5,G28.5andG30.5move in it,G13.1andG49cancel to it, and the robots declare their arm type as such, so on a pumaG53.5 Z50lifts the flange along base Z holding the rest.[AXIS_L]box read on the machine frame for every move, onmaxkinsits carriage through the joint map, the segment cap in canon, the jog cap in motion, as above.kinslimitsprints the cap canon reads for a move next to the Jacobian.G20/G21conversion of a position, an offset, a stored position (#5161,#5181,#5211,#5221onward) or a tool offset goes byTYPE, in the interpreter and in canon, andWRAPPED_ROTARYandLOCKING_INDEXER_JOINTapply to any angular axis. The length F applies to is the feed axes that move, else the other linear axes, else the angular ones in degrees, which with the defaults is today's XYZ, else UVW, else ABC. Motion still measures a line by letter, so canon scales the rates it sends and the move takes the time canon planned. With everyTYPEandFEED_AXESat its default canon's output is unchanged: all 632 programs undertests/andnc_files/, throughrs274with and without a sim INI, give the same canon calls byte for byte. On the way, B and C get the angular minimum displacement A had; they used the linear one. A kinematics type names its orientation axes inkins_ops.orient, or they come from the joint map;G43.5,G53.3andG53.6drive those axes, and a module whose type orients with an axisTYPEmakesLINEARis refused at load. A module may flag several types primary, one per head:G43.4andG43.5stay on the type in force when it is primary and otherwise take the lowest-numbered primary.twinspindlekinsshows it all on one machine: a B tilting head between a main spindle on C and a sub spindle on W, anANGULARW, each spindle a primary type in the frame of its own part, and its sim config drills the same ring of tilted holes into both parts through one subroutine that names no rotary.G13.1on a five axis machine, the tool frame is the primary type's at the same joints, so the marker still leans with the tool and a tool jog still goes along the tilted tool, as Fanuc, Heidenhain and Siemens jog in the tool direction whatever the transformation in force.G13.1selects, Fanuc's second feature coordinate system: requested by Sigma1912 for a mill-turn, the rotaries set once and the slides alone moving. The interpreter takes a fixed map from the primary kinematics to the machine frame with the rotaries held and applies it after the whole offset chain, so a plane is cut on the part andG53.1uses a table rotary underG13.1too. A rotary word keeps the map, as Fanuc, Heidenhain and Siemens keep their plane;G43andG49in the plane take it again with the new length, and a kinematics switch in a plane takes it again or drops it. Status keeps the two apart, as the standard controls report them:g68_offsetandg68_rotationstay the plane as defined under every kinematics type, and the map goes ing68_kins_offsetandg68_kins_rotation, as Siemens keepsCYCLE800's workpiece frame apart from its table and tool frames. UnderG13.1the preview, the backplot and the tool marker are drawn on the part, as the Siemens and Heidenhain simulations hold the workpiece fixed: motion reports the primary type's forward of the commanded joints aspart_position, the backplot draws it, and the preview maps each point it draws the same way outside realtime, so a switch betweenG13.1and the TCP kinematics moves nothing on the screen.genhexkinsorpentakins: its forward iterates, so its frames come from the pose the forward or inverse last solved, the platform's A B C, rather than from the joints, as PI, Siemens and PMAC read the orientation of a hexapod. The tool frame in status, the tool jog,G68.3and the orientation codes then work on it:G53.6,G53.3andG53.2turn the pose's angles, the hexapod taking the shortest rotation from where it stands and no turn about the tool, andG53.1, which turns rotary joints alone, is refused, as Heidenhain gives a machine without rotary axes onlySTAY. The X and Y of a tool offset turn with the platform. The[AXIS_L]box is the platform's pose with the tool left out, as a robot's is its flange and as PI puts its soft limits on the platform.G53.6underG13.1keeps the tool tip on its point all along the move, not only at its ends. It is the primary type's line, the tip held and the rotaries turning, which motion follows every servo cycle with that type's inverse whileG13.1stays in force, so an abort or a feed hold part way leaves the tip on its point and no kinematics to switch back, as Fanuc givesG53.6inG49and Heidenhain'sPLANE ... MOVEcompensates withoutM128. Canon caps it and checks the joints' travel along it on that type, and the preview draws it as under the TCP kinematics. Reported by Sigma1912.G43.4orG43.5made is in effect,G92,G92.1,G92.2,G92.3,G10 L20andM6are refused with a message to cancel it withG49first, and a plainG43with one to change the length withG43.4 H. The current point is then the tool tip with the length along the tilted tool: an offset taken from it outlives theG49, where the same numbers name another point, a tool change moves it, and a plainG43ended the switch with the machine left on the TCP kinematics. Fanuc refusesG92andG43in tool centre point control (PS5421), Siemens refusesPRESETONon axes under a transformation (17600), and Heidenhain resetsM128before aTOOL CALL.G52,G54toG59.3andG10 L2stay, as Siemens and Heidenhain allow their frames and datum shifts under TCP, andG43.1andG43.2change the values of the offset in effect and keep the switch. The rule follows the switch, not the kinematics type: afterG12.1orG13.1, on a hexapod or robot runningG43on its primary type, and on a machine without switchable kinematics, nothing changes.User visible
Fourteen G-codes, all of them non-modal except the plane itself, which has its own cancel.
G68.2,G68.4andG69are on master since #4600:G68.2G68.3G68.4G69G53.1G53.2G53.3G53.6G53.4G53.5G53.7J2=-5naming joint 2 in its own unitsG28.5,G30.5G28andG30, through the same stored positionsG43.5I J KonG0andG1, the rotaries solved by the modulePon the orientation codes picks among the poses that reach the plane, nearest first, andQ1frees the joints that carry the work to take part. The point-to-point codes are modifiers ofG0andG1, and withG1the move takes the time the straight move would at the programmed feed.J<n>=<value>is a new word form, read only whereG53.7uses it.Alongside them:
#<_orient_x>to#<_orient_z>,#<_orient_rot1>to#<_orient_rot3>and#<_orient_valid>hold the poseG53.2last solved: X Y Z in the plane, then the rotary angles in the order the kinematics orients with, first and second the way Fanuc and Siemens number them, whatever their letters, 0 for a third the machine does not have. Mirrored on#5071to#5076and#5080; read-only, and reading one before the firstG53.2is an error.#<_kins_orient_1>to#<_kins_orient_3>name the axes the kinematics type in force orients with, 0 for X to 8 for W and -1 for none, and#<_kins_orient_1_head>to#<_kins_orient_3_head>say whether each turns the head or the table. A five axis machine leaves the third at -1; a robot reads A B C.trsrn_twpconfigs run on the native codes: no remap, no python, notwp-statuspin. Their python maths is the oracle intests/kins-twp.[RS274NGC] MACHINE_MOVES_IN_MACHINE_FRAME, off by default:G53,G28andG30run asG53.5,G28.5andG30.5whenever the kinematics in force is not the machine frame type, andG28.1andG30.1store the machine frame position, so the slides land where the words say whatever the tool and the head are doing. A GUI's preview reads a program on the type the machine is on, read from the status buffer, and theG53the GUI restates the machine's position with stays in the world of that type.stat.kins_typeandstat.kins_type_flagsin python, withlinuxcnc.KINSTYPE_IDENTITY,KINSTYPE_PRIMARYandKINSTYPE_MACHINE: the type motion runs and what the module declares each type to be.[AXIS_L] MIN_LIMITandMAX_LIMITare the machine frame box on a module that declares its machine frame, onmaxkinsits carriage and on a hexapod or pentapod its platform with the tool left out; the velocity and acceleration limits keep their meaning and the joint cap sits on top. A switch no longer needs the limits reset to keep the joints safe; it may still want the velocity limits reset where a letter changes unit at the switch, which the puma's M128 and M129 do and nothing else..identity,.primaryand.machinein the ops table orswitchkinsDeclare()the old way;motion.kins-typeand theswitchkins-type N declared:line at load say what it declared.stat.tool_frame, the tool's axes seen from the work, the primary type's under an identity type,Nonewhere no type reports frames; the preview draws the tool marker along it, with[DISPLAY] GEOMETRYas the fallback.stat.part_position, where the tool stands against the part:positionitself except underG13.1and the like, wherepositionis in machine coordinates. The backplot draws it.jog_frame()andstat.jog_framein python,motion.jog-frame, thehalui.jog-frame.*pins, and in AXIS the View menu,%to cycle and a status label. Both requested by Sigma1912.G53.1is refused on a parallel machine, which has no rotary joint to turn;G53.6,G53.3andG53.2work there as on any five axis machine.G13.1is cut on the part (3+2), andstat.g68_kins_offsetandstat.g68_kins_rotationin python give the map it is cut through, the identity under any other type.G43.4orG43.5is on,G92,G92.1,G92.2,G92.3,G10 L20,M6and a plainG43are refused;G49first, orG43.4 Hfor a new length..tool-offsetnet keep working; the tool comes from motion now and the net is only read until motion has spoken.Left for later
FGREF, Fanuc parameters 1408/1465). It is for a 4th axis without TCP, a wrap on a cylinder, where F today counts the linear axes only; with TCP the path is already measured on the part. Small and independent of this branch; I'll build it if someone in the community wants it and can test it on their machine.Testing
tests/kins-params,tests/kins-jacobian,tests/kins-frames,tests/tool-frame,tests/kins-tool-offset,tests/kins-twp,tests/twp-native,tests/ptp-iterative,tests/ptp-machine-frame,tests/workplane-retainandtests/kins-limitscover the new code;tests/kins-limitsholds the three tests that were red before item 6, the swing of a 400 mm pivot underG0 C180and under an arc, the box read on the carriage instead of the tip, and the same swing as a world jog, each joint following its command throughlimit3at 20 percent over its own limits the way a drive does, so a command over the limits falls behind and trips the following error; the last runs the xyzacb nutating head through the plane, the orientation codes and the point-to-point moves against the python maths, and checks that every point-to-point move keeps its joints on one line in joint space, all together, so a wrong interpolation fails three of its checks.tests/ptp-robotputs pumakins through the same codes, whereG53.5moves the flange along a base axis andG53.7names the joints.tests/axis-typeandtests/interp/axis-typecheckTYPE,FEED_AXESand a wrapped V throughrs274and through motion, the second failing on master at its first check;tests/kins-switchreads the orientation axes of each type,tests/kins-orient-linearchecks the refusal of aLINEARorientation axis, andtests/twinspindlesolves and reaches the same tilted hole on either spindle oftwinspindlekinsand runs its demo.tests/kins-limits/travelswings a tilted head under the tool centre point until the carriage would leave its travel halfway round, and checks the refusal in MDI and in a program and that run from line steps over it;tests/twp-tablerunsG53.1on a table-table machine once more with A limited to one side on the axis and not on the joint.tests/twp-staticruns the same programs under the tool centre point kinematics and underG13.1on a table machine and a head machine and checks the joints agree, then the rotary word, a switch in the plane, the status split, the plane jog, an abort withRETAIN_WORK_PLANEand the preview, which underG13.1must draw the plane, a rotary turn and the live plot where the TCP kinematics draws them and follow a switch either way to where the machine goes.tests/twp-staticthen holds the tool tip throughG53.6underG13.1on both machines, through an abort and a feed hold part way, refuses one whose carriage would pass its travel half way round, and checks the preview draws it as the TCP kinematics does.tests/kins-switchrefusesG92,G92.1,G92.2,G92.3,G10 L20,M6andG43underG43.4andG43.5, passesG43.4 H,G43.1,G43.2,G10 L2,G52andG55there, and the refused codes again afterG49and under a typeG12.1selected.tests/twp-parallelrunsG53.6,G53.3,G68.3, the refusedG53.1and the[AXIS_L]box on a hexapod and a pentapod, andtests/kins-frameschecks their frames against the pose.tests/twp-jog-framejogs along the plane and the tool on a head machine, the tool jog under the identity type too, every servo cycle within each axis's limits, then a machine not homed and the machine frame on a table machine;tests/glcanonchecks the tool marker against the frame in status. The four comp sim configs give identical positions to master through the same MDI sequence, in every kinematics type.Testers wanted
Everything above runs in the sims, and the sims follow their commands exactly. What I cannot see from here is a real five axis machine or a real robot, where the joint cap either keeps a drive inside its following error window or it does not. If you run a
5axiskins,xyzac-trt,xyzbc-trt,maxkinsortrsrnconfig, or apumakinsorgenserkinsarm, a session on this branch would tell me a lot:G0orG1that turns a rotary under TCP with the head tilted, at full rapid, and whether it slows to the carriage's pace or trips a following error. Same with a world jog of the rotary in teleop mode.[AXIS_L]box now reads right on your machine: a tip past the box with the carriage inside accepted,G53.5past the box refused, and any move you expected to be accepted that is refused, with the line and the message.G53.5along a base axis, and whetherG13.1andG49leave you where you expect.motion.kins-typesays what it declared.EMCCANON_KINS_DEBUG=1in the environment makes canon print the cap it read for every segment on stderr, andkinslimitsprints it for one move from the command line.