 # Polar cnc machine with two stepper motors

Hello, I am currently working on a project for my university and we are building a kinetic art table. It uses a Arduino l293D motor shield. We downloaded someones code online but had to modify it since our system is build so that the motors have to move with polar coordinates. It is essentially supposed to read G-code and then make the motors move accordingly but in a polar system. The motors doesnt seem to move but one of them is constantly making a noise as it is running. does anyone know what the issue might be in the code? The original code was using x and y movement and it works just fine. This is the website we found the code from. It requires a couple of programs to run but everything is listed here: How to make Arduino mini CNC plotter machine - Electric Diy Lab

``````#include <AFMotor.h>

#define LINE_BUFFER_LENGTH 512

char steg = MICROSTEP;

const float pi = 3.141592;

// stepper motor stepsperrevolution, change this later.
const int stepsPerRevolution = 48;

// Initialize stepper motors for X- and Y-axis on Arduino using L293D H-bridge.
AF_Stepper motortheta(stepsPerRevolution,1);
AF_Stepper motorrho(stepsPerRevolution,2);

// Structures global variables, these are for coordinates.
struct point {
float x;
float y;
};

// Current position of magnetic ball.
struct point ballPos;

// copy pasta
//  Drawing settings, should be OK
float StepInc = 1;
int StepDelay = 1;
int LineDelay =0;

// Motor steps to go 1 millimeter.
// Use test sketch to go 100 steps. Measure the length of line.
// Calculate steps per mm. Enter here.

float StepsPerMillimeterRho = 100.0;
float StepsPerRadiansTheta = 0.9*pi/180;  // converted to radians for easier change later.

// Drawing robot limits, in mm and degrees
float rhomin = 0;
float rhomax = 180;
float thetamin = 0;
float thetamax = 2*pi;

float rhopos = rhomin;
float thetapos = thetamin;

//  Needs to interpret
//  G1 for moving
//  Discard anything with a (

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

// Sets speed for the stepper motors.
motorrho.setSpeed(100);
motortheta.setSpeed(100);

// Set & move to initial default position
// TBD

// See if it's running properly
Serial.println("Kinetic sand table has started!");
}

void loop() {
// put your main code here, to run repeatedly:

delay(100);
char line[LINE_BUFFER_LENGTH];  // creates an array that can store 512 chars
char var; // creates check variable.
int lineI;  // creates line index for later use.
bool lineIsComment, lineSemiColon;  //creates bools for cooments and semicolons for line. used later to remove inputs.

lineI = 0;  // index for line. ex is i in foor loops.
lineSemiColon = false;
lineIsComment = false;

while(1){
// Serial reception is mostly from Grbl, added semicolon support
while(Serial.available()>0){
if(( var == '\n') || (( var == '\r'))){ // end of line is reached
if(lineI > 0){  // if line is complete then execute the following.
line[lineI] = '\0'; //sets it to null character
processIncomeingLine(line, lineI);
lineI = 0;
}
lineIsComment = false;
lineSemiColon = false;
Serial.println("okey");
}

else{
if((lineIsComment) || (lineSemiColon)){ //used to delete all comments characters.
if(var == ')'){
lineIsComment = false;  // if end of comment is reach resume line.
}
}
else{
if(var <= ' '){ // delete empty space.
}
else if(var == '/'){  // ignore character
lineIsComment = true;
}
else if(var == '('){  // ignore character
lineIsComment = true;
}
else if(var == ';'){
lineSemiColon = true;
}
else if(lineI >= LINE_BUFFER_LENGTH-1){
Serial.println("ERROR linebuffer overflow");;
lineIsComment = false;
lineSemiColon = false;
}
else if(var >= 'a' && var <= 'z'){  //Upcase lowercase
line[lineI++] = var-'a'+'A';
}
else{
line[ lineI++ ] = var;
}
}
}
}
}
}

