2323#include "tp_types.h"
2424#include "spherical_arc.h"
2525#include "motion_types.h"
26+ #include "motion.h"
2627
2728//Debug output
2829#include "tp_debug.h"
2930
31+ // For jerk-limited arc velocity (planner_type 1)
32+ extern emcmot_status_t * emcmotStatus ;
33+
34+ #ifndef GET_TRAJ_PLANNER_TYPE
35+ #define GET_TRAJ_PLANNER_TYPE () (emcmotStatus->planner_type)
36+ #endif
37+
3038
3139double tcGetMaxTargetVel (TC_STRUCT const * const tc ,
3240 double max_scale )
@@ -748,18 +756,111 @@ double pmCircle9Target(PmCircle9 const * const circ9)
748756 return helical_length ;
749757}
750758
751- int tcUpdateCircleAccRatio (TC_STRUCT * tc )
759+ /**
760+ * Apply acceleration and jerk limits to circular/spherical arc segments.
761+ *
762+ * For any arc (TC_CIRCULAR or TC_SPHERICAL), this function:
763+ * 1. Limits velocity based on centripetal acceleration budget
764+ * 2. For planner_type 1 (S-curve), applies three jerk constraints:
765+ * - Steady-state rotational jerk: v³/R²
766+ * - Normal jerk from tangential acceleration coupling: 3·v·a_t/R
767+ * - Entry/exit transition jerk at arc boundaries
768+ * 3. Calculates the tangential acceleration ratio for the arc
769+ *
770+ * This unified approach ensures consistent jerk limiting for both
771+ * programmed arcs (G2/G3) and blend arcs at segment corners.
772+ */
773+ int tcUpdateArcLimits (TC_STRUCT * tc )
752774{
753- if (tc -> motion_type == TC_CIRCULAR ) {
754- PmCircleLimits limits = pmCircleActualMaxVel (& tc -> coords .circle .xyz ,
755- tc -> maxvel ,
756- tcGetOverallMaxAccel (tc ));
757- tc -> maxvel = limits .v_max ;
758- tc -> acc_ratio_tan = limits .acc_ratio ;
759- return 0 ;
775+ double radius , angle ;
776+
777+ // Extract radius and angle based on motion type
778+ switch (tc -> motion_type ) {
779+ case TC_CIRCULAR :
780+ radius = pmCircleEffectiveMinRadius (& tc -> coords .circle .xyz );
781+ angle = tc -> coords .circle .xyz .angle ;
782+ break ;
783+ case TC_SPHERICAL :
784+ radius = tc -> coords .arc .xyz .radius ;
785+ angle = tc -> coords .arc .xyz .angle ;
786+ break ;
787+ default :
788+ return 1 ; // Not an arc, nothing to do
789+ }
790+
791+ if (radius < DOUBLE_FUZZ || angle < TP_ANGLE_EPSILON ) {
792+ return 1 ; // Degenerate arc
760793 }
761- // TODO handle blend arc here too?
762- return 1 ; //nothing to do, but not an error
794+
795+ double a_max = tcGetOverallMaxAccel (tc );
796+ double a_n_max_cutoff = BLEND_ACC_RATIO_NORMAL * a_max ;
797+
798+ // Find the acceleration necessary to reach the maximum velocity
799+ double a_n_vmax = pmSq (tc -> maxvel ) / radius ;
800+
801+ // Find the maximum velocity that still obeys our desired normal/total acceleration ratio
802+ double v_max_cutoff = pmSqrt (a_n_max_cutoff * radius );
803+
804+ double v_max_actual = tc -> maxvel ;
805+ double acc_ratio_tan = BLEND_ACC_RATIO_TANGENTIAL ;
806+
807+ if (a_n_vmax > a_n_max_cutoff ) {
808+ v_max_actual = v_max_cutoff ;
809+ } else {
810+ acc_ratio_tan = pmSqrt (1.0 - pmSq (a_n_vmax / a_max ));
811+ }
812+
813+ // Jerk-based velocity limiting for S-curve planner (planner_type 1)
814+ if (GET_TRAJ_PLANNER_TYPE () == 1 && emcmotStatus -> jerk > TP_POS_EPSILON &&
815+ tc -> cycle_time > TP_TIME_EPSILON ) {
816+
817+ double jerk = emcmotStatus -> jerk ;
818+ double R_sq = pmSq (radius );
819+
820+ // Constraint 1: Steady-state rotational jerk + entry/exit transitions
821+ // The jerk budget is shared between steady-state (v³/R²) and transitions.
822+ // Solving: v³ ≤ R² × j × φ / (2 + φ)
823+ // The (2 + φ) term: 2 for two transitions, φ for steady-state budget
824+ double v_max_jerk_steady = cbrt (R_sq * jerk * angle / (2.0 + angle ));
825+
826+ // Constraint 2: Normal jerk from tangential acceleration coupling
827+ // During S-curve ramps on arc: j_n = 3·v·a_t/R
828+ // Using BLEND_ACC_RATIO_TANGENTIAL as max tangential accel ratio
829+ double a_t_max = BLEND_ACC_RATIO_TANGENTIAL * a_max ;
830+ double v_max_jerk_tan = jerk * radius / (3.0 * a_t_max );
831+
832+ // Constraint 3: Entry/exit transition jerk (centripetal accel ramp)
833+ // At line-arc boundary, centripetal accel changes from 0 to v²/R
834+ // j_entry = (v²/R) / cycle_time ≤ j_max
835+ double v_max_jerk_entry = pmSqrt (jerk * radius * tc -> cycle_time );
836+
837+ double v_max_jerk = fmin (fmin (v_max_jerk_steady , v_max_jerk_tan ), v_max_jerk_entry );
838+
839+ tp_debug_print ("tcUpdateArcLimits: type=%d R=%f phi=%f j=%f\n" ,
840+ tc -> motion_type , radius , angle , jerk );
841+ tp_debug_print (" v_jerk: steady=%f tan=%f entry=%f => min=%f\n" ,
842+ v_max_jerk_steady , v_max_jerk_tan , v_max_jerk_entry , v_max_jerk );
843+
844+ if (v_max_jerk < v_max_actual ) {
845+ tp_debug_print (" Limiting v_max from %f to %f for jerk\n" ,
846+ v_max_actual , v_max_jerk );
847+ v_max_actual = v_max_jerk ;
848+
849+ // Recalculate acc_ratio_tan for jerk-limited velocity
850+ double a_n_at_jerk_vel = pmSq (v_max_actual ) / radius ;
851+ if (a_n_at_jerk_vel < a_max ) {
852+ acc_ratio_tan = pmSqrt (1.0 - pmSq (a_n_at_jerk_vel / a_max ));
853+ }
854+ }
855+ }
856+
857+ tc -> maxvel = v_max_actual ;
858+ tc -> acc_ratio_tan = acc_ratio_tan ;
859+
860+ tp_debug_print ("tcUpdateArcLimits: final v_max=%f acc_ratio_tan=%f\n" ,
861+ tc -> maxvel , tc -> acc_ratio_tan );
862+
863+ return 0 ;
763864}
764865
765866/**
@@ -785,7 +886,7 @@ int tcFinalizeLength(TC_STRUCT * const tc)
785886
786887 tcClampVelocityByLength (tc );
787888
788- tcUpdateCircleAccRatio (tc );
889+ tcUpdateArcLimits (tc );
789890
790891 tc -> finalized = 1 ;
791892 return TP_ERR_OK ;
0 commit comments