Last active
March 28, 2018 03:17
-
-
Save petonic/fec087dabadf4afe5fac913a19554969 to your computer and use it in GitHub Desktop.
Automatically does the math to set the PID params for the MonoPrice Mini Extruder (v2) to the correct calculated values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This script reads in the serial log file after running an auto-tune on | |
for the extruder PID. It then takes averages of each cycle and prints out | |
the average value for each of 'p', 'i', and 'd' for all three of | |
{Classic, Overshoot, Both} sets of values. These are printed out in a | |
g-code format that is ready to cut and paste into OctoPrint's terminal | |
window. Remember to do an "M500" to save the settings after you do so. | |
This is a necessary step because unlikely enough, the Monoprice Mini firmware | |
(even the v2 version) doesn't do correct averaging of the values from | |
the autotune. I guess it's because math is hard? | |
Author: Mike Petonic | |
Date: [2018-03-27 TUE 20:41] | |
""" | |
# Change the following value to point to your serial log file. | |
input_file = '/home/octoprint/config/logs/serial.log' | |
with open(input_file, 'r') as myfile: | |
input_string = myfile.read().splitlines() | |
### | |
# These are the finite state machine transitions and actions | |
### | |
""" | |
# s_start | |
# /Recv: PID Autotune start/ | |
# --Set toggle to 'Classic'. | |
# --Clear arrays holding data. | |
# --> s_at_start | |
# EOF --> s_end | |
# s_at_start | |
# /Recv: PID Autotune failed!/ --> s_start | |
# /Recv: PID autotune finished/ --> | |
# --Finish processing averages, spit out results | |
# --> s_start | |
# /Recv: Classic PID/ | |
# --Set Toggle to 'Classic' | |
# /Recv: Some overshoot/ | |
# --Set toggle to 'Overshoot' | |
# /Recv: K([pid]): ([0-9.]+)/ | |
# --Append $2 into arr[$1] | |
# EOF -->ERROR | |
""" | |
import re, sys | |
from enum import Enum | |
class State(Enum): | |
in_tune = 1 | |
out_tune = 2 | |
class Toggle(Enum): | |
classic = 1 | |
overshoot = 2 | |
both = 3 | |
class Regex(Enum): | |
at_start = 1 | |
at_failed = 2 | |
at_finished = 3 | |
pid_value = 4 | |
toggle_classic = 5 | |
toggle_overshoot = 6 | |
regexes = [ | |
[re.compile(r'Recv: PID Autotune start'), Regex.at_start], | |
[re.compile(r'Recv: PID Autotune failed!'), Regex.at_failed], | |
[re.compile(r'Recv: PID autotune finished'), Regex.at_finished], | |
[re.compile(r'Recv: K([pid]): ([0-9.]+)'), Regex.pid_value], | |
[re.compile(r'Recv: Classic PID'), Regex.toggle_classic], | |
[re.compile(r'Recv: Some overshoot'), Regex.toggle_overshoot], | |
] | |
def get_averages(capture_toggle = Toggle.both): | |
state = State.out_tune | |
# Initialize my dicts of list, where lists is the value of the measurement | |
vals = {k: [] for k in ['p', 'i', 'd']} | |
line_number = 0 | |
if False: # Debugging the regexes | |
for line in input_string: | |
found_match = False | |
for (reg, matched) in regexes: | |
#DBG: print('{}:{}'.format(repr(reg), repr(matched))) | |
match = reg.search(line) | |
if match: | |
found_match = True | |
print('{}: {}'.format(repr(matched), line)) | |
break | |
if not found_match: | |
print('- {}'.format(line)) | |
### | |
# Main loop | |
### | |
for line in input_string: | |
line_number += 1 | |
found_match = False | |
for (reg, matched) in regexes: | |
match = reg.search(line) | |
if match: | |
#DBG: print('{}({}): {}'.format(line_number, repr(state), repr(matched))) | |
found_match = True | |
if state == State.out_tune: | |
# Currently, not in an auto-tune | |
if matched == Regex.at_start: | |
curr_toggle = Toggle.classic | |
vals = {k: [] for k in ['p', 'i', 'd']} | |
state = State.in_tune | |
else: | |
print('{} ({}): Unexpected Regex Match {}: {}'.format( | |
line_number, repr(state), matched, line)) | |
continue | |
elif state == State.in_tune: | |
# Inside an auto-tune | |
if matched == Regex.at_failed: | |
state = State.out_tune | |
elif matched == Regex.at_finished: | |
state = State.out_tune | |
elif matched == Regex.pid_value: | |
# Check to see if we're capturing this type of measurement | |
# which is either Classic or Overshoot | |
if (capture_toggle == Toggle.both) or (capture_toggle == curr_toggle): | |
#print('{} ({}): Found a value {}: {}'.format( | |
#line_number, repr(state), matched, line)) | |
quant_type = match.group(1) | |
quant_val = float(match.group(2)) | |
# (quant_type, quant_val) = (match.group(1), float(match.group(2)) | |
#DBG: print('\t1 = {}, 2 = {}'.format(repr(quant_type), repr(quant_val))) | |
vals[quant_type].append(quant_val) | |
elif matched == Regex.toggle_classic: | |
curr_toggle = Toggle.classic | |
elif matched == Regex.toggle_overshoot: | |
curr_toggle = Toggle.overshoot | |
else: | |
print('{} ({}): Unexpected Regex Match {}: {}'.format( | |
line_number, repr(state), matched, line)) | |
break | |
if not found_match: | |
pass | |
# print('- {}'.format(line)) | |
# Calculate averages and return them as a dict | |
avg = {k: [] for k in ['p', 'i', 'd']} | |
for k in vals: | |
avg[k] = sum(vals[k]) / float(len(vals[k])) | |
return avg | |
#print(repr(get_averages())) | |
for i in [Toggle.classic, Toggle.overshoot, Toggle.both]: | |
avg = get_averages(i) | |
print('{}: {}'.format(i, avg)) | |
print('M301 P{:.2f} I{:.2f} D{:.2f} ;{}'.format(avg['p'], avg['i'], avg['d'], i.name)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment