Skip to content

Commit 7fe2b9c

Browse files
committed
Curvature-matched blends and path deviation measurement
Pass curvature (kappa) and normal direction from adjacent segments into the Bezier blend optimizer for C2 continuity at arc-blend junctions. For lines (kappa=0) behavior is unchanged. Replace bezier9Deviation (corner-point distance) with bezier9PathDeviation (distance to two-segment polyline) for correct tolerance checking on both symmetric and asymmetric blends. Cap blend maxvel at curvature-rate jerk limit so the backward pass prevents high-override entry velocities from exceeding curvature bounds.
1 parent 3638e7b commit 7fe2b9c

2 files changed

Lines changed: 89 additions & 8 deletions

File tree

src/emc/motion_planning/blend_sizing.cc

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,56 @@ static int tcGetTangentAtProgress(TC_STRUCT const * const tc,
481481
return TP_ERR_OK;
482482
}
483483

484+
/* ----------------------------------------------------------------
485+
* Static helpers: curvature at blend boundary (for C2 matching)
486+
* ---------------------------------------------------------------- */
487+
488+
/**
489+
* Compute the XYZ curvature and unit curvature normal of a segment
490+
* at a given progress. Used to set up C2-continuous Bezier blends
491+
* that match the curvature of adjacent arcs at the junction.
492+
*
493+
* For TC_LINEAR: κ = 0, n = {0,0,0}
494+
* For TC_CIRCULAR: κ = 1/R, n = (center − point) / |center − point|
495+
*
496+
* @param tc Segment to query
497+
* @param progress Arc-length progress on the segment
498+
* @param kappa [out] Curvature magnitude (≥ 0)
499+
* @param normal [out] Unit curvature normal in XYZ (toward center)
500+
*/
501+
static void tcGetCurvatureAtProgress(TC_STRUCT const * const tc,
502+
double progress,
503+
double * const kappa,
504+
PmCartesian * const normal)
505+
{
506+
*kappa = 0.0;
507+
normal->x = normal->y = normal->z = 0.0;
508+
509+
if (tc->motion_type == TC_CIRCULAR) {
510+
double radius = tc->coords.circle.xyz.radius;
511+
if (radius > TP_POS_EPSILON) {
512+
*kappa = 1.0 / radius;
513+
514+
/* Normal toward center: (center − point) / |center − point| */
515+
double angle = 0.0;
516+
pmCircleAngleFromProgress(&tc->coords.circle.xyz,
517+
&tc->coords.circle.fit,
518+
progress, &angle);
519+
PmCartesian point;
520+
pmCirclePoint(&tc->coords.circle.xyz, angle, &point);
521+
522+
PmCartesian diff;
523+
pmCartCartSub(&tc->coords.circle.xyz.center, &point, &diff);
524+
double mag;
525+
pmCartMag(&diff, &mag);
526+
if (mag > TP_POS_EPSILON) {
527+
pmCartScalMult(&diff, 1.0 / mag, normal);
528+
}
529+
}
530+
}
531+
/* TC_LINEAR, TC_BEZIER, TC_SPHERICAL: κ = 0 (safe default) */
532+
}
533+
484534
/* ----------------------------------------------------------------
485535
* Public API
486536
* ---------------------------------------------------------------- */
@@ -634,6 +684,13 @@ int findBlendPointsAndTangents9(double Rb,
634684
tcGetIntersectionPoint(prev_tc, tc, &boundary->intersection_point);
635685
}
636686

687+
/* Curvature at blend boundaries (for C2 matching in Bezier blends).
688+
* For arcs, κ = 1/R with normal toward center. For lines, κ = 0. */
689+
tcGetCurvatureAtProgress(prev_tc, boundary->s_prev,
690+
&boundary->kappa_start, &boundary->n_start_xyz);
691+
tcGetCurvatureAtProgress(tc, boundary->s_tc,
692+
&boundary->kappa_end, &boundary->n_end_xyz);
693+
637694
return TP_ERR_OK;
638695
}
639696

@@ -740,19 +797,25 @@ static double findOptimalAlpha9(double Rb,
740797
Bezier9 trial1, trial2;
741798
double v1 = 0.0, v2 = 0.0;
742799

800+
/* Curvature boundary conditions for C2 matching */
801+
double ks = boundary->kappa_start;
802+
PmCartesian const *ns = (ks > BEZIER9_CURVATURE_EPSILON) ? &boundary->n_start_xyz : NULL;
803+
double ke = boundary->kappa_end;
804+
PmCartesian const *ne = (ke > BEZIER9_CURVATURE_EPSILON) ? &boundary->n_end_xyz : NULL;
805+
743806
if (bezier9Init(&trial1, &boundary->P_start, &boundary->P_end,
744807
&boundary->u_start_xyz, &boundary->u_end_xyz,
745808
&boundary->u_start_abc, &boundary->u_end_abc,
746809
&boundary->u_start_uvw, &boundary->u_end_uvw,
747-
a1) == TP_ERR_OK) {
810+
ks, ns, ke, ne, a1) == TP_ERR_OK) {
748811
v1 = bezier9AccLimit(&trial1, v_goal, a_max, j_max);
749812
}
750813

