From 9680132c1007740a6f54e101c99622296ccef73a Mon Sep 17 00:00:00 2001 From: Mike Abbott Date: Tue, 4 Apr 2023 16:49:03 -0600 Subject: [PATCH] Full end-to-end support. Cleaned up a lot of code. Some things are still kind of messy, but it's a vast improvement over what I used to have. Data is easy to pass around the system, and the pattern info has proved invaluable for cleanly connecting prints, scans, and analysis. --- .devcontainer/Dockerfile | 1 + .devcontainer/devcontainer.json | 3 +- analysis.py | 58 ++++- constants.py | 11 +- klipper/gcode.py | 45 +++- main.py | 161 +++++++------ pa.py | 96 ++++++-- pa_patterns.gcode | 398 ++++++++++++++++++++++++++++++++ pa_result.py | 7 + pa_scan.py | 7 + pattern_info.py | 2 +- processing.py | 9 +- record.py | 54 ++--- visualization.py | 23 +- 14 files changed, 723 insertions(+), 152 deletions(-) mode change 100644 => 100755 pa.py create mode 100644 pa_patterns.gcode create mode 100644 pa_result.py create mode 100644 pa_scan.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0b5d710..2ea51aa 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,3 +2,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 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fa1b3ec..d21ca4f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,6 +7,7 @@ "BUILDKIT_INLINE_CACHE": "0" } }, + "runArgs": ["--device=/dev/video2"], "extensions": ["ms-python.python", "076923.python-image-preview"] -} \ No newline at end of file +} diff --git a/analysis.py b/analysis.py index 1810b5c..1cc5606 100644 --- a/analysis.py +++ b/analysis.py @@ -1,9 +1,11 @@ import numpy as np from constants import * +from processing import * +from pa_result import PaResult def brightest_average(pixel_values: np.ndarray): brightest_pixels = np.argsort(pixel_values)[-3:] - line_brightest_x = FRAME_SIZE_X - np.average(brightest_pixels) + line_brightest_x = CROP_FRAME_SIZE_X - np.average(brightest_pixels) return line_brightest_x @@ -14,7 +16,7 @@ def weighted_average(pixel_values: np.ndarray): if adjusted_values.max() == 0: # FIXME: I need an appropriate solution for what to do if there are no non-zero values. return 70 - return FRAME_SIZE_X - np.average(x_values, weights=adjusted_values) + return CROP_FRAME_SIZE_X - np.average(x_values, weights=adjusted_values) def first_non_zero(pixel_values: np.ndarray): @@ -39,3 +41,55 @@ def compute_x_value(pixel_values: np.ndarray): # return algorithms["count_non_zero"](pixel_values) # return algorithms["first_non_zero"](pixel_values) return algorithms["weighted_avg"](pixel_values) + + +def generate_height_data_for_frame(frame: np.ndarray): + frame = crop_frame(frame) + frame = preprocess_frame(frame) + frame = apply_gaussian_blur(frame) + + frame_height_data = np.ndarray(frame.shape[0]) + + for index, line in enumerate(frame): + # if line.max() > 0: + laser_x_val = compute_x_value(line) + frame_height_data[index] = laser_x_val + + return frame_height_data + + +def generate_height_data_from_video(video_file: str): + video = cv2.VideoCapture(video_file) + frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) + height_data: np.ndarray = np.ndarray((frame_count, CROP_FRAME_SIZE_Y)) + + frame_index = 0 + while video.isOpened(): + ret, frame = video.read() + if not ret: + break + + height_data[frame_index] = generate_height_data_for_frame(frame) + frame_index += 1 + + return height_data + + +def compute_score_from_heightmap(height_map: np.ndarray): + sum_of_scores = 0 + + for line in height_map.transpose(): + sum_of_scores += np.std(line) + return sum_of_scores + + +def pa_score_from_video_file(video_file: str) -> PaResult: + video_height_data = generate_height_data_from_video(video_file) + + # if OUTPUT_HEIGHT_MAPS: + # graph_height_map(video_height_data, f"height_maps/{Path(video_file).stem}.png") + + score = compute_score_from_heightmap(video_height_data) + + return PaResult(video_file, video_height_data, score) + diff --git a/constants.py b/constants.py index f437c4d..c20c6e4 100644 --- a/constants.py +++ b/constants.py @@ -1,9 +1,8 @@ OUTPUT_GRAPH = False OUTPUT_FRAMES = False -OUTPUT_HEIGHT_MAPS = True +OUTPUT_HEIGHT_MAPS = False - -X_OFFSET = 200 -Y_OFFSET = 20 -FRAME_SIZE_X = 200 -FRAME_SIZE_Y = 60 +CROP_X_OFFSET = 200 +CROP_Y_OFFSET = 20 +CROP_FRAME_SIZE_X = 200 +CROP_FRAME_SIZE_Y = 60 diff --git a/klipper/gcode.py b/klipper/gcode.py index 5010172..bcec3b8 100644 --- a/klipper/gcode.py +++ b/klipper/gcode.py @@ -1,9 +1,11 @@ # import asyncio # import aiohttp import requests +import websocket # HOST = 'fluiddpi.local' -HOST = 'http://192.168.1.113' +HOST = '192.168.1.113' +WS_PORT = 7125 GCODE_ENDPOINT = '/printer/gcode/script' OBJECTS_ENDPOINT = '/printer/objects/query' @@ -29,21 +31,27 @@ def move_relative(x: float = None, y: float = None, z: float = None, f: float = """) -async def send_gcode(gcode: str): - async with aiohttp.ClientSession() as session: - json_data = { - "script": gcode - } - async with session.post(HOST + GCODE_ENDPOINT, json=json_data) as resp: - return await resp.json() +# async def send_gcode(gcode: str): +# async with aiohttp.ClientSession() as session: +# json_data = { +# "script": gcode +# } +# async with session.post(HOST + GCODE_ENDPOINT, json=json_data) as resp: +# return await resp.json() def send_gcode(gcode: str): json_data = { "script": gcode } - resp = requests.post(HOST + GCODE_ENDPOINT, json=json_data) - return resp.json() + resp = requests.post("http://" + HOST + GCODE_ENDPOINT, json=json_data, timeout=600) + return resp +# ws = websocket.WebSocket() +# ws.connect(f"ws://{HOST}:{WS_PORT}/klippysocket") + +# def send_gcode(): +# ws.send() +# pass def home(): @@ -51,7 +59,7 @@ def home(): def has_homed(): - resp = requests.get(HOST + OBJECTS_ENDPOINT, params="toolhead") + resp = requests.get("http://" + HOST + OBJECTS_ENDPOINT, params="toolhead") result = resp.json()["result"] return result["status"]["toolhead"]["homed_axes"] == "xyz" @@ -64,6 +72,21 @@ def do_initialization_routine(): print("Rehoming Z") send_gcode("G28 Z") +def query_printer_position(): + resp = requests.get("http://" + HOST + OBJECTS_ENDPOINT, params="motion_report") + return resp.json()["result"]["status"]["motion_report"]["live_position"] + +def wait_until_printer_at_location(x = None, y = None, z = None): + while True: + position = query_printer_position() + if x is not None and abs(x - position[0]) > 0.01: + continue + if y is not None and abs(y - position[1]) > 0.01: + continue + if z is not None and abs(z - position[2]) > 0.01: + continue + break + def main(): do_initialization_routine() print("Finished initializing") diff --git a/main.py b/main.py index 6fca349..706b0bb 100755 --- a/main.py +++ b/main.py @@ -1,97 +1,118 @@ #!/usr/bin/python3 -import cv2 -import numpy as np -from glob import glob -from pathlib import Path - from processing import * -from visualization import graph_height_map -from analysis import compute_x_value +from analysis import pa_score_from_video_file +from pattern_info import PatternInfo +from record import record_pattern +from pa_result import PaResult +from pa import * + +import klipper.gcode as g + +import tempfile +from pprint import pprint -def generate_height_data_for_frame(frame: np.ndarray): - frame = crop_frame(frame) - frame = preprocess_frame(frame) - frame = apply_gaussian_blur(frame) - - frame_height_data = np.ndarray(frame.shape[0]) - - for index, line in enumerate(frame): - # if line.max() > 0: - laser_x_val = compute_x_value(line) - frame_height_data[index] = laser_x_val - - return frame_height_data +# This will print a calibrated + control pattern and measure the % improvement after tuning +VALIDATE_RESULTS = True -def generate_height_data_from_video(video_file: str): - video = cv2.VideoCapture(video_file) - frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) - height_data: np.ndarray = np.ndarray((frame_count, FRAME_SIZE_Y)) +PRINT_START = """ +G28 +M104 S180; preheat nozzle while waiting for build plate to get to temp +M190 S{BUILD_PLATE_TEMPERATURE}; +QUAD_GANTRY_LEVEL +CLEAN_NOZZLE +G28 Z +M109 S{HOTEND_TEMPERATURE}; +""" - frame_index = 0 - while video.isOpened(): - ret, frame = video.read() - if not ret: - break - - height_data[frame_index] = generate_height_data_for_frame(frame) - - frame_index += 1 +def generate_pa_results_for_pattern(pattern_info: PatternInfo)-> list[PaResult]: + results = [] - return height_data + # Hardcoding a buffer distance of 3mm here for now. Adjust if needed. + with tempfile.TemporaryDirectory("pa_videos") as dir: + video_files = record_pattern(pattern_info, 3, dir) - -def compute_score_from_heightmap(height_map: np.ndarray): - sum_of_scores = 0 - - for line in height_map.transpose(): - sum_of_scores += np.std(line) - return sum_of_scores + for video_file in video_files: + results.append( + pa_score_from_video_file(video_file) + ) + return results def main(): - ranking = [] + calibration_pattern = PatternInfo( + 0, 0.06, + 30, 30, + 10, + 30, 4 + ) - # frame_score = compute_score_for_frame(laser_x_values) - # print(frame_index, frame_std) + + # 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) + g.send_gcode("M104 S0; let the hotend cool") - # i = 0 - for video_file in sorted(glob("sample_data2/*")): - # if i < 6: - # i += 1 - # continue - video_height_data = generate_height_data_from_video(video_file) + 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]) - if OUTPUT_HEIGHT_MAPS: - graph_height_map(video_height_data, f"height_maps/{Path(video_file).stem}.png") + 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}") - score = compute_score_from_heightmap(video_height_data) - # height_data = compute_height_map(video_file) - # graph_height_map(height_data) + if not VALIDATE_RESULTS: + return - # return + control = PatternInfo( + 0, 0, + 65, 30, + 10, + 30, 4 + ) - # fig.suptitle(video_file) + calibrated = PatternInfo( + best_pa_value, best_pa_value, + 100, 30, + 10, + 30, 4 + ) - # out = cv2.VideoWriter("out.avi", cv2.VideoWriter_fourcc('M','J','P','G'), 30, (400,400)) - frame_index = 0 + gcode = f""" + M109 S{HOTEND_TEMPERATURE}; + CLEAN_NOZZLE + """ + gcode += generate_pa_tune_gcode(control, False) + gcode += generate_pa_tune_gcode(calibrated) + g.send_gcode(gcode) + g.send_gcode("M104 S0; let the hotend cool") + g.wait_until_printer_at_location(FINISHED_X, FINISHED_Y) - # video_std = [] - # red_line = cv2.cvtColor(red_line, cv2.COLOR_GRAY2BGR) - # out.write(red_line) - # exit() - # out.release() - # print(np.std(video_std)) - print(video_file, score) + control_results = generate_pa_results_for_pattern(control) + calibrated_results = generate_pa_results_for_pattern(calibrated) - ranking.append((video_file, score)) - # return + control_scores = list([x.score for x in control_results]) + calibrated_scores = list([x.score for x in calibrated_results]) - print('\nSCORES\n') + control_average = np.average(control_scores) + calibrated_average = np.average(calibrated_scores) - [ print(x) for x in sorted(ranking, key=lambda x: x[1])] + print("Control") + pprint(control_scores) + print("Calibrated") + pprint(calibrated_scores) + print() + print(f"Average Control Score: {control_average}") + print(f"Average Calibrated Score: {calibrated_average}") + print() if __name__=="__main__": diff --git a/pa.py b/pa.py old mode 100644 new mode 100755 index b700c44..d1b6e4a --- a/pa.py +++ b/pa.py @@ -1,10 +1,13 @@ +#!/usr/bin/python3 from pattern_info import PatternInfo -import klipper.gcode as g BUILD_PLATE_TEMPERATURE = 110 HOTEND_TEMPERATURE = 255 -def generate_pa_tune_gcode(info: PatternInfo): +FINISHED_X = 30 +FINISHED_Y = 250 + +def generate_pa_tune_gcode(info: PatternInfo, finished_printing=True): Z_HOP_HEIGHT = 0.75 LAYER_HEIGHT = 0.25 RETRACTION_DISTANCE = 0.5 @@ -18,15 +21,16 @@ def generate_pa_tune_gcode(info: PatternInfo): G92 E0 ; M106 S0 ; set fan speed to 0 - G1 X{info.start_x + info.line_length} Y{info.start_y} F30000 ; move to start position + G1 X{info.start_x + info.line_length} Y{info.start_y - info.spacing} F30000 ; move to start position G1 Z{LAYER_HEIGHT} F300 ; move to layer height G91 ; switch to relative movements ; Print a bounding box to aid with removal and prime the extruder. - G1 Y{info.num_lines * info.spacing} E{info.num_lines * info.spacing * EXTRUSION_DISTANCE_PER_MM} F3000; + G1 E{RETRACTION_DISTANCE * 2} + G1 Y{(len(info.pa_values) + 1) * info.spacing} E{(len(info.pa_values) + 1) * info.spacing * EXTRUSION_DISTANCE_PER_MM} F3000; G1 X{-info.line_length} E{info.line_length * EXTRUSION_DISTANCE_PER_MM}; - G1 Y{-info.num_lines * info.spacing} E{info.num_lines * info.spacing * EXTRUSION_DISTANCE_PER_MM}; + G1 Y{-(len(info.pa_values) + 1) * info.spacing} E{(len(info.pa_values) + 1) * info.spacing * EXTRUSION_DISTANCE_PER_MM}; G1 X{info.line_length} E{info.line_length * EXTRUSION_DISTANCE_PER_MM}; - G1 Z{Z_HOP_HEIGHT} Y{-info.spacing} E{-RETRACTION_DISTANCE} F300; retract and prepare to hop to first line location. + G1 Z{Z_HOP_HEIGHT} E{-RETRACTION_DISTANCE} F300; retract and prepare to hop to first line location. """ for pa_value in info.pa_values: @@ -47,31 +51,85 @@ def generate_pa_tune_gcode(info: PatternInfo): G1 Z{Z_HOP_HEIGHT} F300 ; Move above layer height """ gcode += """ - M117 ; clear LCD message + G1 Z20; move up 20mm + M117 """ + if finished_printing: + gcode += f""" + G90; switch back to absolute coordinates + G1 X{FINISHED_X} Y{FINISHED_Y} F30000; + """ + # print(gcode) return gcode -if __name__=="__main__": + +def main(): # FIXME: this stuff times out when it takes too long for the printer to respond... not sure # how to properly send commands and block until they're finished. Can I poll for the printer # status? - g.do_initialization_routine() - g.send_gcode(f""" + gcode = f""" + G28 M104 S180; preheat nozzle while waiting for build plate to get to temp M190 S{BUILD_PLATE_TEMPERATURE}; + QUAD_GANTRY_LEVEL + CLEAN_NOZZLE + G28 Z M109 S{HOTEND_TEMPERATURE}; - """) - g.send_gcode("CLEAN_NOZZLE") - g.send_gcode( - generate_pa_tune_gcode( - 0, 0.1, - 30, 30, - 10, - 30, 4 - ) + """ + # PA Patterns + control = PatternInfo( + 0, 0, + 30, 30, + 10, + 30, 4 + ) + normal_pattern = PatternInfo( + 0, 0.06, + 65, 30, + 10, + 30, 4 + ) + calibrated = PatternInfo( + 0.04, 0.04, + 100, 30, + 10, + 30, 4 ) + gcode += generate_pa_tune_gcode(control) + gcode += generate_pa_tune_gcode(normal_pattern) + gcode += generate_pa_tune_gcode(calibrated) + + # Reset PA so I don't forget. + gcode += """ + SET_PRESSURE_ADVANCE ADVANCE=0.04 + """ + + with open("pa_patterns.gcode", "w") as f: + f.write(gcode) + # g.do_initialization_routine() + # g.send_gcode(f""" + # M104 S180; preheat nozzle while waiting for build plate to get to temp + # M190 S{BUILD_PLATE_TEMPERATURE}; + # M109 S{HOTEND_TEMPERATURE}; + # """) + # g.send_gcode("CLEAN_NOZZLE") + # g.send_gcode( + # generate_pa_tune_gcode( + # 0, 0.1, + # 30, 30, + # 10, + # 30, 4 + # ) + # ) + + + + +if __name__=="__main__": + main() + # {% set BED = params.BED|default(99)|float %} # {% set EXTRUDER = params.EXTRUDER|default(239)|float %} # PRINT_START BED_TEMP={BED} EXTRUDER_TEMP={EXTRUDER} diff --git a/pa_patterns.gcode b/pa_patterns.gcode new file mode 100644 index 0000000..a6d603a --- /dev/null +++ b/pa_patterns.gcode @@ -0,0 +1,398 @@ + + G28 + M104 S180; preheat nozzle while waiting for build plate to get to temp + M190 S110; + QUAD_GANTRY_LEVEL + CLEAN_NOZZLE + G28 Z + M109 S255; + + G21 ; Millimeter units + G90 ; Absolute XYZ + M83 ; Relative E + SET_VELOCITY_LIMIT ACCEL=3000 ACCEL_TO_DECEL=1500 + G92 E0 ; + M106 S0 ; set fan speed to 0 + + G1 X95 Y30 F30000 ; move to start position + G1 Z0.25 F300 ; move to layer height + G91 ; switch to relative movements + ; Print a bounding box to aid with removal and prime the extruder. + G1 Y40 E1.83596 F3000; + G1 X-30 E1.37697; + G1 Y-40 E1.83596; + G1 X30 E1.37697; + G1 Z0.75 Y-4 E-0.5 F300; retract and prepare to hop to first line location. + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + M117 + + G21 ; Millimeter units + G90 ; Absolute XYZ + M83 ; Relative E + SET_VELOCITY_LIMIT ACCEL=3000 ACCEL_TO_DECEL=1500 + G92 E0 ; + M106 S0 ; set fan speed to 0 + + G1 X60 Y30 F30000 ; move to start position + G1 Z0.25 F300 ; move to layer height + G91 ; switch to relative movements + ; Print a bounding box to aid with removal and prime the extruder. + G1 Y40 E1.83596 F3000; + G1 X-30 E1.37697; + G1 Y-40 E1.83596; + G1 X30 E1.37697; + G1 Z0.75 Y-4 E-0.5 F300; retract and prepare to hop to first line location. + + SET_PRESSURE_ADVANCE ADVANCE=0.0 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.0 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.006666666666666666 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.006666666666666666 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.013333333333333332 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.013333333333333332 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.019999999999999997 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.019999999999999997 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.026666666666666665 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.026666666666666665 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.03333333333333333 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.03333333333333333 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.039999999999999994 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.039999999999999994 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04666666666666666 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04666666666666666 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.05333333333333333 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.05333333333333333 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.06 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.06 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + M117 + + G21 ; Millimeter units + G90 ; Absolute XYZ + M83 ; Relative E + SET_VELOCITY_LIMIT ACCEL=3000 ACCEL_TO_DECEL=1500 + G92 E0 ; + M106 S0 ; set fan speed to 0 + + G1 X130 Y30 F30000 ; move to start position + G1 Z0.25 F300 ; move to layer height + G91 ; switch to relative movements + ; Print a bounding box to aid with removal and prime the extruder. + G1 Y40 E1.83596 F3000; + G1 X-30 E1.37697; + G1 Y-40 E1.83596; + G1 X30 E1.37697; + G1 Z0.75 Y-4 E-0.5 F300; retract and prepare to hop to first line location. + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + SET_PRESSURE_ADVANCE ADVANCE=0.04 ; set Pressure Advance + M117 Testing Pressure Advance at: 0.04 + G1 X-30 Y4 F30000 ; move to start position + G1 Z-0.75 F300 ; move to layer height + G1 E0.5 F1800 ; un-retract + G1 X7.5 E0.3442425 F300 ; print line part one + G1 X15.0 E0.688485 F9000 ; print line part two + G1 X7.5 E0.3442425 F300 ; print line part three + G1 E-0.5 F1800 ; retract + G1 Z0.75 F300 ; Move above layer height + + M117 + + SET_PRESSURE_ADVANCE ADVANCE=0.04 + \ No newline at end of file diff --git a/pa_result.py b/pa_result.py new file mode 100644 index 0000000..3952023 --- /dev/null +++ b/pa_result.py @@ -0,0 +1,7 @@ +import numpy as np + +class PaResult: + def __init__(self, video_file: str, height_data: np.ndarray, score: float): + self.video_file = video_file + self.height_data = height_data + self.score = score diff --git a/pa_scan.py b/pa_scan.py new file mode 100644 index 0000000..3e36742 --- /dev/null +++ b/pa_scan.py @@ -0,0 +1,7 @@ +from pattern_info import PatternInfo + +class PaScan: + def __init__(self, pattern_info: PatternInfo, video_files) -> None: + self.pattern_info = pattern_info + self.video_files = video_files + pass diff --git a/pattern_info.py b/pattern_info.py index c47d77c..56d3206 100644 --- a/pattern_info.py +++ b/pattern_info.py @@ -12,4 +12,4 @@ class PatternInfo: self.spacing = spacing def lines_start_y(self): - return [(x * self.spacing) + self.start_y for x in range(1, len(self.pa_values) + 1)] + return [(x * self.spacing) + self.start_y for x in range(len(self.pa_values))] diff --git a/processing.py b/processing.py index 0a4fe11..69ef38a 100644 --- a/processing.py +++ b/processing.py @@ -3,10 +3,10 @@ import cv2 import numpy as np def crop_frame(frame): - mid_y = 720//2 + Y_OFFSET - mid_x = 1280//2 + X_OFFSET - half_y = FRAME_SIZE_Y / 2 - half_x = FRAME_SIZE_X / 2 + mid_y = 720//2 + CROP_Y_OFFSET + mid_x = 1280//2 + CROP_X_OFFSET + half_y = CROP_FRAME_SIZE_Y / 2 + half_x = CROP_FRAME_SIZE_X / 2 frame = frame[int(mid_y-half_y):int(mid_y+half_y), int(mid_x-half_x):int(mid_x+half_x)] return frame @@ -28,4 +28,3 @@ def apply_gaussian_blur(frame): frame = cv2.GaussianBlur(frame, (11, 11), 0) frame = cv2.GaussianBlur(frame, (11, 11), 0) return frame - diff --git a/record.py b/record.py index 3c4ce33..fbf9f29 100644 --- a/record.py +++ b/record.py @@ -3,10 +3,11 @@ from pattern_info import PatternInfo import numpy as np import subprocess import time +from pathlib import Path # TODO: Populate all of these. -CAMERA_OFFSET_X = 0 -CAMERA_OFFSET_Y = 0 +CAMERA_OFFSET_X = 28 +CAMERA_OFFSET_Y = 50.2 CAMERA_FOV_X = 0 CAMERA_FOV_Y = 0 @@ -15,6 +16,7 @@ LASER_FOCUS_HEIGHT = 17.86 ffmpeg_cmd = [ "ffmpeg", + "-y", "-f", "v4l2", "-framerate", @@ -24,45 +26,43 @@ ffmpeg_cmd = [ "-input_format", "mjpeg", "-i", - "/dev/video0", + "/dev/video2", ] # TODO: make this return a list of files that are saved # TODO: make this save to a unique folder each run (maybe this should be passed in?) -def record_pattern(info: PatternInfo, buffer_distance: float) -> list: +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 + time.sleep(0.5) - y_values = np.ndarray(info.lines_start_y) + CAMERA_OFFSET_Y + lines_start_y = info.lines_start_y() + y_values = np.asarray(lines_start_y) + CAMERA_OFFSET_Y scan_start_x = info.start_x + buffer_distance + CAMERA_OFFSET_X - scan_length = info.line_length - buffer_distance + scan_length = info.line_length - buffer_distance * 2 + scan_end_x = scan_start_x + scan_length + + video_files = [] + Path(output_directory).mkdir(parents=True, exist_ok=True) - time.sleep(2) for index, y_value in enumerate(y_values): move_absolute(scan_start_x, y_value, LASER_FOCUS_HEIGHT, 30000) + wait_until_printer_at_location(scan_start_x, y_value) # start recording - with subprocess.Popen(ffmpeg_cmd + [f"sample_data2/line{index}.mp4"]) as ffmpeg: - # move_relative(64, f=600) - time.sleep(0.5) - print(move_relative(scan_length, f=600)) - - # Total hack here... I need to not hardcode this. - # We should be able to compute this programatically relatively easily. - time.sleep(5) + video_file = f"{output_directory}/line{index}.mp4" + video_files.append(video_file) + + with subprocess.Popen(ffmpeg_cmd + [video_file]) as ffmpeg: + time.sleep(0.5) + print(move_absolute(scan_end_x, f=600)) + wait_until_printer_at_location(scan_end_x) + + # stop recording ffmpeg.terminate() - time.sleep(0.5) - # stop recording - # print(move_relative(-34, 4, f=18000)) - time.sleep(1) + time.sleep(0.6) + send_gcode("LASER_OFF") + return video_files -# start at x=45, y=250, end at x=73 (leaving 8mm off the start and end for now) -def main(): - - # record_pattern() - pass - -if __name__=="__main__": - main() diff --git a/visualization.py b/visualization.py index 1dfa669..ef06a56 100644 --- a/visualization.py +++ b/visualization.py @@ -1,11 +1,19 @@ import cv2 import numpy as np import matplotlib.pyplot as plt -from matplotlib import cm +from pa_result import PaResult from pathlib import Path from constants import * + +## FIXME: +# +# This WHOLE file is currently a broken mess. Needs to be cleaned up and fixed. +# +# +# + def graph_frame(pixel_values: np.ndarray, output_file: str): # fig. return @@ -43,17 +51,12 @@ def graph_height_map(z_data: np.ndarray, output_file: str): fig.savefig(output_file) -def generate_graph_from_heightmap(): - if OUTPUT_GRAPH: - graph_frame(laser_x_values, f"graphs/{Path(video_file).stem}-{frame_index}.png") - pass - -def generate_frames_from_heightmap(): - if OUTPUT_FRAMES: - cv2.imwrite(f"frame_data/{Path(video_file).stem}-{frame_index}.png", frame) - pass +def generate_graphs_for_pa_results(pa_data: PaResult): + graph_frame(laser_x_values, f"graphs/{Path(video_file).stem}-{frame_index}.png") +def generate_frames_from_heightmap(pa_data: PaResult): + cv2.imwrite(f"frame_data/{Path(video_file).stem}-{frame_index}.png", frame) fig = plt.figure()