Close

Test 2

A project log for A Better Turret

Need a turret but the not impressed by the servo type. Perhaps a stepper motor version may be better.

agpcooperagp.cooper 12/14/2017 at 01:553 Comments

Update to Code

Spent the morning checking the code to determine why the X axis motor controller smoked. I don't know! The code seems to be doing what it should.

The code now scans a circles. The circle is pretty rough:

Set the driver to 1/8 micro-steps:

It has taken me a while to determine that what I am seeing is overshoot:

The overshoot is in the order of half a full step. Unfortunately there is not much I can do about it.

Here is the current code:

/*
  Simple 3 Axis Motion Controller
  ===============================
  Written by Alan Cooper (agp.cooper@gmail.com)
  This work is licensed under the Creative Commons Attribution-NonCommercial 2.5 License.
  This means you are free to copy and share the code (but not to sell it).
*/
#include <math.h> // For PI, radians(), sin() and cos()

// Motor controller (CNC board) pin mapping: 
#define DirX     2
#define DirY     3
#define DirZ     4
#define StepX    5
#define StepY    6
#define StepZ    7
#define Enable   8  // Active low
#define Laser   12  // Turn laser on or off

// Motor Controller direction settings
bool xReverse=false;
bool yReverse=true;
bool zReverse=false;

// Movement control parameters
long stepCountDown;              // Movement steps remaining
unsigned long movementRate;      // Steps per second
unsigned long movementInterval;  // Step period (uS)
unsigned long movementRampIncr;  // Step period (uS)
long xNew,yNew,zNew;             // New co-ordinates
long xCurrent,yCurrent,zCurrent; // Current co-ordinates

void motionControl(void)
{  
  static unsigned long movementPreviousMicros=0;
  static unsigned long movementCurrentMicros=0;
  static unsigned long movementRamp;
  static long stepCountUp=0; 
  static long stepX,stepY,stepZ;
  static long dx,dy,dz,ax,ay,az,sx,sy,sz,mx,my,mz;

  // Process motion command at end of current movement
  if (stepCountDown==-1) {
    stepCountDown=0;
    stepCountUp=0;

    // Determine movement parameters
    dx=xNew-xCurrent;
    dy=yNew-yCurrent;
    dz=zNew-zCurrent;
    ax=abs(dx);
    ay=abs(dy);
    az=abs(dz);
    sx=xNew<xCurrent?-1:xNew>xCurrent?1:0;
    sy=yNew<yCurrent?-1:yNew>yCurrent?1:0;
    sz=zNew<zCurrent?-1:zNew>zCurrent?1:0;
    if ((ax>=ay)&&(ax>=az)) {
      mx=0;
      my=ay-(ax>>1);
      mz=az-(ax>>1);
      stepCountDown=ax;
    } else if ((ay>=ax)&&(ay>=az)) {
      mx=ax-(ay>>1);
      my=0;
      mz=az-(ay>>1);
      stepCountDown=ay;
    } else {
      mx=ax-(az>>1);
      my=ay-(az>>1);
      mz=0;
      stepCountDown=az;
    }

    // Set counters
    if (stepCountDown>0) {
      stepCountUp=1;
    } else {
      stepCountDown=-1;
      stepCountUp=-1;        
    }

    // Set the stepper directions
    if (xReverse) {
      digitalWrite(DirX,(1-sx)>>1);
    } else {
      digitalWrite(DirX,(sx+1)>>1);
    } 
    if (yReverse) {
      digitalWrite(DirY,(1-sy)>>1);
    } else {
      digitalWrite(DirY,(sy+1)>>1);
    }
    if (zReverse) {
      digitalWrite(DirZ,(1-sz)>>1);
    } else {
      digitalWrite(DirZ,(sz+1)>>1);
    }
  }

  // Advance Steppers 
  if (stepCountDown>0) {
    movementRamp=movementInterval;
    if ((stepCountUp==1)||(stepCountDown==1)) movementRamp+=movementRampIncr;
    
    movementCurrentMicros=micros();    
    if (movementCurrentMicros-movementPreviousMicros>movementRamp) {
      movementPreviousMicros=movementCurrentMicros;
            
      // Advance steppers
      stepX=0;
      stepY=0;
      stepZ=0;
      if ((ax>=ay)&&(ax>=az)) {
        if (my>=0) {
          my-=ax;
          stepY=sy;
        }
        if (mz>=0) {
          mz-=ax;
          stepZ=sz;
        }
        my+=ay;
        mz+=az;
        stepX=sx;
      } else if ((ay>=ax)&&(ay>=az)) {
        if (mx>=0) {
          mx-=ay;
          stepX=sx;
        }
        if (mz>=0) {
          mz-=ay;
          stepZ=sz;
        }
        mx+=ax;
        mz+=az;
        stepY=sy;
      } else {
        if (mx>=0) {
          mx-=az;
          stepX=sx;
        }
        if (my>=0) {
          my-=az;
          stepY=sy;
        }
        mx+=ax;
        my+=ay;
        stepZ=sz;
      }
      xCurrent+=stepX;
      yCurrent+=stepY;
      zCurrent+=stepZ;

      // Step pulse (high for at least 10 us)
      digitalWrite(StepX,abs(stepX));
      digitalWrite(StepY,abs(stepY));
      digitalWrite(StepZ,abs(stepZ));
      delayMicroseconds(10);
      digitalWrite(StepX,LOW);
      digitalWrite(StepY,LOW);
      digitalWrite(StepZ,LOW);
      stepCountDown--;
      stepCountUp++;

      // Flag end of movement 
      if (stepCountDown==0) {
        stepCountDown=-1;
        stepCountUp=-1;
      }
      
    }
    
  } else {
    movementPreviousMicros=micros(); // Reset movement rate clock to ensure near full cycle on start
  }
}

