Focus Stepper

Focus Stepper is an application for controlling a stepper motor and camera shutter for the purpose of focus stacking. The essential operation order is as such: The application controls an Arduino, which controls a stepper motor, which then controls a focusing device.

Main UI

Main Window

In order to fully operate Focus Stepper, first you must connect the hardware via USB or Bluetooth. Unless you've won the lottery twice this year, you shouldn't bet on Bluetooth working in Windows, so aim for USB. Windows users also need to have the proper drivers for their Arduino installed before you can connect to it. Then the application must be connected to the correct port. This is accomplished with the top left drop down box and the Connect button. Focus Stepper can be operated in limited capacity without being connected to an Arduino. It can run a fake 'dummy' stack based on most of the settings, though Start and End points will not function properly without being connected. Check the console window to see the fake stack's output.

IMPORTANT: In OS X you must create a lock directory in order to be able to connect to your Arduino. More information here. To do this, type the following commands in terminal:

sudo mkdir /var/lock
sudo chmod go+rwx /var/lock

After connected, Focus Stepper is ready to use. Displayed in the left column is the Focus-Step size. This is the amount that the motor will move between each photo, or between each motor actuation(via arrow buttons in the top middle area). Three choices are selectable: Calculate, Distance, or Motor-Steps. Calculate mode will calculate the optimal focus-step size based on several user inputs (inspired by this PMN thread). Distance will move a set distance per focus-step, in millimeters. Motor-Steps will move a set number of motor actuations per focus-step. If microstepping is enabled, it will move that number of microsteps, not full steps.

The second column is used to set the total depth for the stack. This also has three options: Start and End points, Total Travel, and Number of Focus-Steps. Total Travel will attempt to cover this distance in millimeters for the total depth of the stack. If it can not achieve this exact value, it will always round down as to not exceed this distance.  Number of Focus-Steps will preform this number of focus-steps in order to complete the stack. Start and End points allows the user to set a point for the stack to begin at, focus to another point using the arrows in the top middle column, and set a point for the stack to stop at. During the process of focusing the user can switch between focus-step sizes in the left column to make focusing faster or more precise. After the start and end points are set you can simple switch to a focus-step size you wish for the stack to use. If the number of motor-steps between the start and end point range and the number motor-steps to be performed are not evenly divisible, it will automatically truncate (remove) the needed number of motor-steps. You can select whether motor-steps will be truncated from the start or end of the range in the settings.

When using Start and End points for the stack distance, it also provides you with some helpful utilities. Go To buttons allow you to have the motor move to the Start, Middle, or End of the range respectively. The Shift Range buttons allow you to move both the start and end points together, useful if you have a subject moving in the depth or the subject gets bumped. S2C moves the start point to the current focus position. This also shifts the end point the same relative distance so the stack remains the same size. E2C moves the end point to the current focus position and shifts the start point respectively. The # button allows the user specify a set number of motor steps(positive or negative) to shift the range by without regard to the current focus position. Shift Point will move the Start point or End point a user specified number of motor-steps.

Keep in mind when using Start and End points that backlash is not compensated for during normal movements. Backlash only will get reeled in and compensated for at the start of a stack, and only when enabled in the settings.

The right hand column contains Info and Statistics about the stack to be performed. Because sometimes the amount a rig can move by and what is set in the UI maybe different, this panel shows the actual values to be used. If Focus Stepper can not achieve a set focus-step size or stack size, it will always strive for smaller values. For example, if the calculated focus-step size is 0.05225, and the smallest increment it can move by is 0.001, then the final focus-step size will be 0.052. A time estimate is also provide in the info panel, based upon the stack size, delays, and motor speed.

Focus WindowWhen using live view on your computer, it maybe desirable for Focus Stepper's window to be smaller. In the top right the F Win button provides a small always-on-top window with focus controls, and Start and End point controls. Closing this window returns the main Focus Stepper window

When you are ready to perform a stack, press the Start button in the top right panel. To stop a stack press the STOP/ABORT button. The STOP/ABORT button will also stop motor movements done using the Manual Controls panel.

SettingsSettings

The settings window provides various controls for inputing rig information and setting some of Focus Stepper's behavior.

The motor settings are the most critical. Without Distance per Full Motor-Step set properly, much of Focus Stepper's UI will be inaccurate and practically unusable. This setting is simply the linear distance your rig moves from a single full sized motor step. In example, lets say that my rig is a 400 step per revolution motor hooked up directly to a microscope's fine focus knob with 0.1mm per revolution. That means this value should be 0.1mm/400, or 0.00025.

Motor Microstepping allows you to set the level of microstepping to be used. The UI will automatically take the reduction in step size for this into account. It is not recommended to count of micro stepping for increased accuracy and resolution. Instead it is best to design around gear reduction to increase resolution, and use microstepping to increase motor smoothness and reduce vibration during motor operation.

Motor speed is set using a combination of two controls. First, Full Steps Per Revolution is set to the number of full steps required for the stepper motor to complete a single turn. This is done to accommodate motors of various step angles, and geared stepper motors. Once set, you can control the motor speed using Motor RPM(revolutions per minute). This value should be taken as approximate, and maybe slightly slower at higher speeds.

