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
Yuri D'Elia 2021-08-28 19:29:52 +02:00 committed by DRracer
parent 734612238e
commit bb9e2a075b
2 changed files with 60 additions and 45 deletions

View File

@ -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;

View File

@ -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():