Base Capabilities
This page documents the base capabilities that are available across each piezo device in psj-lib. These capabilities provide the core functionality for position control, signal processing, data acquisition, and system configuration.
Device-specific implementations may extend these base capabilities with additional features or provide specialized versions. Refer to device-specific documentation (e.g., d-Drive and 30DV50/300) for device-specific capabilities and enhancements.
Overview
Capabilities are modular features accessed as properties of device channels. Each capability provides a focused set of operations for a specific aspect of device control.
from psj_lib import DDriveDevice, TransportType
device = DDriveDevice(TransportType.SERIAL, "COM3")
async with device:
channel = device.channels[0]
# Access capabilities as channel properties
await channel.setpoint.set(50.0)
position = await channel.position.get()
await channel.pid_controller.set(p=10.0, i=5.0)
Position Control
Setpoint
API Reference: Setpoint
The setpoint capability controls the target position or voltage for the actuator. In closed-loop mode, the controller drives the actuator to match this setpoint. In open-loop mode, the setpoint directly controls the output voltage.
# Set target position
await channel.setpoint.set(75.5)
# Read current setpoint
target = await channel.setpoint.get()
print(f"Target: {target:.2f} µm")
Key Points:
Units match position units (typically µm in closed-loop, V in open-loop)
Range limited by actuator specifications
Movement speed affected by slew rate settings
Position
API Reference: Position
Reads the current position of the actuator. In closed-loop systems, this represents the sensor feedback value. In open-loop systems, this may represent the output voltage.
# Get current position
current_pos = await channel.position.get()
print(f"Position: {current_pos:.2f} µm")
# Calculate position error
target = await channel.setpoint.get()
error = target - current_pos
print(f"Error: {error:.3f} µm")
Key Points:
Read-only capability
Update rate depends on device (typically every control loop cycle)
Units depend on device configuration
Closed-Loop Controller
API Reference: ClosedLoopController
Enables or disables closed-loop position control. When enabled, the controller uses sensor feedback to actively maintain the actuator at the desired setpoint, compensating for drift, hysteresis, and external loads.
# Enable closed-loop control
await channel.closed_loop_controller.set(True)
# Check control mode
is_closed_loop = await channel.closed_loop_controller.get_enabled()
print(f"Mode: {'Closed-loop' if is_closed_loop else 'Open-loop'}")
# Get controller sampling period
period_us = channel.closed_loop_controller.sample_period
print(f"Control rate: {1e6/period_us:.0f} Hz")
Key Points:
Requires position sensor for feedback
Better accuracy and stability than open-loop
PID parameters affect closed-loop performance
Changing modes may cause position jumps
Slew Rate
API Reference: SlewRate
Controls the maximum rate of change for actuator movement. Slew rate limiting prevents mechanical shock, reduces vibration, and protects delicate samples.
# Set gentle slew rate for smooth motion
await channel.slew_rate.set(10.0)
# Query current rate
rate = await channel.slew_rate.get()
print(f"Max speed: {rate:.1f} V/ms")
Key Points:
Units typically V/ms or %/ms (device-specific)
Lower values = smoother, slower movements
Zero or maximum may disable rate limiting (device-specific)
Control System
PID Controller
API Reference: PIDController
Configures PID (Proportional-Integral-Derivative) controller parameters for closed-loop operation. The PID controller determines how the system responds to position errors.
# Set PID parameters
await channel.pid_controller.set(
p=10.0, # Proportional gain
i=5.0, # Integral gain
d=0.5, # Derivative gain
diff_filter=100.0 # Derivative filter
)
# Read individual parameters
p_gain = await channel.pid_controller.get_p()
i_gain = await channel.pid_controller.get_i()
print(f"PID: P={p_gain}, I={i_gain}")
Key Points:
P (Proportional): Response proportional to error. Higher = faster but may overshoot
I (Integral): Eliminates steady-state error. Too high causes oscillation
D (Derivative): Dampens oscillation. Higher = more damping but noise sensitive
Diff Filter: Filters derivative term to reduce noise amplification
Only active when closed-loop control is enabled
Improper tuning can cause poor performance
Pre-Control Factor (PCF)
API Reference: PreControlFactor
The Pre-Control Factor provides feedforward compensation to improve control system response. It anticipates required control action based on setpoint changes, reducing settling time and tracking error.
# Set moderate feedforward
await channel.pcf.set(0.5)
# Query current value
value = await channel.pcf.get()
print(f"PCF: {value}")
Key Points:
Typical range: 0.0 (no feedforward) to 1.0 (full feedforward)
Higher values = faster response but potential overshoot
Only active in closed-loop mode
Tune in conjunction with PID parameters
Signal Filtering
Notch Filter
API Reference: NotchFilter
Notch filters suppress specific frequency components to eliminate mechanical resonances that can cause instability or oscillation in closed-loop systems.
# Suppress 500 Hz resonance
await channel.notch_filter.set(
enabled=True,
frequency=500.0,
bandwidth=50.0
)
# Check configuration
freq = await channel.notch_filter.get_frequency()
bw = await channel.notch_filter.get_bandwidth()
enabled = await channel.notch_filter.get_enabled()
print(f"Notch: {freq}±{bw/2} Hz, {'On' if enabled else 'Off'}")
Key Points:
Center frequency should match mechanical resonance
Narrow bandwidth = precise suppression
Wide bandwidth = broader suppression, affects more frequencies
Low-Pass Filter
API Reference: LowPassFilter
Low-pass filters attenuate high-frequency noise while allowing low-frequency signals to pass. This improves signal quality and reduces noise in position measurements or control output.
# Enable 100 Hz low-pass filter
await channel.lpf.set(
enabled=True,
cutoff_frequency=100.0
)
# Check settings
freq = await channel.lpf.get_cutoff_frequency()
enabled = await channel.lpf.get_enabled()
print(f"LPF: {freq} Hz, {'Active' if enabled else 'Bypassed'}")
Key Points:
Lower cutoff = more filtering, slower response
Higher cutoff = less filtering, faster response
Adds phase lag proportional to filtering strength
Error Low-Pass Filter
API Reference: ErrorLowPassFilter
Applies low-pass filtering specifically to the position error signal (setpoint - position) before it enters the PID controller. This reduces high-frequency noise that could cause unstable control behavior.
# Configure 2nd-order error filter
await channel.error_lpf.set(
cutoff_frequency=200.0,
order=2
)
# Query settings
freq = await channel.error_lpf.get_cutoff_frequency()
order = await channel.error_lpf.get_order()
print(f"{order}-order error filter at {freq} Hz")
Key Points:
Only affects closed-loop control
Higher order = steeper rolloff, more phase lag
Helps stabilize noisy systems
Coordinate with PID tuning for best stability
Data Acquisition
Data Recorder
API Reference: DataRecorder
The data recorder captures device signals (position, setpoint, voltage, etc.) at high speed into device memory for later retrieval and analysis.
recorder = channel.data_recorder
# Configure for 10000 samples, no decimation
await recorder.set(memory_length=10000, stride=1)
# Start recording
await recorder.start()
# ... perform motion or measurements ...
# Retrieve data with progress callback
def progress(current, total):
print(f"Downloaded {current}/{total} samples")
from psj_lib import DataRecorderChannel
data = await recorder.get_all_data(
DataRecorderChannel.CHANNEL_1,
callback=progress
)
print(f"Captured {len(data)} samples")
# Check recorder specifications
sample_rate = recorder.sample_rate # Hz
sample_period = recorder.sample_period # microseconds
print(f"Recording at {sample_rate/1000:.0f} kHz")
Key Points:
Multiple channels (device-dependent, typically 2)
Memory length limits total capture time
Stride (decimation) allows longer time spans at lower data rate
Large data transfers may take several seconds
Use
sample_rateproperty to get base recording frequency
Trigger Output
API Reference: TriggerOut
Generates digital trigger pulses when monitored signals cross threshold values. Useful for synchronizing external equipment (cameras, data acquisition, etc.) with actuator movement.
from psj_lib import TriggerEdge, TriggerDataSource
# Trigger every 10µm from 20µm to 80µm
await channel.trigger_out.set(
start_value=20.0,
stop_value=80.0,
interval=10.0,
length=100, # Pulse duration in cycles
edge=TriggerEdge.BOTH,
src=TriggerDataSource.POSITION
)
# Query configuration
start = await channel.trigger_out.get_start_value()
interval = await channel.trigger_out.get_interval()
print(f"Trigger every {interval}µm from {start}µm")
Key Points:
Output typically 0V/5V TTL signal
Window mode: triggers when signal enters/exits range
Interval mode: periodic triggers at fixed spacing
Edge sensitivity: rising, falling, or both
Signal Generation
Static Waveform Generator
API Reference: StaticWaveformGenerator
Generates continuous periodic waveforms (sine, square, triangle, etc.) for scanning applications, vibration testing, frequency response characterization, and dynamic positioning.
wfg = channel.waveform_generator
# Generate 10 Hz sine wave, 20µm amplitude, centered at 50µm
await wfg.set(
frequency=10.0,
amplitude=20.0,
offset=50.0
)
# Create square wave with 30% duty cycle
await wfg.set(
frequency=5.0,
duty_cycle=30.0
)
# Query current settings
freq = await wfg.get_frequency()
amp = await wfg.get_amplitude()
offset = await wfg.get_offset()
print(f"{freq} Hz, ±{amp/2} µm around {offset} µm")
Key Points:
Configurable frequency, amplitude, offset, and duty cycle
May require modulation source selection to use waveform output
Frequency limited by device capabilities and actuator resonance
Amplitude limited by actuator travel range
Note
Device-specific implementations may provide enhanced waveform generators with additional waveform types and features. See device-specific documentation for details.
System Monitoring
Status Register
API Reference: Status
Queries the device status register to retrieve real-time hardware state information, error conditions, and operational flags. The status register format is device-specific.
status = await channel.status_register.get()
# Access device-specific status properties
print(f"Raw status: {status.raw}")
# Device-specific implementations provide interpreted properties
# (see device-specific documentation)
Key Points:
Status format is device-specific
Provides real-time device state information
Device implementations decode raw status into meaningful properties
Temperature
API Reference: Temperature
Monitors the internal temperature of device electronics or power stages. Temperature monitoring helps prevent overheating and can be used for thermal management.
temp = await channel.temperature.get()
print(f"Device temperature: {temp:.1f}°C")
if temp > 60:
print("Warning: High temperature")
Key Points:
Temperature typically in degrees Celsius
Sensor location varies by device (electronics, power stage)
Use for thermal monitoring and diagnostics
Fan Control
API Reference: Fan
Enables or disables the internal cooling fan for thermal management. The fan helps dissipate heat from power electronics during operation.
# Enable cooling fan
await channel.fan.set(True)
# Check fan status
is_running = await channel.fan.get_enabled()
print(f"Fan: {'On' if is_running else 'Off'}")
Key Points:
Not all devices have controllable fans
Some fans run automatically based on temperature
Disabling may cause thermal shutdown under heavy load
Device Information
Actuator Description
API Reference: ActuatorDescription
Retrieves a human-readable description of the piezoelectric actuator connected to a channel. This may include model number, specifications, or identifying information.
desc = await channel.actuator_description.get()
print(f"Connected actuator: {desc}")
# Example output: "MIPOS 100"
Key Points:
Description format is actuator-specific
May include model, travel range, resolution
Some devices return empty string if not configured
Unit
API Reference: Unit
Queries the measurement unit for a specific quantity exposed by the mapped command (for example open-loop voltage unit or closed-loop position unit).
unit = await channel.openloop_unit.get()
print(f"Open-loop unit: {unit}")
# Example output: "V"
Key Points:
Returns a single unit string for the configured command mapping
Typical values include
V,µmandmrad
Limits
API Reference: Limits
Reads lower/upper limits for a device quantity (for example voltage or position limits).
lower = await channel.openloop_limits.get_lower()
upper = await channel.openloop_limits.get_upper()
print(f"Allowed range: {lower} .. {upper}")
Key Points:
Provides read-only range boundaries
get_range()returns(lower, upper)
Display
API Reference: Display
Controls device display brightness (when supported).
await device.display.set(brightness=40.0)
Multi-Channel Helpers
API References:
These capabilities allow setting or reading multiple channels synchronously on devices that expose group operations (for example NV40/3 variants).
await device.multi_setpoint.set([10.0, 20.0, 30.0])
positions = await device.multi_position.get()
Signal Routing
Modulation Source
API Reference: ModulationSource
Configures which signal source is used to modulate the actuator position or voltage. Common sources include external analog input, internal waveform generator, or serial commands.
# Device-specific enum (example for d-Drive)
from psj_lib import DDriveModulationSourceTypes
# Use internal waveform generator
await channel.modulation_source.set_source(
DDriveModulationSourceTypes.INTERNAL_WAVEFORM
)
# Check current source
source = await channel.modulation_source.get_source()
print(f"Modulation from: {source.name}")
Key Points:
Source enum is device-specific
External input typically 0-10V
May need to enable modulation mode separately
Monitor Output
API Reference: MonitorOutput
Configures which internal signal is routed to the device’s analog monitor output connector. This allows real-time observation using an oscilloscope or data acquisition system.
# Device-specific enum (example for d-Drive)
from psj_lib import DDriveMonitorOutputSource
# Route position to monitor output
await channel.monitor_output.set_source(
DDriveMonitorOutputSource.POSITION
)
# Check current source
source = await channel.monitor_output.get_source()
print(f"Monitoring: {source.name}")
Key Points:
Output typically 0-10V
Scaling depends on device and selected source
Source enum is device-specific
Useful for debugging and real-time monitoring
Configuration Management
Factory Reset
API Reference: FactoryReset
Resets the device to factory default settings. This restores all parameters (PID, filters, control modes, etc.) to their original values.
# IMPORTANT: Backup first!
backup = await device.backup()
# Reset to factory defaults
await device.factory_reset.execute()
print("Device reset to factory defaults")
Key Points:
All custom settings are permanently lost
Use
device.backup()to save configuration firstCannot be undone
Warning
Factory reset is irreversible. Always backup your configuration before performing a factory reset.
See Also
d-Drive and 30DV50/300 - d-Drive specific capabilities and enhancements
API Reference - Complete API reference
Examples - Usage examples and tutorials