mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
219 lines
8.4 KiB
Python
219 lines
8.4 KiB
Python
"""
|
|
Mobile Microgripper (MMG) - Claw-like robotic gripper for cell spheroid bioassembly
|
|
|
|
Based on: Purdue University research (APL Bioengineering, 2026)
|
|
Reference: https://phys.org/news/2026-04-mobile-microgrippers-cells.html
|
|
|
|
Design:
|
|
- Two-arm claw structure connected by hinge
|
|
- Magnetic field control (biocompatible)
|
|
- Force-sensing with real-time adjustment
|
|
- Handles fragile cell spheroids (3D cell clumps)
|
|
- Microscopic scale
|
|
|
|
Integration with Research Stack:
|
|
- Buckyball-MOF QCA composite materials
|
|
- FAMM frustration physics for assembly control
|
|
- Magnetic field steering (MoonRF-adapted phased arrays)
|
|
"""
|
|
|
|
import build123d as bd
|
|
import trimesh
|
|
import numpy as np
|
|
import math
|
|
import json
|
|
from typing import List, Tuple, Optional
|
|
|
|
# Microgripper Geometry Parameters (microscopic scale, scaled up for CAD visualization)
|
|
SCALE_FACTOR = 1000.0 # Scale from micrometers to millimeters for visualization
|
|
|
|
ARM_LENGTH = 200.0 / SCALE_FACTOR # 200 μm arm length
|
|
ARM_WIDTH = 30.0 / SCALE_FACTOR # 30 μm arm width
|
|
ARM_THICKNESS = 15.0 / SCALE_FACTOR # 15 μm arm thickness
|
|
HINGE_RADIUS = 10.0 / SCALE_FACTOR # 10 μm hinge radius
|
|
HINGE_WIDTH = 40.0 / SCALE_FACTOR # 40 μm hinge width
|
|
GRIPPER_GAP_OPEN = 100.0 / SCALE_FACTOR # 100 μm open gap
|
|
GRIPPER_GAP_CLOSED = 20.0 / SCALE_FACTOR # 20 μm closed gap
|
|
|
|
# Magnetic Control Parameters
|
|
MAGNETIC_COATING_THICKNESS = 5.0 / SCALE_FACTOR # 5 μm Fe3O4 coating
|
|
MAGNETIC_FIELD_STRENGTH = 1.2 # Tesla (from buckyball-MOF spec)
|
|
|
|
# Force-Sensing Parameters
|
|
SENSOR_THICKNESS = 10.0 / SCALE_FACTOR # 10 μm piezoresistive sensor
|
|
SENSOR_LOCATION = 0.8 # 80% along arm from hinge
|
|
|
|
class MicrogripperGeometry:
|
|
"""Geometry parameters for mobile microgripper."""
|
|
|
|
def __init__(self):
|
|
self.arm_length = ARM_LENGTH
|
|
self.arm_width = ARM_WIDTH
|
|
self.arm_thickness = ARM_THICKNESS
|
|
self.hinge_radius = HINGE_RADIUS
|
|
self.hinge_width = HINGE_WIDTH
|
|
self.gap_open = GRIPPER_GAP_OPEN
|
|
self.gap_closed = GRIPPER_GAP_CLOSED
|
|
self.magnetic_coating = MAGNETIC_COATING_THICKNESS
|
|
self.sensor_thickness = SENSOR_THICKNESS
|
|
self.sensor_location = SENSOR_LOCATION
|
|
|
|
def create_arm(geometry: MicrogripperGeometry, angle_offset: float = 0.0) -> list:
|
|
"""Create a single gripper arm with hinge and magnetic coating - returns list of shapes."""
|
|
|
|
shapes = []
|
|
|
|
# Arm body (rectangular prism)
|
|
arm_body = bd.Box(
|
|
length=geometry.arm_length,
|
|
width=geometry.arm_width,
|
|
height=geometry.arm_thickness
|
|
)
|
|
arm_body = arm_body.move(bd.Location((-geometry.arm_length/2, 0, 0)))
|
|
shapes.append(arm_body)
|
|
|
|
# Add hinge (cylinder)
|
|
hinge = bd.Cylinder(
|
|
radius=geometry.hinge_radius,
|
|
height=geometry.hinge_width,
|
|
align=bd.Align.CENTER
|
|
)
|
|
hinge = hinge.rotate(axis=bd.Axis((0, 0, 0), (0, 1, 0)), angle=90)
|
|
hinge = hinge.move(bd.Location((0, 0, 0)))
|
|
shapes.append(hinge)
|
|
|
|
# Add magnetic coating (thin layer on arm surface)
|
|
coating = bd.Box(
|
|
length=geometry.arm_length,
|
|
width=geometry.arm_width,
|
|
height=geometry.magnetic_coating
|
|
)
|
|
coating = coating.move(bd.Location((-geometry.arm_length/2, 0, geometry.arm_thickness/2)))
|
|
shapes.append(coating)
|
|
|
|
# Add force sensor (piezoresistive strip)
|
|
sensor_length = geometry.arm_length * 0.2
|
|
sensor_x = -geometry.arm_length * geometry.sensor_location
|
|
sensor = bd.Box(
|
|
length=sensor_length,
|
|
width=geometry.arm_width * 0.8,
|
|
height=geometry.sensor_thickness
|
|
)
|
|
sensor = sensor.move(bd.Location((sensor_x, 0, geometry.arm_thickness/2 + geometry.magnetic_coating)))
|
|
shapes.append(sensor)
|
|
|
|
# Rotate all shapes if angle_offset is specified
|
|
if angle_offset != 0:
|
|
shapes = [s.rotate(axis=bd.Axis((0, 0, 0), (0, 0, 1)), angle=angle_offset) for s in shapes]
|
|
|
|
return shapes
|
|
|
|
def create_microgripper(geometry: MicrogripperGeometry, grip_state: str = "open") -> bd.BuildPart:
|
|
"""Create complete mobile microgripper with two arms."""
|
|
|
|
# Calculate arm angles based on grip state
|
|
if grip_state == "open":
|
|
arm_angle = math.degrees(math.atan(geometry.gap_open / (2 * geometry.arm_length)))
|
|
elif grip_state == "closed":
|
|
arm_angle = math.degrees(math.atan(geometry.gap_closed / (2 * geometry.arm_length)))
|
|
else:
|
|
arm_angle = 0.0
|
|
|
|
with bd.BuildPart() as gripper:
|
|
# Left arm (rotated -arm_angle)
|
|
left_arm_shapes = create_arm(geometry, angle_offset=-arm_angle)
|
|
for shape in left_arm_shapes:
|
|
bd.add(shape)
|
|
|
|
# Right arm (rotated +arm_angle)
|
|
right_arm_shapes = create_arm(geometry, angle_offset=+arm_angle)
|
|
for shape in right_arm_shapes:
|
|
bd.add(shape)
|
|
|
|
# Add magnetic control coils (simplified representation)
|
|
# Four coils for phased array steering (MoonRF-adapted)
|
|
coil_radius = 5.0 / SCALE_FACTOR
|
|
coil_positions = [
|
|
(-geometry.arm_length * 0.3, geometry.gap_open/2, geometry.arm_thickness/2 + 5.0/SCALE_FACTOR),
|
|
(-geometry.arm_length * 0.3, -geometry.gap_open/2, geometry.arm_thickness/2 + 5.0/SCALE_FACTOR),
|
|
(geometry.arm_length * 0.3, geometry.gap_open/2, geometry.arm_thickness/2 + 5.0/SCALE_FACTOR),
|
|
(geometry.arm_length * 0.3, -geometry.gap_open/2, geometry.arm_thickness/2 + 5.0/SCALE_FACTOR),
|
|
]
|
|
|
|
for pos in coil_positions:
|
|
coil = bd.Cylinder(radius=coil_radius, height=geometry.hinge_width, align=bd.Align.CENTER)
|
|
coil = coil.rotate(axis=bd.Axis((0, 0, 0), (0, 1, 0)), angle=90)
|
|
coil = coil.move(bd.Location(pos))
|
|
bd.add(coil)
|
|
|
|
return gripper
|
|
|
|
def gen_part():
|
|
"""Generate the Mobile Microgripper CAD model for text-to-cad."""
|
|
|
|
geometry = MicrogripperGeometry()
|
|
|
|
# Create microgripper in open state
|
|
gripper = create_microgripper(geometry, grip_state="open")
|
|
|
|
return gripper
|
|
|
|
if __name__ == "__main__":
|
|
print("Generating Mobile Microgripper CAD Model...")
|
|
print(f"Scale Factor: {SCALE_FACTOR}x (μm → mm)")
|
|
print(f"Arm Length: {ARM_LENGTH/SCALE_FACTOR:.1f} μm")
|
|
print(f"Arm Width: {ARM_WIDTH/SCALE_FACTOR:.1f} μm")
|
|
print(f"Hinge Radius: {HINGE_RADIUS/SCALE_FACTOR:.1f} μm")
|
|
print(f"Open Gap: {GRIPPER_GAP_OPEN/SCALE_FACTOR:.1f} μm")
|
|
print(f"Closed Gap: {GRIPPER_GAP_CLOSED/SCALE_FACTOR:.1f} μm")
|
|
print(f"Magnetic Coating: {MAGNETIC_COATING_THICKNESS/SCALE_FACTOR:.1f} μm")
|
|
print(f"Sensor Thickness: {SENSOR_THICKNESS/SCALE_FACTOR:.1f} μm")
|
|
|
|
gripper = gen_part()
|
|
print(f"Created CAD model")
|
|
|
|
# Export geometry as JSON for physics simulation
|
|
output_json = "/home/allaun/Documents/Research Stack/5-Applications/text-to-cad/models/mobile_microgripper.json"
|
|
|
|
geometry_data = {
|
|
"type": "mobile_microgripper",
|
|
"scale_factor": SCALE_FACTOR,
|
|
"dimensions": {
|
|
"arm_length_um": ARM_LENGTH,
|
|
"arm_width_um": ARM_WIDTH,
|
|
"arm_thickness_um": ARM_THICKNESS,
|
|
"hinge_radius_um": HINGE_RADIUS,
|
|
"hinge_width_um": HINGE_WIDTH,
|
|
"gap_open_um": GRIPPER_GAP_OPEN,
|
|
"gap_closed_um": GRIPPER_GAP_CLOSED
|
|
},
|
|
"magnetic_control": {
|
|
"coating_thickness_um": MAGNETIC_COATING_THICKNESS,
|
|
"field_strength_tesla": MAGNETIC_FIELD_STRENGTH,
|
|
"coil_count": 4,
|
|
"control_method": "phased_array_magnetic_field"
|
|
},
|
|
"force_sensing": {
|
|
"sensor_thickness_um": SENSOR_THICKNESS,
|
|
"sensor_location_fraction": SENSOR_LOCATION,
|
|
"sensor_type": "piezoresistive"
|
|
},
|
|
"integration": {
|
|
"buckyball_mof_composite": True,
|
|
"famm_frustration_control": True,
|
|
"moonrf_phased_array": True
|
|
},
|
|
"references": [
|
|
"Purdue University APL Bioengineering (2026)",
|
|
"BUCKYBALL_MOF_QCA_SPEC.md",
|
|
"BUCKYBALL_FAMM_TORSIONAL_FLUID.md",
|
|
"ENERGY_EXTRACTION_COMPRESSION_BUCKYBALL.md"
|
|
]
|
|
}
|
|
|
|
with open(output_json, 'w') as f:
|
|
json.dump(geometry_data, f, indent=2)
|
|
print(f"Exported geometry JSON to: {output_json}")
|
|
|
|
print("\nMobile Microgripper CAD model generation complete!")
|
|
print("Note: STEP export requires skill tool. Use: ./.venv/bin/python skills/cad/scripts/gen_step_part models/mobile_microgripper.py")
|