Notifications
Clear all

Fully Electronic Speed Control of a DC Motor

22 Posts
8 Users
17 Likes
1,789 Views
(@emilyq1)
Member
Joined: 1 year ago
Posts: 7
Topic starter  

Hi everyone!

I am working to optimize a device I built for a research project. A single motor rotates the device. I followed the directions from this post on theΒ DroneBot Workshop post from 2017, where an Arduino, L298N H-Bridge, and potentiometer are used to control a DC motor. I have gotten the setup and code working for my motor, but I am hoping to further develop the setup to have more electronic control.

My ultimate goal is to get a DC motor to run continuously at a preset constant speed until the Arduino is told otherwise. I have a way to do that through the setup shown in the post linked above, but I want to take it a step further, if possible.

I understand the speed of a DC motor is influenced by the load, therefore it is not valid to set the speed based on the voltage supplied to the device (heavier load will require more volts supplied to reach the same speed compared to the device rotated with a lighter load). Using the potentiometer, I can manually change the voltage supplied to the motor. I was wondering if there are different drivers or components that I can incorporate into my Arduino setup and sketch to do these few things:

1. A sensor that can detect the current speed of the motor. (Maybe using a laser to detect the speed at which a piece of tape on my motor gear is rotating?)

2. A digital potentiometer so the voltage being supplied to the motor can be controlled electronically (as opposed to having to turn the knob manually)

3. Arduino code that can read the motor speed input by the sensor and adjust the resistance from the digital potentiometer until a preset speed is matched by the sensor. (i.e. the motor begins to rotate and the goal speed is set to 7 RPM, the sensor determines it is spinning at 4 RPM, then the resistance is decreased until the motor reaches 7 RPM. That resistance and therefore voltage remains constant until a new desired speed is set)

I am not sure if these goals are possible as I do not have much experience with electronics. I apologize in advance if my understanding of these concepts is inaccurate or explained in an inadequate way. I am self-taught and trying to make this work to the best of my ability. I will gladly answer any questions if something does not make sense. Any advice, resources, and recommendations would be greatly appreciated!!

Thanks so much!

Emily πŸ™‚


   
Quote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6985
 

@emilyq1 #1 is accomplished with a wheel encoder. #2 I believe is possible although I don't have any. If #2 exists then #3 is possible. Let us know how it goes.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
Emily reacted
ReplyQuote
Will
 Will
(@will)
Member
Joined: 3 years ago
Posts: 2533
 

@emilyq1

Hi Emily, welcome to the forum.

It's not possible to give you a precise answer to your questions because it depends on the physical environment and the scale of operation. For instance, driving the wheel of a car kitversus driving a one ton grain grinding wheel.

Some ways to monitor the rotational speed of the motor are by using reflective tape (as you suggested) with a sensor that shines a light on the area and reacts to getting a reflection back; using a motor with a built-in encoder as @zander suggested, making your own encoder using a (3D printed or commercial) wheel with a light source and sensor; gluing a magnet to the spinning object and using a Hall sensor to note the proximity as it passes a sensor; and so on.

Once you've determined the rotational speed of the motor, you could use PWM to increase or decrease power delivered to the motor. If, for instance, you're using a 12V motor, you could use a MOSFET controlled by an Arduino (or similar) to deliver a PWM signal to the gate. This could make the MOSFET appear to deliver a voltage from 0V-12V depending on the duty cycle of the PWM signal.

These are not detailed answers to your query, but may help you search for alternatives suitable for your project's current physical and electronic components.

Anything seems possible when you don't know what you're talking about.


   
Emily and Ron reacted
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2043
 

@emilyq1

I understand the speed of a DC motor is influenced by the load, therefore it is not valid to set the speed based on the voltage supplied to the device (heavier load will require more volts supplied to reach the same speed compared to the device rotated with a lighter load).

If the load increases on the motor output it will slow down, the current will increase, and the number encoder pulses will decrease. If the PWM has the voltage on all the time and the encoder signals say it isn't going fast enough for a given load then I guess you would need to increase the voltage but I assume there is a limit to the fully on maximum voltage that a motor can deal with otherwise you just burn the motor out.

