Have a 48 VDC brushed motor, a Cytron MDS40B controller, Arduino Uno (r3), single axis rocker potentiometer.
Everything seems to be working fine, except the motor has an intermittent (and persistent) stutter at any potentiometer input - slow to fast - and either direction. I've replaced every bit of hardware in an effort to chase down the problem, but it persists, so I'm left to believe it's glitch in the code - the one thing I know essentially nothing about...
Desired operation is pretty simple: The speed and direction of a brushed DC motor is controlled by a single axis potentiometer (center-sprung thumbstick). The motor moves a device in 2 directions on a single axis. There are NC limit switches at either end of the axis. There is an adjustable (via code) speed ramp-down function to prevent instant stopping once a limit switch is hit. There is an emergency stop function. There is also a speed limit function: set a switch in one direction and full throttle only allows 50% power to the motor; set in the other direction full throttle sends 100% power to the motor.
That's it, essentially.
Original coder is unavailable. Any help, guidance or outright fixes will be greatly appreciated (and potentially compensated). Anyone see a glaring glitch (or outright error) in this code:
#include "CytronMotorDriver.h"
#define PWM 5
#define DIR 6
// Configure the motor driver.
CytronMD motor(PWM_DIR, PWM, DIR); // PWM = Pin 3, DIR = Pin 4.
#define ramp_down_speed 10 //increasing the number will slow the ramp down process and vice versa
#define noise_window 10 //assuming at rest position potentiometer will output voltage VCC/2. When potentiometer is tilt forward it will output in the range VCC/2-VCC and vice versa.
//As per data sheet there is tolerance in accuracy of rest position. noise window will ensure that when potentiometer is at rest motor is also not moving.
const int Pot_Pin = A0; // select the input pin for the potentiometer
const int FSW_Pin = 7; // select the input pin for the forward limit switch
const int BSW_Pin = 8; // select the input pin for the backward limit switch
const int ESW_Pin = 9; // select the input pin for the emergency stop
const int SLS_Pin=10; //Speed limit switch pin
bool BSW_state = 0;
bool FSW_state = 0;
bool ESW_state = 0;
int power = 0;
int forward_power = 0;
int backward_power = 0;
int last_forward_power = 0;
int last_backward_power = 0;
int last_power = 0;
void setup()
{
pinMode(FSW_Pin, INPUT_PULLUP);
pinMode(BSW_Pin, INPUT_PULLUP);
pinMode(ESW_Pin, INPUT_PULLUP);
pinMode(SLS_Pin, INPUT_PULLUP);
delay(1000);
}
void loop()
{
ESW_state = digitalRead(ESW_Pin);
if (ESW_state == LOW)
{
BSW_state = digitalRead(BSW_Pin);
FSW_state = digitalRead(FSW_Pin);
power = get_direction();
if (power < 0 && BSW_state == LOW)
{
backward_power = power;
}
else if (power > 0 && FSW_state == LOW)
{
forward_power = power;
}
if (FSW_state == HIGH)
{
//Serial.println(" Ramp forward");
ramp_down_forward(forward_power);
forward_power = 0;
}
if (BSW_state == HIGH)
{
//Serial.println(" Ramp Backward");
ramp_down_backward(backward_power);
backward_power = 0;
}
if (BSW_state == LOW && FSW_state == LOW)
{
// Serial.print("Power value=");
// Serial.println(power);
motor.setSpeed(power);
delay(50);
}
else if (FSW_state == LOW)
{
// Serial.print("Forward Power value=");
// Serial.println(forward_power);
motor.setSpeed(forward_power);
delay(50);
}
else if (BSW_state == LOW)
{
// Serial.print("Backward Power value=");
// Serial.println(backward_power);
motor.setSpeed(backward_power);
delay(50);
}
}
else
{
// Serial.println("Emergency Mode");
motor.setSpeed(0);
}
}
int get_direction()
{
int x = analogRead(Pot_Pin);
if((x<511+noise_window)&&(x>511-noise_window))
{
return 0;
}
if(digitalRead(SLS_Pin)==HIGH)
{
return map(x, 0, 1023, -128, 128);//full speed
}
else if(digitalRead(SLS_Pin)==LOW)
{
return map(x, 0, 1023, -64, 64);//half speed
}
}
void ramp_down_backward(int x)
{
// Ramp down from current value motor to 0
if (x < 0)
{
for (int y = x; y <= 0 ; y ++)
{
// Serial.println(y);
motor.setSpeed(y);
delay(ramp_down_speed);
}
forward_power = 0;
backward_power = 0;
}
}
void ramp_down_forward(int x)
{
if (x > 0)
{
for (int y = x; y >= 0 ; y --)
{
//Serial.println(y);
motor.setSpeed(y);
delay(ramp_down_speed);//
}
forward_power = 0;
backward_power = 0;
}
}
There are 3 variables involved in the conditions here, and you handle 2 of the 8 possible combinations. That leaves 6 un-handled. Not a good idea.
The names of most of your variables leaves a lot to be desired. Maybe they meant something to the original coder, but they mean nothing to me.
There is an adjustable (via code) speed ramp-down function to prevent instant stopping once a limit switch is hit.
How is that supposed to work? Are the limit switches not actually at the end of the travel? Otherwise, how would triggering a limit switch allow time to slow down?
As to your other comments, well, the original coder (VERY nice guy) didn't speak much English, so yes, his nomenclature is a bit clunky.
Limit switches:
Very good observation! Yes, the limit switches are placed to allow continued movement of the device so that it can effectively coast to a stop. The physical setup keeps the limit switch triggered until the device moves fully away in the opposite direction.
As to the un-handled variables, well, I suppose this is what I'm looking for help with (I can make changes to the code, but I can't write it).
Good news
i know your problem: the ramping up and down PWM is lowering voltage too much .
It is explained in a 2012 post on the forum
If you really are trying to run a 24V motor at a fraction of a volt then this is expected behaviour. If the stall current is .75A at 24V then it should be taking about 11mA at 0.375V - the torque at that low current isn't enough to overcome static and dynamic friction so its only just running.
Using PWM from a higher voltage may improve matters but there is a fundamental issue here that you can't run a brushed DC motor at a tiny fraction of its design speed without friction being dominant.
I have added code to introduce accelleration and dcelleration during normal running.
I have added code to add hysteresis to normal running speed. This may help with the stuttering.
I have added code to not invoke setSpeed() when the motor is already at the speed you want. Maybe this will also help the stuttering.
I have removed the ramp functions and the loops in them. Unnecessary: loop() is al the loop anyone needs.
I have removed extraneous variables.
//#include "CytronMotorDriver.h"
/** I don't have CytronMotorDriver.h, so I'll just rig up some dummy variables*/
// ==== START OF DUMMY VARIABLES
const int PWM_DIR = 0;
class CytronMD {
public:
CytronMD(int a, int b, int c) {}
void setSpeed(int) {}
};
// ==== END OF DUMMY VARIABLES
#define PWM 5
#define DIR 6
// Configure the motor driver.
CytronMD motor(PWM_DIR, PWM, DIR); // PWM = Pin 3, DIR = Pin 4.
#define ramp_down_acceleration 10 //increasing the number will slow the ramp down process and vice versa
#define running_acceleration 50 //increasing the number will slow the rate of normal acelleration
#define noise_window 10 //assuming at rest position potentiometer will output voltage VCC/2. When potentiometer is tilt forward it will output in the range VCC/2-VCC and vice versa.
#define running_noise_window 2 // this is the noise window *after* conversion from pot read to motor speed
//As per data sheet there is tolerance in accuracy of rest position. noise window will ensure that when potentiometer is at rest motor is also not moving.
const int Pot_Pin = A0; // select the input pin for the potentiometer
const int FSW_Pin = 7; // select the input pin for the forward limit switch
const int BSW_Pin = 8; // select the input pin for the backward limit switch
const int ESW_Pin = 9; // select the input pin for the emergency stop
const int SLS_Pin = 10; //Speed limit switch pin
bool BSW_state = 0;
bool FSW_state = 0;
bool ESW_state = 0;
int power = 0;
void setup()
{
pinMode(FSW_Pin, INPUT_PULLUP);
pinMode(BSW_Pin, INPUT_PULLUP);
pinMode(ESW_Pin, INPUT_PULLUP);
pinMode(SLS_Pin, INPUT_PULLUP);
motor.setSpeed(0);
delay(1000);
}
void loop()
{
// emergency stop
ESW_state = digitalRead(ESW_Pin);
if (ESW_state == HIGH) {
power = 0;
motor.setSpeed(power);
// frobbing the emergency stop stops things for 1 second at least.
// this give you time to twirl the pot to zero
delay(1000);
}
else {
BSW_state = digitalRead(BSW_Pin);
FSW_state = digitalRead(FSW_Pin);
// handle stop switches
if (power < 0 && BSW_state == LOW)
{
power++;
motor.setSpeed(power);
delay(ramp_down_acceleration);
}
else if (power > 0 && FSW_state == LOW)
{
power--;
motor.setSpeed(power);
delay(ramp_down_acceleration);
}
// handle normal movement
else {
int target_power = get_direction();
// if the target speed is zero, ignore the running noise window
// and use "Stop!" acceleration.
if (target_power == 0) {
if (power > 0) power --;
else if (power < 0) power ++;
else {
// no action needed - we are at the correct speed;
return;
}
motor.setSpeed(power);
delay(ramp_down_acceleration);
}
else {
// respect the limit switches. don't attempt to go back if the back limt switch is on
if(target_power < 0 && BSW_state == LOW)
return; // do nothing.
if(target_power > 0 && FSW_state == LOW)
return; // do nothing.
// otherwise, acellerate within our noise window.
// this means we never actually get to the target speed, but meh.
// the operator of the train will just tweak the pot to get the speed they want
if (target_power - power > running_noise_window) {
power++;
}
else if (target_power - power < running_noise_window) {
power--;
}
else {
// no action needed - we are at the correct speed;
return;
}
// otherwise, accelerate as required
motor.setSpeed(power);
delay(running_acceleration);
}
}
}
}
int get_direction()
{
int x = analogRead(Pot_Pin);
if ((x < 511 + noise_window) && (x > 511 - noise_window))
{
return 0;
}
if (digitalRead(SLS_Pin) == HIGH)
{
return map(x, 0, 1023, -128, 128);//full speed
}
else if (digitalRead(SLS_Pin) == LOW)
{
return map(x, 0, 1023, -64, 64);//half speed
}
}