@@ -35,6 +35,7 @@ extern "C" {
3535 // Access to global pointers (defined in usrmotintf.cc, initialized by milltask)
3636 extern struct emcmot_struct_t *emcmotStruct;
3737 extern struct emcmot_internal_t *emcmotInternal;
38+
3839}
3940
4041/* *
@@ -445,7 +446,6 @@ static void tpComputeKinkVelocity_9D(TP_STRUCT *tp, TC_QUEUE_STRUCT *queue,
445446
446447 double vel_cap = fmin (prev_tc->maxvel , tc->maxvel );
447448 double dt = tp->cycleTime ;
448- double dt_sq = dt * dt;
449449 // Physical limit starts unconstrained — only jerk/accel physics determine it.
450450 // vel_cap (programmed feed) is applied separately for the backward pass.
451451 double kink_vel = 1e9 ;
@@ -486,6 +486,7 @@ static void tpComputeKinkVelocity_9D(TP_STRUCT *tp, TC_QUEUE_STRUCT *queue,
486486 // --- Part A: Direction-change limiting ---
487487 // For non-trivkins: joint velocity impulse = v * sum_a(|J[j][a]| * |delta_u[a]|) / dt
488488 // For trivkins (J = identity): simplifies to v * |delta_u[j]| / dt
489+ double dt_sq = dt * dt;
489490 if (delta_mag_sq >= 1e-12 ) {
490491 for (int j = 0 ; j < num_joints; j++) {
491492 double projection;
@@ -571,6 +572,68 @@ static void tpComputeKinkVelocity_9D(TP_STRUCT *tp, TC_QUEUE_STRUCT *queue,
571572 }
572573 }
573574
575+ // --- Part C: Virtual arc curvature for line-line junctions ---
576+ // Consecutive small line segments approximate a smooth curve. Compute the
577+ // discrete curvature from three points (start of prev, junction, end of tc)
578+ // and apply per-joint jerk and centripetal acceleration limits.
579+ // This caps kink_vel at the physically safe velocity for the approximated
580+ // curve's curvature, preventing kink_ratio from exceeding geometric limits.
581+ // Ported from planner 0/1 (tp.c:2266-2337) with Jacobian extension.
582+ if (prev_tc->motion_type == TC_LINEAR && tc->motion_type == TC_LINEAR) {
583+ PmCartesian P0 = prev_tc->coords .line .xyz .start ;
584+ PmCartesian P1 = prev_tc->coords .line .xyz .end ; // junction point
585+ PmCartesian P2 = tc->coords .line .xyz .end ;
586+
587+ PmCartesian A, B;
588+ pmCartCartSub (&P1, &P0, &A);
589+ pmCartCartSub (&P2, &P1, &B);
590+
591+ double A_mag, B_mag;
592+ pmCartMag (&A, &A_mag);
593+ pmCartMag (&B, &B_mag);
594+
595+ if (A_mag > 1e-12 && B_mag > 1e-12 ) {
596+ // kappa = 2*(t_out - t_in) / (|A| + |B|)
597+ // Equals the curvature of the circumscribed circle through P0, P1, P2.
598+ PmCartesian t_in, t_out, delta_t , curv_vec;
599+ pmCartScalMult (&A, 1.0 / A_mag, &t_in);
600+ pmCartScalMult (&B, 1.0 / B_mag, &t_out);
601+ pmCartCartSub (&t_out, &t_in, &delta_t );
602+ pmCartScalMult (&delta_t , 2.0 / (A_mag + B_mag), &curv_vec);
603+
604+ double curv_xyz[3 ] = {curv_vec.x , curv_vec.y , curv_vec.z };
605+
606+ for (int j = 0 ; j < num_joints; j++) {
607+ double curv_proj;
608+ if (have_jacobian) {
609+ curv_proj = 0.0 ;
610+ for (int a = 0 ; a < 3 ; a++)
611+ curv_proj += fabs (J[j][a]) * fabs (curv_xyz[a]);
612+ } else {
613+ if (j >= 3 ) continue ; // trivkins: XYZ curvature only
614+ curv_proj = fabs (curv_xyz[j]);
615+ }
616+ if (curv_proj < 1e-15 ) continue ;
617+
618+ // Curvature-change jerk: v^2 * curv_proj / dt <= jerk_limit
619+ double jerk_lim = g_userspace_kins_planner.isEnabled ()
620+ ? g_userspace_kins_planner.getJointJerkLimit (j) : 1e9 ;
621+ if (jerk_lim > 0 && jerk_lim < 1e9 ) {
622+ double v_curv_jerk = sqrt (jerk_lim * dt / curv_proj);
623+ if (v_curv_jerk < kink_vel) kink_vel = v_curv_jerk;
624+ }
625+
626+ // Centripetal acceleration: v^2 * curv_proj <= acc_limit
627+ double acc_lim = g_userspace_kins_planner.isEnabled ()
628+ ? g_userspace_kins_planner.getJointAccLimit (j) : 1e9 ;
629+ if (acc_lim > 0 && acc_lim < 1e9 ) {
630+ double v_curv_acc = sqrt (acc_lim / curv_proj);
631+ if (v_curv_acc < kink_vel) kink_vel = v_curv_acc;
632+ }
633+ }
634+ }
635+ }
636+
574637 // Floor at a small positive value to avoid numerical issues
575638 if (kink_vel < 1e-6 ) kink_vel = 1e-6 ;
576639
0 commit comments