In other words I imagine there is an optimum fully on voltage for any given motor for any given load and that is its limit.

You need a motor capable of delivering the speed required for the maximum load it will have to deal with when the voltage is fully on.

You can increase the torque to the load if you change the gearing but at a cost of speed.

POWER (watt) = PRESSURE (voltage)Β  xΒ  CURRENT (amp)

Β 


   
Emily reacted
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1689
 

Hi @emilyq1,

Β  There are others with more experience on this forum, and some have already replied with some sound advice. However, I should like to raise one other point for you to consider. Please accept my apologies if you have already have a solution to this issue.

--------

The revolution speeds you mention are very slow ... 4-7 rpm, which implies 1 revolution could take 9 to 15 seconds. If the speed sensor only produces one pulse per revolution, then any change of speed due to change of mechanical loading and/or electrical power supply will not be 'measurable' to a control system until at least one revolution has occurred under the new loading and/or supply. This implies any control system will take a long time to respond to any change, and depending on the control system characteristics, may 'over react' causing erratic speed changes.

Common methods of measuring revolution speeds, such as Hall effect or light reflection/interruption typically use either a magnet or a visual marking, as a marker attached to the wheel or cog, and produce 1 signal pulse per revolution.

Obviously, the number of markers (magnetic or visual) attached to the rotating objects can be increased, and the number of pulses per revolution will increase by the same factor, but there will be a physical limit to this approach, depending upon the size of the rotating object and the size of the markings.

DC motors have a minimum rotation speed which is much higher than 4rpm, so I assume that it is attached to a physical gearbox. So it may be better to attach the sensing system to the electric motor shaft or an intermediate gearwheel/shaft which is rotating at a much higher rate.

Of course, I don't know what your mechanical arrangement looks like, so this suggestion may be impracticable without major mechanical changes.

---------------

A possible alternative is to consider measuring the current flow to the motor ... DC motors have commutators which momentarily interrupt the current flow as they pass from one segment to the next. In principle these current interruptions can be measured, but I do not have any personal experience of using this method.Β  I believe commercial motor control systems have been developed that use this principle but do not know if there is any convenient Arduino information or products.

I would expect this to require some experimentation, and possibly building a small electronic circuit using amplifiers and/or comparators from scratch. Although not essential, use of an oscilloscope to examine the signals while developing the system would make the task a lot easier.

----------------

I note you have also asked about the power control side of the project, but I think that will be easier to address this when you have prototyped an Arduino (or similar microcontroller) receiving an appropriate rotation speed data stream from the motor and displaying it appropriately .. maybe as a graph on the Arduino IDE display. This data will then form part of the 'specification' of the functionality needed in the power control side of the system.

----------------

I am sorry this is rather vague, but I hope it is sufficient to make some progress. e.g. Try Googling methods of motor speed measurement using power input monitoring.If you get stuck or confused, please feel free to ask more specific questions.

Others on the forum may be able to share more practical experience.

Best wishes, Dave


   
Emily and Ron reacted
ReplyQuote
(@emilyq1)
Member
Joined: 1 year ago
Posts: 7
Topic starter  

Hi all!

Thank you for the responses so far! They have all been extremely helpful in furthering my understanding of my project and determining the next steps.

I plan to combine a few ideas from above. I believe a rotary encoder will work well to display an RPM count of my motor while still using an L298N H-Bridge. I am not driving a heavy load (between 100-500g), so 12V DC motors have seemed to work well thus far. I have tried using motors with different gearing (30rpm, 100rpm, and 300rpm when 12V is supplied). I need a relatively low motor speed and found the 30rpm motor has a working range from about 4-30rpm, so that is the one I've been using while testing the setups found online.Β 

I ordered a rotary encoder and am going to see if I can get it to display the speed at which my motor is rotating similar to what was done in this DroneBot Workshop tutorial and this ProgrammingBoss tutorial. I will update you all if I get that bit working.Β 

I also wanted to ask about motor choice alternatives if the one I have been using does not work to accomplish what I need it to do. I am using this motor brand (12V, 30rpm). I am not sure which specifications I should be looking at to make the best choice. In the tutorial linked above, the motor used has an encoder incorporated into its build. I worry since the motor I am using may cause inaccurate outputs since it is geared differently depending on the max speed. If anyone has any input on motor choice, I'd love to hear it. I want it to rotate at very low speeds, but also to be able to be controlled by a rotary encoder.Β 

