Skip to content

Commit c28a7c8

Browse files
committed
Joint-space feedforward via finite differences for planner type 2
Replace the direction-vector decomposition (which assumed joints = XYZ) with proper finite-difference computation of per-joint velocity, acceleration, and jerk from IK output. Correct for all kinematics.
1 parent 43db874 commit c28a7c8

1 file changed

Lines changed: 35 additions & 15 deletions

File tree

src/emc/motion/control.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ static unsigned long last_period = 0;
6060
/* servo cycle time */
6161
static 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+
6371
extern 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

Comments
 (0)