Skip to content

Commit

Permalink
Feature / Global Axes and Framework for Enhanced Motion Control (open…
Browse files Browse the repository at this point in the history
…pnp#1035)

* First implementation round. GCodeDriver Axis management is still redundantly in there.

* Reworked Drivers to flat list.

* Machine Setup Panel Tabs per class restoring and out of bounds bugfix.

* Work in progress, backup commit.

* Add

* Axes and Transformations' Wizards implemented. Reorganized into own axis package. Wrestle with WindowBuilder.

* First time tests run successful. GcodeDriver still partially running on its own (redundant) Axis mapping.

* Testing and refining migration with examples.

* Resolved openpnp#998 merging issues.

* Cosmetics.

* Created new AxesLocation for Controller side. Reworked all Transformations.

* Made tests run with AxesLocation.

* Testing: First steps towards "simulated imperfection".

* Tested using NullDriver.
* Added simulated Homing Error corrected by Visual Homing
* Added simulated non-squareness corrected by Linear Axis Non-Squareness Transformation
* Added simulated vibration to test Camera Settle
* Added simulated camera noise to test Camera Settle
* Added simulated nozzle runout to test Runout Compensation
No Unit yet.

* Restrict image transfer to the area of the source image, to fix some wrapping-around when going beyond the image margin.

* Backup commit.

* Factored OpenCvUtils.createFootprintTemplate() out for use outside of stages.

* SimulationModeMachine can now check pick and place Locations by inspecting the ImageCamera view at the location.

* Sub-pixel rendering etc.

* Refined the NullMotionPlanner and various stuff.

* Fix the camera view rotation jog to work with and respect mapped axes.

* MotionPlanner first implementation. Before MappedAxes refactoring.

* Reworked: no MappedAxes. Axis limits. Rotation wrap-around.

* Added solver.

* Better description and simpler formula. Getting the hang of this.

* More cosmetics

* Backup commit.

* Backup commit.

* Added motion solver test. Does not work (yet).

* Backup commit

* Backup commit before solver remove.

* Removed tnc solver. Almost completed profile solver cases.

* Backup commit

* Changed profile solver to rudimentary secant method.

* Solver seems to work now.

* Backup commit

* New region solver.

* Tests augmented.

* Before reworking path solver.

* Backup before reworking path solving/half sided profiles.

* Finish without trying to implement advanced motion planning. Cleanup and better comments.

* Better comments.

* Removed unnecessary (whitespace) changes etc. Better comments.

* Self code review bugfixes.

* Constant acceleration profiles.
Optionally test an imperfect machine in the SampleJobTest.

* Simplify Motion and MotionOption.
Implement analytical constant acceleration profile solving for symmetric cases.
Lots of cosmetics.

* Testing on the machine with GcodeDriver: refinements and bugfixes.

* Test bugfix.
  • Loading branch information
markmaker committed Aug 13, 2020
1 parent 55b8c5d commit f6713b6
Show file tree
Hide file tree
Showing 93 changed files with 12,876 additions and 2,001 deletions.
4 changes: 3 additions & 1 deletion src/main/java/org/openpnp/gui/JogControlsPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.openpnp.model.Length;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.model.Motion.MotionOption;
import org.openpnp.spi.Actuator;
import org.openpnp.spi.Head;
import org.openpnp.spi.HeadMountable;
Expand Down Expand Up @@ -228,7 +229,8 @@ else if (c < 0) {
}
}

tool.moveTo(targetLocation);
tool.moveTo(targetLocation, MotionOption.JogMotion);
// to test without backlash comp for continous jogging: add MotionOption.SpeedOverPrecision
}

