tp: allow tangent blending across near-collinear ABC and UVW motion - #4421
tp: allow tangent blending across near-collinear ABC and UVW motion#4421greatEndian wants to merge 3 commits into
Conversation
Previously any move with ABC (or UVW) motion was rejected outright from tangent-blend consideration (tcRotaryMotionCheck), forcing an exact stop at every corner of a 5-axis program. This permits a tangent blend across ABC motion when the ABC tangent direction is near-collinear across the junction (<= 1 deg), so the rotary kink acceleration is negligible. Split the old ABC|UVW reject into tcUVWMotionCheck (UVW/spherical still rejected -- not handled by the blend geometry) and an ABC near-collinear gate using the ABC tangent unit vectors. The gate is conservative: any larger ABC direction change, or ABC motion that starts/stops at the junction, falls back to the existing safe exact stop -- it never enables a faster blend, only a continuous one when it is provably cheap. NOTE / WIP: this CHANGES TP0 corner behaviour for 5-axis programs and must be validated on a real rotary config. The kink-velocity limiter bounds XYZ acceleration only (tp->xyz_acc_bound); the complete solution is to plumb an ABC acc_bound through the TP and fold it into the acc_scale computation so the rotary kink velocity is limited by the machine's ABC acceleration limits (rather than gated on near-collinearity). Signed-off-by: Ladislav Chabron <chabron94@gmail.com>
The near-collinear ABC tangent gate added in 425e223 was not safe as written: it gated purely on direction, and the reject paths left term_cond ambiguous. Both produced real, measured axis-limit violations rather than just the documented "no ABC accel bound yet" caveat: 1. Direction-only gating let two segments with identical ABC direction but very different ABC-rate-per-distance through as "tangent" (e.g. dA/dX = 0.125 then 1.125). Since the kink-acceleration check only ever looked at XYZ, this produced a real ~1500-2200 deg/s^2 A-axis acceleration spike against a configured 500 deg/s^2 limit (3-4x over). Fix: after the direction check passes, convert each side's ABC tangent + displacement into an actual angular velocity via the chain rule (dABC/dt = (abc_tmag/target) * v_max), diff the two, and reject to a safe stop if the implied acceleration exceeds kink_ratio of a new tpGetABCAccelBounds() (axis indices 3/4/5, mirrors the existing XYZ tpGetMachineAccelBounds()). 2. Neither ABC-reject branch called tcSetTermCond(..., TC_TERM_COND_STOP), unlike the pre-existing sharp-corner check a few lines below. This was invisible for an isolated reject, but when the rejected junction was immediately preceded by a segment that had just been successfully tangent-linked, the ambiguous term_cond let the blend-arc geometry mishandle the corner: X-axis velocity spiked to ~75mm/s against a configured 50mm/s limit (50% over). A pure-XYZ control case (two collinear segments then a hard 90 degree turn, no ABC at all) proved this chaining pattern is safe in stock LinuxCNC, so the bug was specifically the missing tcSetTermCond call. Fix: call it explicitly in both reject branches, same as the sharp-corner check does. While fixing this, also extend the same (now-correct) gate to UVW (secondary linear axes), which the original commit blanket-rejected alongside spherical motion for no real technical reason -- UVW is plain linear motion, same units as XYZ, and the tmag/uVec data already exists identically for it. Refactored the per-axis-group logic into one tpCheckGroupTangent() helper parameterized by function pointers (tcGetABCTangent/tpGetABCAccelBounds vs new tcGetUVWTangent/tpGetUVWAccelBounds, axis indices 6/7/8) instead of duplicating it, and renamed tcUVWMotionCheck to tcSphericalMotionCheck now that it only guards TC_SPHERICAL (internal 3D blend-arc geometry, unrelated to the interpreter's U/V/W axes). Validated with a 25-case automated combination matrix (not just the original demo): pure XYZ regression, ABC-only and UVW-only each across matched-rate/mismatched-rate/isolated-reversal/chained-reversal/ start-stop/TC_CIRCULAR-TC_CIRCULAR/long-chain/mixed-transitions, ABC+UVW combined simultaneously (both matched, either one mismatched or starting/stopping while the other is fine, both reversing at once), and a near-zero-segment-length robustness case. All 60 joint-level safety checks (commanded velocity within 2% of configured MAX_VELOCITY, finite-differenced acceleration within 15% of configured MAX_ACCELERATION) pass with zero regressions on the original demo. Signed-off-by: Ladislav Chabron <chabron94@gmail.com>
grandixximoo (Luca) found that tpCheckGroupTangent()'s velocity-jump check evaluated each side of the junction at its own v_max1/v_max2, but path speed is actually continuous across a tangent junction -- both sides execute at the slower segment's cap, exactly the same v_max the pre-existing XYZ kink check just below already shares between both tangent vectors. Using the separate per-segment caps reported a phantom jump for matched-rate segments that simply differ in feed (e.g. a roughing/finishing feed change on an otherwise- tangent rotary move), forcing a full exact stop with a trip threshold well under 1mm/s given default kink/accel settings. Fix evaluates both sides at v_j = fmin(v_max1, v_max2). Verified with a new case (matched dA/dX across a feed drop 40->20mm/s): before this fix the corner forced X down to ~1mm/s and back up (textbook phantom stop); after, it decelerates smoothly straight through 40->20mm/s. Re-ran the existing 25-case combination matrix afterward with 0 regressions (26/26 total, 0 joint-level safety fails).
|
I tried to reproduce the exact stop the problem statement describes and could not, so I want to check my reading before trusting it. Following I measured it on
Sampling is 2 ms polls of If that reading is right, the You give a reason for those calls: omitting them left the blend-arc geometry ambiguous when a rejected junction follows a tangent-linked segment. Would setting STOP only in that situation cover it, and let everything else fall through to On placement: the gate runs before the Smaller: |
Problem
Any move with simultaneous ABC (rotary) or UVW (secondary linear) motion was
rejected outright from tangent-blend consideration in
tpSetupTangent(),regardless of how small the direction change across the junction actually was.
In practice that forces a full exact stop at every corner of a 5-axis (or
UVW-carrying) program, even when the tool orientation barely changes direction
and the corner would otherwise blend smoothly.
Fix
tpSetupTangent()may now blend across ABC and/or UVW motion, gated by a newshared helper
tpCheckGroupTangent()applied once per group. For each groupthe blend is allowed only when both hold:
tangent vector; and
Direction alone is not sufficient: two segments can share an ABC direction
exactly and still demand very different rates, which would let a large
instantaneous jump through as if it were free. Computed by the chain rule
(
dGroup/dt = (group_tmag/target) * v), diffed, and checked against newtpGetABCAccelBounds()/tpGetUVWAccelBounds()(axis indices 3/4/5 and6/7/8), mirroring the existing XYZ
tpGetMachineAccelBounds().The jump is evaluated at the shared cap
v = fmin(v_max1, v_max2), becausepath speed is continuous across a tangent junction — the same v_max the
existing XYZ kink check already shares between both tangent vectors. Using
each side's own cap reports a phantom jump for matched-rate segments that
merely differ in commanded feed (a roughing/finishing feed change on an
otherwise tangent rotary move), forcing a needless exact stop.
Anything else — larger direction change, a jump beyond the accel budget, or
motion that starts or stops at the junction — falls back to the exact stop
that happens today. This is a conservative gate, not a speed dial: outside the
window we stop, and we never blend faster than the limits allow.
Both reject branches now also call
tcSetTermCond(prev_tc, tc, TC_TERM_COND_STOP), matching the pre-existing sharp-corner check a few linesbelow. Omitting it left the corner's blend-arc geometry ambiguous whenever a
rejected junction immediately followed a successfully tangent-linked segment.
TC_SPHERICAL— the internal 3D blend-arc geometry the TP creates forout-of-plane corners, unrelated to the interpreter's U/V/W axes — keeps its
own hard reject, now via
tcSphericalMotionCheck(), renamed fromtcUVWMotionCheck()which had incorrectly bundled real UVW motion into thesame blanket reject.
Testing
Build clean,
cppcheckclean on the changed file.Validated with an automated combination-matrix harness that runs each case
against the built tree, samples commanded joint velocity, and asserts two hard
physical-safety invariants across the whole trace: velocity never exceeds the
configured joint
MAX_VELOCITY(+2%), and finite-differenced accelerationnever exceeds
MAX_ACCELERATION(+15%, allowing for 10 ms sampling of anexact-limit ramp).
26 cases, 0 joint-level safety failures. Coverage includes the ABC group,
the UVW group and both together; matched and mismatched rates; circular
junctions; reversals both isolated and chained; start/stop at a junction; an
8-segment collinear chain; mixed accept/reject transitions within one program;
the feed-change case above; and a near-zero-length segment for the
divide-guard.
Before/after velocity plots on a representative 5-axis demo program show the
baseline dipping at each corner and the patched run holding through.
Real-machine validation is still outstanding — everything above is sim.
Relationship to #4221
No interaction. #4221 ("tp: apply parabolic half-accel only during an actual
blend") replaced
tcGetTangentialMaxAccel()withtcGetCycleMaxAccel()in theper-cycle update functions. This patch works at planning time in
tpSetupTangent()/tpRunOptimization()/tpGetMachineAccelBounds()andadds disjoint helpers. Different phase, disjoint APIs — though both reason
about the same physical quantity, so it seemed worth stating explicitly.