[Solved] Torque estimation of stepper motor by current measurement

Hi everybody!

[Application]
I am trying to upgrade my paste extruder from Cerambot which consists of two stepper motors. The first motor drives a screw extruder which extrudes the paste from a nozzle in a controlled fashion. The second motor drives a plunger which supplies the paste to the screw extruder from a reservoir. If the set up is unclear or you want to see it in action you can click this link.

The problem I encounter, is that when the plunger motor rotates too fast (and hence supplies too much paste to the screw extruder), the pressure inside the screw extruder builds up and causes the (screw) motor to stall. I am conducting experiments with a lot of different pastes and don’t want to manually calibrate the rotation speeds for every paste individually. Therefore I wondered if I could measure the torque the screw motor has to deliver so I can program the arduino to switch the plunger motor off before the pressure gets to high.

[Question]
How can I measure the torque a stepper motor supplies without changing the setup (see link above) too much, since no torque sensor can fit inside the screw casing?

[My idea]
I was thinking of using ACS712 current sensor to measure how much current the (screw) motor draws and to link this to the amount of torque it supplies. However I am confused how the torque-current-speed relation works for stepper motors. Does anybody knows if and how this would work?

[Additional info]
NEMA 17 42BYGHM207 (screw)
NEMA 23 57HD4016-01 (plunger)
DRV8825 stepper driver
ACS712 20A current sensor
Arduino Uno
Operation speeds of both motors are very low (~3-10 rpm)

Hope that this is somewhat clear :sweat_smile:

Thank you in advance!
RSand

I see problems in trying to relate the current pulses powering a stepper motor to the pulsing torque of the motor. Basically, the torque is the same until the motor begins to miss steps.

Perhaps if you used a plunger motor that had less torque it would begin to skip steps when the reaction force was at the point of stalling the screw stepper. Then later, the plunger motor would have room to move in more material.

Back before the 2008-9 depression, I had a customer with a similar product that produced ready-to-apply drywall corner paper strips and used several steppers. I don't recall all the details of the machine, but I still had his test unit when I closed my company in 2019. It used 4.x volt stepper motors with a 42 volt supply to their driver circuits. What voltage are you running your steppers at?
Paul

It is extremely difficult, and probably impossible with consumer grade sensors, to estimate the torque or detect stall in a stepper motor by monitoring the motor current. They do not act at all like brushed DC motors. Furthermore the stepper driver modifies the motor current dynamically, if properly configured.

You need to pursue some other approach to solve the problem.

I don't want to manually calibrate the rotation speeds for every paste individually.

That will probably be faster, easier and more accurate in the long run.

The only way I can think of is to use a small load cell. This has been done before with an Arduino. Check this link:

Hi guys,

Thanks for the replies!
Sorry, I was absent for a while but I actually got a bad case of Corona. Now that I am recovered, I'll keep working on this project.

Paul_KD7HB:
What voltage are you running your steppers at?

@Paul they are both running at 12V
Currently, I am looking at the DRV8889-Q1 which has integrated stall detection. The idea being that when the screw motor stalls, this is detected by the driver. Then I would reverse the plunger motor direction for a few rotations to decrease the pressure until the screw motor no longer stalls.

It's not perfect in the sense that the motor will still stall and this might effect the paste extrusion, but it's at least somewhat automated :slight_smile:

If you actually get that to work, please update your post with the details.

TI sells the evaluation board for $75

Hi everyone,

I took a while but i got this project to work! After some looking around and trying different drivers i stumbled upon the TMC2209 stepper motor drivers which are simply amazing. They pocess a stall detection setting based on the phase angle of motor current which gives you a value for how much load there is on the motor. They are great budget options for those who seek torque control!
Furthermore, teemalut has made an amazing library to use all TMC steppers to their full potential.

Anyway, here is my code so anybody who is googling this topic has an idea of what i did. The code is very ugly but cut me some slack since this is my first arduino project :slightly_smiling_face:

#include <TMCStepper_UTILITY.h>
#include <TMCStepper.h>
#include <SPI.h>

#define BAUDRATE 115200

//Plunger motor TMC2130
#define EN_PIN_PLUNGER      7   // Enable
#define DIR_PIN_PLUNGER     8   // Direction
#define STEP_PIN_PLUNGER    9   // Step
#define CS_PIN_PLUNGER      10  // Chip select (for SPI communication)

//Screw motor TMC2209
#define SW_RX            2  // SoftwareSerial receive pin
#define SW_TX            3  // SoftwareSerial transmit pin
#define EN_PIN_SCREW     4  // Enable
#define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2

#define R_SENSE 0.11f // Match to your driver
                      // SilentStepStick series use 0.11


const double microstep = 16;
const double fullStepsPerRotation = 200;
bool plungerDisabled = false;
double plungerSpeed;

//Sample smoothening
const int sampleLength = 20;
bool readings[sampleLength];
int index = 0;
double stallTotal =   0;
double stallValue =   0;
bool isStalled =      0;
bool wasStalled =     0;

TMC2130Stepper plunger(CS_PIN_PLUNGER, R_SENSE);
TMC2209Stepper screw(SW_RX, SW_TX, R_SENSE, DRIVER_ADDRESS);

using namespace TMC2209_n;