From there, I believe I am looking to incorporate something like a MOSFET but will need to do more research to understand how it works and how I can use it in my setup.

Again, thank you for your help!

Emily


   
ReplyQuote
(@emilyq1)
Member
Joined: 1 year ago
Posts: 7
Topic starter  

Hi everyone!

I have been trying to incorporate a rotary encoder into my setup. I got a setup that worked but cannot get an RPM output on my computer. I believe it is because I am using a DC motor without an encoder. I was wondering if it was possible to use a setup similar to this Electropeak post. The TB6612FNG motor driver seems to be a better option for my project.

Is there any way to get RPM as an output if I use a DC motor without an encoder and a rotary encoder in place of the potentiometer? Thank you in advance!

Best,

Emily πŸ™‚

Β 


   
ReplyQuote
(@dronebot-workshop)
Workshop Guru Admin
Joined: 5 years ago
Posts: 1085
 

Hi Emily

I always use the TB6612FNG for small projects, and I've created a few videos and articles about it that you might find useful:

https://dronebotworkshop.com/tb6612fng-h-bridge/

https://dronebotworkshop.com/dc-motor-drivers/

I've also done one on using rotary encoders, including with DC motors:

https://dronebotworkshop.com/rotary-encoders-arduino/

It includes a sketch that measures motor RPM with a rotary encoder.

As for using an external rotary encoder with a motor, are you speaking of common ones used as volume controls? If so, keep in mind that those are indented to provide tactile feedback, which is great for humans but will probably cause your motor to run rough.

Motors with inbuilt rotary encoders are always the best bet, most of those use either optical or hall-effect sensors and don't put any additional load on the motor. They also provide two outputs, so you can measure both speed and direction.

