diff --git a/.idea/StagePerformaceDocu.iml b/.idea/StagePerformaceDocu.iml index 4a536cc..456b7f2 100644 --- a/.idea/StagePerformaceDocu.iml +++ b/.idea/StagePerformaceDocu.iml @@ -8,4 +8,7 @@ + + \ No newline at end of file diff --git a/Config/paths.json b/Config/paths.json deleted file mode 100644 index 7453ff7..0000000 --- a/Config/paths.json +++ /dev/null @@ -1,5 +0,0 @@ -{ -"meas_scripts_dir": 'C:\Users\berti_r\Python_Projects\metrology\metrology' -"meas_scripts_dir_local": r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Scripts" -"config_path": r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Config\config.json" -} \ No newline at end of file diff --git a/README.md b/README.md index e69de29..a5ada1a 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,72 @@ +

User Guide

+This Project has three main Components: +

+ +
    +
  1. Taking Measurements
  2. +
  3. Analyse the Data
  4. +
  5. Results and Documentation
  6. +
+ +

Taking a measurement

+For all measurments working with the standard TCMC Lib the notbook +Measurements offers a simple interface. My TwinCat project for that can be found under:_____ + +

Requierments:

+
    +
  1. TwinCat Project setup acording to ESS dokumentation
  2. +
  3. All libraryies installed (requirents.txt)
  4. +
  5. Static paths on top of every file set
  6. +
  7. Correct AMS-net Id for PLC set in metrology_functions (line52)
  8. +
  9. Using the microscope X02DA-PG-USB:cam1
  10. +
  11. The X-Axis needs to be linked to the reference Axis 2
  12. +
+If another microscope is used which can use the same epics interface, the ID can be changed in the AD.py file +

Location of static Paths:

+
    +
  1. Y_Axis_Meas (line 31,36,39,55)
  2. +
  3. myutility (line 58)
  4. +
  5. metrology_functions (line 13-16)
  6. +
+ + +

Meaurement types suprted by the Notebook:

+
    +
  1. Image Test
  2. +
  3. Static short term
  4. +
  5. Static long term
  6. +
  7. Repetability
  8. +
  9. Repability motors of
  10. +
+ +

Y-Axis Measurements

+The used file to execute the measurement is the Y_AxisMeas.py + +For the Y-Axis a new interface over ADS was needed. To minimize the required time for Testing +the Y-Axis was developed with as little cohesion to the other measurement scripts as possible. +On the beckhof side no TCMC/ESS lib was used. The interface consists of functions of the Beckhof Motion Liba linked to +variables and some custom functions for communicating states. The communication was done with the ADS protocol +writing/reading from GVLs. The project can be found under: _______ + +

Requirements:

+
    +
  1. Custom TwinCat Project on PLC from:______
  2. +
  3. All libraries installed (requirents.txt)
  4. +
  5. Static paths adjusted in Y_Axis_Meas.py
  6. +
  7. Correct AMS-net Id for PLC set in metrology_functions (line52)
  8. +
  9. Using the microscope X02DA-PG-USB:cam1
  10. +
  11. Vertical Axis are linked to reference Axis 1 and 4
  12. +
+ +

Analyzing new Data

+ +For analyzing new data the notebook Measurements Analysis offers some functionalities. +The temperature measurement needs to be added manually. In generally the newest measurement is selected by default. +If no "conf_YearMoDa.json" is generated automatically with the measurement it must be copied from another measurement and +the name needs to be adjusted. Especially important is the pixel size variable. + +

Results

+ +Overall result of the measurements can be found in the Results notebook. +Intermediate results are Documented in MessberichtXXXXXX notebooks where also a quick overview of the controller tuning +process can be found. \ No newline at end of file diff --git a/Scripts/Test_torque.py b/Scripts/Y_Axis_Meas.py similarity index 99% rename from Scripts/Test_torque.py rename to Scripts/Y_Axis_Meas.py index c1de363..b79b26b 100644 --- a/Scripts/Test_torque.py +++ b/Scripts/Y_Axis_Meas.py @@ -310,7 +310,7 @@ if __name__ == "__main__": #plc.write_by_name("GVL.StopCmd", False, pyads.PLCTYPE_BOOL) #moveAbsolut(plc, 0, 37.68, 3) #executeCmd(plc) - cyclic_meas(plc,[30,37.68],[5,25],100) + cyclic_meas(plc,[30,37.68],[5,25 ],100) #time.sleep(3) disableAxis(plc) gearout(plc) diff --git a/Scripts/metrology_functions.py b/Scripts/metrology_functions.py index 8c0f910..f511d5f 100644 --- a/Scripts/metrology_functions.py +++ b/Scripts/metrology_functions.py @@ -8,9 +8,13 @@ import sys import scipy.ndimage as spnd import threading as th from scipy.optimize import curve_fit -from skimage.io import imshow - +from utils import get_datestr, get_timestr +config_path = r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Config\config.json" #Path to the config file which has the parameters for the next measurement +library_path = r"C:\Users\berti_r\Python_Projects\templates\motion_libs" #Path to the motion function lib (ESS based) +measurement_mov_path = r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Config\measurement.json" +workdir = \ + os.path.expanduser(rf'C:\Users\berti_r\Python_Projects\StagePerformaceDocu\data\data{get_datestr()}_alignment_tests') #error chatchign and hard code catch stuff def check_path(path_str): @@ -21,30 +25,24 @@ def check_path(path_str): print(f"Path exists: {path_str}") except FileNotFoundError as e: print(f"Error: {e}") -config_path = r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Config\config.json" + check_path(config_path) - - - -library_path = r"C:\Users\berti_r\Python_Projects\templates\motion_libs" check_path(library_path) sys.path.append(library_path) -measurement_mov_path = r"C:\Users\berti_r\Python_Projects\StagePerformaceDocu\Config\measurement.json" check_path(measurement_mov_path) import motionFunctionsLib as mfl # ToDo: comment out when using torque test from PIL import Image, ImageDraw import numpy as np from image_analysis import image_center_of_mass -from utils import get_datestr, get_timestr + import ad import myutility as myu import cv2 import json import shutil -workdir = \ - os.path.expanduser(rf'C:\Users\berti_r\Python_Projects\StagePerformaceDocu\data\data{get_datestr()}_alignment_tests') + if not os.path.exists(workdir): os.makedirs(workdir) @@ -175,12 +173,6 @@ def __process_img(img , retimg=0): x = popt[1] y = popt[2] - #result , path = gradient_ascent(img, (x_gues, y_gues)) - #x,y = result - #y,x = np.unravel_index(np.argmax(z), z.shape) - """ret1, th1 = cv2.threshold(z, 40, 1, cv2.THRESH_BINARY) - z = z * th1 - x, y = image_center_of_mass(th1, plot=False, verbose=False)""" if retimg: return x,y,z return x, y @@ -294,117 +286,7 @@ def run_repeatability_series( del camera -def run_repeatability_series_K( - motor_pv_prefix=0, ntries=100, save_images=True, run_analysis=False): - # improv - exit(-1) - ntries = init_nr_of_cycles() - print(f"started with {ntries} cycles") - if os.getenv("EPICS_CA_ADDR_LIST") is not None: - pass - else: - os.environ["EPICS_CA_ADDR_LIST"] = "129.129.181.64" - camera = ad.AD() - pixel_size = 1.1 - - savedir = os.path.join(workdir, - f'{get_timestr()}_repeatibility_{motor_pv_prefix}') - safe_meas_settings(savedir) - savefile = os.path.join(savedir, - f'repeatibility_{motor_pv_prefix}.dat') - os.makedirs(savedir) - camera.start() - - # enable axis clean up later - - axis1.setAcceleration(5.0) - axis1.setDeceleration(5.0) - axis1.setVelocity(3) - axis1.disableAxis() - sleep(1) - axis1.enableAxis() - - - sleep(1) - - axis4.setAcceleration(5.0) - axis4.setDeceleration(5.0) - axis4.setVelocity(3) - - axis4.disableAxis() - sleep(1) - axis4.enableAxis() - sleep(1) - - # --------------------------------------load coordinates from file----------------------- - x_coordinates_json = myu.load_object(measurement_mov_path) - x_coordinates = x_coordinates_json.get('std_test_mov') - wait_x_json = myu.load_object(measurement_mov_path) - wait_x = wait_x_json.get('std_test_wait') - print("WaitPattern: ",wait_x) - print("movPattern",x_coordinates) - start1 = axis1.getActPos() - print("start1",start1) - print("target1",start1+x_coordinates[0]) - start4 = axis4.getActPos() - - for i in range(ntries): - # ---------------------------------------------move------------------------------------------ - # add multithreading for simultanious movement of y and x axis - for mov, wait in zip(x_coordinates, wait_x): - t1 = start1+mov - print("t1",t1) - while axis1.getMovingStatus() or axis4.getMovingStatus(): - print("waiting on axis") - axis4.moveAbsolute(start4+(mov)) - axis1.moveAbsolute(t1) - sleep(0.05) - - - sleep(wait) - start_pos_rbv = 4 # ???? - meas_pos_rbv = 5 # ???? - # ---------------------------------------------capture------------------------------------------ - x_array = [] - y_array = [] - for nr_img in range(10): - sleep(0.1) - im = camera.get_image() - sleep(0.1) - - if (1 == init_image_processing_yes_no()): - - com_x_tmp, com_y_tmp = __process_img(im) - else: - com_x_tmp, com_y_tmp = image_center_of_mass(im, plot=False, verbose=False) - x_array.append(com_x_tmp) - y_array.append(com_y_tmp) - com_x = np.average(x_array) - com_y = np.average(y_array) - - data_str = " {:6d} {:18f} {:18f} {:8.3f} {:8.3f} {:14.3f}\n".format( - i, start_pos_rbv, meas_pos_rbv, com_x, com_y, time.time()) - print(data_str, end='') - # -------------------------------------------Save---------------------------------------------------- - with open(savefile, 'a') as fh: - fh.write(data_str) - - if save_images: - imobj = Image.fromarray(im) - imfile = os.path.join(savedir, - f'im_{i:05d}.tif') - imobj.save(imfile) - - # --------------------------------------------analyse---------------------------------------------- - if run_analysis: - print("") - analyze_repeatability(savefile, pixel_size=pixel_size, units='um') - # -----------------------------------------cleanup----------------------------------------- - - axis2.disableAxis() - camera.stop() - del camera def run_repeatability_series_motor_off( @@ -452,8 +334,8 @@ def run_repeatability_series_motor_off( for mov, wait in zip(x_coordinates, wait_x): axis2.moveRelativeAndWait(mov) sleep(wait) - start_pos_rbv = 4 # ???? - meas_pos_rbv = 5 # ???? + start_pos_rbv = 4 # <-- only for perceiving the original formating of the measurement file + meas_pos_rbv = 5 # <-- only for perceiving the original formating of the measurement file axis2.disableAxis() # ---------------------------------------------capture------------------------------------------ x_array = [] @@ -577,11 +459,7 @@ def analyze_repeatability(input_file, pixel_size, units='um'): 'Y-neg', p2v_y_neg, rms_y_neg, sigma_y_neg, 3*sigma_y_neg)) print(result_str) - """ - result_file = os.path.splitext(input_file)[0] + "_results.dat" - with open(result_file, 'w') as fh: - fh.write(result_str) - print(f"Results saved in: {result_file:s}")""" + def plot_repeatability( ax, index, com, mean, p2v, rms, pos_mask, neg_mask): @@ -650,6 +528,7 @@ def camera_thread_function( img_queue): if os.getenv("EPICS_CA_ADDR_LIST") is not None: pass else: + print("EPICS_CA_ADDR_LIST environment variable not set, if it fails after that change IP in metrology_functions.py line 532") os.environ["EPICS_CA_ADDR_LIST"] = "129.129.181.64" camera = ad.AD() diff --git a/notebooks/Results.html b/notebooks/Results.html new file mode 100644 index 0000000..02ef7e9 --- /dev/null +++ b/notebooks/Results.html @@ -0,0 +1,7848 @@ + + + + + +Results + + + + + + + + + + + + +
+ + + + +
+ + diff --git a/requirements.txt b/requirements.txt index e69de29..ae736f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,15 @@ +numpy~=2.2.6 +plumbum~=1.9.0 +pyads~=3.5.0 +matplotlib~=3.10.3 +scipy~=1.16.0 +opencv-python~=4.12.0.88 +pillow~=11.3.0 +scikit-learn~=1.7.1 +scikit-image~=0.25.2 +pandas~=2.3.1 +seaborn~=0.13.2 +ipywidgets~=8.1.7 +ipython~=9.4.0 +webcolors~=24.11.1 +tifffile~=2025.6.11 \ No newline at end of file