Skip to content

Commit 5d4614f

Browse files
authored
Merge pull request #3744 from grandixximo/master
S-Curve refinements
2 parents 0bfcad6 + 732a18e commit 5d4614f

14 files changed

Lines changed: 413 additions & 643 deletions

File tree

configs/sim/axis/axis_9axis_scurve.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ HALFILE = axis_9axis_scurve.hal
5454
# PLANNER_TYPE: Select the acceleration profile shape
5555
# 0 = Trapezoidal (traditional - constant acceleration)
5656
# 1 = S-curve (smoother - limited jerk/derivative of acceleration)
57-
# 2 = S-curve (smoother - but sharp in corners - to be deprecated)
5857
# Default: 0 (trapezoidal)
5958
#
6059
# NOTE: This parameter can be changed at RUNTIME via HAL pin:

configs/sim/axis/axis_mm_scurve.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ HALFILE = axis_mm_scurve.hal
5454
# PLANNER_TYPE: Select the acceleration profile shape
5555
# 0 = Trapezoidal (traditional - constant acceleration)
5656
# 1 = S-curve (smoother - limited jerk/derivative of acceleration)
57-
# 2 = S-curve (smoother - but sharp in corners - to be deprecated)
5857
# Default: 0 (trapezoidal)
5958
#
6059
# NOTE: This parameter can be changed at RUNTIME via HAL pin:

