From 9f60da8950b0eec2adde8502ac6edcf433902f67 Mon Sep 17 00:00:00 2001 From: Mike Abbott Date: Thu, 13 Apr 2023 20:31:13 -0600 Subject: [PATCH] Started work on generating report data --- .devcontainer/Dockerfile | 1 + .devcontainer/devcontainer.json | 16 ++++++--- constants.py | 2 +- generate_bulk_scans.py | 58 +++++++++++++++++++++++++++++++++ generate_report_data.py | 57 ++++++++++++++++++++++++++++++++ main.py | 2 +- pa.py | 4 +-- pa_result.py | 3 ++ record.py | 2 ++ results.py | 29 +++++++++++++++++ visualization.py | 43 ++++++++++++++++++++---- 11 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 generate_bulk_scans.py create mode 100644 generate_report_data.py create mode 100644 results.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2ea51aa..b8887bd 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,3 +3,4 @@ FROM debian:bullseye-slim RUN apt-get update && apt-get install -y python3 python3-pip git ffmpeg RUN pip3 install opencv-python-headless matplotlib aiohttp requests RUN pip3 install websocket-client +RUN pip3 install scipy diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d21ca4f..f8574a4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,13 +1,19 @@ { "name": "rubedo", "build": { - "dockerfile":"Dockerfile", + "dockerfile": "Dockerfile", "args": { "USERNAME": "vscode", "BUILDKIT_INLINE_CACHE": "0" - } + } }, - "runArgs": ["--device=/dev/video2"], - "extensions": ["ms-python.python", "076923.python-image-preview"] - + "runArgs": [ + "--device=/dev/video2" + ], + "customizations": { + "extensions": [ + "ms-python.python", + "076923.python-image-preview" + ] + } } diff --git a/constants.py b/constants.py index c20c6e4..7fa977c 100644 --- a/constants.py +++ b/constants.py @@ -3,6 +3,6 @@ OUTPUT_FRAMES = False OUTPUT_HEIGHT_MAPS = False CROP_X_OFFSET = 200 -CROP_Y_OFFSET = 20 +CROP_Y_OFFSET = 0 CROP_FRAME_SIZE_X = 200 CROP_FRAME_SIZE_Y = 60 diff --git a/generate_bulk_scans.py b/generate_bulk_scans.py new file mode 100644 index 0000000..73f1e44 --- /dev/null +++ b/generate_bulk_scans.py @@ -0,0 +1,58 @@ +from pprint import pprint +import klipper.gcode as g +from main import generate_pa_results_for_pattern, PRINT_START +from pa import * +from pa_result import PaResult +import pickle + +def main(): + patterns: list[PatternInfo] = [] + for x in range(20, 286, 31): + for y in range(20, 130, 45): + patterns.append( + PatternInfo( + 0, 0.06, + x, y, + 10, + 30, 4 + )) + + # g.send_gcode(PRINT_START) + # g.send_gcode("M109 S255") + # g.send_gcode("CLEAN_NOZZLE") + # for pattern in patterns: + # g.send_gcode(generate_pa_tune_gcode(pattern, False)) + # g.send_gcode("G90;") + # g.send_gcode(f"G1 X{FINISHED_X} Y{FINISHED_Y} F30000") + # g.wait_until_printer_at_location(FINISHED_X, FINISHED_Y) + # g.send_gcode("M104 S0; let the hotend cool") + + pa_scans: list[PaResult] = [] + + for pattern in patterns: + pa_scans.extend( + zip(pattern.pa_values, + generate_pa_results_for_pattern( + pattern + )) + ) + break + + with open("testing_adjustments.pkl", "wb") as f: + pickle.dump(pa_scans, f) + + # results = generate_pa_results_for_pattern(calibration_pattern) + + # sorted_results = list(sorted(zip(results, calibration_pattern.pa_values), key=lambda x: x[0].score)) + # sorted_results = list([(x.score, y) for x, y in sorted_results]) + + # best_pa_value = sorted_results[0][1] + # print() + # pprint(sorted_results) + # print() + # print(f"Recommended PA Value: {best_pa_value}, with a score of {sorted_results[0][0]}") + # print() + # g.send_gcode(f"SET_PRESSURE_ADVANCE ADVANCE={best_pa_value}") + +if __name__=="__main__": + main() diff --git a/generate_report_data.py b/generate_report_data.py new file mode 100644 index 0000000..12cd31e --- /dev/null +++ b/generate_report_data.py @@ -0,0 +1,57 @@ +import pickle +import matplotlib.pyplot as plt +from pprint import pprint +import numpy as np +from pa_result import PaResult + +with open("testing_adjustments.pkl", "rb") as f: + data: list[PaResult] = pickle.load(f) + +pa_values = list([x[0] for x in data[:10]]) + +data_clean = list([(x, y.score) for x, y in data]) +pprint(list(sorted(data_clean, key=lambda x: x[1]))) +x, y = list(zip(*data_clean)) +p = np.polyfit(x, y, 3) +plt.plot(pa_values, np.poly1d(p)(pa_values)) +plt.scatter(x, y) +plt.plot(pa_values, np.poly1d(p)(pa_values)) + + +from matplotlib.colors import LinearSegmentedColormap +from scipy.stats import gaussian_kde +from collections import Counter + + +# Calculate the point density +# xy = np.vstack([x,y]) +# z = gaussian_kde(xy)(xy) + +# fig, ax = plt.subplots() +# ax.set_xlabel("PA Value") +# ax.set_ylabel("Score") +# ax.scatter(x, y, c=z, s=100) +# ax.plot(pa_values, np.poly1d(p)(pa_values)) + + +winning_results = [] + +for i in range(0, len(data_clean), 10): + x = i + individual_scan = list(sorted(data_clean[x:x+10], key=lambda x: x[1])) + pprint(individual_scan[0]) + winning_results.append(individual_scan[0][0]) + # pprint(data_clean[x:x+10]) + +counter = Counter(winning_results) +print(counter) +fig, ax = plt.subplots() +ax.set_ylabel("Winning Frequency") +ax.set_xlabel("PA Value") +ax.bar(counter.keys(), counter.values(), width=0.06/10) + +from visualization import generate_color_map, generate_3d_height_map +generate_color_map(data[3][1]) +generate_3d_height_map(data[3][1]) + +plt.show() diff --git a/main.py b/main.py index 18a8726..b938dce 100755 --- a/main.py +++ b/main.py @@ -50,7 +50,7 @@ def main(): ) - # g.send_gcode(PRINT_START) + g.send_gcode(PRINT_START) g.send_gcode("CLEAN_NOZZLE") g.send_gcode(generate_pa_tune_gcode(calibration_pattern)) g.wait_until_printer_at_location(FINISHED_X, FINISHED_Y) diff --git a/pa.py b/pa.py index fb40b06..64c49e1 100755 --- a/pa.py +++ b/pa.py @@ -73,13 +73,13 @@ def generate_pa_tune_gcode(info: PatternInfo, finished_printing=True): G1 Z{Z_HOP_HEIGHT} F300 ; Move above layer height """ gcode += """ - G1 Z20; move up 20mm + G1 Z5; move up 1mm M117 """ if finished_printing: gcode += f""" G90; switch back to absolute coordinates - G1 X{FINISHED_X} Y{FINISHED_Y} F30000; + G1 X{FINISHED_X} Y{FINISHED_Y} Z20 F30000; """ # print(gcode) return gcode diff --git a/pa_result.py b/pa_result.py index 3952023..a44ddf7 100644 --- a/pa_result.py +++ b/pa_result.py @@ -5,3 +5,6 @@ class PaResult: self.video_file = video_file self.height_data = height_data self.score = score + + def __str__(self): + return f"{self.score}" diff --git a/record.py b/record.py index fbf9f29..6eab375 100644 --- a/record.py +++ b/record.py @@ -35,6 +35,7 @@ ffmpeg_cmd = [ def record_pattern(info: PatternInfo, buffer_distance: float, output_directory: str) -> list: send_gcode("STATUS_OFF") # Turn off LEDs send_gcode("LASER_ON") # Turn on line laser + send_gcode("SET_LED LED=chamber_lights WHITE=0.01") time.sleep(0.5) lines_start_y = info.lines_start_y() @@ -64,5 +65,6 @@ def record_pattern(info: PatternInfo, buffer_distance: float, output_directory: time.sleep(0.6) send_gcode("LASER_OFF") + send_gcode("SET_LED LED=chamber_lights WHITE=1") return video_files diff --git a/results.py b/results.py new file mode 100644 index 0000000..72a44db --- /dev/null +++ b/results.py @@ -0,0 +1,29 @@ +import numpy as np +control = \ +[186.56256372513926, + 236.4280909963605, + 194.8965990884127, + 186.02849956667927, + 201.05698347607975, + 201.70169943918023, + 195.83328724309604, + 236.14796974386718, + 224.61775628475698, + 443.5180396174067 +] +print("Average deviation of lines in control pattern") +print(np.average(control)) +calibrated = \ +[ + 35.745380947164946, + 42.096965823872175, + 45.43428879223724, + 41.415640249952666, + 52.08084270611824, + 50.53732451711894, + 44.22630732805901, + 42.33189729658413, + 52.967477038659496 +] +print("Average deviation of lines in calibrated pattern") +print(np.average(calibrated)) diff --git a/visualization.py b/visualization.py index ef06a56..5725cf5 100644 --- a/visualization.py +++ b/visualization.py @@ -1,4 +1,4 @@ -import cv2 +# import cv2 import numpy as np import matplotlib.pyplot as plt from pa_result import PaResult @@ -14,6 +14,36 @@ from constants import * # # +def generate_color_map(pa_result: PaResult): + fig, ax = plt.subplots() + + y = np.arange(len(pa_result.height_data)) + x = np.arange(len(pa_result.height_data[0])) + (x ,y) = np.meshgrid(x,y) + + # ax.plot_surface(x, y, z_data,cmap=cm.coolwarm,linewidth=0, antialiased=False) + ax.pcolormesh(x, y, pa_result.height_data, cmap='RdBu') + # ax.scatter(x, y, z) + return fig + +def generate_cross_section_video(): + pass + +def generate_cross_sections(): + pass + +def generate_3d_height_map(pa_result: PaResult): + fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) + + y = np.arange(len(pa_result.height_data)) + x = np.arange(len(pa_result.height_data[0])) + (x ,y) = np.meshgrid(x,y) + ax.plot_surface(x, y, pa_result.height_data, cmap="RdBu") + ax.set_zlim3d(60, 100) + + return fig + + def graph_frame(pixel_values: np.ndarray, output_file: str): # fig. return @@ -30,7 +60,6 @@ def graph_frame(pixel_values: np.ndarray, output_file: str): return def graph_height_map(z_data: np.ndarray, output_file: str): - # fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) fig, ax = plt.subplots() # points = [] @@ -59,8 +88,8 @@ def generate_frames_from_heightmap(pa_data: PaResult): cv2.imwrite(f"frame_data/{Path(video_file).stem}-{frame_index}.png", frame) -fig = plt.figure() -from matplotlib.animation import FFMpegWriter -writer = FFMpegWriter(fps=30) -plt.ylim([0, 200]) -l = None +# fig = plt.figure() +# from matplotlib.animation import FFMpegWriter +# writer = FFMpegWriter(fps=30) +# plt.ylim([0, 200]) +# l = None