Pages: [1]   Go Down
Author Topic: (Trying to) develop a speed servo using Arduino due & arduino motor shield R3  (Read 1414 times)
0 Members and 1 Guest are viewing this topic.
Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I'm trying to develop a simple "software speed servo controller" for a hobby car.

Te motor is a hobby DC one with an encoder (10 teeth) mounted on the shaft. I've had trouble when the speed is above 60 - 70 rpm, for the time measured in between teeth starts (randomly?) giving intervals much shorter than expected. The minimum interval -for a speed of 150 rpm- is 40 mS which, I think, the arduino due should be capable enough to handle.

As I'm a novice (not in programming neither in engineering) in the arduino realm, I've looking for something similar in the forum, and found this piece of code:

Code:
const int sensorPin = 2;
const int ledPin = 13;
const int sensorInterrupt = 4;
const int timeoutValue = 5;

volatile unsigned long lastPulseTime;
volatile unsigned long interval = 0;
volatile int timeoutCounter;

bool blink = false;

void setup()
{
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);    // enable internal pullup (if Hall sensor needs it)
  pinMode(ledPin, OUTPUT);
  attachInterrupt(sensorInterrupt, sensorIsr, RISING);
  Serial.begin(9600);
  lastPulseTime = micros();
  timeoutCounter = 0;
}

void sensorIsr()
{
  unsigned long now = micros();
  interval = now - lastPulseTime;
  lastPulseTime = now;
  timeoutCounter = timeoutValue;
}

void loop()
{
//  Serial.print("Sensor ");
//  Serial.print(digitalRead(sensorPin) == HIGH ? 'H' : 'L');
  Serial.print(" RPM ");
  if (timeoutCounter != 0)
  {
    --timeoutCounter;
    float rpm = 60e6/(float)interval;
    Serial.print(rpm, 1);
  }
  Serial.println();
  delay(500);
  blink = !blink;
  digitalWrite(ledPin, blink ? HIGH : LOW);
}


posted, long time ago, by dc42 (in fact, the only modification is the interrupt pin -const int sensorInterrupt). The code runs fine , but having exactly the same problem (at exactly the same speed).

I'm going to buy a cheap oscilloscope to see the optical encoder signal quality; maybe the problem comes from electrical noise or something similar (I promise keeping you all informed), but, in the meanwhile . . . has somebody something to suggest?

Thanks.

Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I opened this thread a few days ago to 1) explain what my project is, 2) report my very first problem developing it and 3) asking for help.

I had no success: No one has read the post.

Nevertheless, I will keep trying: should I get no comments, I, at least, will use the thread as a kind of blog of my project.

Here I go: my first goal is to (speed - servo) control a hobby DC motor so that I’m able to make a hobby model moving ahead – backwards “reasonably” straight; those motor are so simple that a “non feedback” control does not control (in fact) the rpm of the motor. The initial idea is to, at least, make the motor to rotate within a “window” of, say, a +- 10% of the DesiredSpeed.

I bought a “Baron” kit –which looks fine- that has four DC motors and two encoders to mount in the shafts. I started mounting the encoder in the shaft and writing a simple code (interrupt based; I will post it later) to see what happened: at low speed it worked (roughly), but as the speed went above 60 - 70 rpm, the system started to either oscillate, stop or going to full range.

My experience is that the best is to isolate (and attack) the problems one by one, so I tried to imagine what was going wrong: the code (roughly, but did) work at low speeds, so the first guess is that the problem was not coming by this side (the maximum frequency of the encoder signal is ~ 25 Hz, so interrupts come every 40 ms. The arduino has to cope whit it easily -if not, as we say in Spanish, “apaga y vámonos”). If I’m right: what else could be it?.

By means of the serial.print (be careful with it if you are dealing with ms, as it takes a “long” time to communicate trough a USB serial) I observed that the time intervals in between interrupts (RISING edge of the encoder signal) seemed (seemed: remember what I said about the time communicating) to divide into smaller pieces (i.e.: ordinary intervals of 100 ms; then four intervals of 40, 50, 5 & 5 ms –this is an example; it wasn’t so easy).

