Skip to content

Instantly share code, notes, and snippets.

@petonic
Last active March 28, 2018 03:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petonic/fec087dabadf4afe5fac913a19554969 to your computer and use it in GitHub Desktop.
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
#!/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