The Camera Information panel is used when calculating focus-step size, and calculating diffraction in the info panel. Imagining Wavelength is only used for wave optics DOF calculations.

The Timing Settings are used to set the delays to be used during the stacking process. Here you can also set multiple Shutter Counts per focus-step, with a delay between each additional shutter. This is useful for mirror lock up mode, or HDR photography.

Serial Command Timeout is the time that Focus Stepper will wait until it decides that the command failed, and stops functioning. This prevents any possible problems with losing connection and the hardware misbehaving. Shutter Actuation Time is the time that the shutter will be 'held down' for. Make sure your camera is in single shot mode. This here because values that are too small may not trigger live view consistently. For example a value of about 600ms is needed to make sure live view on a 500d always triggers properly. See THIS THREAD for some measurements on the variability in shutter time during live view. You might be able to use this setting to use a bulb mode on your camera as well, but I haven't tried it yet.

The Misc. panel provides various Focus Stepper behaviors. Double Arrow Multiplier sets the amount the [<<] and [>>] buttons will move by relative to the currently selected focus step size. Truncate Start/End point allows you select where steps will be truncated from(when required) while using Start and End points. Take up Backlash allows you to enable and set the backlash in millimeters. Backlash will be removed by moving the rail away from the subject by this distance, and then back to the start point. Carriage Return will cause the rail to move back to the start position at the end of the stack(backlash will not be removed when returning).

Swap Motor Direction is an important setting. Make sure that when pressing the [>] focus button the rail moves toward the subject, and [<] moves away from the subject. If not, then change this check box. This ensures backlash is taken up away from the subject, and that the stack progresses from front to back.

Once the settings are correct, press the Save to Settings.xml file. This saves all the settings and the main UI selections and input(except Start and End points, as these are always reset between application launch). If you find yourself often using the same optics, input them in the main UI and then use this button to save it as default. The settings.xml is saved to ~/Library/Application Support/FocusStepper on OS X, and %APPDATA%\..\Local\FocusStepper on Windows.

Known issues

- Microstepping maybe inaccurate based upon the motor and micro stepping value used. This requires individual testing to determine what level of microstepping can be safely used. Louder than normal noise maybe heard for a motor that isn't microstepping optimally as well.

- The first step after switching from a higher microstepping setting to a lower microstepping will be up to one full step off from what is expected. This happens as the motor "snaps" to the next possible position for the new microstepping value.

- Connecting to an incorrect port may behave erratic. Try restarting the application.

- Disconnecting can some times behave erratically.

- Windows serial communication may require a higher time-out period.

- Windows Bluetooth has been pretty much impossible for me to get to work. If you are on windows, plan on using a long USB cable. I wouldn't even recommend buying Bluetooth components to try.

- OS X populates port list with several "dummy" ports. You are looking for /dev/tty.PORT to try to connect to. Either USB.XX, or the name of the Bluetooth device.

- The UI might not update properly after unlocking from motor movement or stacking. The UI is fine and usable still, this is just a redraw bug.

Downloads 

 

One thought on “Focus Stepper

  1. Hi!

    I am trying to get your software FOCUS STEPPER to work. Any ideas on what could be wrong?

    I have uploaded the hex file and created the /var/lock file. I am on El Capitan osx. I cannot connect, using Usb.

    On tty I get:
    /dev/tty.usbserial-A9007KAH
    Adding Listener!
    In PingTask: Connected == false
    In PingTask: Connected == false
    In PingTask: Connected == false
    In PingTask: Connected == false
    In PingTask: Connected == false
    Removing Listener!
    May 09, 2017 1:04:46 AM focusstepper.FocusStepper_Main_UI$ConnectionSendTask run
    INFO: Connection Failed
    May 09, 2017 1:04:46 AM focusstepper.FocusStepper_Main_UI$ConnectionSendTask run
    WARNING: Connection Failure. Please check your settings.

    on cu:
    /dev/cu.usbserial-A9007KAH
    May 09, 2017 1:05:22 AM focusstepper.SerialPortContainer connect
    SEVERE: null
    gnu.io.PortInUseException: Unknown Application
    at gnu.io.CommPortIdentifier.open(CommPortIdentifier.java:354)
    at focusstepper.SerialPortContainer.connect(SerialPortContainer.java:91)
    at focusstepper.FocusStepper_Main_UI.Button_connectPortActionPerformed(FocusStepper_Main_UI.java:2204)
    at focusstepper.FocusStepper_Main_UI.access$4000(FocusStepper_Main_UI.java:49)
    at focusstepper.FocusStepper_Main_UI$37.actionPerformed(FocusStepper_Main_UI.java:1812)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at focusstepper.SerialPortContainer.connect(SerialPortContainer.java:9Cool
    at focusstepper.FocusStepper_Main_UI.Button_connectPortActionPerformed(FocusStepper_Main_UI.java:2204)
    at focusstepper.FocusStepper_Main_UI.access$4000(FocusStepper_Main_UI.java:49)
    at focusstepper.FocusStepper_Main_UI$37.actionPerformed(FocusStepper_Main_UI.java:1812)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Leave a Reply

Your email address will not be published. Required fields are marked *