temp_motion_ramp: test single and multiple axes moving
stepTimerQuantum introduces discretization error, which makes the acceleration curves noisy. In rampgen generate ramps for a single moving axis in addition of two axes moving together. Then, in test_ramp_gen, test a single axis moving accurately, while allow for some discretization error when two (or more) axes are running.pull/108/head
parent
734612238e
commit
bb9e2a075b
|
|
@ -5,12 +5,13 @@
|
|||
using namespace modules::motion;
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
const char *output = argv[1];
|
||||
if (!argv[1]) {
|
||||
if (argc != 2 || !argv[1]) {
|
||||
fprintf(stderr, "Usage: %s <output>\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
// "parse" arguments
|
||||
const char *output = argv[1];
|
||||
FILE *fd = fopen(output, "w");
|
||||
if (!fd) {
|
||||
fprintf(stderr, "%s: can't open output file %s: ", argv[0], output);
|
||||
|
|
@ -29,43 +30,49 @@ int main(int argc, const char *argv[]) {
|
|||
// write common parameters
|
||||
fprintf(fd, "{\"timebase\": %lu}\n", F_CPU / config::stepTimerFrequencyDivider);
|
||||
|
||||
for (int accel = 2000; accel <= 50000; accel *= 2) {
|
||||
// first axis defines the nominal values
|
||||
motion.SetJerk(ax_a, maxJerk);
|
||||
motion.SetPosition(ax_a, 0);
|
||||
motion.SetAcceleration(ax_a, accel);
|
||||
motion.PlanMoveTo(ax_a, steps_a, maxFeedRate);
|
||||
for (int ax_cnt = 0; ax_cnt != 2; ++ax_cnt) {
|
||||
for (int accel = 2000; accel <= 50000; accel *= 2) {
|
||||
// first axis defines the nominal values
|
||||
motion.SetJerk(ax_a, maxJerk);
|
||||
motion.SetPosition(ax_a, 0);
|
||||
motion.SetAcceleration(ax_a, accel);
|
||||
motion.PlanMoveTo(ax_a, steps_a, maxFeedRate);
|
||||
|
||||
fprintf(fd, "[{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}, ",
|
||||
steps_a, maxJerk, accel, maxFeedRate);
|
||||
fprintf(fd, "[{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}",
|
||||
steps_a, maxJerk, accel, maxFeedRate);
|
||||
|
||||
// second axis finishes slightly sooner at triple acceleration to maximize the
|
||||
// aliasing effects
|
||||
int accel_3 = accel * 3;
|
||||
motion.SetJerk(ax_b, 1);
|
||||
motion.SetPosition(ax_b, 0);
|
||||
motion.SetAcceleration(ax_b, accel_3);
|
||||
motion.PlanMoveTo(ax_b, steps_b, maxFeedRate);
|
||||
if (ax_cnt > 1) {
|
||||
// second axis finishes slightly sooner at triple acceleration to maximize the
|
||||
// aliasing effects
|
||||
int accel_3 = accel * 3;
|
||||
motion.SetJerk(ax_b, 1);
|
||||
motion.SetPosition(ax_b, 0);
|
||||
motion.SetAcceleration(ax_b, accel_3);
|
||||
motion.PlanMoveTo(ax_b, steps_b, maxFeedRate);
|
||||
|
||||
fprintf(fd, "{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}]\n",
|
||||
steps_b, maxJerk, accel_3, maxFeedRate);
|
||||
fprintf(fd, ", {\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}",
|
||||
steps_b, maxJerk, accel_3, maxFeedRate);
|
||||
}
|
||||
|
||||
// initial state
|
||||
unsigned long ts = 0;
|
||||
st_timer_t next = 0;
|
||||
fprintf(fd, "%lu %u %d %d\n", ts, next, motion.CurPosition(ax_a), motion.CurPosition(ax_b));
|
||||
fprintf(fd, "]\n");
|
||||
|
||||
// step and output time, interval and positions
|
||||
do {
|
||||
next = motion.Step();
|
||||
pos_t pos_idler = motion.CurPosition(ax_a);
|
||||
pos_t pos_selector = motion.CurPosition(ax_b);
|
||||
// initial state
|
||||
unsigned long ts = 0;
|
||||
st_timer_t next = 0;
|
||||
fprintf(fd, "%lu %u %d %d\n", ts, next, motion.CurPosition(ax_a), motion.CurPosition(ax_b));
|
||||
|
||||
fprintf(fd, "%lu %u %d %d\n", ts, next, pos_idler, pos_selector);
|
||||
// step and output time, interval and positions
|
||||
do {
|
||||
next = motion.Step();
|
||||
pos_t pos_idler = motion.CurPosition(ax_a);
|
||||
pos_t pos_selector = motion.CurPosition(ax_b);
|
||||
|
||||
ts += next;
|
||||
} while (next);
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "%lu %u %d %d\n", ts, next, pos_idler, pos_selector);
|
||||
|
||||
ts += next;
|
||||
} while (next);
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return EX_OK;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ def load_data(data):
|
|||
return info, runs
|
||||
|
||||
|
||||
def check_axis(info, ax_info, data):
|
||||
def check_axis(info, ax_info, data, fine_check):
|
||||
tb = info['timebase']
|
||||
|
||||
# remove duplicate positions (meaning another axis was moved, not the current)
|
||||
|
|
@ -92,7 +92,7 @@ def check_axis(info, ax_info, data):
|
|||
startrate = data['rate'].iat[2] # skip first two null values
|
||||
endrate = data['rate'].iat[-1]
|
||||
|
||||
maxdev_coarse = (maxrate - startrate) / 20 # 5% speed deviation
|
||||
maxdev_coarse = (maxrate - startrate) / 10 # 10% speed deviation
|
||||
maxdev_fine = 20 # absolute maximum deviation
|
||||
maxdev_acc = 0.05 # 5% acceleration deviation
|
||||
|
||||
|
|
@ -108,12 +108,14 @@ def check_axis(info, ax_info, data):
|
|||
# acceleration (fine)
|
||||
acc_data['exp_fine'] = acc_data['rate'].iat[0] + acc_data['ts_s'] \
|
||||
/ acc_time * (acc_data['rate'].iat[-1] - startrate)
|
||||
assert ((acc_data['exp_fine'] - acc_data['rate']).abs().max() <
|
||||
maxdev_fine)
|
||||
if fine_check:
|
||||
assert ((acc_data['exp_fine'] - acc_data['rate']).abs().max() <
|
||||
maxdev_fine)
|
||||
|
||||
# check effective acceleration rate
|
||||
acc_vel = (acc_data['rate'].iat[-1] - acc_data['rate'].iat[0]) / acc_time
|
||||
assert (abs(acc_vel - ax_info['accel']) / ax_info['accel'] < 0.05)
|
||||
if fine_check:
|
||||
assert (abs(acc_vel - ax_info['accel']) / ax_info['accel'] < 0.05)
|
||||
|
||||
# deceleration (coarse)
|
||||
dec_data = data[(data['pos'] > (data['pos'].iat[-1] - acc_dist))][2:]
|
||||
|
|
@ -127,21 +129,25 @@ def check_axis(info, ax_info, data):
|
|||
# deceleration (fine)
|
||||
dec_data['exp_fine'] = dec_data['rate'].iat[0] - dec_data['ts_s'] \
|
||||
/ dec_time * (dec_data['rate'].iat[0] - endrate)
|
||||
assert ((dec_data['exp_fine'] - dec_data['rate']).abs().max() <
|
||||
maxdev_fine)
|
||||
if fine_check:
|
||||
assert ((dec_data['exp_fine'] - dec_data['rate']).abs().max() <
|
||||
maxdev_fine)
|
||||
|
||||
# check effective deceleration rate
|
||||
dec_vel = (dec_data['rate'].iat[-1] - dec_data['rate'].iat[0]) / dec_time
|
||||
print(abs(dec_vel - ax_info['accel']) / ax_info['accel'] < 0.05)
|
||||
dec_vel = (dec_data['rate'].iat[0] - dec_data['rate'].iat[-1]) / dec_time
|
||||
if fine_check:
|
||||
# TODO: deceleration rate is not as accurate as acceleration!
|
||||
assert (abs(dec_vel - ax_info['accel']) / ax_info['accel'] < 0.15)
|
||||
|
||||
|
||||
def check_run(info, run):
|
||||
# unpack the axis data
|
||||
ax_info, data = run
|
||||
ax_count = len(ax_info)
|
||||
|
||||
# split axis information
|
||||
ax_data = []
|
||||
for ax in range(2):
|
||||
for ax in range(len(ax_info)):
|
||||
ax_info[ax]['name'] = ax
|
||||
tmp = []
|
||||
for i in range(len(data)):
|
||||
|
|
@ -150,9 +156,11 @@ def check_run(info, run):
|
|||
|
||||
ax_data.append(pd.DataFrame(tmp, columns=['ts', 'int', 'pos']))
|
||||
|
||||
# check each axis independently
|
||||
for ax in range(2):
|
||||
check_axis(info, ax_info[ax], ax_data[ax])
|
||||
# check each axis independently, but only perform fine-grained checks when a single
|
||||
# axis is run due to stepperTimerQuantum introducing discretization noise
|
||||
fine_check = ax_count == 1
|
||||
for ax in range(ax_count):
|
||||
check_axis(info, ax_info[ax], ax_data[ax], fine_check)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
Loading…
Reference in New Issue