
Pd amp
CoraBeanz/ROS-Optics-Bench
A mostly 3D-printed optical bench that couples a 635 nm laser into an optical fiber by itself. Knock a mirror out of line and four stepper-driven adjusters steer the beam back to peak coupling, with no hands on the…

A mostly 3D-printed optical bench that couples a 635 nm laser into an optical fiber by itself. Knock a mirror out of line and four stepper-driven adjusters steer the beam back to peak coupling, with no hands on the…
Control board is an open-source printed circuit board by CoraBeanz, from the CoraBeanz/ROS-Optics-Bench repository on GitHub. It is a 2-layer board of 100 × 100 mm with 31 parts on 41 nets, routed with 378 tracks and 318 vias.
It is built around the MF-RHT200-0 and P6KE18CA. Its parts include 10 connectors, 5 capacitors, 4 ICs, 3 diodes and 2 resistors. It belongs with the ESP32 boards of this library.
How it works · The bench · Optical path · Auto-align · Electronics · Software · Roadmap · Parts list
Getting a laser into a single-mode fiber means landing a focused spot on a core a few microns across, at the right angle. By hand that takes two kinematic mirrors, four knobs and a lot of patience. Here a NEMA 8 stepper turns each knob, and two photodiodes tell the motors how close they are.
| 1 · Score the coupling | 2 · Find the light | 3 · Steer and walk |
|---|---|---|
| A beamsplitter sends part of the laser to a reference photodiode, and the fiber's output lands on a second one. Their ratio is the score, so laser flicker cancels out. | After a knock the spot has usually left the core and the output reads nothing. M2 steps the spot outward in a square spiral until the output photodiode sees light again. | M2 alone moves the spot across the core. M1 with a matched, opposite M2 move changes only the angle. Alternating the two climbs the long, thin peak that one-knob-at-a-time adjusting stalls on. |
Laser, polarizer and beamsplitter, then M1 and M2, each with two NEMA 8 motors turning its adjusters through brass couplers and hex rods. |
Light. A 635 nm Quarton laser module under 1 mW, trimmed by a 2 mm printed aperture and dimmed by two crossed polarizers. Steering. Two 1 in mirrors in Thorlabs KMS-style kinematic mounts. A NEMA 8 stepper turns each 100 TPI adjuster through a brass coupler and a 2 mm hex rod: four motorized axes, 79 nm of screw travel per microstep. Focus. An f = 8 mm asphere on a micrometer stage focuses the beam onto a fixed FC bulkhead. The lens moves; the fiber stays put. Sensing. BPW34 photodiodes on MCP6002 transimpedance amps, read by a 16-bit ADS1115. Control. An ESP32 Feather V2 drives four TMC2209s on one UART bus and takes plain-text commands over USB from a desktop test panel or from ROS 2 on a Jetson. Base. Printed breadboard tiles on a 25 mm hole grid, so parts come off and go back in the same place. |
The full CAD model is one STEP file exported from Onshape with its subassembly tree, cad/assemblies/ros_laser_bench_main_assembly.step. Every part to buy, print or check is in the parts list (source: docs/bom/bom.csv).
The beam folds in a Z. M1 and M2 sit 150 mm apart, and M2 is 310 mm from the lens, so tilting the two mirrors together can set both where the beam crosses the lens and the angle it arrives at, and through them where the focused spot lands on the fiber face and the angle it enters at. One microstep tilts a mirror by about 4.6 µrad. One turn of M2's X adjuster moves the spot about 230 µm across the fiber face, and one turn of its Y adjuster about 165 µm, because at a 45° fold a vertical tilt bends the beam by √2 times the tilt instead of twice it.
The 2 mm aperture does mode matching. The laser's 7 x 3 mm beam is much bigger than the single-mode fiber's mode seen at the lens (1.5 mm across), so only its centre can couple. In the bench twin 83% of the light through the aperture couples, against 25% of the whole beam without it. The fiber gets about a quarter of the laser's power either way, but with the aperture the light that could never couple is stopped before the beamsplitter, so the out/ref score at the peak is 0.67 instead of 0.20. In phase 1 the OV9281's bare sensor takes the lens's place, and with the two irises it lets the mirrors put the beam on the lens axis by spot position before the lens and fiber go in.
With a single-mode fiber the coupling depends on where the spot lands on the core (set by the beam's angle at the lens) and on the angle it comes in at (set by the beam's offset at the lens). The first is about 25 times more sensitive, and both mirrors change both, so in motor coordinates the peak is a ridge 23 times longer than it is wide, running diagonally across M1 and M2. Turning one knob at a time crawls along it. Auto-align, in the test panel and as a ROS 2 action, uses the two moves people use by hand instead:
The first run on a fiber also measures the walk directions on the real bench, since they depend on motor directions and lever arms. In 100 simulated knocks of 0.3 to 3 mrad, steer and walk brought all 100 back above 99.9% of the best coupling, with a median of 19 s and a worst case of 66 s. One motor at a time got 59 back to 90%. Everything here comes from the bench twin; Auto-align hasn't run on the hardware yet.
| Part | Role |
|---|---|
| Adafruit ESP32 Feather V2 | Real-time side: steppers, soft limits, photodiode sampling, laser |
| 4x Adafruit TMC2209 breakout on one UART bus | Stepper drivers: 3200 microsteps per adjuster turn, StealthChop, current set over UART |
| ADS1115 (16-bit ADC) + 2x BPW34 photodiode on an MCP6002 transimpedance amp (47 kΩ) | Reference and output power |
| 12 V supply | Motor power; the Feather runs from USB |
| Jetson (ROS 2) | Camera, optimizer, experiment control |
| Arducam OV9281 (global shutter, UVC) | Bare-sensor beam profiler for phase 1 |
![]() | ![]() |
| Control board, 100 x 100 mm, two layers. The Feather, the four TMC2209 breakouts and the ADS1115 plug in. | Photodiode amp, 34 x 25 mm. Build two: reference and output. |
Both boards are KiCad 10 projects generated from Python, all through-hole, with Gerbers ready to order in electronics/pcb. Every cable on the bench is in electronics/system_wiring.svg, and the full pin table is in electronics/wiring.md.
Where each piece lives, and how far it has been run:
| Module | What it does | Run so far |
|---|---|---|
firmware/optics_bench | Arduino IDE sketch for the ESP32: four axes with soft limits and positions kept in flash, auto-ranging photodiode sampling, the laser, and the serial protocol | On the bench: laser, all four motors and both photodiodes |
tools/test_gui.py | Dark-mode PySide6 test panel: jog, go-to, photodiode readout and plots, CSV logging and Auto-align; --sim runs it on the bench twin | On the bench; Auto-align on the simulator only |
tools/bench_twin | Physical model of the bench and the Aligner both front ends run; tools/twin.py runs trials and draws figures | 14 unit tests |
host/ros2_ws | ROS 2 Humble packages for the Jetson: the bench as topics, services and actions, Align as an action, and a beam-spot node for the OV9281 | Against the simulator on Humble |
firmware/optics_bench/optics_bench.ino.Code in src/ is compiled with the sketch automatically. Record any library added through the Library Manager here so the build can be reproduced.
| Library | Version | Used for |
|---|---|---|
| TMCStepper (teemuatlut) | 0.7.3 | TMC2209 register setup over UART |
| AccelStepper (Mike McCauley) | 1.64 | Ramped STEP/DIR motion |
| Adafruit ADS1X15 | 2.6.2 | ADS1115 photodiode ADC over I2C (pulls in Adafruit BusIO) |
The firmware was last built with the esp32 boards package 3.3.7.
Every pin is set in firmware/optics_bench/config.h. The wiring diagram and full pin table for the Feather ESP32 V2, the four Adafruit TMC2209 breakouts, the laser, the ADS1115 and the two photodiode amplifier boards are in electronics/wiring.md. The serial protocol is plain text and documented in firmware/optics_bench/src/comms/protocol.h, so the Serial Monitor or the Jetson can use the same commands.
tools/test_gui.py is a small desktop app for the bench tests: laser on/off, jog, nudge and go-to for the four mirror motors, and live photodiode readings. It is a dark-mode Qt window built on PySide6 (the LGPL Qt binding) and pyserial, both listed in tools/requirements.txt.
py -3 -m pip install -r tools/requirements.txt
py -3 tools/test_gui.py # pick the ESP32's COM port, Connect
py -3 tools/test_gui.py --sim # try it without hardwarepy -3 picks the regular Python install on Windows; a bare python can resolve to another bundled interpreter such as KiCad's.
AUTO_RELEASE in config.h turns this off.--sim (or the Simulator port) runs the bench twin behind the same serial commands: the photodiodes read what the modelled optics couple into the chosen fiber, with the best position hidden a little way from zero. Knock M1 and Knock M2 tilt a mirror by 0.5 to 2 mrad as if bumped, Unknock takes the knocks out, and Peak? shows where the best position is. Knock, then Align, to watch it recover. The same commands work from the console as SIM KNOCK M1 [mrad], SIM PEAK, SIM FIBER sm630 and SIM RESET.REPROBE ALL (or just move the axis, which retries). The dots should go green, and the REPROBE reply should end in slots=ok (the connection bar picks it up on the next INFO).invert in config.h if needed.ADC: ok. Cover each diode and shine a light on it to check its channel, then press Measure dark with the room lit as it will be during runs.tools/bench_twin is a small physical model of the bench, used to size and test the alignment routine before the hardware can. It models:
py -3 tools/twin.py key numbers (--fiber sm630, mm50 or smf28)
py -3 tools/twin.py ceiling best coupling vs aperture size and lens focal length
py -3 tools/twin.py trials -n 100 --compare knock a mirror and recover, 100 times, both methods
py -3 tools/twin.py landscape redraws docs/images/twin_landscape.png (needs matplotlib)
py -3 -m unittest discover tools/testsWhat it says so far, for the single-mode fiber:
The numbers rest on estimates to replace with bench measurements, all set in tools/bench_twin/bench.py and optics.py:
| Estimate | Value used | How to measure |
|---|---|---|
| Adjuster lever arm | 17.3 mm (from the 24.5 mm motor spacing) | Scan M2X across the peak; the width scales with it |
| Play per hex coupling | 1 degree (9 microsteps) | Hysteresis test: approach the same point from both sides |
| Laser beam | 7 x 3 mm across (taken as the 1/e^2 width) | Camera or knife edge |
| Fiber mode | 4.2 um across (630HP) | Datasheet |
| Lens clear aperture | 5 mm | The lens listing |
| Beamsplitter, laser power, polarizer setting | 50/50, 0.9 mW, 30% | Photodiode volts |
The banner at the top of this page replays one of these runs: M1 knocked 1.5 mrad on the single-mode fiber, recovered in 17 s. The figures are drawn by the scripts in docs/images/src; the banner and the Auto-align figure run the twin to get their numbers.
host/ros2_ws holds ROS 2 Humble packages for the Jetson: a driver node that puts the ESP32 (or the simulator) on ROS topics, services and actions, with Align as an action; a node that finds the laser spot on the OV9281; and launch files. They use tools/bench_link.py and tools/bench_twin directly, so they align the same way the test panel does. Setup and usage: host/ros2_ws/README.md.
ros2 launch optics_bench_bringup bench.launch.py # on the simulator
ros2 launch optics_bench_bringup bench.launch.py port:=/dev/optics_bench # on the benchThe laser, all four mirror motors and both photodiodes (on hand-wired amplifiers for now) work on the bench, and first fiber coupling by hand is under way. The PCBs are designed but not ordered yet. Auto-align and the ROS 2 packages have run only against the bench twin so far, and the ROS 2 packages haven't run on the Jetson, the ESP32 or the camera yet.
cad/)electronics/pcb)| Layer | Built with |
|---|---|
| Optics | 635 nm diode laser, 2 mm aperture, crossed polarizers, 1 in mirrors in kinematic mounts, f = 8 mm asphere, FC fiber |
| Mechanics | 3D-printed mounts and base designed in Onshape; NEMA 8 steppers, brass couplers and hex rods on 100 TPI adjusters |
| Electronics | ESP32 Feather V2, TMC2209 drivers, ADS1115 and BPW34 photodiodes on MCP6002 amps; KiCad 10 boards generated from Python |
| Firmware | C++ Arduino IDE sketch on the ESP32: TMCStepper, AccelStepper, plain-text serial protocol |
| Desktop | Python, PySide6 and pyserial: the dark-mode test panel |
| Simulation | Python and numpy: Gaussian-beam coupling, motors with play, photodiodes, and the Aligner |
| Robotics | ROS 2 Humble on a Jetson: driver node with Align and MoveTo actions, v4l2_camera and a beam-spot node |
cad/
├── assemblies/ the whole bench as one STEP from Onshape, and its README
├── parts/pcb/ STEP models and 3D renders of the two PCBs
└── print/, vendor/ placeholders for print-ready files and purchased-part models
docs/
├── bom/ parts list: bom.csv, and the README tools/make_bom_md.py generates from it
└── images/ README figures; images/src/ holds the scripts that draw the SVGs
electronics/
├── wiring.md, wiring.svg pin table and wiring diagram for the Feather, drivers, laser, ADC and amps
├── system_wiring.svg every cable on the bench
└── pcb/ KiCad projects (control board, photodiode amp), their generator and fab outputs
firmware/optics_bench/ Arduino IDE sketch for the ESP32
├── optics_bench.ino setup() and loop()
├── config.h pin map, bus addresses, motor limits
└── src/ motion (drivers, soft limits), sensing (ADS1115), laser, comms (protocol)
host/ros2_ws/ ROS 2 Humble workspace for the Jetson: driver, camera, interfaces, bringup
tools/
├── test_gui.py the bench test panel
├── bench_link.py serial and simulator links to the controller, shared by the panel and ROS
├── bench_twin/ the bench simulator and the Aligner
├── twin.py trials, coupling ceiling, landscape figure
├── tests/ unit tests for the twin and the Aligner
└── make_bom_md.py writes docs/bom/README.md from bom.csv| Qty | Value | Footprint | References |
|---|---|---|---|
| 4 | 100uF 25V | Capacitor_THT:CP_Radial_D6.3mm_P2.50mm | C1, C2, C3, C4 |
| 4 | M3 | MountingHole:MountingHole_3.2mm_M3 | H1, H2, H3, H4 |
| 1 | 12V IN | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J5 |
| 1 | 12V IN 5.5x2.1 | Connector_BarrelJack:BarrelJack_Horizontal | J6 |
| 1 | 12V ON (green) | LED_THT:LED_D3.0mm | D3 |
| 1 | 1N5822 | Diode_THT:D_DO-201AD_P15.24mm_Horizontal | D1 |
| 1 | 1k | Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal | R1 |
| 1 | 4.7k | Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal | R2 |
| 1 | 470uF 25V | Capacitor_THT:CP_Radial_D10.0mm_P5.00mm | C5 |
| 1 | ADS1115 (0x48) | optics_bench:Adafruit_ADS1115_Socket | A2 |
| 1 | ESP32 Feather V2 | optics_bench:Adafruit_Feather_ESP32_V2_Socket | A1 |
| 1 | Laser | Connector_JST:JST_XH_B3B-XH-A_1x03_P2.50mm_Vertical | J7 |
| 1 | MF-RHT200 | Fuse:Fuse_Bourns_MF-RHT200 | F1 |
| 1 | P6KE18CA | Diode_THT:D_DO-15_P10.16mm_Horizontal | D2 |
| 1 | PD output | Connector_JST:JST_XH_B3B-XH-A_1x03_P2.50mm_Vertical | J9 |
| 1 | PD reference | Connector_JST:JST_XH_B3B-XH-A_1x03_P2.50mm_Vertical | J8 |
| 1 | TMC2209 M1X (addr 0) | optics_bench:Adafruit_TMC2209_Socket | U1 |
| 1 | TMC2209 M1Y (addr 1) | optics_bench:Adafruit_TMC2209_Socket | U2 |
| 1 | TMC2209 M2X (addr 2) | optics_bench:Adafruit_TMC2209_Socket | U3 |
| 1 | TMC2209 M2Y (addr 3) | optics_bench:Adafruit_TMC2209_Socket | U4 |
| 1 | Test (Analog Discovery) | Connector_PinHeader_2.54mm:PinHeader_1x10_P2.54mm_Vertical | J10 |
| 1 | VM M1X | Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical | J1 |
| 1 | VM M1Y | Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical | J2 |
| 1 | VM M2X | Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical | J3 |
| 1 | VM M2Y | Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical | J4 |

CoraBeanz/ROS-Optics-Bench
A mostly 3D-printed optical bench that couples a 635 nm laser into an optical fiber by itself. Knock a mirror out of line and four stepper-driven adjusters steer the beam back to peak coupling, with no hands on the…



techana/psu-breakout
ATX/AT PSU breakout boards — passive 2-layer PCB with banana jacks for bench testing of vintage and modern PC power supplies

stefanofinetti/MF_OverHead_Panel
Complete project for the Airbus A319/320/321/330 OverHead Panel

joeycastillo/Sensor-Watch
A board replacement for the classic Casio F-91W wristwatch

hoglet67/RGBtoHDMI
Bare-metal Raspberry Pi project that provides pixel-perfect sampling of Retro Computer RGB/YUV video and conversion to HDMI

hackclub/OnBoard
💡 Join 1,000 teenagers and make your first PCB with a $100 grant!

openwch/ch32v003
CH32V003 is an ultra-cheap RISC-V MCU with 2KB SRAM, 16KB flash, and up to 18 GPIOs that sells for under $0.10
It measures 100 × 100 mm and has 2 copper layers, 1.6 mm thick.
31 parts, built around the MF-RHT200-0 and P6KE18CA. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the CoraBeanz/ROS-Optics-Bench repository on GitHub, at the commit this page was made from: the board is electronics/pcb/control_board/control_board.kicad_pcb and its schematic electronics/pcb/control_board/control_board.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository states no licence, so its author keeps all rights: ask CoraBeanz before reusing the design.