The main suspect became, then, the signal coming form the encoder: I have (for the moment; I’m equipping myself: USB oscilloscope) no means to confirming that the signal is not good, but, despite not having, still, any laboratory tools, I DO HAVE THE OPPORTUNITY TO CHECK THAT THE ARDUINO SEES THE INTERRUPT SIGNAL AND TRATS IT WELL. How?: easy, you can program the arduino to generate an almost square wave and to connect it to the pin interrupt. This is the code (excuse me for using such mixture of Spanish and english terms):
Code:
const int AInt = 4;

const int AGenInt = 3;

const int Frecuencia = 100;

volatile unsigned long MASpCalCurTime = 1;



//////////////////// SETUP SECTION ////////////////////

void setup() {

Serial.begin(9600);

attachInterrupt(AInt,motor_a_isr,RISING);

pinMode(AGenInt, OUTPUT);
digitalWrite(AGenInt, HIGH);

}

//////////////////// MAIN SECTION ////////////////////
void loop()
{

delay (500 / Frecuencia);

digitalWrite(AGenInt,LOW);

delay (500 / Frecuencia);

digitalWrite(AGenInt, HIGH);

}

//////////////// MOTOR_A_ISR code ////////////////////
void motor_a_isr ()
{

// detachInterrupt(AInt); 
 
MASpCalCurTime = millis();

Serial.println(MASpCalCurTime);


}
//////////////// MOTOR_A_ISR end //////////////////////


The trick (making the arduino interrupt itself as if it were an alien interrupt) is not as lying yourself, as the interrupt code executes on its own account (this is the idea. I could have used an external well shaped signal, but, as I’ve explained by now, I have not). The output (serial.println; be careful) is excellent: the times correspond exactly with the indicated frequency (const int Frequencies = 100; there is a little increase due to the other code lines and to the serial.println monitoring). I would say that it works fine to 10000 Hz: for frequencies above this the serial.println monitoring gets corrupted by the observing method itself (like Heisenberg principle. Tiene cojones ).

That’s all boys (for the moment. Keep you informed).

Regards
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

(Of course, I forgot indicating that you have to connect pins #3 & #4 for the previous code to work. Sorry) smiley-roll-sweat
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

Here's a summary of the situation:

- I have the intention of making a hobby 4 wheels proto to move at a controlled speed and/or turn a certain -prefixed- angle: the goal is to control the movement through the encoder (a feedback loop) so that you don't have to guess the amount of space made by multiplying speed by time (without any idea of what the speed was in fact).

- The very first feat is to read the encoder in "real time" basis: you have to connect it to an interrupt and, then, write a software that makes whatever you need every teeth of the encoder: the simplest is to store the "millis" so that, next time, everything you have to do is to subtract to have the "period" ("T"; the inverse -1/T- is the speed).  If everything goes well, you should have a series of smooth readings (Serial.print). A few days ago I posted a piece of software that did that.

- Sometimes it worked, sometimes it didn't. It did (work well) at slow speeds but when you speeded it up, the readings started being a bit random (not exactly random; it looked that the controller read 2, 3, . . . tooth instead of just one -not every time: it could start making good readings for a while and, suddenly, again failing).

- As we say in Spanish, "sabe más el diablo por viejo que por diablo" (devil is sage not because he's clever, but for he is old): I had the impression that it was impossible to keep on going without a tool to see what happens in "real time". Even for this simple project things happen at 25 Hz (25 times a second. The maximum speed of the motor is about 150 rpm and the encoder has 10 teeth: 150 * 10 / 60 = 25), so an oscilloscope is mandatory. Fortunately, what costed 30 years ago 2.000 $ now costs 60 $: I bought a "Hantek" (a chinese made that is quite well aspected and works reasonably well). Here are the results:

** The very first image I got was the #1 I attach. It corresponds to the interrupt pin I'm using: it looks fine, specially the neat rising and falling edges; there are some spikes coming up and down from the flat parts but they don't look so big (as to interrupt the controller), so further investigation was needed.

** I added two lines of code to the interrupt (code) to send to a(nother) pin a HIGH signal while the interrupt itself is executing: I got the figure #2: Green lines (spikes) are the interrupt executing itself: a complete disaster.


** 1st, the interrupt should occur JUST in the RISING edge (as coded): in the image you can see that it happens when rising and when falling. In addition to this (2nd), there are additional executions of the interrupt that appear here and there. A disaster. (Or not?. Well, at least it corresponds with that the code makes: more interrupts than the correct ones means a bigger speed than the real one).

** These kind of problems are associated wiht "noise". It is a kind of "noise" you can't (normally) hear. In the images I enclose you can see the noise on the bottom and on the top. (I can't explain the basics of the -general- problem; sure you can find excellent papers in the net).

** I do have some experience on suppressing noise: 1) some coming from power supply and some "noisy" parts (the motor itself in this case) is diminished easily by connecting small capacitors in parallel. 2) The signal coming from the encoder (by the way: is an optically coupled one. A led that makes an "opto"transistor to conduct by lighting it)  itself can carry noise too: another cap in parallel (to earth: see below).
[BE AWARE: Noise suppression has some of sorcery: you can't use any cap (even worse: some kinds of them make noise themselves). For this purpose small ceramic capacitors are the best. I have a beginners kit with a number of them (no idea of the value: a few uF)].

** So I used three of them. You can see the results in the figure #3. Falling edge interrupts have dissapeared completely: sure the encoder signal carried noise that has been (at least in part) removed.

The problem is, by no means, solved. You can see remaining (undesired) shootings (but I keep some bullets in my cartridge). I'm going to make the following.

1) Separate power supplies: up to now I've been using the same DC supply for both the arduino and the motor. I'm going to supply them from different DC sources.