int iSin[360];
int iCos[360];

void setup()
{
  // Initialise Motor Controller
  // Note: The motor controller is using 1/16 micro-stepping
  pinMode(DirX,OUTPUT);
  pinMode(StepX,OUTPUT);
  pinMode(DirY,OUTPUT);
  pinMode(StepY,OUTPUT);
  pinMode(DirZ,OUTPUT);
  pinMode(StepZ,OUTPUT);
  pinMode(Enable,OUTPUT);
  pinMode(Laser,OUTPUT);
  digitalWrite(DirX,LOW);
  digitalWrite(StepX,LOW);
  digitalWrite(DirY,LOW);
  digitalWrite(StepY,LOW);
  digitalWrite(DirZ,LOW);
  digitalWrite(StepZ,LOW);
  digitalWrite(Enable,LOW); // Active low
  digitalWrite(Laser,LOW);  // Active high
  
  // Set Global Variables
  xNew=0;
  yNew=0;
  zNew=0;
  xCurrent=0;
  yCurrent=0;
  zCurrent=0;
  stepCountDown=-1;                      // Number of steps remaining in motion
  movementRate=2000;                     // Step per second
  movementInterval=1000000/movementRate; // Micro-seconds
  movementRampIncr=500;                  // Micro-seconds
  // LED
  pinMode(LED_BUILTIN,OUTPUT);
  digitalWrite(LED_BUILTIN,LOW);
  
  // Prepare circle array
  for (int i=0;i<360;i++) {
    iSin[i]=(int)(200*sin(radians(i)));
    iCos[i]=(int)(200*cos(radians(i)));   
  }
}

void loop()
{
  // LED timer
  static unsigned long intervalMillisLED=0;
  static unsigned long currentMillisLED=0;
  static unsigned long previousMillisLED=0;

  // Use LED to indicate working
  if (stepCountDown==0) {
    intervalMillisLED=1000;
  } else {
    intervalMillisLED=100;    
  }
  currentMillisLED=millis();
  if (currentMillisLED-previousMillisLED>intervalMillisLED) {
    previousMillisLED=currentMillisLED;   
    digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
  }

  /* Motion Command */
  static int angle=-1;
  // Make a circle
  if (stepCountDown==-1) {
    if (angle==-1) {
      digitalWrite(Laser,LOW);
      angle=0;
      xNew=iCos[angle];
      yNew=iSin[angle];
      zNew=0;
    } else {
      digitalWrite(Laser,HIGH);
      angle+=15;
      if (angle>=360) angle=0;
      xNew=iCos[angle];
      yNew=iSin[angle];
      zNew=0;
    }  
  }

  // Process Motion Command
  motionControl();
}

Well that just about wraps it up.


AlanX

Discussions

Bharbour wrote 04/04/2019 at 13:10 point

A trapazoidal velocity profile will help too.

  Are you sure? yes | no

agp.cooper wrote 04/04/2019 at 10:34 point

You are correct with regard to the problem which is oscillation like a spring but more mass will slow the frequency but not the magnitude.

The answer is to dampen (again you are correct) the oscillation by adding friction, say a "spring loaded disk to disk brake" between the stepper and the mounting hub.

So it is a mechanical issue. Having said that a position sensor and PID will work also.

I have the same problem with my direct drive SCARA, but in this case I can solve the problem by using belt drive gearing. The belt and gearing adds some friction to the system.

This project gives an example of a PID solution:

https://hackaday.io/project/164193-as5600-stepper-motor-encoder

AlanX

  Are you sure? yes | no

Daren Schwenke wrote 04/04/2019 at 03:47 point

I know I'm like 1.5 years late here, but how about just adding more mass to dampen your overshoot?

  Are you sure? yes | no