Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
0f6fdd6
temp save
piyush5103 Jun 6, 2023
9ac7b43
Merge pull request #3 from changhwang/mongodb
piyush5103 Jun 7, 2023
a898217
devices showing up in table
piyush5103 Jun 9, 2023
415e519
progress june 13
piyush5103 Jun 13, 2023
b152a86
progress june 15 midday
piyush5103 Jun 15, 2023
64ceee2
added requirements and lts150
piyush5103 Jun 15, 2023
ff1030f
june 16 3 pages
piyush5103 Jun 16, 2023
d8537f8
june 20 prog
piyush5103 Jun 20, 2023
c5e16be
prog
piyush5103 Jun 26, 2023
3fd428b
renamed temp1 to app
piyush5103 Jun 26, 2023
bdc91f7
refactored and cleaned code
piyush5103 Jun 26, 2023
cec4bac
added python editor
piyush5103 Jun 27, 2023
7222d56
py editor and refactor
piyush5103 Jun 28, 2023
e883b59
prog
piyush5103 Jun 29, 2023
40754cb
june 30 demo
piyush5103 Jun 30, 2023
d0719de
gitignore update
piyush5103 Jun 30, 2023
ac82f53
updated depreciated app.run()
piyush5103 Jul 1, 2023
b4fa47d
updated ximea to work with pip library
piyush5103 Jul 10, 2023
4b16836
updated devices to include abstract methods
piyush5103 Jul 10, 2023
aa1407e
updated homepage with more columns
piyush5103 Jul 10, 2023
8f64e9a
refactoring app.py
piyush5103 Jul 10, 2023
a7e8916
added manual-control.py
piyush5103 Jul 10, 2023
bd1bb26
jul 12 prog (manual control)
piyush5103 Jul 12, 2023
26967f9
merge prep
piyush5103 Jul 13, 2023
a999ffd
heating stage updated for devices_ref_redundancy
piyush5103 Jul 13, 2023
75afc0a
partially updated multi stepper util definition
piyush5103 Jul 13, 2023
2535e35
added database browser
piyush5103 Jul 13, 2023
75e0943
updated database
piyush5103 Jul 13, 2023
6a448b5
added devices to util and simplified schema viewer
piyush5103 Jul 13, 2023
d1d556d
Delete temp.py
piyush5103 Jul 13, 2023
21fccec
Delete temp2.py
piyush5103 Jul 13, 2023
5971dbd
separated load recipe and added manual ctrl alert
piyush5103 Jul 13, 2023
e9b6c42
added logging for manual control
piyush5103 Jul 14, 2023
8d5d02b
fixed heating stage command name
piyush5103 Jul 14, 2023
ed26b80
added current keithley to branch and util
piyush5103 Jul 14, 2023
7526ae8
added abstract methods to current keithley
piyush5103 Jul 14, 2023
bb6cb2c
upgraded manual execute modal
piyush5103 Jul 14, 2023
fc62e22
many many changes
piyush5103 Jul 19, 2023
c984e29
added execution saving
piyush5103 Jul 19, 2023
19d2931
changed desc to notes and del interceptor
piyush5103 Jul 19, 2023
9ae7ceb
moved navbar button
piyush5103 Jul 19, 2023
1932de7
updated lts serial sequence
piyush5103 Jul 20, 2023
858466f
added utility commands and updated page metadata
piyush5103 Jul 24, 2023
bd4e17c
clean but all reqmts
piyush5103 Jul 24, 2023
f1e5226
simplified reqmts
piyush5103 Jul 24, 2023
2de0e07
added execution options on view recipe
piyush5103 Jul 26, 2023
3078c31
Delete to_load.yaml
piyush5103 Jul 26, 2023
4ebfb8b
Delete to_save.yaml
piyush5103 Jul 26, 2023
801d329
execution options loading and saving from db
piyush5103 Jul 27, 2023
fd56b35
formatting
piyush5103 Jul 27, 2023
656cc20
file upload after execution works
piyush5103 Jul 27, 2023
ff3d2cf
moved file
piyush5103 Jul 27, 2023
100378d
rearranged execution options
piyush5103 Jul 27, 2023
7f57fa9
added dstore to gitignore
piyush5103 Jul 28, 2023
1e5308b
added festo modules as of 4123ed0
piyush5103 Jul 28, 2023
51a8d88
added mts50_z8
piyush5103 Jul 31, 2023
2a774ba
added mts50_z8 commands and to util
piyush5103 Jul 31, 2023
8ae1227
working changes
piyush5103 Aug 4, 2023
be4e617
edits
piyush5103 Aug 14, 2023
1536da6
added new devices and commands modules
piyush5103 Aug 14, 2023
4fa4131
restructure
piyush5103 Aug 14, 2023
1135202
updated gitignore
piyush5103 Aug 14, 2023
9e2b1b6
added mfc to util
piyush5103 Aug 15, 2023
a2f50e6
added sht85 to util
piyush5103 Aug 16, 2023
4cf87a6
telemetry working, options page, home update
piyush5103 Aug 17, 2023
3b6df24
added auth rejection and optimized command
piyush5103 Aug 18, 2023
da46caa
updated recipe_tool
piyush5103 Aug 18, 2023
d16951d
Changed Mongo credentials to mine to access my cluster for testing
Nov 20, 2024
0b880d6
added the validation schema creation scripts, and made it add an empt…
Jan 14, 2025
85cb96e
added random name generation with timestamp for recipes
Jan 27, 2025
d9566c6
fixed recipes schema validation, reordered recipe list
Jan 28, 2025
1a7deef
Changed validation level to warning, now the only devices that don't …
Jan 28, 2025
3594d7a
Updated dash ui branch with Chang's changes so all devices work up un…
Feb 11, 2025
df15f0c
Added logs to database for manual control, simple schema
Feb 12, 2025
59d93fe
added initial image upload functionality
Feb 25, 2025
a4c4f2a
Added the pump device and documentation for adding more devices in th…
Feb 27, 2025
0a7d78a
added step 6 to documentation
Feb 27, 2025
3ed10cf
Fixed typo in the adding device documentation
Mar 5, 2025
d8d6554
first ximea device add commit to test if it works on chang's side
Mar 5, 2025
2e6bb73
added the ximea camera and all commands to util.py, still have to tes…
Mar 6, 2025
785837d
changed xiapi import statement and removed it from requirements.txt, …
Mar 6, 2025
1af4bb0
added visualizations, working sampling, normalization
Mar 24, 2025
600916d
Added contour plot for parameter space visualization and more solvent…
Mar 24, 2025
b5017f2
added the option to include custom values for all dropdowns
Mar 27, 2025
0ab2d73
added suggestions for campaign metadata inputs and image input
Mar 28, 2025
4810f48
Added continuous options for every parameter, including individual te…
Mar 28, 2025
4421e38
ordered temperature in the outputted parameter sets
Apr 3, 2025
78b4c19
fixed upload to database and added gpc data
Apr 10, 2025
7ff9f64
added image to campaign document, handled duplicate campaign, image p…
Apr 17, 2025
6d8ef43
Made recipe page
sahasramesh Apr 28, 2025
5b328ef
populated campaign dropdown in recipe builder from the database
sahasramesh Apr 28, 2025
17a1219
opens the sets when a campaign is selected
sahasramesh Apr 28, 2025
1cc7db3
working on selecting parameter sets for recipe creation
sahasramesh Apr 28, 2025
89a7322
added solution position as a manual entry field, got recipe template …
sahasramesh May 5, 2025
1f8bd38
added the manual entry of solution position and run all recipes button
sahasramesh May 5, 2025
ed812c7
created solution map functionality
sahasramesh May 12, 2025
2d68ca8
added help tooltips for my new pages
sahasramesh May 12, 2025
066902b
testing access
sahasramesh May 15, 2025
58d4382
Added documentation, fixed a bug in the sampler, made it so that reci…
sahasramesh May 15, 2025
d13dc86
changed gitignore to include recipe sample
sahasramesh May 16, 2025
2077f20
updated README with docs on how to add new recipes for recipe builder…
sahasramesh May 16, 2025
dd7f152
added batch number and polymer to parameter sets, now each batch in a…
sahasramesh May 17, 2025
35e05cd
add optimizer page
weiqiz3 Aug 6, 2025
e4f8455
Integrate optimizer and add plots
weiqiz3 Sep 19, 2025
8cd1f80
test for retrieving plots with gridfs
weiqiz3 Sep 21, 2025
0028b7d
test for retrieving plots with gridfs
weiqiz3 Sep 21, 2025
0c54601
Store plots for sampler
weiqiz3 Sep 27, 2025
aeed52d
done testing saving optimizer plots
weiqiz3 Oct 1, 2025
f1ac9ab
done debugging sampler
weiqiz3 Oct 1, 2025
47c1452
Update AAMP app
weiqiz3 Feb 15, 2026
d48531c
Switchc to DB for Dev
weiqiz3 Feb 15, 2026
ffdf5d1
edited requirements.txt
changhwang Feb 16, 2026
a5af65e
revisited ximea camera device/commands.
changhwang Feb 16, 2026
3c5ee5d
Update sobol sampling
weiqiz3 Feb 23, 2026
75b1a27
Add hardware integrations for ESP301, stages, Z812, and 94043A
changhwang Mar 23, 2026
3ee3683
Add APIS, P4PP, heating stage, and Ximea integrations
changhwang Mar 25, 2026
e116cae
Add APIS MongoDB imaging draft and device examples
changhwang Mar 30, 2026
c46e4aa
Align APIS imaging params with MongoDB set fields
changhwang Mar 30, 2026
2dd862e
Add Festo valve example and docs
changhwang Apr 24, 2026
ee9a4c1
Weight spectral decay by wavelength spacing
changhwang Apr 24, 2026
197b266
Update Kinova fallback and Kortex install notes
changhwang Apr 24, 2026
14456d5
Update APIS core defaults and commands
changhwang May 27, 2026
967e8cc
Improve StellarNet detector selection
changhwang May 27, 2026
e8c49a6
Add Mongo-backed printing recipe and PSD6 tests
changhwang Jun 29, 2026
71adcfe
Improve StellarNet spectrometer merging
changhwang Jun 29, 2026
0d67409
Add remaining hardware examples
changhwang Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,31 @@ dmypy.json
.pytype/

