Skip to content

Commit

Permalink
Testing: First steps towards "simulated imperfection".
Browse files Browse the repository at this point in the history
  • Loading branch information
markmaker committed May 12, 2020
1 parent 3e72d57 commit c08725c
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 30 deletions.
Expand Up @@ -29,7 +29,10 @@
import org.openpnp.CameraListener;
import org.openpnp.gui.support.Wizard;
import org.openpnp.machine.reference.ReferenceCamera;
import org.openpnp.machine.reference.ReferenceMachine;
import org.openpnp.machine.reference.camera.wizards.ImageCameraConfigurationWizard;
import org.openpnp.machine.reference.driver.NullDriver;
import org.openpnp.model.Configuration;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.spi.PropertySheetHolder;
Expand Down Expand Up @@ -128,7 +131,10 @@ public synchronized BufferedImage internalCapture() {

Graphics gFrame = frame.getGraphics();

Location location = getLocation();
Location location = getLocation()
.convertToUnits(LengthUnit.Millimeters);
location = NullDriver.simulateImperfectLocation(location, getLooking());

double locationX = location.getX();
double locationY = location.getY();

Expand Down
Expand Up @@ -13,13 +13,16 @@
import org.openpnp.CameraListener;
import org.openpnp.gui.support.Wizard;
import org.openpnp.machine.reference.ReferenceCamera;
import org.openpnp.machine.reference.ReferenceMachine;
import org.openpnp.machine.reference.camera.wizards.SimulatedUpCameraConfigurationWizard;
import org.openpnp.machine.reference.driver.NullDriver;
import org.openpnp.model.Configuration;
import org.openpnp.model.Footprint;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.model.Part;
import org.openpnp.spi.Head;
import org.openpnp.spi.Machine;
import org.openpnp.spi.Nozzle;
import org.openpnp.spi.PropertySheetHolder;
import org.openpnp.util.Utils2D;
Expand Down Expand Up @@ -70,12 +73,12 @@ public BufferedImage internalCapture() {
location.getY() - phyHeight / 2, phyWidth, phyHeight);

// determine if there are any nozzles within our bounds and if so render them
for (Head head : Configuration.get()
.getMachine()
.getHeads()) {
for (Head head : Configuration.get()
.getMachine().getHeads()) {
for (Nozzle nozzle : head.getNozzles()) {
Location l = nozzle.getLocation()
.convertToUnits(LengthUnit.Millimeters);
l = NullDriver.simulateImperfectLocation(l, getLooking());
if (phyBounds.contains(l.getX(), l.getY())) {
drawNozzle(g, nozzle);
}
Expand All @@ -97,9 +100,10 @@ private void drawNozzle(Graphics2D g, Nozzle nozzle) {

// Draw the nozzle
// Get nozzle offsets from camera
Location offsets = nozzle.getLocation()
.convertToUnits(units)
.subtractWithRotation(getLocation());
Location l = nozzle.getLocation()
.convertToUnits(LengthUnit.Millimeters);
l = NullDriver.simulateImperfectLocation(l, getLooking());
Location offsets = l.subtractWithRotation(getLocation());

// Create a nozzle shape
fillShape(g, new Ellipse2D.Double(-0.5, -0.5, 1, 1), Color.green, unitsPerPixel, offsets, false);
Expand Down
106 changes: 93 additions & 13 deletions src/main/java/org/openpnp/machine/reference/driver/NullDriver.java
Expand Up @@ -20,31 +20,25 @@
package org.openpnp.machine.reference.driver;

import java.io.IOException;
import java.util.HashMap;

import javax.swing.Action;
import javax.swing.Icon;

import org.openpnp.gui.support.PropertySheetWizardAdapter;
import org.openpnp.gui.support.Wizard;
import org.openpnp.machine.reference.ReferenceActuator;
import org.openpnp.machine.reference.ReferenceDriver;
import org.openpnp.machine.reference.ReferenceHead;
import org.openpnp.machine.reference.ReferenceHeadMountable;
import org.openpnp.machine.reference.ReferenceMachine;
import org.openpnp.machine.reference.ReferenceNozzle;
import org.openpnp.model.AxesLocation;
import org.openpnp.model.Configuration;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.model.MappedAxes;
import org.openpnp.spi.Head;
import org.openpnp.spi.Machine;
import org.openpnp.spi.Axis;
import org.openpnp.spi.Camera.Looking;
import org.openpnp.spi.ControllerAxis;
import org.openpnp.spi.Movable.MoveToOption;
import org.openpnp.spi.PropertySheetHolder;
import org.openpnp.spi.base.AbstractDriver;
import org.pmw.tinylog.Logger;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;

/**
* An example of the simplest possible driver that can support multiple heads. This driver maintains
Expand All @@ -56,12 +50,31 @@ public class NullDriver extends AbstractDriver implements ReferenceDriver {
@Attribute(required = false)
private double feedRateMmPerMinute = 5000;

/**
* The simulated non-squareness is applied to what the simulated cameras see.
* Works on ImageCamera and SimulatedUpCamera
*/
@Attribute(required = false)
private double simulatedNonSquarenessFactor;

/**
* The simulated home offsets are applied to what the simulated cameras see.
* This works like G92.
* Works on ImageCamera and SimulatedUpCamera
*/
@Element(required = false)
private Location homingOffsets = new Location(LengthUnit.Millimeters);

@Element(required = false)
private Location simulatedRunout = new Location(LengthUnit.Millimeters, 0.1, 0.02, 0, 0);

private boolean enabled;

@Override
public void home(ReferenceMachine machine, MappedAxes mappedAxes) throws Exception {
Logger.debug("home()");
checkEnabled();
homingOffsets = new Location(LengthUnit.Millimeters);
mappedAxes.setLocation(new AxesLocation());
}

Expand All @@ -70,6 +83,17 @@ public void home(ReferenceMachine machine, MappedAxes mappedAxes) throws Excepti
public void resetLocation(ReferenceMachine machine, MappedAxes mappedAxes, AxesLocation location)
throws Exception {
Logger.debug("resetLocation("+location+")");
AxesLocation offsets = location.subtract(mappedAxes.getLocation());
double x = 0, y = 0;
for (ControllerAxis axis : mappedAxes.getAxes()) {
if (axis.getType() == Axis.Type.X) {
x = offsets.getCoordinate(axis) + homingOffsets.getX();
}
else if (axis.getType() == Axis.Type.Y) {
y = offsets.getCoordinate(axis) + homingOffsets.getY();
}
}
homingOffsets = new Location(AxesLocation.getUnits(), x, y, 0, 0);
mappedAxes.setLocation(location);
}

Expand Down Expand Up @@ -108,7 +132,7 @@ public void moveTo(ReferenceHeadMountable hm, MappedAxes mappedAxes, AxesLocatio
*/
protected void simulateMovement(ReferenceHeadMountable hm, MappedAxes mappedAxes, AxesLocation location, AxesLocation hl,
double speed) throws Exception {
AxesLocation delta = hl.subtract(location);
AxesLocation delta = location.subtract(hl);
double distanceLinear = delta.distanceLinear();
double distanceRotational = delta.distanceRotational();
double timeLinear = distanceLinear / (feedRateMmPerMinute/60.0 * speed);
Expand All @@ -119,15 +143,15 @@ protected void simulateMovement(ReferenceHeadMountable hm, MappedAxes mappedAxes
double dt;
do {
double t = (System.currentTimeMillis() - t0)*0.001;
dt = Math.min(1.0, (t-t0)/time);
dt = Math.min(1.0, t/time);
AxesLocation l = hl.add(delta.multiply(dt));
mappedAxes.setLocation(l);

// Provide live updates to the Machine as the move progresses.
((ReferenceMachine) Configuration.get().getMachine())
.fireMachineHeadActivity(hm.getHead());
try {
Thread.sleep(100);
Thread.sleep(1);
}
catch (Exception e) {

Expand Down Expand Up @@ -199,4 +223,60 @@ public void migrateDriver(ReferenceMachine machine) throws Exception {
machine.addDriver(this);
createAxisMappingDefaults(machine);
}


public double getSimulatedNonSquarenessFactor() {
return simulatedNonSquarenessFactor;
}


public void setSimulatedNonSquarenessFactor(double simulatedNonSquarenessFactor) {
this.simulatedNonSquarenessFactor = simulatedNonSquarenessFactor;
}

public Location getHomingOffsets() {
return homingOffsets;
}

public void setHomingOffsets(Location homingOffsets) {
this.homingOffsets = homingOffsets;
}

public Location getSimulatedRunout() {
return simulatedRunout;
}


public void setSimulatedRunout(Location simulatedRunout) {
this.simulatedRunout = simulatedRunout;
}


/**
* Simulates imperfection in a NullDriver. If any other driver is configured this remains ineffective.
*
* @param location The perfect location to be made imperfect.
* @param looking
* @return The imperfect location adjusted for non-squareness compensation and a visual homing offset.
*/
public static Location simulateImperfectLocation(Location location, Looking looking) {
ReferenceMachine machine = (ReferenceMachine) Configuration.get()
.getMachine();
if (machine.getDefaultDriver() instanceof NullDriver) {
NullDriver driver = (NullDriver) machine.getDefaultDriver();
double simulatedNonSquarenessFactor = driver.getSimulatedNonSquarenessFactor();
Location homeOffset = driver.getHomingOffsets();
if (looking == Looking.Down) {
location = location.subtract(homeOffset);
}
else if (looking == Looking.Up) {
location = location.add(homeOffset);
location = location.add(driver.getSimulatedRunout().rotateXy(location.getRotation()));
}
location = location.add(new Location(LengthUnit.Millimeters,
simulatedNonSquarenessFactor*location.getY(),
0, 0, 0));
}
return location;
}
}
Expand Up @@ -130,18 +130,13 @@ private void createUi() {
visualHomingMethod = new JComboBox(VisualHomingMethod.values());
visualHomingMethod.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
boolean homingCapture = (visualHomingMethod.getSelectedItem() == VisualHomingMethod.ResetToFiducialLocation);
boolean homingFiducial = (visualHomingMethod.getSelectedItem() != VisualHomingMethod.None);
captureHomeCoordinatesAction.setEnabled(homingCapture);
positionHomeCoordinatesAction.setEnabled(homingCapture);
homingFiducialX.setEnabled(homingFiducial);
homingFiducialY.setEnabled(homingFiducial);
adaptDialog();
}
});
panel.add(visualHomingMethod, "4, 6, 3, 1, fill, default");

JLabel lblWarningChangingThese = new JLabel("<html>Warning: changing the above settings or physically moving the <br/>\r\nhoming fiducial might break all your captured locations on the machine!<br/>\r\n&nbsp;</html>");
lblWarningChangingThese.setForeground(Color.RED);
JLabel lblWarningChangingThese = new JLabel("<html><p>\r\n<strong>Important Notice</strong>: the homing fiducial should be mounted \r\nand configured early in the build process, before you start capturing a large number of\r\nlocations for the Machine Setup (nozzle tip changer, feeders etc.) \r\n</p>\r\n<p style=\"color:red\">Each time the above settings are changed or the fiducial physically moved, all the already captured locations in the Machine Setup will be broken. </p></html>");
lblWarningChangingThese.setForeground(Color.BLACK);
panel.add(lblWarningChangingThese, "4, 8, 7, 1");

JLabel lblParkLocation = new JLabel("Park Location");
Expand Down Expand Up @@ -317,7 +312,17 @@ public void createBindings() {
ComponentDecorators.decorateWithAutoSelectAndLengthConversion(minY);
ComponentDecorators.decorateWithAutoSelectAndLengthConversion(maxX);
ComponentDecorators.decorateWithAutoSelectAndLengthConversion(maxY);


adaptDialog();
}

protected void adaptDialog() {
boolean homingCapture = (visualHomingMethod.getSelectedItem() == VisualHomingMethod.ResetToFiducialLocation);
boolean homingFiducial = (visualHomingMethod.getSelectedItem() != VisualHomingMethod.None);
captureHomeCoordinatesAction.setEnabled(homingCapture);
positionHomeCoordinatesAction.setEnabled(homingCapture);
homingFiducialX.setEnabled(homingFiducial);
homingFiducialY.setEnabled(homingFiducial);
}

private static Location getParsedLocation(JTextField textFieldX, JTextField textFieldY) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/openpnp/spi/base/AbstractHead.java
Expand Up @@ -79,7 +79,7 @@ public enum VisualHomingMethod {
private VisualHomingMethod visualHomingMethod = VisualHomingMethod.None;

@Element(required = false)
protected Location homingFiducialLocation;
protected Location homingFiducialLocation = new Location(LengthUnit.Millimeters);


protected Machine machine;
Expand Down

0 comments on commit c08725c

Please sign in to comment.