A collision check answers an important question: does the modeled tool assembly intersect the modeled part, fixture or machine envelope? It does not answer a different question: will the cutting contact remain stable while the rotary axes change direction?

A short surface witness line can appear near an A- or C-axis reversal even when simulation reports no collision. Possible contributors include a collapsing tool-tip feed, contact moving toward the center of a ball nose, an abrupt change in the inverse-kinematic solution, or a single damaged flute becoming visible as motion slows.

The useful diagnostic step is to align four things on one timeline:

  1. toolpath time,
  2. linear and rotary axis motion,
  3. contact-point or tool-vector data,
  4. the measured location of the mark on the part.

This post builds a deliberately small Python screen for recorded axis samples. It does not edit a toolpath or approve machine motion.

A narrow rotary-reversal screen

Suppose a controller or offline trace has been exported to CSV with time, linear position and rotary position. The following function marks a sample only when a rotary velocity changes sign and the calculated linear speed is below a review threshold.

from dataclasses import dataclass from math import dist @dataclass(frozen=True) class Sample: t: float xyz: tuple[float, float, float] a: float c: float def delta(value, previous, dt): if dt <= 0: raise ValueError("timestamps must increase") return (value - previous) / dt def changed_sign(previous, current, epsilon=1e-6): if abs(previous) <= epsilon or abs(current) <= epsilon: return False return (previous < 0 < current) or (current < 0 < previous) def reversal_candidates(samples, low_linear_speed): if low_linear_speed <= 0: raise ValueError("low_linear_speed must be positive") if len(samples) < 3: return [] segments = [] for left, right in zip(samples, samples[1:]): dt = right.t - left.t segments.append({ "end_time": right.t, "linear_speed": dist(left.xyz, right.xyz) / dt, "a_velocity": delta(right.a, left.a, dt), "c_velocity": delta(right.c, left.c, dt), }) findings = [] for previous, current in zip(segments, segments[1:]): axes = [ axis for axis, key in (("A", "a_velocity"), ("C", "c_velocity")) if changed_sign(previous[key], current[key]) ] if axes and current["linear_speed"] < low_linear_speed: findings.append({ "time": current["end_time"], "axes": axes, "linear_speed": current["linear_speed"], "previous": previous, "current": current, }) return findings 

The result is named reversal_candidates, not bad_surface_points. A flagged timestamp is where an engineer should compare contact geometry, axis limits, controller interpolation and the actual surface. An unflagged timestamp is not proof of a good part.

Test a synthetic trace

trace = [ Sample(0.0, (0.0, 0.0, 0.0), a=10.0, c=30.0), Sample(0.1, (1.0, 0.0, 0.0), a=10.5, c=30.3), Sample(0.2, (1.1, 0.0, 0.0), a=10.2, c=30.6), Sample(0.3, (2.1, 0.0, 0.0), a=9.9, c=30.9), ] events = reversal_candidates(trace, low_linear_speed=2.0) assert len(events) == 1 assert events[0]["axes"] == ["A"] assert events[0]["time"] == 0.2 # A reversal at normal linear speed is intentionally not flagged by this screen. fast_trace = [ Sample(0.0, (0.0, 0.0, 0.0), a=0.0, c=0.0), Sample(0.1, (1.0, 0.0, 0.0), a=1.0, c=0.0), Sample(0.2, (2.0, 0.0, 0.0), a=0.5, c=0.0), ] assert reversal_candidates(fast_trace, low_linear_speed=2.0) == [] 

This example uses simple finite differences. A real trace may require angle unwrapping around 360 degrees, filtering that preserves short events, controller-specific units and synchronized tool-center-point data. Those requirements should be explicit rather than hidden inside a convenient plot.

What to inspect at each candidate

For each timestamp, ask:

  • Where is the cutting contact on the ball, barrel or tapered tool?
  • What is the effective cutting diameter at that contact point?
  • Did the commanded tool vector change smoothly?
  • Did the controller choose a different valid inverse solution?
  • Are any linear or rotary axes close to velocity, acceleration or travel limits?
  • Does the measured mark align with the predicted contact location?
  • Is the line repeated once per reversal or once per flute?

The 5-axis tool-orientation decision guide connects these checks to ball, barrel and tapered-tool geometry. The ball-nose stepover guide separates cusp height from contact and slope. For thin blades, the duplex impeller tooling case adds support and work-hardening constraints.

Do not fix a local event with a global guess

If the mark is tied to contact approaching the ball center, a smoothly bounded lead or lean adjustment may be worth validating. If a rotary axis reverses on the functional surface, the setup or orientation solution may need to move the event elsewhere. If actual tool-tip feed collapses because an axis is near a machine limit, raising programmed feed is not the same as making the kinematics feasible.

After a revision, rerun the full holder and neck envelope, then inspect the blend around the old event. The correction should be released against the drawing's profile and texture requirements, not against one attractive screenshot.

The script's purpose is modest: turn a long trace into a short, explainable list of timestamps for engineering review. It is an observation tool, not a postprocessor, collision system or machine-safety control.