void setup() {
  Serial.begin(BAUDRATE);         // Serial connection with PC
  while (!Serial) {
        ; // wait for connection
     }  

  //Plunger Pins
  pinMode(EN_PIN_PLUNGER,   OUTPUT);
  pinMode(DIR_PIN_PLUNGER,  OUTPUT);
  pinMode(STEP_PIN_PLUNGER, OUTPUT);
  pinMode(CS_PIN_PLUNGER,   OUTPUT);
  digitalWrite(EN_PIN_PLUNGER, LOW);      

  SPI.begin();
  plunger.begin();
  
  plunger.toff(5);
  plunger.rms_current(600); 
  plunger.microsteps(16);
  plunger.en_pwm_mode(true);
  plunger.pwm_autoscale(true); 
  Serial.println(plunger.version(),BIN);

  //Screw Pins
  pinMode(EN_PIN_SCREW,     OUTPUT);
  digitalWrite(EN_PIN_SCREW,   LOW);                            

  screw.beginSerial(115200);      // Serial connection via SW UART pins
  screw.begin();                  // UART: Init SW UART (if selected) with default 115200 baudrate
   
  screw.toff(5);                  // Enables driver in software
  screw.rms_current(600);         // Set motor RMS current
  screw.microsteps(16);           // Set microsteps to 1/16th                               
  //screw.en_spreadCycle(false);  // Toggle spreadCycle on TMC2209
  screw.pwm_autoscale(true);     // Needed for stealthChop
  screw.TCOOLTHRS(0xFFFFF);
  screw.SGTHRS(50);
  screw.VACTUAL(5000);

  cli();
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  // 256 microsteps 
  //      -> Prescale 1:  3744  = 5 rpm
  //      -> Prescale 8:  468   = 5 rpm
  //      -> Prescale 64: 58    = 5 rpm
  // 16 microsteps 
  //      -> Prescale 1:  60000 = 5 rpm
  //      -> Prescale 8:  7500  = 5 rpm
  //      -> Prescale 64: 937   = 5 rpm
  OCR1A = 5000;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1 prescaler
  TCCR1B |= (1 << CS10);  //(1 << CS12) | 
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei();

  Serial.setTimeout(10);
  Serial.readString();   
  Serial.println(screw.SGTHRS());
  
  Serial.println(screw.SGTHRS());
  
  Serial.println("################");
  
}

ISR(TIMER1_COMPA_vect){    // Every time the counter reaches OCR1A this interupt is trown
  PORTB |=  (1 << 1);      // One step pulse is sent
  PORTB &= ~(1 << 1);
}

void loop() {
  static uint32_t last_time = 0;
  uint32_t ms = millis();

  if((ms - last_time) > 50){
    stallTotal -=  readings[index];   // throw out last reading
    readings[index] = screw.diag();   // update reading
    stallTotal +=  readings[index];   // add new reading
    index++;
    if(index == sampleLength) {
      index = 0;
    }
    stallValue = stallTotal/sampleLength;   
    Serial.print(screw.diag());
    Serial.print("       ");
    Serial.print(stallValue);
    Serial.print("       ");
    isStalled = stallValue > 0.05;
    Serial.print(isStalled);
    Serial.println();
    if(isStalled && !wasStalled){   // Stall detected for first time
      stallSequence();
    }
    if(!isStalled && wasStalled){   // Motor is no longer stalled
      normalOperation();
    }
    Serial.println(screw.SG_RESULT());
  }
  
}

void serialEvent() {
  // write x:y, where x is the motor number (1 or 2) and y is the rpm
  String cmd = Serial.readString();
  char motor = cmd.charAt(0);
  double rpm = cmd.substring(2).toDouble();
  
  if(abs(rpm) > 150){
    Serial.println("Speed must be in interval [-150; 150] or 0 to disable motor");
  }
  
  if(motor == '1'){       //Screw
    setSpeedScrew(rpm);
  }

  if(motor == '2'){       //Plunger
    plungerSpeed = rpm;
    setSpeedPlunger(rpm);
  }
}

  // TMC2209 can produce its own step pulses, this is just reforming from rpm to correct input
void setSpeedScrew(double rpm){
  int vactual = (int) (rpm*fullStepsPerRotation*screw.microsteps()/(60.0*0.715)); //0.715 from datasheet
  screw.VACTUAL(vactual);
  Serial.println("Screw motor speed updated");
}

  // TMC2130 relies on the clock and interupt to provide step pulses to it
  // This method adapts the clock threshold for required rpm
void setSpeedPlunger(double rpm){
  if(rpm == 0){
    TIMSK1 &= ~(1 << OCIE1A); // Stop comparing counter to threshold
    plungerDisabled = true;
    Serial.println("Plunger motor is disabled");
    return;
  }
  if(plungerDisabled){
    TIMSK1 |=  (1 << OCIE1A); // Start comparing counter again
    plungerDisabled = false;
    Serial.println("Plunger motor is enabled");
    if(rpm == -1)return;      // So speed does not get updated
  }
  plunger.shaft(rpm > 0);     // rpm: CCW > 0, CW < 0
  double stepsPerSecond =  (abs(rpm) * (microstep * fullStepsPerRotation))/60;
  OCR1A = (int)(16000000/stepsPerSecond); //clockPulsesPerStep; 1/stepsPerSecond / (1/16Mhz)
  Serial.println("Plunger motor speed updated");
}

void stallSequence(){
  wasStalled = true;
  setSpeedPlunger(-120);
}

void normalOperation(){
  wasStalled = false;
  setSpeedPlunger(plungerSpeed);
}

But does it work reliably? I’ve played with the stall detection in the DRV8711 and its flaky as anything, pretty unusable.

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