751814
if (bezier9Init(&trial2, &boundary->P_start, &boundary->P_end,
752815
&boundary->u_start_xyz, &boundary->u_end_xyz,
753816
&boundary->u_start_abc, &boundary->u_end_abc,
754817
&boundary->u_start_uvw, &boundary->u_end_uvw,
755-
a2) == TP_ERR_OK) {
818+
ks, ns, ke, ne, a2) == TP_ERR_OK) {
756819
v2 = bezier9AccLimit(&trial2, v_goal, a_max, j_max);
757820
}
758821

@@ -768,7 +831,7 @@ static double findOptimalAlpha9(double Rb,
768831
&boundary->u_start_xyz, &boundary->u_end_xyz,
769832
&boundary->u_start_abc, &boundary->u_end_abc,
770833
&boundary->u_start_uvw, &boundary->u_end_uvw,
771-
a2) == TP_ERR_OK) {
834+
ks, ns, ke, ne, a2) == TP_ERR_OK) {
772835
v2 = bezier9AccLimit(&trial2, v_goal, a_max, j_max);
773836
} else {
774837
v2 = 0.0;
@@ -784,7 +847,7 @@ static double findOptimalAlpha9(double Rb,
784847
&boundary->u_start_xyz, &boundary->u_end_xyz,
785848
&boundary->u_start_abc, &boundary->u_end_abc,
786849
&boundary->u_start_uvw, &boundary->u_end_uvw,
787-
a1) == TP_ERR_OK) {
850+
ks, ns, ke, ne, a1) == TP_ERR_OK) {
788851
v1 = bezier9AccLimit(&trial1, v_goal, a_max, j_max);
789852
} else {
790853
v1 = 0.0;
@@ -894,8 +957,13 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
894957
continue;
895958
}
896959

897-
/* Check deviation against tolerance */
898-
double deviation = bezier9Deviation(&trial, &boundary.intersection_point);
960+
/* Check deviation against tolerance.
961+
* bezier9PathDeviation measures true distance to the two-segment
962+
* programmed path (P_start → corner → P_end), which is correct
963+
* for both symmetric and asymmetric blends. */
964+
double deviation = bezier9PathDeviation(&trial,
965+
&boundary.P_start.tran, &boundary.intersection_point,
966+
&boundary.P_end.tran, 9);
899967

900968
tp_debug_print(" iter=%d Rb=%g alpha=%g dev=%g tol=%g v_limit=%g v_goal=%g len=%g\n",
901969
iter, Rb, alpha, deviation, tolerance, v_limit, params.v_goal,
@@ -954,7 +1022,9 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
9541022
j_max_blend, params.v_goal,
9551023
&trial, &alpha);
9561024
if (v_limit > 0.0) {
957-
double deviation = bezier9Deviation(&trial, &boundary.intersection_point);
1025+
double deviation = bezier9PathDeviation(&trial,
1026+
&boundary.P_start.tran, &boundary.intersection_point,
1027+
&boundary.P_end.tran, 9);
9581028
if (deviation <= tolerance) {
9591029
result->Rb = Rb;
9601030
result->bezier = trial;
@@ -1027,9 +1097,15 @@ int createBlendSegment9(TC_STRUCT const * const prev_tc,
10271097
tcSetupMotion(blend_tc, v_req, v_plan, a_max, jerk);
10281098

10291099
/* Hard cap: curvature-rate jerk limit (like kink_vel, ignores feed override).
1030-
* Centripetal jerk = v³ · dκ/ds must stay within the jerk budget. */
1100+
* Centripetal jerk = v³ · dκ/ds must stay within the jerk budget.
1101+
* Also cap maxvel so the backward pass limits entry velocity from
1102+
* adjacent segments — prevents high-override arcs from entering
1103+
* the blend faster than the curvature allows. */
10311104
double v_jerk_limit = bezier9AccLimit(&solution->bezier, v_req, a_max, jerk);
10321105
blend_tc->kink_vel = v_jerk_limit;
1106+
if (v_jerk_limit < blend_tc->maxvel) {
1107+
blend_tc->maxvel = v_jerk_limit;
1108+
}
10331109

10341110
/* Store bezier curve in coords union */
10351111
blend_tc->coords.bezier = solution->bezier;

src/emc/motion_planning/blend_sizing.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ typedef struct {
6969

7070
double s_prev; /* Progress on prev_tc at blend start */
7171
double s_tc; /* Progress on tc at blend end */
72+
73+
double kappa_start; /* Curvature of prev_tc at blend start (xyz) */
74+
PmCartesian n_start_xyz; /* Unit curvature normal at start (toward center) */
75+
double kappa_end; /* Curvature of tc at blend end (xyz) */
76+
PmCartesian n_end_xyz; /* Unit curvature normal at end (toward center) */
7277
} BlendBoundary9;
7378

7479
/**

0 commit comments

Comments
 (0)