private boolean nozzleLocationIsSafe(Location origin, Location dimension, Location nozzle,
Expand Down
27 changes: 15 additions & 12 deletions src/main/java/org/openpnp/gui/components/CameraView.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@
import org.openpnp.gui.components.reticle.Reticle;
import org.openpnp.model.Configuration;
import org.openpnp.model.Location;
import org.openpnp.spi.Axis;
import org.openpnp.spi.Camera;
import org.openpnp.spi.HeadMountable;
import org.openpnp.spi.Nozzle;
import org.openpnp.util.MovableUtils;
import org.openpnp.util.UiUtils;
Expand Down Expand Up @@ -610,7 +612,8 @@ private boolean isPointInsideCircle(int pointX, int pointY, int circleX, int cir
}

private boolean isPointInsideDragJogRotationHandle(int x, int y) {
if (camera.getHead() == null) {
HeadMountable selectedTool = MainFrame.get().getMachineControls().getSelectedTool();
if (selectedTool.getAxisRotation() == null) {
return false;
}

Expand All @@ -622,7 +625,7 @@ private boolean isPointInsideDragJogRotationHandle(int x, int y) {

// The rotation handle is drawn on an imaginary circle centered in the view
double rotHandleRadius = Math.min(width, height) / 2 * .80;
double rotHandleAngle = -Utils2D.normalizeAngle(camera.getLocation().getRotation() + 90);
double rotHandleAngle = -Utils2D.normalizeAngle(selectedTool.getLocation().getRotation() + 90);
double rotHandleX = rotHandleRadius * Math.cos(Math.toRadians(rotHandleAngle)) + (width / 2.);
double rotHandleY = rotHandleRadius * Math.sin(Math.toRadians(rotHandleAngle)) + (height / 2.);

Expand Down Expand Up @@ -657,7 +660,8 @@ private double snapRotationAngleToTypicalAngles(double angle) {
}

private void paintDragJogRotationHandle(Graphics2D g2d, boolean active) {
if (camera.getHead() == null) {
HeadMountable selectedTool = MainFrame.get().getMachineControls().getSelectedTool();
if (selectedTool.getAxisRotation() == null) {
return;
}

Expand All @@ -667,7 +671,7 @@ private void paintDragJogRotationHandle(Graphics2D g2d, boolean active) {

// The rotation handle is drawn on an imaginary circle centered in the view
double rotHandleRadius = Math.min(width, height) / 2 * .80;
double rotHandleAngle = -Utils2D.normalizeAngle(camera.getLocation().getRotation() + 90);
double rotHandleAngle = -Utils2D.normalizeAngle(selectedTool.getLocation().getRotation() + 90);
double rotHandleX = rotHandleRadius * Math.cos(Math.toRadians(rotHandleAngle)) + (width / 2.);
double rotHandleY = rotHandleRadius * Math.sin(Math.toRadians(rotHandleAngle)) + (height / 2.);

Expand Down Expand Up @@ -1333,6 +1337,8 @@ private void moveToClick(MouseEvent e) {
Nozzle nozzle = MainFrame.get().getMachineControls().getSelectedNozzle();
// Add the offsets to the Camera's nozzle calibrated position.
Location location = camera.getLocation(nozzle).add(offsets);
// Only change X/Y.
location = nozzle.getLocation().derive(location, true, true, false, false);
MovableUtils.moveToLocationAtSafeZ(nozzle, location);
}
else {
Expand All @@ -1358,15 +1364,12 @@ private void rotateToClick(MouseEvent e) {
}

double targetAngle = Utils2D.normalizeAngle(-(rotTargetHandleAngle + 90));
HeadMountable selectedTool = MainFrame.get().getMachineControls().getSelectedTool();

UiUtils.submitUiMachineTask(() -> {
if (camera.getHead() == null) {
Logger.warn("Drag rotate not yet implemented for upward facing cameras.");
}
else {
Location location = camera.getLocation();
location = location.derive(null, null, null, targetAngle);
MovableUtils.moveToLocationAtSafeZ(camera, location);
}
Location location = selectedTool.getLocation();
location = location.derive(null, null, null, targetAngle);
MovableUtils.moveToLocationAtSafeZ(selectedTool, location);
});
}

Expand Down
93 changes: 93 additions & 0 deletions src/main/java/org/openpnp/gui/support/AxesComboBoxModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2020 <mark@makr.zone>
* inspired and based on work
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/

package org.openpnp.gui.support;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.swing.DefaultComboBoxModel;

import org.openpnp.model.Configuration;
import org.openpnp.spi.Axis;
import org.openpnp.spi.base.AbstractMachine;

@SuppressWarnings({"serial", "rawtypes"})
public class AxesComboBoxModel extends DefaultComboBoxModel implements PropertyChangeListener {
private Comparator<Axis> comparator = new Comparator<Axis>() {
@Override
public int compare(Axis o1, Axis o2) {
return o1.getName().compareTo(o2.getName());
}
};
final private boolean addEmpty;
final private Class<? extends Axis> types;
final private AbstractMachine machine;
private Axis.Type axisType;

public AxesComboBoxModel(AbstractMachine machine, Class<? extends Axis> types, Axis.Type axisType, boolean addEmpty) {
this.machine = machine;
this.addEmpty = addEmpty;
this.types = types;
this.axisType = axisType;
if (machine != null) { // we're not in Window Builder Design Mode
addAllElements();
machine.addPropertyChangeListener("axes", this);
}
}

private void addAllElements() {
if (machine != null) { // we're not in Window Builder Design Mode
ArrayList<Axis> axes = null;
axes = new ArrayList<>(machine.getAxes());
Collections.sort(axes, comparator);
for (Axis axis : axes) {
if (types.isInstance(axis)) {
if (axisType == null || axisType == axis.getType()) {
addElement(axis.getName());
}
}
}
if (addEmpty) {
addElement(new String());
}
}
}

public Axis.Type getAxisType() {
return axisType;
}

public void setAxisType(Axis.Type axisType) {
this.axisType = axisType;
removeAllElements();
addAllElements();
}

@Override
public void propertyChange(PropertyChangeEvent evt) {
removeAllElements();
addAllElements();
}
}
63 changes: 63 additions & 0 deletions src/main/java/org/openpnp/gui/support/DriversComboBoxModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2020 <mark@makr.zone>
* inspired and based on work
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/

package org.openpnp.gui.support;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.DefaultComboBoxModel;

import org.openpnp.spi.Driver;
import org.openpnp.spi.base.AbstractMachine;

@SuppressWarnings({"serial", "rawtypes"})
public class DriversComboBoxModel extends DefaultComboBoxModel implements PropertyChangeListener {
final private AbstractMachine machine;
final private boolean addEmpty;

public DriversComboBoxModel(AbstractMachine machine, boolean addEmpty) {
this.machine = machine;
this.addEmpty = addEmpty;
addAllElements();
if (machine != null) { // we're not in Window Builder Design Mode
this.machine.addPropertyChangeListener("drivers", this);
}
}

private void addAllElements() {
if (machine == null) {
return;// we're in Window Builder Design Mode
}
for (Driver driver : machine.getDrivers()) {
addElement(driver.getName());
}
if (addEmpty) {
addElement(new String());
}
}

@Override
public void propertyChange(PropertyChangeEvent evt) {
removeAllElements();
addAllElements();
}
}
4 changes: 4 additions & 0 deletions src/main/java/org/openpnp/gui/support/Icons.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public class Icons {
public static Icon processActivity1Icon = getIcon("/icons/process-activity-1.svg");
public static Icon processActivity2Icon = getIcon("/icons/process-activity-2.svg");

public static Icon axisCartesian = getIcon("/icons/axis-cartesian.svg");
public static Icon axisRotation = getIcon("/icons/axis-rotate.svg");
public static Icon driver = getIcon("/icons/driver.svg");

public static Icon getIcon(String resourceName, int width, int height) {
if (resourceName.endsWith(".svg")) {
return new SvgIcon(Icons.class.getResource(resourceName), width, height);
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/org/openpnp/gui/support/NamedConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2019 <mark@makr.zone>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/

package org.openpnp.gui.support;

import java.util.List;

import org.jdesktop.beansbinding.Converter;
import org.openpnp.model.Named;

public class NamedConverter<NamedType extends Named> extends Converter<NamedType, String> {
final List<NamedType> pool;

public NamedConverter(List<NamedType> pool) {
super();
this.pool = pool;
}

@Override
public String convertForward(NamedType named) {
if (named == null) {
return "";
}
else {
return named.getName();
}
}

@Override
public NamedType convertReverse(String s) {
for (NamedType named : pool) {
if (named.getName().equals(s)) {
return named;
}
}
return null;
}
}

0 comments on commit f6713b6

Please sign in to comment.