@@ -60,6 +60,14 @@ static unsigned long last_period = 0;
6060/* servo cycle time */
6161static double servo_period ;
6262
63+ /* Planner type 2: joint-space velocity feedforward via finite differences.
64+ * Correct for ALL kinematics (trivkins, tilting pivot, etc.) unlike the
65+ * previous direction-vector decomposition which assumed joints = XYZ axes. */
66+ static double p2_prev_pos [EMCMOT_MAX_JOINTS ];
67+ static double p2_prev_vel [EMCMOT_MAX_JOINTS ];
68+ static double p2_prev_acc [EMCMOT_MAX_JOINTS ];
69+ static int p2_ff_init = 0 ;
70+
6371extern struct emcmot_status_t * emcmotStatus ;
6472
6573// *pcmd_p[0] is shorthand for emcmotStatus->carte_pos_cmd.tran.x
@@ -1207,6 +1215,7 @@ static void get_pos_cmds(long period)
12071215 /* run traj planner code depending on the state */
12081216 switch ( emcmotStatus -> motion_state ) {
12091217 case EMCMOT_MOTION_FREE :
1218+ p2_ff_init = 0 ; /* Reset feedforward state when leaving coord mode */
12101219 /* in free mode, each joint is planned independently */
12111220 /* initial value for flag, if needed it will be cleared below */
12121221 SET_MOTION_INPOS_FLAG (1 );
@@ -1356,39 +1365,47 @@ static void get_pos_cmds(long period)
13561365 }
13571366
13581367 if (emcmotStatus -> planner_type == 2 ) {
1359- /* Planner type 2 (Ruckig): bypass cubic interpolator. */
1368+ /* Planner type 2 (Ruckig): bypass cubic interpolator.
1369+ * Joint velocity/acceleration/jerk feedforward computed from
1370+ * finite differences of IK output — correct for ALL kinematics. */
13601371 EmcPose current_pos = emcmotStatus -> carte_pos_cmd ;
13611372
13621373 result = kinematicsInverse (& current_pos , positions , & iflags , & fflags );
13631374 if (result == 0 ) {
1364- double path_vel = emcmotStatus -> current_vel ;
1365- double path_acc = emcmotStatus -> current_acc ;
1366- double path_jerk = emcmotStatus -> current_jerk ;
1367- PmCartesian dir = emcmotStatus -> current_dir ;
1375+ double inv_dt = 1.0 / servo_period ;
13681376
13691377 for (joint_num = 0 ; joint_num < NO_OF_KINS_JOINTS ; joint_num ++ ) {
13701378 if (!isfinite (positions [joint_num ])) {
13711379 reportError (_ ("kinematicsInverse gave non-finite joint location on joint %d" ),
13721380 joint_num );
13731381 SET_MOTION_ERROR_FLAG (1 );
13741382 emcmotInternal -> enabling = 0 ;
1383+ p2_ff_init = 0 ;
13751384 break ;
13761385 }
13771386 joint = & joints [joint_num ];
13781387 joint -> coarse_pos = positions [joint_num ];
13791388 joint -> pos_cmd = positions [joint_num ];
13801389
1381- /* Decompose path vel/acc/jerk into per-axis using direction vector.
1382- * For Cartesian machines joint 0,1,2 = X,Y,Z */
1383- double d = 0.0 ;
1384- if (joint_num == 0 ) d = dir .x ;
1385- else if (joint_num == 1 ) d = dir .y ;
1386- else if (joint_num == 2 ) d = dir .z ;
1387-
1388- joint -> vel_cmd = path_vel * d ;
1389- joint -> acc_cmd = path_acc * d ;
1390- joint -> jerk_cmd = path_jerk * d ;
1390+ if (p2_ff_init ) {
1391+ double vel = (positions [joint_num ] - p2_prev_pos [joint_num ]) * inv_dt ;
1392+ double acc = (vel - p2_prev_vel [joint_num ]) * inv_dt ;
1393+ joint -> vel_cmd = vel ;
1394+ joint -> acc_cmd = acc ;
1395+ joint -> jerk_cmd = (acc - p2_prev_acc [joint_num ]) * inv_dt ;
1396+ p2_prev_vel [joint_num ] = vel ;
1397+ p2_prev_acc [joint_num ] = acc ;
1398+ } else {
1399+ joint -> vel_cmd = 0.0 ;
1400+ joint -> acc_cmd = 0.0 ;
1401+ joint -> jerk_cmd = 0.0 ;
1402+ p2_prev_vel [joint_num ] = 0.0 ;
1403+ p2_prev_acc [joint_num ] = 0.0 ;
1404+ }
1405+ p2_prev_pos [joint_num ] = positions [joint_num ];
13911406 }
1407+ p2_ff_init = 1 ;
1408+
13921409 /* Still feed the cubic so it stays primed if we switch planner types */
13931410 while (cubicNeedNextPoint (& (joints [0 ].cubic ))) {
13941411 for (joint_num = 0 ; joint_num < NO_OF_KINS_JOINTS ; joint_num ++ ) {
@@ -1399,8 +1416,10 @@ static void get_pos_cmds(long period)
13991416 reportError (_ ("kinematicsInverse failed" ));
14001417 SET_MOTION_ERROR_FLAG (1 );
14011418 emcmotInternal -> enabling = 0 ;
1419+ p2_ff_init = 0 ;
14021420 }
14031421 } else {
1422+ p2_ff_init = 0 ;
14041423 /* Planner type 0 and 1: use cubic interpolator as before */
14051424
14061425 /* Fill cubic buffer if needed (may need multiple points per cycle) */
@@ -1476,6 +1495,7 @@ static void get_pos_cmds(long period)
14761495 break ;
14771496
14781497 case EMCMOT_MOTION_TELEOP :
1498+ p2_ff_init = 0 ; /* Reset feedforward state when leaving coord mode */
14791499 ext_offset_teleop_limit = axis_calc_motion (servo_period );
14801500 if (!ext_offset_teleop_limit ) {
14811501 ext_offset_coord_limit = 0 ; //in case was set in prior coord motion
0 commit comments