# Cython debug symbols
cython_debug/
cython_debug/

.vscode/

temp.py
temp2.py
to_save.yaml
to_load.yaml
out.json
dashapp.py
dashapp-tut.py
data.csv
e1.json
e1.yaml
e1mongo.yaml
e2.yaml
e3.yaml
e4.yaml
e4mongo.yaml
project_const.py
pw.py
.DS_Store
blank.yaml
pw.txt
ximea_linux_sp_beta.tgz
package
firmware/
to_implement/
98 changes: 96 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ Lab Automation is a modular framework for automating multiple devices or instrum
4. [Usage](#usage)
- [Automation](#automation)
- [Optimization](#autonomous-process-optimization)
5. [Further Details](#further-details)
5. [Dash UI](#dash-ui)
- [UI Setup Instructions](#ui-setup-instructions)
- [Adding Recipes for the Recipe Builder](#adding-recipes-for-the-recipe-builder)
- [Next Steps](#next-steps)
6. [Further Details](#further-details)
- [Creating Device Modules](#creating-device-modules)
- [Creating Command Modules](#creating-command-modules)
- [Composite Commands](#composite-commands)
- [Program Scheme](#program-scheme)
6. [License](#license)
7. [License](#license)

## Description
Lab Automation was developed for the purpose of automating laboratory experiments to enable high-throughput data collection and process optimization. It can be used to automate experimental procedures that involve instruments from different vendors, with different communication protocols, and also home-built equipment. The automated procedures can then be tied into sequential model-based optimization algorithms (e.g. Bayesian optimization) to enable self-driving, autonomous lab experiments.
Expand Down Expand Up @@ -111,6 +115,96 @@ Example 6 emulates optimization of a material processing experiment. It uses a f

You will need some more packages to run example 6 (listed above). Run example 6 as stated above from root.

## Dash UI
The entire functionality of the app can also be accessed through a user-friendly Dash app interface. This includes creating recipes, running them, granulated manual control, and new features involved in automated process optimization, such as parameter set sampling, the recipe builder, and the solution mapper.

### UI Setup Instructions

1. Clone this repository.
```
git clone https://github.com/moleculemaker/Lab_Automation
```
2. Enter the Lab Automation folder.
```
cd Lab_Automation
```
3. Check out the dash-ui branch.
```
git checkout dash-ui
```
4. Create a virtual environment.
```
python3 -m venv venv
```
5. Activate the virtual environment.

Mac:
```
source venv/bin/activate
```
Windows:
```
venv\Scripts\activate
```
6. Install the requirements.
```
pip install -r requirements.txt
```
7. Add a .env file to the aamp_app folder and aamp_app/db/validation containing your MongoDB credentials. The files should be in the following format:
```
MONGO_URI=mongodb+srv://<username>:<connection-string>
MONGO_DB_NAME=diaogroup
```
8. Enter the aamp_app folder.
```
cd aamp_app
```
9. When running the app for the first time with an empty database, the correct collections with validation schemas need to be created. Uncomment the following lines in app.py:
```
# solutions.init_collection()
# devices.init_collection()
# films.init_collection()
# recipes.init_collection()
```
They can be commented again once the collections are created in the database.

10. Run the following:
```
pip install --upgrade dotenv
plotly_get_chrome
```

11. Run the app
```
python -m app
```

### Adding Recipes for the Recipe Builder
Right now the recipe builder is formatted to work with one recipe template, and that is recipe_sample.py. A keyword search for this in the app.py file will reveal the place where it is being read in and saved as a constant string literal. To add a new recipe:
1. Include it as a Python file in the recipes directory.
2. Replace all literals for template variables where appropriate (concentration, printing gap, arm position, etc.). You can refer to recipe_sample.py for an example implementation of this. Adding additional template variables requires modification of the generate_recipes callback in app.py.
3. Change the filename being read into app.py from recipe_sample.py to your new recipe's filename.
4. Ensure that the generate_recipes callback runs without error with the new recipe.
5. By default, all recipes are ignored. If you want git to track the new recipe, add !\<your-recipe>.py to the .gitignore file in the recipes folder.

### Next Steps

There are a number of pending tasks required for the Dash UI and automated optimization workflow to be fully operational:
- Send experimental results to the Bayesian optimizer
- Receive parameters from the optimizer in the recipe builder, either through database triggers or directly in the app
- Validate custom inputs in the sampler
- Add different sampling methods to the sampler (currently uses simple random sampling)
- Allow a campaign to have more than one polymer
- Attach polymer information to each parameter set rather than to campaign metadata
- Validate the solution map JSON
- Add a way to update or include more recipe templates
- Get the Ximea Camera module working on Mac and Linux devices
- Test running recipes sequentially in the recipe builder
- Visualize parameter space exploration over the course of a campaign
- Visualize optimal parameter ranges over the course of a campaign
- Track campaign progress in the UI, along with logging and error tracking
- Merge the dash-ui branch to master once a full campaign can be run through the UI (and most of the above objectives are complete)

## Further Details
### Creating Device Modules
Please see the devices folder for examples of implementing device modules
Expand Down
18 changes: 18 additions & 0 deletions aamp_app/Image_Processing/Image_processing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Custom image processing pipeline for coverage analysis."""

from .config import UniformityConfig
from .coverage import analyze_film_coverage
from .pipeline import analyze_image
from .scoring import compute_uniformity_score, _match_reference_for
from .uvvis import analyze_uvvis_data, find_uvvis_file

__all__ = [
"UniformityConfig",
"analyze_film_coverage",
"analyze_image",
"compute_uniformity_score",
"_match_reference_for",
"analyze_uvvis_data",
"find_uvvis_file",
]

48 changes: 48 additions & 0 deletions aamp_app/Image_Processing/Image_processing/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional
from bson import ObjectId


@dataclass
class UniformityConfig:
"""Lightweight configuration object.

Maintains compatibility with the existing imageproc.uniformity.config interface
by keeping the same field names.
"""

campaign_id: Optional[ObjectId] = None
use_reference: bool = True
reference_name: str = "background_normal_0deg"

roi_x: int = 0
roi_y: int = 0
roi_width: int = 100
roi_height: int = 100

uvvis_abs_threshold: float = 0.1
uvvis_wavelength_min: float = 300.0
uvvis_wavelength_max: float = 800.0
uvvis_required: bool = False

coverage_threshold: float = 0.1
coverage_smoothing: float = 1.5
# Sigma multiplier (K value) used in HSV S channel threshold calculation. Adjust default value here if needed.
coverage_k: float = 3.0
coverage_min_std: float = 10.0

# Uniformity calculation sensitivity parameters (for Reference-based Absolute Scoring)
uniformity_std_sensitivity: float = 15.0 # K1: Sensitivity to Std difference
uniformity_ent_sensitivity: float = 2.0 # K2: Sensitivity to Entropy difference

compute_learning_features: bool = False

extra: dict = field(default_factory=dict)

@property
def roi_bbox(self) -> tuple[int, int, int, int]:
return (self.roi_x, self.roi_y, self.roi_width, self.roi_height)

149 changes: 149 additions & 0 deletions aamp_app/Image_Processing/Image_processing/coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""HSV-based film coverage analysis module.

Instead of the traditional reference subtraction approach,
this module calculates coverage by combining dynamic thresholds
based on reference image S-channel statistics with sample image HSV information.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Tuple, Dict

import cv2
import numpy as np
from skimage.measure import shannon_entropy


@dataclass
class ROIParams:
x: int
y: int
width: int
height: int


def _clamp_roi(image: np.ndarray, roi: ROIParams) -> ROIParams:
"""Clamp ROI to ensure it stays within image boundaries."""
h, w = image.shape[:2]
x = max(0, min(roi.x, w - 1))
y = max(0, min(roi.y, h - 1))
width = max(1, min(roi.width, w - x))
height = max(1, min(roi.height, h - y))
return ROIParams(x, y, width, height)


def _extract_roi(image: np.ndarray, roi: ROIParams) -> np.ndarray:
"""Extract the image region corresponding to the clamped ROI."""
roi = _clamp_roi(image, roi)
return image[roi.y:roi.y + roi.height, roi.x:roi.x + roi.width]


def _ensure_same_shape(sample_roi: np.ndarray, reference_roi: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Resize reference ROI if sample and reference ROI sizes differ."""
if sample_roi.shape[:2] == reference_roi.shape[:2]:
return sample_roi, reference_roi
resized_ref = cv2.resize(reference_roi, (sample_roi.shape[1], sample_roi.shape[0]))
return sample_roi, resized_ref


def _apply_mask_smoothing(mask: np.ndarray, sigma: float) -> np.ndarray:
"""Apply Gaussian blur to reduce noise."""
if sigma <= 0:
return mask
blurred = cv2.GaussianBlur(mask, (0, 0), sigma)
return (blurred > 127).astype(np.uint8) * 255


def analyze_film_coverage(
image: np.ndarray,
reference: np.ndarray,
roi_x: int,
roi_y: int,
roi_width: int,
roi_height: int,
smoothing: float = 1.5,
k: float = 3.0,
min_std: float = 10.0,
) -> Tuple[float, np.ndarray, np.ndarray, np.ndarray, Dict[str, float]]:
"""HSV-based film coverage analysis.

Args:
image: Sample BGR image
reference: Reference BGR image
roi_x, roi_y, roi_width, roi_height: ROI coordinates
smoothing: Gaussian sigma for mask post-processing
k: Standard deviation multiplier for dynamic threshold
min_std: Minimum value to use when standard deviation is 0

Returns:
(coverage_pct, sample_roi_bgr, reference_roi_bgr, final_mask, debug_info)
"""
roi = ROIParams(roi_x, roi_y, roi_width, roi_height)
sample_roi = _extract_roi(image, roi)
reference_roi = _extract_roi(reference, roi)
sample_roi, reference_roi = _ensure_same_shape(sample_roi, reference_roi)

# Convert to HSV
sample_hsv = cv2.cvtColor(sample_roi, cv2.COLOR_BGR2HSV)
reference_hsv = cv2.cvtColor(reference_roi, cv2.COLOR_BGR2HSV)

# Reference S channel statistics
ref_s = reference_hsv[:, :, 1].astype(np.float32)
ref_s_mean = float(np.mean(ref_s))
ref_s_std = float(np.std(ref_s))
effective_std = ref_s_std if ref_s_std > 0 else float(min_std)
saturation_threshold = float(np.clip(ref_s_mean + k * effective_std, 0, 255))

# Condition A: S channel dynamic threshold
sample_s = sample_hsv[:, :, 1].astype(np.float32)
condition_a = sample_s > saturation_threshold

# Condition B: V channel Otsu (dark regions)
sample_v = sample_hsv[:, :, 2].astype(np.uint8)
otsu_threshold, otsu_mask = cv2.threshold(
sample_v, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU
)
condition_b = otsu_mask > 0

# Final mask
final_mask = np.logical_or(condition_a, condition_b).astype(np.uint8) * 255
final_mask = _apply_mask_smoothing(final_mask, smoothing)

total_pixels = final_mask.size
film_pixels = int(np.sum(final_mask > 0))
coverage_percentage = float(film_pixels) / float(total_pixels) if total_pixels else 0.0

# Reference statistics (for Uniformity calculation)
ref_s_uint8 = reference_hsv[:, :, 1].astype(np.uint8)
ref_entropy = float(shannon_entropy(ref_s_uint8))

# Sample statistics (for Uniformity calculation)
sample_s_uint8 = sample_hsv[:, :, 1].astype(np.uint8)
sample_std = float(np.std(sample_s_uint8.astype(np.float32)))
sample_entropy = float(shannon_entropy(sample_s_uint8))

debug_info: Dict[str, float] = {
"coverage_percentage": coverage_percentage,
"roi_width": float(sample_roi.shape[1]),
"roi_height": float(sample_roi.shape[0]),
"ref_s_mean": ref_s_mean,
"ref_s_std": ref_s_std,
"effective_std": effective_std,
"saturation_threshold": saturation_threshold,
"k_value": k,
"min_std_floor": min_std,
"otsu_threshold": float(otsu_threshold),
"condition_a_pixels": int(np.sum(condition_a)),
"condition_b_pixels": int(np.sum(condition_b)),
"film_pixels": film_pixels,
"total_pixels": int(total_pixels),
# Reference statistics (for Uniformity calculation)
"ref_std": ref_s_std,
"ref_entropy": ref_entropy,
"sample_std": sample_std,
"sample_entropy": sample_entropy,
}

return coverage_percentage, sample_roi, reference_roi, final_mask, debug_info

Loading