2) Build a "solid" signal earth: whatever you might think, cables have resistance (i.e: they are not zero ohms), so there are minimal voltage drops in between points that are wired together (specially with those stacks of boards we use to make the prototipes). Sometimes these drops are big enough as to shoot the interrupt (this is not -only- a "noise" problem).

3) Shorten the cables: in addition to contribute to the last problem, cables act as antennas. Everyone wants to keep them as long as possible (just in case) but, some times, you have to cut.

I'll keep you informed.  smiley-roll-sweat





* 140508_1.jpg (15.58 KB, 336x338 - viewed 25 times.)

* 140508_2.jpg (22.5 KB, 435x336 - viewed 18 times.)

* 140508_3.jpg (21.69 KB, 436x336 - viewed 24 times.)
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I have good news:

* I've gone deeper into the extrashootings problem: finally i've managed placing a small cap ( 1uF) in between the encoder input and ground (see photo. By the moment I'll use ground and earth as the same thing: It's not).

* I have reinforced ground too (used bigger cables and connected things closer to the controller). By means of that two things I've almost suppressed extra interrupts

* By adding a line of code, I've completely supressed the remaining extra shoots (I'll post the code complete when completely debugged, tuned and commented), so I've got a beatiful graph like the one in the last picture. (Speed response over 15 s)

I hope having It ready for next weekend.

I'll keep you informed.


* 140513_CAP.jpg (17.13 KB, 448x318 - viewed 15 times.)

* 140513_5_ANALISIS.jpg (150.82 KB, 1973x1193 - viewed 14 times.)
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

First of all, well done for getting so far on your project!

Secondly, I have had strange errors from encoders before and solved this by adding pull-up resistors to the signal lines. This stopped any floating signal noise from creating unwanted encoder counts. I also had to add a capacitor to the encoder input in order to eliminate noise.

Thirdly, the following posts may be of use:

Arduino Due with Quad Encoder (works with only 1 encoder): http://forum.arduino.cc/index.php?topic=140205.0

Teensy Library for use with encoders (works with multiple) : http://www.pjrc.com/teensy/td_libs_Encoder.html

Good Luck!

JW
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

Thank you.

 I've made more progress this week. I'll post them today or tomorrow (I hope).

Keep in contact.

Thanks again.
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

Well, I have reached my first objective. A summary:

1.   The goal was to build (program) a “speed servo” for a hobby DC motor (see photo). The motor I’ve been working on is the standard that almost any kit includes; it mounts a 10 teeth encoder.
2.   Even being (much) more difficult, I choose to develop it using interrupts instead of waiting for the encoder input to change: it pays more and more as new projects are developed over this (first) one.
3.   I had these problems:
a.   Noise: although the encoder has a (surprisingly) clean signal, the first attempt (code) detected interrupts almost four times than expected. Solved by adding a capacitor (see earlier posts).
b.   Serial.print: is a powerful tool, but for time dependent code it will betray you (by the way: I haven’t managed to change the baud rate. As I received the oscilloscope I discarded that method to debug, even at higher baud rates).
c.   Floating point arithmetic/variables/computing/existence (y la puta madre que lo parió, as we say in spanish; excuse me –in case you understand; for others I WILL NOT translate it- but it –float everything, I mean- is absolutely discouraging): the arduino reference tells it: “Floating point numbers are not exact, and may yield strange results when compared.”. They really do. I have lost hours (if not days) trying to guess what was wrong in the code: finally I had to test EVERY line of code having floating point variables in a different sketch, for I wasn’t –never- sure (still I am not) if the line needed more “float()” or “x.0” decimals or whatever make up to make it work. (By the way, I cannot imagine a code having the lesser relation with engineering in which floating point arithmetic can “be avoided” –reference page again-. Perhaps it is a C language matter; it is the first time I use it, so I will never complain again).
4.   After solving all these, I had to struggle with the motor itself, but I guessed it in advance: those motors aren’t linear (although “freewheel” they are almost linear) and, the worst, as my next step is to use all this stuff to make an obstacle avoiding car (the following ones are more related with speed), they start spinning at an unpredictable voltage. I have spent about 15 hours improving this; I’ll explain it later.
5.   So finally I have the results you can see in the pictures; left screens correspond to no feedback response and their counterparts are the “servo” ones. All of them correspond to the motor previously trimmed for freewheel spinning and loaded to make the test. (Remarks: 1) green: reference; yellow: motor speed. 2) Spikes: they are noise. 3) “visible” ripple (in the yellow line): is not caused by the loop. What happens is that the encoder teeth are not equal. (Believe me: look at the left –not servo- curves. They have the same ripple!). 4) “Freewheel” conditions the yellow and green curves superimposed almost perfectly for the no loop case.)
6.   As you can see, the motor follows the reference much better in servo conditions, that was the main goal. A comment on the ramp case: the code has a “non feedback” part that starts the motor (I will upload the code fully commented –I menace-); then the loop -feedback- section takes control. What you see is the best I’ve got after trimming the two variables (loop gain and “heave” to start) that make the motor to spin and control it’s speed: it looks awful, but without the “heave” and the loop control, the motor made what you see (or, in many times, it didn’t start at all. It happened sometimes with the triangle too –the motor stopped al low speeds and didn’t restart).

It’s all. (By the way: will anybody explain me how to change the baud rate and how can I upload the code?)

Thanks.


* 1.jpg (109.39 KB, 1603x1133 - viewed 10 times.)

* 2.jpg (64.18 KB, 1404x993 - viewed 12 times.)

* IMAG0502.jpg (320.69 KB, 720x1280 - viewed 11 times.)

* BAUD_RATE.bmp (1417.25 KB, 955x506 - viewed 9 times.)
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I keep with the project: This guy http://hydraraptor.blogspot.com.es/2007/09/dc-to-daylight.html is a genius; deserves a monument.

(I will explain why tomorrow)
Logged

Logroño - Spain
Offline Offline
Full Member
***
Karma: 2
Posts: 245
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok. Here I go:

After testing the motor and the code (speed servo), results were acceptable for a hobby proto, so I duplicated the code and connected a second motor (for the other side) and watched what happened.

The code worked fine, but the second motor induced such a big quantity of noise that for certain speeds the whole system became ungovernable. I wandereded on the net for a while and found this superb, fantastic, precise and useful article.

God bless him. Thanks.
Logged

Pages: [1]   Go Up
Jump to: