The Nanosurf Interface#
\(_{Yongtao}\) \(_{Liu,}\) \(_{liuy3@ornl.gov,}\) \(_{youngtaoliu@gmail.com}\)
\(_{July}\) \(_{2026}\)
Overview#
PyNanosurf is the AEcroscopy interface for the Nanosurf DriveAFM, connecting to a running Nanosurf Studio session through the official Nanosurf Python API rather than the Igor Pro / COM route used by PyCypher (Chapter 2). It offers the same kind of control — scan parameters, imaging lifecycle, tip positioning, signal readout — but there are a few conventions worth knowing if you’re coming from the Cypher tutorial:
Units. All spatial parameters use SI units (meters, Hz, N/m, …).
PyCypherinstead works in LVDT voltage units.Pixel origin. Nanosurf uses a top-left image origin — pixel
(0, 0)is the top-left corner of the scan frame.PyCypheruses bottom-left.Connection.
PyNanosurfconnects to an already-running Nanosurf Studio session; it does not launch the software.Dependency.
PyNanosurfimports Nanosurf’s ownnanosurfPython package, which is not installed bypip install aecroscopywave— it ships with your Nanosurf Studio installation. Make sure it’s importable in your environment before using this interface.
Import#
from aecroscopywave.interfaces import Nanosurf
Initialize PyNanosurf#
Creates an instance of PyNanosurf, which connects to the running Nanosurf Studio session. Nanosurf Studio must already be open before calling this.
pdn = Nanosurf.PyNanosurf()
Set and Get Scan Parameters#
set_scan_parameters() is the Nanosurf analogue of PyCypher.PV() — it takes a dictionary of parameter names and values, but in physical units rather than Igor Pro command strings. Supported keys: scan_range_x, scan_range_y (fast/slow axis range, meters), line_rate (Hz), points_per_line, lines_per_frame, offset_x, offset_y (frame center, meters), and rotation (degrees).
Unknown keys are not applied — they come back in the ignored field of the returned status dict, so typos are easy to catch.
status = pdn.set_scan_parameters({
"scan_range_x": 5e-6,
"scan_range_y": 5e-6,
"points_per_line": 256,
"lines_per_frame": 256,
"line_rate": 1.0,
})
status
pdn.get_scan_parameters()
Start / Stop Imaging#
start_imaging() and stop_imaging() are the Nanosurf analogues of PyCypher.set_Master_Panel(do_scan="Frame Up" / "Stop"). set_imaging() is a convenience wrapper that mirrors set_Master_Panel() directly: set parameters, then optionally issue a scan command in one call.
pdn.start_imaging()
# Poll until the frame finishes
import time
while pdn.is_scanning():
time.sleep(0.5)
# Equivalent to the two cells above, in one call
pdn.set_imaging(
parameters={"scan_range_x": 5e-6, "points_per_line": 256},
do_scan="start",
)
Approach#
start_approach() brings the tip into contact with the sample surface — the DriveAFM equivalent of PyCypher.engage().
pdn.start_approach()
Move Tip#
move_tip() positions the tip at a pixel location within the current scan frame, the same idea as PyCypher.move_tip() from Chapter 2 — but remember the top-left pixel origin and physical (meter) units mentioned above. Internally it converts the pixel index to a normalized [-1, 1] coordinate (pixel_to_xy()), then to an absolute physical position (xy_to_physical()), and writes that as the new frame offset; pixel_to_physical() combines both steps if you just need the coordinates without moving the tip.
# Move the tip to the pixel at the center of a 256x256 frame
pdn.move_tip((128, 128), transit_time=0.5)
Read Signals and Cantilever Properties#
read_signals() returns the instantaneous deflection, lateral, detector-sum, and lock-in amplitude/phase signals. get_cantilever_properties() returns the active cantilever’s calibration values.
pdn.read_signals()
pdn.get_cantilever_properties()
Z Controller Setpoint#
pdn.get_z_setpoint()
pdn.set_z_setpoint(0.5)
Workspace and Automation Queue#
Beyond single-frame imaging, PyNanosurf exposes Nanosurf Studio’s workspace and automation queue directly — a built-in way to queue up multiple frames or spectroscopy grids and run them unattended, without the manual per-point looping shown for PyCypher in Chapter 6.
# Define a couple of regions to image, add them to the queue, then run it
frame_a = pdn.add_frame("Region A", x=5e-6, y=-3e-6, width=2e-6, height=2e-6)
frame_b = pdn.add_frame("Region B", x=-5e-6, y=3e-6, width=2e-6, height=2e-6)
pdn.add_to_queue(frame_a)
pdn.add_to_queue(frame_b)
pdn.run_automation_queue()
while pdn.is_automation_running():
time.sleep(1)
Disconnect#
Releases the Nanosurf Studio session when you’re done.
pdn.disconnect()