src/emc/ini/inihal.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,12 @@ int check_ini_hal_items(int numjoints)
297297
if (CHANGED(traj_planner_type)) {
298298
if (debug) SHOW_CHANGE_INT(traj_planner_type)
299299
UPDATE(traj_planner_type);
300-
if (0 != emcTrajPlannerType(NEW(traj_planner_type))) {
300+
// Only 0 and 1 are supported, set to 0 if invalid
301+
int planner_type = NEW(traj_planner_type);
302+
if (planner_type != 0 && planner_type != 1) {
303+
planner_type = 0;
304+
}
305+
if (0 != emcTrajPlannerType(planner_type)) {
301306
if (emc_debug & EMC_DEBUG_CONFIG) {
302307
rcs_print("check_ini_hal_items:bad return value from emcTrajPlannerType\n");
303308
}

src/emc/ini/initraj.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ static int loadTraj(EmcIniFile *trajInifile)
225225
old_inihal_data.traj_default_jerk = jerk;
226226
planner_type = 0; // Default: 0 = trapezoidal, 1 = S-curve
227227
trajInifile->Find(&planner_type, "PLANNER_TYPE", "TRAJ");
228+
// Only 0 and 1 are supported, set to 0 if invalid
229+
if (planner_type != 0 && planner_type != 1) {
230+
planner_type = 0;
231+
}
228232
if (0 != emcTrajPlannerType(planner_type)) {
229233
if (emc_debug & EMC_DEBUG_CONFIG) {
230234
rcs_print("bad return value from emcTrajPlannerType\n");

src/emc/motion/command.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,12 @@ void emcmotCommandHandler_locked(void *arg, long servo_period)
11941194
/* set the type of planner: 0 = trapezoidal, 1 = S-curve */
11951195
/* can do it at any time */
11961196
rtapi_print_msg(RTAPI_MSG_DBG, "SET_PLANNER_TYPE, type(%d)", emcmotCommand->planner_type);
1197-
emcmotStatus->planner_type = emcmotCommand->planner_type;
1197+
// Only 0 and 1 are supported, set to 0 if invalid
1198+
if (emcmotCommand->planner_type != 0 && emcmotCommand->planner_type != 1) {
1199+
emcmotStatus->planner_type = 0;
1200+
} else {
1201+
emcmotStatus->planner_type = emcmotCommand->planner_type;
1202+
}
11981203
break;
11991204

12001205
case EMCMOT_PAUSE:

src/emc/task/emccanon.cc

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -704,13 +704,16 @@ static double getStraightJerk(double x, double y, double z,
704704
}
705705

706706
// Pure linear move:
707+
// For jerk-limited motion: d = (1/6)*j*t³, so t = cbrt(6*d/j)
708+
// We use t = cbrt(d/j) as a characteristic time (omitting the constant factor,
709+
// which cancels out when we compute path jerk = dtot / tmax³)
707710
if (canon.cartesian_move && !canon.angular_move) {
708-
tx = dx? (dx / FROM_EXT_LEN(emcAxisGetMaxJerk(0))): 0.0;
709-
ty = dy? (dy / FROM_EXT_LEN(emcAxisGetMaxJerk(1))): 0.0;
710-
tz = dz? (dz / FROM_EXT_LEN(emcAxisGetMaxJerk(2))): 0.0;
711-
tu = du? (du / FROM_EXT_LEN(emcAxisGetMaxJerk(6))): 0.0;
712-
tv = dv? (dv / FROM_EXT_LEN(emcAxisGetMaxJerk(7))): 0.0;
713-
tw = dw? (dw / FROM_EXT_LEN(emcAxisGetMaxJerk(8))): 0.0;
711+
tx = dx? cbrt(dx / FROM_EXT_LEN(emcAxisGetMaxJerk(0))): 0.0;
712+
ty = dy? cbrt(dy / FROM_EXT_LEN(emcAxisGetMaxJerk(1))): 0.0;
713+
tz = dz? cbrt(dz / FROM_EXT_LEN(emcAxisGetMaxJerk(2))): 0.0;
714+
tu = du? cbrt(du / FROM_EXT_LEN(emcAxisGetMaxJerk(6))): 0.0;
715+
tv = dv? cbrt(dv / FROM_EXT_LEN(emcAxisGetMaxJerk(7))): 0.0;
716+
tw = dw? cbrt(dw / FROM_EXT_LEN(emcAxisGetMaxJerk(8))): 0.0;
714717
out.tmax = MAX3(tx, ty ,tz);
715718
out.tmax = MAX4(tu, tv, tw, out.tmax);
716719

@@ -720,38 +723,38 @@ static double getStraightJerk(double x, double y, double z,
720723
out.dtot = sqrt(du * du + dv * dv + dw * dw);
721724

722725
if (out.tmax > 0.0) {
723-
out.jerk = out.dtot / (out.tmax * out.tmax);
726+
out.jerk = out.dtot / (out.tmax * out.tmax * out.tmax);
724727
}
725728
}
726729
// Pure angular move:
727730
else if (!canon.cartesian_move && canon.angular_move) {
728-
ta = da? (da / FROM_EXT_ANG(emcAxisGetMaxJerk(3))): 0.0;
729-
tb = db? (db / FROM_EXT_ANG(emcAxisGetMaxJerk(4))): 0.0;
730-
tc = dc? (dc / FROM_EXT_ANG(emcAxisGetMaxJerk(5))): 0.0;
731+
ta = da? cbrt(da / FROM_EXT_ANG(emcAxisGetMaxJerk(3))): 0.0;
732+
tb = db? cbrt(db / FROM_EXT_ANG(emcAxisGetMaxJerk(4))): 0.0;
733+
tc = dc? cbrt(dc / FROM_EXT_ANG(emcAxisGetMaxJerk(5))): 0.0;
731734
out.tmax = MAX3(ta, tb, tc);
732735

733736
out.dtot = sqrt(da * da + db * db + dc * dc);
734737
if (out.tmax > 0.0) {
735-
out.jerk = out.dtot / (out.tmax * out.tmax);
738+
out.jerk = out.dtot / (out.tmax * out.tmax * out.tmax);
736739
}
737740
}
738741
// Combination angular and linear move:
739742
else if (canon.cartesian_move && canon.angular_move) {
740-
tx = dx? (dx / FROM_EXT_LEN(emcAxisGetMaxJerk(0))): 0.0;
741-
ty = dy? (dy / FROM_EXT_LEN(emcAxisGetMaxJerk(1))): 0.0;
742-
tz = dz? (dz / FROM_EXT_LEN(emcAxisGetMaxJerk(2))): 0.0;
743-
ta = da? (da / FROM_EXT_ANG(emcAxisGetMaxJerk(3))): 0.0;
744-
tb = db? (db / FROM_EXT_ANG(emcAxisGetMaxJerk(4))): 0.0;
745-
tc = dc? (dc / FROM_EXT_ANG(emcAxisGetMaxJerk(5))): 0.0;
746-
tu = du? (du / FROM_EXT_LEN(emcAxisGetMaxJerk(6))): 0.0;
747-
tv = dv? (dv / FROM_EXT_LEN(emcAxisGetMaxJerk(7))): 0.0;
748-
tw = dw? (dw / FROM_EXT_LEN(emcAxisGetMaxJerk(8))): 0.0;
743+
tx = dx? cbrt(dx / FROM_EXT_LEN(emcAxisGetMaxJerk(0))): 0.0;
744+
ty = dy? cbrt(dy / FROM_EXT_LEN(emcAxisGetMaxJerk(1))): 0.0;
745+
tz = dz? cbrt(dz / FROM_EXT_LEN(emcAxisGetMaxJerk(2))): 0.0;
746+
ta = da? cbrt(da / FROM_EXT_ANG(emcAxisGetMaxJerk(3))): 0.0;
747+
tb = db? cbrt(db / FROM_EXT_ANG(emcAxisGetMaxJerk(4))): 0.0;
748+
tc = dc? cbrt(dc / FROM_EXT_ANG(emcAxisGetMaxJerk(5))): 0.0;
749+
tu = du? cbrt(du / FROM_EXT_LEN(emcAxisGetMaxJerk(6))): 0.0;
750+
tv = dv? cbrt(dv / FROM_EXT_LEN(emcAxisGetMaxJerk(7))): 0.0;
751+
tw = dw? cbrt(dw / FROM_EXT_LEN(emcAxisGetMaxJerk(8))): 0.0;
749752
out.tmax = MAX9(tx, ty, tz,
750753
ta, tb, tc,
751754
tu, tv, tw);
752755

753756
if(debug_velacc)
754-
printf("getStraightJerk t^2 tx %g ty %g tz %g ta %g tb %g tc %g tu %g tv %g tw %g\n",
757+
printf("getStraightJerk t tx %g ty %g tz %g ta %g tb %g tc %g tu %g tv %g tw %g\n",
755758
tx, ty, tz, ta, tb, tc, tu, tv, tw);
756759

757760
if(dx || dy || dz)
@@ -760,7 +763,7 @@ static double getStraightJerk(double x, double y, double z,
760763
out.dtot = sqrt(du * du + dv * dv + dw * dw);
761764

762765
if (out.tmax > 0.0) {
763-
out.jerk = out.dtot / (out.tmax * out.tmax);
766+
out.jerk = out.dtot / (out.tmax * out.tmax * out.tmax);
764767
}
765768
}
766769
//if(debug_velacc)

src/emc/tp/blendmath.c

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,7 @@ int blendComputeParameters(BlendParameters * const param)
11561156

11571157
// Find maximum velocity allowed by accel and radius
11581158
double v_normal;
1159-
1159+
11601160
if(GET_TRAJ_PLANNER_TYPE() == 1){
11611161
v_normal = findSCurveVPeak(param->a_n_max, emcmotStatus->jerk, R_geom);
11621162
}else{
@@ -1179,7 +1179,38 @@ int blendComputeParameters(BlendParameters * const param)
11791179
double s_blend = t_blend * param->v_plan;
11801180
double R_blend = fmin(s_blend / param->phi, R_geom); //Clamp by limiting radius
11811181

1182-
param->R_plan = fmax(pmSq(param->v_plan) / param->a_n_max, R_blend);
1182+
// Calculate minimum radius needed to keep jerk within limits at v_plan
1183+
// For circular motion: j = v³/R² (worst case at corner transitions)
1184+
// Therefore: R_min = v^(3/2) / sqrt(j_max)
1185+
double R_jerk_min = 0.0;
1186+
if (GET_TRAJ_PLANNER_TYPE() == 1 && emcmotStatus->jerk > TP_POS_EPSILON) {
1187+
// R_min = v^(3/2) / sqrt(j)
1188+
double v_32 = pmSqrt(param->v_plan) * param->v_plan; // v^(3/2)
1189+
double j_sqrt = pmSqrt(emcmotStatus->jerk);
1190+
R_jerk_min = v_32 / j_sqrt;
1191+
1192+
tp_debug_print("R_jerk_min = %f (for v=%f, j=%f)\n",
1193+
R_jerk_min, param->v_plan, emcmotStatus->jerk);
1194+
}
1195+
1196+
// Calculate radius from acceleration constraint
1197+
double R_accel = pmSq(param->v_plan) / param->a_n_max;
1198+
1199+
// Apply jerk constraint - use the larger of R_blend and R_jerk_min
1200+
double R_min = fmax(R_blend, R_jerk_min);
1201+
1202+
// Final radius must satisfy both acceleration and minimum (blend/jerk) constraints
1203+
param->R_plan = fmax(R_accel, R_min);
1204+
1205+
// Calculate arc length
1206+
param->s_arc = param->R_plan * param->phi;
1207+
1208+
tp_debug_print("R_accel=%f, R_blend=%f, R_jerk_min=%f, R_plan=%f\n",
1209+
R_accel, R_blend, R_jerk_min, param->R_plan);
1210+
1211+
// Note: Velocity jerk limiting for blend arcs is now handled uniformly
1212+
// in tcUpdateArcLimits() during segment finalization, along with TC_CIRCULAR arcs.
1213+
11831214
param->d_plan = param->R_plan / tan(param->theta);
11841215

11851216
tp_debug_print("v_plan = %f\n", param->v_plan);
@@ -1662,43 +1693,6 @@ double pmCartAbsMax(PmCartesian const * const v)
16621693
}
16631694

16641695

1665-
PmCircleLimits pmCircleActualMaxVel(PmCircle const * circle,
1666-
double v_max,
1667-
double a_max)
1668-
{
1669-
double a_n_max_cutoff = BLEND_ACC_RATIO_NORMAL * a_max;
1670-
1671-
double eff_radius = pmCircleEffectiveMinRadius(circle);
1672-
// Find the acceleration necessary to reach the maximum velocity
1673-
double a_n_vmax = pmSq(v_max) / fmax(eff_radius, DOUBLE_FUZZ);
1674-
// Find the maximum velocity that still obeys our desired tangential / total acceleration ratio
1675-
double v_max_cutoff = pmSqrt(a_n_max_cutoff * eff_radius);
1676-
1677-
double v_max_actual = v_max;
1678-
double acc_ratio_tan = BLEND_ACC_RATIO_TANGENTIAL;
1679-
1680-
if (a_n_vmax > a_n_max_cutoff) {
1681-
v_max_actual = v_max_cutoff;
1682-
} else {
1683-
acc_ratio_tan = pmSqrt(1.0 - pmSq(a_n_vmax / a_max));
1684-
}
1685-
1686-
tp_debug_json_start(pmCircleActualMaxVel);
1687-
tp_debug_json_double(eff_radius);
1688-
tp_debug_json_double(v_max);
1689-
tp_debug_json_double(v_max_cutoff);
1690-
tp_debug_json_double(a_n_max_cutoff);
1691-
tp_debug_json_double(a_n_vmax);
1692-
tp_debug_json_double(acc_ratio_tan);
1693-
tp_debug_json_end();
1694-
1695-
PmCircleLimits limits = {
1696-
v_max_actual,
1697-
acc_ratio_tan
1698-
};
1699-
1700-
return limits;
1701-
}
17021696

17031697

17041698
/** @section spiralfuncs Functions to approximate spiral arc length */

src/emc/tp/blendmath.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,6 @@ int blendPoints3Print(BlendPoints3 const * const points);
243243

244244
double pmCartAbsMax(PmCartesian const * const v);
245245

246-
typedef struct {
247-
double v_max;
248-
double acc_ratio;
249-
} PmCircleLimits;
250-
251-
PmCircleLimits pmCircleActualMaxVel(const PmCircle *circle,
252-
double v_max_nominal,
253-
double a_max_nominal);
254-
255246
int findSpiralArcLengthFit(PmCircle const * const circle,
256247
SpiralArcLengthFit * const fit);
257248
int pmCircleAngleFromProgress(PmCircle const * const circle,

0 commit comments

Comments
 (0)