void processIncomeingLine(char* line, int charNB){
int currentI = 0;
char buffer; // 64 for 1 parameter. the buffer to store the bytes in
struct point newPos;

newPos.x = 0.0;
newPos.y = 0.0;

//  Needs to interpret
//  G1 for moving
//  G1 X60 Y30
//  G1 X30 Y50
//  Discard anything with a (

while(currentI < charNB){
switch(line[currentI++]){
case 'U':
break;
case 'D':
break;
case 'G':
buffer = line[currentI++]; // /!\ Dirty - Only works with 2 digit commands
//      buffer = line[currentI++];
//      buffer = '\0':
buffer = '\0';

switch(atoi(buffer)){                 // Select G command // atoi takes a str and converts it to int
case 0:                             // G00 & G01 - Movement or fast movement. Same here
case 1:
// /!\ Dirty - Suppose that X is before Y
char* indexX = strchr(line+currentI, 'X'); // Get X/Y position in the string (if any)
char* indexY = strchr(line+currentI, 'Y');
if(indexY <= 0){
newPos.x = atof(indexX+1);
newPos.y = ballPos.y;
}
else if(indexX <= 0){
newPos.y = atof(indexX +1);
newPos.x = ballPos.x;
}
else{
newPos.y = atof(indexY+1);
//free(indexY);
*indexY = '\0';
newPos.x = atof(indexX +1);
}
drawLine(newPos.x, newPos.y);
//  Serial.println("ok");
ballPos.x = newPos.x;
ballPos.y = newPos.y;
break;
}
break;
case 'M':
break;
}
}
}

//Draw a line from (x0;y0) to (x1;y1)
//(x0,y0) Starting
//(x1,y1) Ending
void drawLine(float x1, float y1) {

float rho1 = rho(x1, y1);
float theta1 = theta(x1,y1);

// max range for instructions
if (rho1 >= rhomax){
rho1 = rhomax;
}
if (rho1 <= rhomin){
rho1 = rhomin;
}
if(theta1 >= thetamax){
theta1 = theta1 - 2*pi;
}
if(theta1 <= thetamin){
theta1 = 2*pi-theta1;
}

//  Convert coordinates to steps
rho1 = (int)(rho1*StepsPerMillimeterRho);
float rho0 = rhopos;
float theta0 = thetapos;

//  Let's find out the change for the coordinates
long drho = abs(rho1-rho0);
long dtheta = abs(theta1-theta0);  // /!\ could maybe not needd abs here

// one lined if statement, StepInc becomes negative if otherwise.
int srho = rho0<rho1 ? StepInc : -StepInc;
int stheta = theta0<theta1 ? StepInc : -StepInc;
//Serial.println(dx, dy);

long i;
long over = 0;

float drho_norm = drho/StepsPerMillimeterRho;
// Serial.println(drho_norm);
if (drho_norm > dtheta_norm) {
for (i=0; i<drho_norm; ++i) {
motorrho.onestep(srho,steg);
over+=dtheta_norm;
if (over>=drho_norm) {
over-=drho_norm;
motortheta.onestep(stheta,steg);
}
delay(StepDelay);
}
}
else {
for (i=0; i<dtheta_norm; ++i) {
motortheta.onestep(stheta,steg);
over+=drho_norm;
if (over>=dtheta_norm) {
over-=dtheta_norm;
motorrho.onestep(srho,steg);
}
delay(StepDelay);
}
}
//  Delay before any next lines are submitted
delay(LineDelay);
//  Update the positions
rhopos = rho1;
thetapos = theta1;
}

// converts xy coordinates to polar coordinates.
float rho(float x, float y){
float r = 0.0;
r = sqrt(x * x + y * y);
return r;
}

float theta(float x, float y){
float t = 0.0;
if(x == 0){
if(y < 0){
t = pi/2;
}
else if(y > 0){
t = 3/2*pi;
}
else if(y == 0){
t = 0;
}
}
else{
t = atan(y / x);
}
return t;
}``````

I would guess it is in the hardware design, and if you had posted a schematic, not a frizzy thing including all power, ground and interconnections we could validate that. I got this motor that goes this way and that way and I am to guess what motor you are using. Post links to technical information on all the hardware items. As a hint you might also let us know which Arduino you are using? I would also suggest you find a different shield, the L293D is a very poor choice.

The rectangular to polar coordinate transform is not linear, as it involves arctangents and square-roots, so you wont be able to convert GCODE lines and circles to polar coordinates without compiling them down to lots of very short segments (to approximate the non-linear curves).

Why you have a noise I don’t know, but perhaps the approach used is numerically unstable and is oscillating somewhat?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.