Electronic – How to make more precise a CNC

craspberry pistepper motor

I have a problem with precision for my CNC machine, when I try to draw a diagonal line on paper.

I use 3 unipolar stepper motors (RS 440-442) for 3 axis (x,y,z.) These 3 motors are controlled separately with 3 H-bridges (LN298N.) I use a PC power supply for them.

To control these 3 motors, I use a Raspberry Pi 2 and a C++ program. Here is the code https://bitbucket.org/crish4cks/steppermotor/src/master/src/StepperMotor.cpp

The problem: I want to improve the precision of the draw and I don't know if the problem is a sequential software problem like for example:

x.run(RIGHT, 1);   // turn the motor to 1 step in RIGHT side
y.run(RIGHT, 1);   // turn the Y motor to 1 step in RIGHT side

Should I use parallel programming? I also don't know how to change the value stepAngle for my 3 RS 440-442. (I changed 2D array of sequence don't worry.)

Documentation of the RS 440-442 (ttps://docs.rs-online.com/e325/0900766b81579a91.pdf)

I'm not a Electrical Engineering student but a Computer Science student, and I do not have a good
understanding of stepper motors. Can you help me?

the draw of the CNC of a diagonal line

#include <cassert>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <wiringPi.h>
#include "StepperMotor.hpp"

using namespace std;

// Switching sequence for the 28BYJ48 (clockwise)
static const unsigned SEQUENCE[8][4] = { {1, 0, 0, 0},
                                         {1, 1, 0, 0},
                                         {0, 1, 0, 0},
                                         {0, 1, 1, 0},
                                         {0, 0, 1, 0},
                                         {0, 0, 1, 1},
                                         {0, 0, 0, 1},
                                         {1, 0, 0, 1} };

// stepAngle = (Step angle / gear reduction ratio) = (5.625 / 63.68395)
static const float stepAngle = 0.0883268076179f;

// Default constructor
StepperMotor::StepperMotor() {
    running = false;
    threshold = 0;
    current_pos = 0;
    sequence.resize(8);
    for(unsigned i = 0; i < sequence.size(); i++)
        sequence.at(i).resize(4);
    for(unsigned i = 0; i < sequence.size(); i++)
        for(unsigned j = 0; j < sequence.at(i).size(); j++)
            sequence.at(i).at(j) = SEQUENCE[i][j];
}

// Returns the number of steps associated to a certain angle
unsigned StepperMotor::getSteps(unsigned angle) const {
    return (unsigned) roundf(angle / stepAngle);
}

// Sets the GPIO outputs needed by inputs of the stepper motor driver (ULN2003APG)
// For more details concerning the wiringPi GPIO table conversion refere here:
// http://wiringpi.com/pins/
void StepperMotor::setGPIOutputs(unsigned in1, unsigned in2, unsigned in3, unsigned in4) {
    this->in1 = in1;
    pinMode(in1, OUTPUT);
    this->in2 = in2;
    pinMode(in2, OUTPUT);
    this->in3 = in3;
    pinMode(in3, OUTPUT);
    this->in4 = in4;
    pinMode(in4, OUTPUT);
}

// Sets a maximum threshold angle for the motor rotation
void StepperMotor::setThreshold(unsigned threshold) {
    assert(threshold < 180);
    this->threshold = threshold;
}

// Runs the stepper motor.
// * direction: 1 to go clockwise, -1 to go counterclockwise
// * angle: can assume values from 0 to 360 degrees
// * speed: from 20% (minimum speed) to 100% (maximum speed)
void StepperMotor::run(int direction, unsigned angle, unsigned speed) {
    float td;
    unsigned nsteps, count, ndegrees;

    running = true;

    // Check the direction and angle values
    assert(direction == 1 || direction == -1);
    assert(angle <= 360);

    // Check the speed value (5 speed modes allowed, from 20% to 100%)
    switch(speed) {
        case(20): break;
        case(40): break;
        case(60): break;
        case(80): break;
        case(100): break;
        default: return;
    }

    // Delay between each step of the switching sequence (in microseconds)
    td = (5 * 100 / (float) speed) * 1000;

    // Set the right number of steps to do, taking in account of the threshold
    if(abs(current_pos + direction * angle) > threshold && threshold != 0)
        ndegrees = threshold - direction * current_pos;
    else ndegrees = angle;

    nsteps = getSteps(ndegrees);

    // To go counterclockwise we need to reverse the switching sequence
    if(direction == -1)
        reverse(sequence.begin(), sequence.end());

    count = 0;
    for(unsigned i = 0; i < nsteps; i++) {
        if(count == 8)
            count = 0;

        if(sequence.at(count).at(0) == 1)
            digitalWrite(in1, HIGH);
        else digitalWrite(in1, LOW);

        if(sequence.at(count).at(1) == 1)
            digitalWrite(in2, HIGH);
        else digitalWrite(in2, LOW);

        if(sequence.at(count).at(2) == 1)
            digitalWrite(in3, HIGH);
        else digitalWrite(in3, LOW);

        if(sequence.at(count).at(3) == 1)
            digitalWrite(in4, HIGH);
        else digitalWrite(in4, LOW);

        count++;
        delayMicroseconds(td); // minimum delay 5ms (speed 100%), maximum delay 25ms (speed 20%)
    }

    // Cleanup (recommended in order to prevent stepper motor overheating)
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);

    // Reverse again in order to restore the original vector for the next operations
    if(direction == -1)
        reverse(sequence.begin(), sequence.end());

    // Update the state
    this->nsteps += nsteps;
    current_pos += direction * ndegrees;
    running = false;
}

Best Answer

I want to improve the precision of the draw and I don't know if the problem is a sequential software problem

I suspect you are currently limited by the absolute resolution of one step (1.8 degree). Parallel programming can't fix this. It will however be required later on when you need to calculate a path in advanced from a defined source to follow at a given speed. You won't be able to to that sequentially like you've written it now. (You'd get calculate-go-stop-calculate-go-stop... with code like this)

However, that is not where you're at yet. Right now you need to investigate microstepping. With the right hardware you can multiply your 200 steps to get a higher accuracy and thus a smoother line. At the costs of torque.

You haven't thought about holding torque yet, since you do this:

// Cleanup (recommended in order to prevent stepper motor overheating)
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW); 

Which means you always round to one full 1.8 degree step. That's your first bottleneck now.

Microstepping example: enter image description here

I don't know how feasable microstepping is with your current L298N driver, maybe someone else can comment about that.

Related Topic