If you do want to use an external encoder, I'd recommend an optical (or hall-effect) sensor instead, as it won't load the motor. Once again I have an article & video to assist (although it's about using them with a robot car to measure distance you can easily measure speed) :

https://dronebotworkshop.com/rotary-encoders-arduino/

Each of the above articles also has a corresponding YouTube video as well.

Hope some of this is useful, good luck with your project!

😎

Bill

"Never trust a computer you can’t throw out a window." β€” Steve Wozniak


   
Emily and Ron reacted
ReplyQuote
(@emilyq1)
Member
Joined: 1 year ago
Posts: 7
Topic starter  

Hello everyone!

I wanted to reach out and update you all on where I am at, and to get a little more help, if possible.

First off, thank you for your help! I was able to get my setup to work and print RPM as the output. For my output, I wanted to print rpm and speed (in both deg/s and rad/s), but I am having an issue correcting the code to show the value to the hundredths or thousands place for the speed values. Currently, it prints as something like this; SPEED: 25 RPMΒ  Β  Β SPEED: 150 deg/sΒ  Β  Β SPEED: 2 rad/s.

I calculated the speeds using the code below, with the rpm calculation coming from this Dronebot Workshop tutorial. Β 

rpm = (float)(encoderValue * 60 / ENC_COUNT_REV);
degspeed = (float)(rpm * 6);
radspeed = (float)(rpm * 0.10472);

The code to print looks like this, but I want the decimals to print as well. Ideally, the output would be something like this; SPEED: 25 RPMΒ  Β  Β SPEED: 150 deg/sΒ  Β  Β SPEED: 2.618 rad/s. How do I print with the decimals? I looked through Arduino forums, and they recommended doing Serial.print(radspeed, 3) where 4 is the number of decimal points, but it is not working when I change my code.Β 

if (motorPwm > 0 || rpm > 0) {
Serial.print(" SPEED: ");
Serial.print(rpm);
Serial.print(" RPM");
Serial.print('\t');
Serial.print(" SPEED: ");
Serial.print(degspeed);
Serial.print(" deg/s ");
Serial.print('\t');
Serial.print(" SPEED: ");
Serial.print(radspeed, 4);
Serial.println(" rad/s ");
}

Β Any suggestions would be appreciated! Thanks so much!!

Best,

Emily πŸ™‚


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1689
 

Hi @emilyq1,

Β You didn't show your declarations, etc. so I had to guess them ...

Β  And as I didn't have an Arduino system to hand, I used on online compiler ( https://www.onlinegdb.com/online_c++_compiler) to simulate what I think you might be doing...I've cut and pasted the code and the output below, with values that match yours

#include <iostream>

using namespace std;

int main()
{
     int encoderValue = 3;
    int ENC_COUNT_REV = 7;
    float     rpm = (float)(encoderValue * 60 / ENC_COUNT_REV);
    float  degspeed = (float)(rpm * 6);
    int radspeed = (float)(rpm * 0.10472);  
    std::cout << "rpm " << rpm << "\n";
    std::cout << "degspeed " << degspeed << "\n";
    std::cout << "radspeed " << radspeed<< "\n";

    return 0;
}


rpm 25
degspeed 150
radspeed 2

Β Basically, I think you are mixing integer and floating point arithmetic.

For example, the rpm line, with these values reads as:

rpm = float (3 * 60 / 7);

But 3, 60 and 7 are all integers ... 180/7 = 25

because the remainder of the division is discarded ... trying to convert to a float later, happens too late!Β  The easiest fix is to write as 'obvious' floating point numbers .. 3.0, 60.0 etc.

--------

radspeed calculation should have worked .. but as you reported '2', I can only assume you defined it as an integer, not a float!

------------------

So with a few integer to float changes ...

#include <iostream>

using namespace std;

int main()
{
  
     int encoderValue = 3;
    float ENC_COUNT_REV = 7.0;
    float     rpm = (float)(encoderValue * 60.0 / ENC_COUNT_REV);
    float  degspeed = (float)(rpm * 6);
    float radspeed = (float)(rpm * 0.10472);  
    std::cout << "rpm " << rpm << "\n";
    std::cout << "degspeed " << degspeed << "\n";
    std::cout << "radpeed " << radspeed<< "\n";

    return 0;
}

rpm 25.7143
degspeed 154.286
radpeed 2.6928

Β I may not have caught all of your problems, but hopefully that will be a start.

Best wishes, Dave


   
frogandtoad and Emily reacted
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6985
 

@emilyq1 You need to make sure everything is float. For constants just add a .0 as in 6.0 and change the int's to float.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
Emily reacted
ReplyQuote
frogandtoad
(@frogandtoad)
Member
Joined: 5 years ago
Posts: 1458
 

@daveeΒ 

Hi Dave, you probably know this, but some readers may be a little confused by part of your example code.

Employing a using declaration such as:

Β  Β  using namespace std;

...pulls the whole standard library of identifiers, into the global namespace for total visibility, therefore there is no need to also prefix cout with the std:: prefix, as cout will work just fine without it.

To reduce pollution of the global namespace, you could just include what you need, for example:

Β  Β  using std::cout;

Explicitly prefixing what you use, such as in your example:

Β  Β  std::cout << "rpm " << rpm << "\n";

...provides the most strict and secure method of use.

The first option is usually good enough for personal small programs, but if you work with many libraries and want to create your own in the future, it's good to know the options available to you, so as to avoid name conflicts.

By the way... the link you provided to the online compiler has an errant ')' at the end, breaking the link.

Cheers


   
Emily reacted
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1689
 

Hi @frogandtoad,

Β Β  Thanks for the extra explanation ... it was a quick early morning reply for me 😴 .

I did wonder whether it was adding a complication... but I hoped my explanation be clear enough to understand what I was showing and more importantly, for Emily (@emilyq1) to move forward.

----------------

As it happens, the namespace and std: 'stuff' is all part of the default program the website provides .. I just did a quick copy and paste to print three variable values instead of Hello World, without looking at the syntax.

The extra bracket on the link is a 'grab' by the forum formatter ... I copied in a plain text string, but failed to notice what the forum formatter was going to convert it to. I have just done a quick preview test with brackets directly before the 'h' of https, and immediately after the 'r' of compiler. It inserts a space before the 'h' but includes the ')' with the link... bizarre!

Sorry if any of my failings caused any confusion.

Best wishes, Dave


   
Emily and frogandtoad reacted
ReplyQuote
(@emilyq1)
Member
Joined: 1 year ago
Posts: 7
Topic starter  

Hi everyone,

Thanks again for the help! Changing the int to float fixed my decimal problem, but I am back for a little more guidance.

Since I am unfamiliar with C++ and Arduino, I was hoping to get some help understanding how to write code to either input a speed and have the Arduino code control the PMW (such as define a variable to be the PMW and the resistance is set automatically afterwards) or find a way to get the motor speed more consistent at lower speeds (when there is a load).

Currently, the motor speed is adjusted using a 50K potentiometer (as shown in the diagram). Say I want to set my device to run at 18RPM, and I adjust the potientiometer to that speed, how would I get it to run at 18RPM consistently? When I try with a load (attached to my device), the speed varies between 14-19PRM. I understand the load might be driving the inconsistencies, but are there things (different drivers, potentiometers, code) I can do/add to my setup make the pulses value not change so drastically?Β  Β  Β 

RPM Circuit
2022 11 29

Here is the code I have so far, modified from this DroneBot Workshop tutorial. My motor speed is 55RPM at 12V, but runs a max speed of about 47RPM when the PMW value is 255. I calculated the ENC_COUNT_REV to be 1400 for this motor.Β 

// Motor encoder output pulse per rotation (change as required)
#define ENC_COUNT_REV 1400

// Encoder output to Arduino Interrupt pin
#define ENC_IN 3

// L298N enA connected to pin 10
#define enA 10

// L298N DIR connected to pin 12
#define IN1 9
#define IN2 8

// Analog pin for potentiometer
int speedcontrol = 0;

// Pulse count from encoder
volatile long encoderValue = 0;

// One-second interval for measurements
int interval = 1000;

// Counters for milliseconds during interval
long previousMillis = 0;
long currentMillis = 0;

// Variable for RPM measuerment
int rpm = 0.00;
float degspeed = 0.0000;
float radspeed = 0.0000;

// Variable for PWM motor speed output
int motorPwm = 0.0000;

void setup()
{
// Setup Serial Monitor
Serial.begin(9600);

// Set encoder as input with internal pullup
pinMode(ENC_IN, INPUT_PULLUP);

// Set PWM and DIR connections as outputs
pinMode(enA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);

// Attach interrupt
attachInterrupt(digitalPinToInterrupt(ENC_IN), updateEncoder, RISING);

// Setup initial values for timer
previousMillis = millis();
}

void loop()
{
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);

// Control motor with potentiometer
motorPwm = map(analogRead(speedcontrol), 0, 1023, 0, 255);

// Write PWM to controller
analogWrite(enA, motorPwm);

// Update RPM value every second
currentMillis = millis();
if (currentMillis - previousMillis > interval) {
previousMillis = currentMillis;


// Calculate RPM
rpm = (float)(encoderValue * 60 / ENC_COUNT_REV);
degspeed = (float)(rpm * 6);
radspeed = (float)(rpm * 0.10472);

// Only update display when there is a reading
if (motorPwm > 0 || rpm > 0) {
Serial.print("PWM VALUE: ");
Serial.print(motorPwm);
Serial.print('\t');
Serial.print(" PULSES: ");
Serial.print(encoderValue);
Serial.print('\t');
Serial.print(" SPEED: ");
Serial.print(rpm);
Serial.print(" RPM");
Serial.print('\t');
Serial.print(" SPEED: ");
Serial.print(degspeed);
Serial.print(" deg/s ");
Serial.print('\t');
Serial.print(" SPEED: ");
Serial.print(radspeed, 4);
Serial.println(" rad/s ");
}

encoderValue = 0;
}
}

void updateEncoder()
{
// Increment value for each pulse from encoder
encoderValue++;
}

I really appreciate all the help I am getting! Every suggestion has helped me make a lot of progress since joining the forum, so thank you again!

All the best,Β 

Emily πŸ™‚


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6985
 

@emilyq1 Unless I misunderstand, you use the encoder. When you have a speed you want to maintain, note the encoder value and from then on monitor the encoder value and if it deviates, either increase or decrease the speed accordingly.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
Emily reacted
ReplyQuote
Page 1 / 2