From e1f305e378fdab75b4030c507349f45b896718af Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Sun, 3 Sep 2023 13:03:39 -0700 Subject: [PATCH 1/6] intensity to height image converter --- .../Converter_IntensityToHeight.java | 135 ++++++++++++++++++ .../imageconverter/Converter_Pulse.java | 2 +- .../imageconverter/ImageConverterFactory.java | 1 + .../plotter/plottercontrols/MarlinPanel.java | 10 +- .../plottercontrols/MarlinPanelEvent.java | 5 + .../plottercontrols/PlotterControls.java | 22 ++- src/main/resources/languages/english.xml | 8 ++ 7 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java new file mode 100644 index 000000000..8cf0d6197 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java @@ -0,0 +1,135 @@ +package com.marginallyclever.makelangelo.makeart.imageconverter; + +import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.Translator; +import com.marginallyclever.makelangelo.makeart.TransformedImage; +import com.marginallyclever.makelangelo.makeart.imagefilter.Filter_Greyscale; +import com.marginallyclever.makelangelo.paper.Paper; +import com.marginallyclever.makelangelo.select.SelectSlider; +import com.marginallyclever.makelangelo.turtle.Turtle; + + +/** + * Horizontal lines. The height of each line is determined by the average intensity of the pixels in that line. + * @author Dan Royer + * @since 7.40.3 + */ +public class Converter_IntensityToHeight extends ImageConverter { + private static int spacing = 2; + private static int maxHeight = 10; + private static int sampleRate = 5; + + public Converter_IntensityToHeight() { + super(); + + SelectSlider selectSize = new SelectSlider("size",Translator.get("Converter_IntensityToHeight.spacing"), 20,1,getSpacing()); + SelectSlider selectMaxHeight = new SelectSlider("maxHeight",Translator.get("Converter_IntensityToHeight.maxHeight"),20,1,getMaxHeight()); + SelectSlider selectSampleRate = new SelectSlider("sampleRate",Translator.get("Converter_IntensityToHeight.sampleRate"),20,1,getSampleRate()); + + add(selectSize); + add(selectMaxHeight); + add(selectSampleRate); + + selectSize.addPropertyChangeListener(evt->{ + setSpacing((int) evt.getNewValue()); + fireRestart(); + }); + selectMaxHeight.addPropertyChangeListener(evt->{ + setMaxHeight((int) evt.getNewValue()); + fireRestart(); + }); + selectSampleRate.addPropertyChangeListener(evt->{ + setSampleRate((int) evt.getNewValue()); + fireRestart(); + }); + } + + @Override + public String getName() { + return Translator.get("Converter_IntensityToHeight.name"); + } + + public int getSpacing() { + return spacing; + } + public void setSpacing(int value) { + spacing = Math.max(1,value); + } + + public int getSampleRate() { + return sampleRate; + } + public void setSampleRate(int value) { + sampleRate = Math.max(1,value); + } + + public int getMaxHeight() { + return maxHeight; + } + public void setMaxHeight(int value) { + maxHeight = Math.max(1,value); + } + + protected void convertLine(TransformedImage img, double sampleSpacing, double halfStep, Point2D a, Point2D b) { + Point2D dir = new Point2D(b.x-a.x,b.y-a.y); + double len = dir.length(); + dir.scale(1.0/len); + + boolean first=true; + + for (double p = 0; p <= len; p += sampleSpacing) { + double x = a.x + dir.x * p; + double y = a.y + dir.y * p; + // read a block of the image and find the average intensity in this block + double z = img.sample( x - sampleSpacing, y - halfStep, x + sampleSpacing, y + halfStep); + // scale the intensity value + double scale_z = z / 255.0f; + //scale_z *= scale_z; // quadratic curve + double pulseSize = halfStep * scale_z; + double py=y + pulseSize; + if(first) { + turtle.jumpTo(x, py); + first = false; + } else { + turtle.moveTo(x, py); + } + } + } + + /** + * Converts images into zigzags in paper space instead of image space + */ + @Override + public void start(Paper paper, TransformedImage image) { + super.start(paper, image); + + Filter_Greyscale bw = new Filter_Greyscale(255); + TransformedImage img = bw.filter(myImage); + + double yBottom = myPaper.getMarginBottom(); + double yTop = myPaper.getMarginTop(); + double xLeft = myPaper.getMarginLeft(); + double xRight = myPaper.getMarginRight(); + + // from top to bottom of the image... + int i=0; + Point2D a = new Point2D(); + Point2D b = new Point2D(); + + turtle = new Turtle(); + + // horizontal + for (double y = yBottom; y < yTop; y += spacing) { + if ((++i % 2) == 0) { + a.set(xLeft,y); + b.set(xRight,y); + } else { + a.set(xRight,y); + b.set(xLeft,y); + } + convertLine(img,sampleRate,maxHeight,a,b); + } + + fireConversionFinished(); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Pulse.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Pulse.java index 57cc9939d..4f7d49ee7 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Pulse.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Pulse.java @@ -73,7 +73,7 @@ public void setDirectionIndex(int value) { protected void convertLine(TransformedImage img, double zigZagSpacing, double halfStep, Point2D a, Point2D b) { Point2D dir = new Point2D(b.x-a.x,b.y-a.y); double len = dir.length(); - dir.scale(1/len); + dir.scale(1.0/len); Point2D ortho = new Point2D(-dir.y,dir.x); turtle.jumpTo( diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/ImageConverterFactory.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/ImageConverterFactory.java index 42f0b4c0d..00308661e 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/ImageConverterFactory.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/ImageConverterFactory.java @@ -18,6 +18,7 @@ public class ImageConverterFactory { new Converter_Crosshatch(), new Converter_EdgeDetection(), new Converter_FlowField(), + new Converter_IntensityToHeight(), new Converter_Moire(), new Converter_Multipass(), new Converter_Pulse(), diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanel.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanel.java index d9adddf71..2f5e1834c 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanel.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanel.java @@ -106,7 +106,7 @@ private void onTimeoutCheck() { if (delay > TIMEOUT_DELAY) { if (delay > FATAL_TIMEOUT_DELAY) { logger.error("No answer from the robot"); - notifyListeners( MarlinPanelEvent.COMMUNICATION_FAILURE, "communicationFailure"); + notifyListeners( MarlinPanelEvent.COMMUNICATION_FAILURE, MarlinPanelEvent.COMMUNICATION_COMMAND); chatInterface.displayError("No answer from the robot, retrying..."); } else { logger.trace("Heartbeat: M400"); @@ -178,13 +178,13 @@ private void onHearError(String message) { // only notify listeners of a fatal error (MarlinInterface.ERROR) if the printer halts. if (message.contains(STR_PRINTER_HALTED)) { - notifyListeners( MarlinPanelEvent.ERROR, STR_PRINTER_HALTED ); + notifyListeners( MarlinPanelEvent.ERROR, MarlinPanelEvent.HALTED_COMMAND ); } } private void onHearHomeXYFirst() { logger.warn("Home XY First"); - notifyListeners( MarlinPanelEvent.HOME_XY_FIRST,"homeXYFirst" ); + notifyListeners( MarlinPanelEvent.HOME_XY_FIRST,MarlinPanelEvent.HOME_XY_COMMAND ); } private void onHearActionCommand(String command) { @@ -196,11 +196,11 @@ private void onHearActionCommand(String command) { } private void onDidNotFindCommandInHistory() { - notifyListeners( MarlinPanelEvent.DID_NOT_FIND, "didNotFind" ); + notifyListeners( MarlinPanelEvent.DID_NOT_FIND, MarlinPanelEvent.DID_NOT_FIND_COMMAND ); } private void fireIdleNotice() { - notifyListeners( MarlinPanelEvent.IDLE, "idle" ); + notifyListeners( MarlinPanelEvent.IDLE, MarlinPanelEvent.IDLE_COMMAND ); } private void clearOldHistory() { diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanelEvent.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanelEvent.java index 1d1ea5cdb..7bc2901c9 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanelEvent.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/MarlinPanelEvent.java @@ -34,6 +34,11 @@ public class MarlinPanelEvent { * There should have been news and very likely the connection has failed. */ public static final int COMMUNICATION_FAILURE = 6; + public static final String HALTED_COMMAND = "Printer halted"; + public static final String HOME_XY_COMMAND = "homeXYFirst"; + public static final String COMMUNICATION_COMMAND = "communicationFailure"; + public static final String DID_NOT_FIND_COMMAND = "didNotFind"; + public static final String IDLE_COMMAND = "idle"; private final Object source; private final int id; diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/PlotterControls.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/PlotterControls.java index 383883f86..663a5d599 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/PlotterControls.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plottercontrols/PlotterControls.java @@ -93,15 +93,29 @@ private void onMarlinEvent(MarlinPanelEvent e) { MarlinPanelEvent.DID_NOT_FIND, MarlinPanelEvent.COMMUNICATION_FAILURE -> { if (!isErrorAlreadyDisplayed) { - /* TODO source of dialog box titled "Error" that says "PlotterControls.null". Caused by - * robot being turned off while COM port is connected. + String message; + + switch(e.getActionCommand()) { + case "communicationFailure" -> message = Translator.get("PlotterControls.communicationFailure"); + case "didNotFind" -> message = Translator.get("PlotterControls.didNotFind"); + case "Printed halted" -> message = Translator.get("PlotterControls.halted"); + default -> message = e.getActionCommand(); + } + /* TODO Source of dialog box titled "Error" that says "PlotterControls.null". + * Caused by robot being turned off while COM port is connected. */ - JOptionPane.showMessageDialog(this, Translator.get("PlotterControls."+e.getActionCommand()), Translator.get("ErrorTitle"), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, + message, + Translator.get("ErrorTitle"), + JOptionPane.ERROR_MESSAGE); isErrorAlreadyDisplayed = true; } } case MarlinPanelEvent.HOME_XY_FIRST -> - JOptionPane.showMessageDialog(this, Translator.get("PlotterControls.homeXYFirst"), Translator.get("InfoTitle"), JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(this, + Translator.get("PlotterControls.homeXYFirst"), + Translator.get("InfoTitle"), + JOptionPane.WARNING_MESSAGE); } updateProgressBar(); } diff --git a/src/main/resources/languages/english.xml b/src/main/resources/languages/english.xml index f6461a9b2..b11a6b8b7 100644 --- a/src/main/resources/languages/english.xml +++ b/src/main/resources/languages/english.xml @@ -362,6 +362,9 @@ PlotterControls.JogTabRobot controls PlotterControls.AdvancedControlsAdvanced controls PlotterControls.homeXYFirstHome the printer first. + PlotterControls.didNotFindError in communicating with the robot. + PlotterControls.communicationFailureNo answer from the robot, please check the USB cable or speed settings. + PlotterControls.haltedPrinter halted! PlotterControls.StepStep RobotMenu.RobotStyleRobot style @@ -381,4 +384,9 @@ ConversationHistory.CopyCopy CartesianButtons.buttonCenter + + Converter_IntensityToHeight.nameIntensity to height + Converter_IntensityToHeight.spacingWave spacing + Converter_IntensityToHeight.maxHeightWave height + Converter_IntensityToHeight.sampleRateSample rate From 04e9776296131069f3e6c8653636d222d3bd5315 Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Sun, 3 Sep 2023 13:03:45 -0700 Subject: [PATCH 2/6] 7.41.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index def988c3b..04c383872 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.marginallyclever Makelangelo - 7.40.3 + 7.41.0 Makelangelo Makelangelo Software is a Java program that prepares art for CNC plotters. It is especially designed for the Makelangelo Robot. It pairs really well with Marlin-polargraph, the code in the brain of the robot that receives instructions and moves the motors. From 7644d459b1bc26c451a7d504cc8b78c2f1aba70c Mon Sep 17 00:00:00 2001 From: Mohammed Thaier <70210166+itsMohammedThaier@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:23:00 +0300 Subject: [PATCH 3/6] wave converter better artistic style Edited 'Converter_IntensityToHeight.java' Better Converter_IntensityToHeight implementation at capturing image details with less space calculations. previous version determines 'z_scale' based on the average color of a rect, my method determines it based on a horizontal line average color; Thus, I got rid of 'maxHeight' and added 'waveIntensity' --- .../Converter_IntensityToHeight.java | 30 +++++++++---------- src/main/resources/languages/english.xml | 26 ++++++++-------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java index 8cf0d6197..46a48874c 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java @@ -16,32 +16,32 @@ */ public class Converter_IntensityToHeight extends ImageConverter { private static int spacing = 2; - private static int maxHeight = 10; private static int sampleRate = 5; + private static int waveIntensity = 30; public Converter_IntensityToHeight() { super(); SelectSlider selectSize = new SelectSlider("size",Translator.get("Converter_IntensityToHeight.spacing"), 20,1,getSpacing()); - SelectSlider selectMaxHeight = new SelectSlider("maxHeight",Translator.get("Converter_IntensityToHeight.maxHeight"),20,1,getMaxHeight()); SelectSlider selectSampleRate = new SelectSlider("sampleRate",Translator.get("Converter_IntensityToHeight.sampleRate"),20,1,getSampleRate()); + SelectSlider selectWaveIntensity = new SelectSlider("waveIntensity",Translator.get("Converter_IntensityToHeight.waveIntensity"),150,-150,getWaveIntensity()); add(selectSize); - add(selectMaxHeight); add(selectSampleRate); + add(selectWaveIntensity); selectSize.addPropertyChangeListener(evt->{ setSpacing((int) evt.getNewValue()); fireRestart(); }); - selectMaxHeight.addPropertyChangeListener(evt->{ - setMaxHeight((int) evt.getNewValue()); - fireRestart(); - }); selectSampleRate.addPropertyChangeListener(evt->{ setSampleRate((int) evt.getNewValue()); fireRestart(); }); + selectWaveIntensity.addPropertyChangeListener(evt->{ + setWaveIntensity((int) evt.getNewValue()); + fireRestart(); + }); } @Override @@ -63,11 +63,11 @@ public void setSampleRate(int value) { sampleRate = Math.max(1,value); } - public int getMaxHeight() { - return maxHeight; + public int getWaveIntensity(){ + return waveIntensity; } - public void setMaxHeight(int value) { - maxHeight = Math.max(1,value); + public void setWaveIntensity(int value){ + waveIntensity = value; } protected void convertLine(TransformedImage img, double sampleSpacing, double halfStep, Point2D a, Point2D b) { @@ -83,10 +83,10 @@ protected void convertLine(TransformedImage img, double sampleSpacing, double ha // read a block of the image and find the average intensity in this block double z = img.sample( x - sampleSpacing, y - halfStep, x + sampleSpacing, y + halfStep); // scale the intensity value - double scale_z = z / 255.0f; + double scale_z = 1-z / 255.0f; //scale_z *= scale_z; // quadratic curve - double pulseSize = halfStep * scale_z; - double py=y + pulseSize; + double pulseSize = waveIntensity * scale_z; + double py=y + pulseSize - waveIntensity; if(first) { turtle.jumpTo(x, py); first = false; @@ -127,7 +127,7 @@ public void start(Paper paper, TransformedImage image) { a.set(xRight,y); b.set(xLeft,y); } - convertLine(img,sampleRate,maxHeight,a,b); + convertLine(img,sampleRate,0,a,b); } fireConversionFinished(); diff --git a/src/main/resources/languages/english.xml b/src/main/resources/languages/english.xml index b11a6b8b7..94baf863b 100644 --- a/src/main/resources/languages/english.xml +++ b/src/main/resources/languages/english.xml @@ -2,21 +2,21 @@ - - - English - Dan Royer (dan@marginallyclever.com) - - - BorderNameOutline paper + + English + Dan Royer (dan@marginallyclever.com) + + + BorderNameOutline paper Generator_TruchetTiles.NameTruchet Tiles Generator_TruchetTiles.LineSpacingSpace between lines (mm) Generator_TruchetTiles.LinesPerTileLines per tile @@ -261,9 +261,9 @@ TurtleGenerators.LearnMore.Link.TextLearn more LoadScratch3.foreverNotAllowedForever loops are forever forbidden. - SaveGCode.splitGCodeTitleMany colors detected + SaveGCode.splitGCodeTitleMany colors detected - Converter_CMYK_Circles.nameCMYK circles + Converter_CMYK_Circles.nameCMYK circles Converter_CMYK_Circles.maxCircleSizemax circle size Converter_EdgeDetection.nameEdge detection @@ -387,6 +387,6 @@ Converter_IntensityToHeight.nameIntensity to height Converter_IntensityToHeight.spacingWave spacing - Converter_IntensityToHeight.maxHeightWave height Converter_IntensityToHeight.sampleRateSample rate - + Converter_IntensityToHeight.waveIntensityWave intensity + \ No newline at end of file From 94d850bf0efd66a558e292f70351931090ebf6a4 Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Mon, 4 Sep 2023 17:22:31 -0700 Subject: [PATCH 4/6] lowered maximum wave intensity, centered wave on original line, added comments to explain variables. --- .../imageconverter/Converter_IntensityToHeight.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java index 46a48874c..5124e03b4 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java @@ -15,8 +15,11 @@ * @since 7.40.3 */ public class Converter_IntensityToHeight extends ImageConverter { + // vertical distance between lines private static int spacing = 2; + // horizontal distance between samples. more samples = more detail. private static int sampleRate = 5; + // max height of the wave will be +/-(waveIntensity/2) private static int waveIntensity = 30; public Converter_IntensityToHeight() { @@ -24,7 +27,7 @@ public Converter_IntensityToHeight() { SelectSlider selectSize = new SelectSlider("size",Translator.get("Converter_IntensityToHeight.spacing"), 20,1,getSpacing()); SelectSlider selectSampleRate = new SelectSlider("sampleRate",Translator.get("Converter_IntensityToHeight.sampleRate"),20,1,getSampleRate()); - SelectSlider selectWaveIntensity = new SelectSlider("waveIntensity",Translator.get("Converter_IntensityToHeight.waveIntensity"),150,-150,getWaveIntensity()); + SelectSlider selectWaveIntensity = new SelectSlider("waveIntensity",Translator.get("Converter_IntensityToHeight.waveIntensity"),50,-50,getWaveIntensity()); add(selectSize); add(selectSampleRate); @@ -83,10 +86,10 @@ protected void convertLine(TransformedImage img, double sampleSpacing, double ha // read a block of the image and find the average intensity in this block double z = img.sample( x - sampleSpacing, y - halfStep, x + sampleSpacing, y + halfStep); // scale the intensity value - double scale_z = 1-z / 255.0f; + double scale_z = 1 - z / 255.0f; //scale_z *= scale_z; // quadratic curve double pulseSize = waveIntensity * scale_z; - double py=y + pulseSize - waveIntensity; + double py=y + pulseSize - waveIntensity/2.0f; if(first) { turtle.jumpTo(x, py); first = false; From 8377b35d0524fed45b46aaa394b8d999a3e8beb8 Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Mon, 4 Sep 2023 17:49:18 -0700 Subject: [PATCH 5/6] fix conflicts --- .../makeart/imageconverter/Converter_IntensityToHeight.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java index 4fb3ecc1f..cc8b615ba 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_IntensityToHeight.java @@ -130,7 +130,7 @@ public void start(Paper paper, TransformedImage image) { a.set(xRight,y); b.set(xLeft,y); } - convertLine(img,sampleRate,sampleSpacing/2.0,a,b); + convertLine(img,sampleRate,sampleRate/2.0,a,b); } fireConversionFinished(); From aac659b055ec797ad0b872de0c51d01cf87c7994 Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Mon, 4 Sep 2023 17:50:24 -0700 Subject: [PATCH 6/6] 7.41.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04c383872..a070e73c4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.marginallyclever Makelangelo - 7.41.0 + 7.41.3 Makelangelo Makelangelo Software is a Java program that prepares art for CNC plotters. It is especially designed for the Makelangelo Robot. It pairs really well with Marlin-polargraph, the code in the brain of the robot that receives instructions and moves the motors.