Setup¶

Temperatur Sensors¶
| Chanal | +Placement | +Type | +
|---|---|---|
| 1 | +X-Axis Motor | +CrNi | +
| 2 | +Base | +CrNi | +
| 3 | +Ambient Air | +P304 | +
| 4 | +X-Axis Motor | +P304 | +
| 5 | +Base | +P304 | +
Currently, the temperature is logged on a private computer, since the PSI does not allow selecting drivers for unknown devices. This step is necessary because the measuring device only has an RS232 connection. A .dat file is saved on the laptop in the following format:
+| Temp1 | +Temp2 | +Temp3 | +Temp4 | +Temp5 | +Unix Time | +
|---|---|---|---|---|---|
| float | +float | +float | +float | +float | +float | +
Microscope¶
The position was always measured relative to the microscope, on which a region of interest was selected. The top-left corner of the image served as the origin (X=0,Y=0)
+import matplotlib.pyplot as plt
+
+# Create the figure
+fig, ax = plt.subplots(figsize=(4, 4))
+
+# Set limits
+ax.set_xlim(0, 1)
+ax.set_ylim(0, 1)
+
+
+ax.annotate(
+ "", xy=(0.1, 0.1), xytext=(0.1, 0.9),
+ arrowprops=dict(arrowstyle="->", linewidth=2)
+)
+
+
+ax.annotate(
+ "", xy=(0.9, 0.9), xytext=(0.1, 0.9),
+ arrowprops=dict(arrowstyle="->", linewidth=2)
+)
+
+ax.text(0.12, 0.5, "y", va="center", ha="left", fontsize=14)
+ax.text(0.5, 0.92, "x", va="bottom", ha="center", fontsize=14)
+
+
+ax.set_aspect('equal')
+ax.set_xticks([])
+ax.set_yticks([])
+for spine in ax.spines.values():
+ spine.set_visible(False)
+
+
+plt.tight_layout()
+
+plt.show()
+Decisions¶
| What | +Why | +Contact | +
|---|---|---|
| Implementation of image processing | +The original setup was sensitive to lens flares | +Roman | +
| Static testing for establishing a baseline | +A preliminary measurement showed poor results | +Roman | +
| Static long-term testing | +To assess the impact of air temperature | +Roman | +
| Windowing for Y-axis | +A long timespan is not applicable to real-world conditions; the camera was mounted on a long aluminum pole | +Roman | +
Image Processing¶
Originally, the position of the pinhole was determined by calculating the center of mass of the image. However, this approach led to errors caused by lens flare. The best-performing algorithm involved upscaling the image and fitting two Gaussian curves. Other approaches that were tested included a gradient optimizer and thresholding.
+| Befor | +After | +
|---|---|
![]() |
+![]() |
+
Measurements¶
Initially, a set of two measurements was planned: a repeatability test, where a measuring cycle was replicated as accurately as possible, and an absolute accuracy test. The repeatability test was conducted as planned, but the absolute accuracy measurement was canceled due to time constraints.
+Static measurement¶
No motors were enabled, and all brakes were engaged. Images were taken as quickly as the camera and scripts allowed. This measurement was used to assess the measurement accuracy of the camera with as little outside influence as possible.
+The baseline is visualized below. The measurement was taken on 18.07.25 with image processing enabled. Each bin corresponds to $0.02 \mu m$. The total duration was about 2 minutes, during which the temperature rose slightly, leading to a larger spread along the Y-axis.
+| Standard deviation X | +Standard deviation Y | +
|---|---|
| $0.04\,\mu m$ | +$0.06\,\mu m$ | +
import sys
+sys.path.append(rf"..\Scripts")
+import numpy as np
+import myutility as myu
+import matplotlib.pyplot as plt
+import pathlib as path
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+axis_data_file_path_1 =rf"..\data\data20250718_alignment_tests\20250718_113013_static_0\static_0.dat"
+#mf.analyze_repeatability(axis_data_file_path_1,1.1)
+pixle_size_mu = 0.275
+x_vals1, y_vals1, times1 = myu.load_xy_data(axis_data_file_path_1)
+
+# Fixing random state for reproducibility
+
+
+x = x_vals1 * pixle_size_mu
+y = y_vals1 * pixle_size_mu
+
+fig, ax = plt.subplots(figsize=(5.5, 5.5))
+
+ # the scatter plot:
+ax.scatter(x, y)
+ax.set_xlabel(rf"$\mu m$")
+
+ # Set aspect of the main Axes.
+ax.set_aspect(1.)
+
+# create new Axes on the right and on the top of the current Axes
+divider = make_axes_locatable(ax)
+# below height and pad are in inches
+ax_histx = divider.append_axes("top", 1.2, pad=0.1, sharex=ax)
+ax_histy = divider.append_axes("right", 1.2, pad=0.1, sharey=ax)
+
+ # make some labels invisible
+ax_histx.xaxis.set_tick_params(labelbottom=False)
+ax_histy.yaxis.set_tick_params(labelleft=False)
+
+ # TODO: correct selection fo square with equal bins but not dependent on absolut values
+binwidth = 0.02
+xmax = (np.max(np.abs(x)))
+ymax = (np.max(np.abs(y)))
+xmin = (np.min(np.abs(x)))
+ymin = (np.min(np.abs(y)))
+xlimP = (int(xmax / binwidth) + 0.01) * binwidth
+ylimP = (int(ymax / binwidth) + 0.01) * binwidth
+xlimM = (int(xmin / binwidth) - 0.01) * binwidth
+ylimM = (int(ymin / binwidth) - 0.01) * binwidth
+"""print("xymax:", xymax)
+print("xymin:", xymin)
+print("limP:", limP)
+print("limM:", limM)"""
+xbins = np.arange(xlimM, xlimP + binwidth, binwidth)
+ybins = np.arange(ylimM, ylimP + binwidth, binwidth)
+"""print("bins:", bins)"""
+ax_histx.hist(x, bins=xbins)
+ax_histy.hist(y, bins=ybins, orientation='horizontal')
+
+ # the xaxis of ax_histx and yaxis of ax_histy are shared with ax,
+ # thus there is no need to manually adjust the xlim and ylim of these
+ # axis.
+
+ax_histx.set_yticks([0, 50, 100])
+ax_histy.set_xticks([0, 50, 100])
+[<matplotlib.axis.XTick at 0x24bc45a4910>, + <matplotlib.axis.XTick at 0x24bc45ea490>, + <matplotlib.axis.XTick at 0x24bc45eac10>]+
Static Long-Term¶
After initial testing, it was obvious that the setup was susceptible to outside influences. To analyze the behavior of the setup without any movement over a longer timespan, a measurement was performed.
+The results show that the measurement drifts significantly over time, with the Y-axis exhibiting more movement than the X-axis.
+
X-Axis¶
The X-axis has a standard deviation of $0.19\mu\text{m}$, assuming all temperatures are stable within 0.15°C.
+
Y-Axis¶
For the Y-axis measurement, some interfacing with the PLC was required. The solution is available on my Git repository; axes 1 and 4 correspond to the Y-axis.
+The accuracy over multiple hours and under temperature fluctuations is poor. Therefore, a 15-minute window was used. Focusing on short-term repeatability, the accuracy was sufficient, with a standard deviation of $0.2\mu\text{m}$.
+
Temperature¶
The temperature was measured using three PT100 sensors. Channel 0 was placed directly on the X-axis motor, channel 1 on the opposite side of the motor, and channel 2 in the air, roughly 50 cm above the table.
+The temperature drop and rise around 06:00 are caused by fresh air supplied by the ventilation system. Additionally, the AC control behaves differently during the night. This pattern was observed in all measurements except the one on 25.08.25. On that date, the air conditioning settings were changed by the facility management, causing significant disturbances in the measurements.
+This measurement was taken on the 23.07.25
+

