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; using namespace modules::motion;
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
const char *output = argv[1]; if (argc != 2 || !argv[1]) {
if (!argv[1]) {
fprintf(stderr, "Usage: %s <output>\n", argv[0]); fprintf(stderr, "Usage: %s <output>\n", argv[0]);
return EX_USAGE; return EX_USAGE;
} }
// "parse" arguments
const char *output = argv[1];
FILE *fd = fopen(output, "w"); FILE *fd = fopen(output, "w");
if (!fd) { if (!fd) {
fprintf(stderr, "%s: can't open output file %s: ", argv[0], output); fprintf(stderr, "%s: can't open output file %s: ", argv[0], output);
@ -29,6 +30,7 @@ int main(int argc, const char *argv[]) {
// write common parameters // write common parameters
fprintf(fd, "{\"timebase\": %lu}\n", F_CPU / config::stepTimerFrequencyDivider); fprintf(fd, "{\"timebase\": %lu}\n", F_CPU / config::stepTimerFrequencyDivider);
for (int ax_cnt = 0; ax_cnt != 2; ++ax_cnt) {
for (int accel = 2000; accel <= 50000; accel *= 2) { for (int accel = 2000; accel <= 50000; accel *= 2) {
// first axis defines the nominal values // first axis defines the nominal values
motion.SetJerk(ax_a, maxJerk); motion.SetJerk(ax_a, maxJerk);
@ -36,9 +38,10 @@ int main(int argc, const char *argv[]) {
motion.SetAcceleration(ax_a, accel); motion.SetAcceleration(ax_a, accel);
motion.PlanMoveTo(ax_a, steps_a, maxFeedRate); motion.PlanMoveTo(ax_a, steps_a, maxFeedRate);
fprintf(fd, "[{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}, ", fprintf(fd, "[{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}",
steps_a, maxJerk, accel, maxFeedRate); steps_a, maxJerk, accel, maxFeedRate);
if (ax_cnt > 1) {
// second axis finishes slightly sooner at triple acceleration to maximize the // second axis finishes slightly sooner at triple acceleration to maximize the
// aliasing effects // aliasing effects
int accel_3 = accel * 3; int accel_3 = accel * 3;
@ -47,8 +50,11 @@ int main(int argc, const char *argv[]) {
motion.SetAcceleration(ax_b, accel_3); motion.SetAcceleration(ax_b, accel_3);
motion.PlanMoveTo(ax_b, steps_b, maxFeedRate); motion.PlanMoveTo(ax_b, steps_b, maxFeedRate);
fprintf(fd, "{\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}]\n", fprintf(fd, ", {\"steps\": %d, \"jerk\": %d, \"accel\": %d, \"maxrate\": %d}",
steps_b, maxJerk, accel_3, maxFeedRate); steps_b, maxJerk, accel_3, maxFeedRate);
}
fprintf(fd, "]\n");
// initial state // initial state
unsigned long ts = 0; unsigned long ts = 0;
@ -67,6 +73,7 @@ int main(int argc, const char *argv[]) {
} while (next); } while (next);
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
}
return EX_OK; return EX_OK;
} }

View File

@ -36,7 +36,7 @@ def load_data(data):
return info, runs return info, runs
def check_axis(info, ax_info, data): def check_axis(info, ax_info, data, fine_check):
tb = info['timebase'] tb = info['timebase']
# remove duplicate positions (meaning another axis was moved, not the current) # 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 startrate = data['rate'].iat[2] # skip first two null values
endrate = data['rate'].iat[-1] 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_fine = 20 # absolute maximum deviation
maxdev_acc = 0.05 # 5% acceleration deviation maxdev_acc = 0.05 # 5% acceleration deviation
@ -108,11 +108,13 @@ def check_axis(info, ax_info, data):
# acceleration (fine) # acceleration (fine)
acc_data['exp_fine'] = acc_data['rate'].iat[0] + acc_data['ts_s'] \ acc_data['exp_fine'] = acc_data['rate'].iat[0] + acc_data['ts_s'] \
/ acc_time * (acc_data['rate'].iat[-1] - startrate) / acc_time * (acc_data['rate'].iat[-1] - startrate)
if fine_check:
assert ((acc_data['exp_fine'] - acc_data['rate']).abs().max() < assert ((acc_data['exp_fine'] - acc_data['rate']).abs().max() <
maxdev_fine) maxdev_fine)
# check effective acceleration rate # check effective acceleration rate
acc_vel = (acc_data['rate'].iat[-1] - acc_data['rate'].iat[0]) / acc_time acc_vel = (acc_data['rate'].iat[-1] - acc_data['rate'].iat[0]) / acc_time
if fine_check:
assert (abs(acc_vel - ax_info['accel']) / ax_info['accel'] < 0.05) assert (abs(acc_vel - ax_info['accel']) / ax_info['accel'] < 0.05)
# deceleration (coarse) # deceleration (coarse)
@ -127,21 +129,25 @@ def check_axis(info, ax_info, data):
# deceleration (fine) # deceleration (fine)
dec_data['exp_fine'] = dec_data['rate'].iat[0] - dec_data['ts_s'] \ dec_data['exp_fine'] = dec_data['rate'].iat[0] - dec_data['ts_s'] \
/ dec_time * (dec_data['rate'].iat[0] - endrate) / dec_time * (dec_data['rate'].iat[0] - endrate)
if fine_check:
assert ((dec_data['exp_fine'] - dec_data['rate']).abs().max() < assert ((dec_data['exp_fine'] - dec_data['rate']).abs().max() <
maxdev_fine) maxdev_fine)
# check effective deceleration rate # check effective deceleration rate
dec_vel = (dec_data['rate'].iat[-1] - dec_data['rate'].iat[0]) / dec_time dec_vel = (dec_data['rate'].iat[0] - dec_data['rate'].iat[-1]) / dec_time
print(abs(dec_vel - ax_info['accel']) / ax_info['accel'] < 0.05) 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): def check_run(info, run):
# unpack the axis data # unpack the axis data
ax_info, data = run ax_info, data = run
ax_count = len(ax_info)
# split axis information # split axis information
ax_data = [] ax_data = []
for ax in range(2): for ax in range(len(ax_info)):
ax_info[ax]['name'] = ax ax_info[ax]['name'] = ax
tmp = [] tmp = []
for i in range(len(data)): 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'])) ax_data.append(pd.DataFrame(tmp, columns=['ts', 'int', 'pos']))
# check each axis independently # check each axis independently, but only perform fine-grained checks when a single
for ax in range(2): # axis is run due to stepperTimerQuantum introducing discretization noise
check_axis(info, ax_info[ax], ax_data[ax]) fine_check = ax_count == 1
for ax in range(ax_count):
check_axis(info, ax_info[ax], ax_data[ax], fine_check)
def main(): def main():