RobTillaart / AS5600

Arduino library for AS5600 magnetic rotation meter
MIT License
109 stars 20 forks source link

Repeatable jumps in angle value #24

Closed dead5ailor closed 1 year ago

dead5ailor commented 1 year ago

Hello

I use this library in with a Seeed Xiao ESP32C3. The AS5600 measures the angle of a DC motor (rpm: about 1400 rpm). I'm interested in the absolute position of the motor (i.e. values will continue at 360° and not start again at 1° after each rotation). Therefore im using the if-statement in my loop function to detect a full rotation and calculate the cumulative angle:

void loop() {
  ledcWrite(pwmChannel1, motorSpeed);
  ledcWrite(pwmChannel2, 0);

  raw_ang = ams5600.getRawAngle();
  ang = (raw_ang * 360)/4096; //angle in degrees

  if (old_ang > 340 && ang < 20)
    {
      revs++; // number of rotations/revolutions
    }
   if (old_ang < 20 && ang > 340)
    {
      revs--;
    }

  abs_ang = revs * 360 + ang; //cumulative angle over several revolutions
  Serial.print(ang);
  Serial.print("\t");
  Serial.print(raw_ang);
  Serial.print("\t");
  Serial.print(abs_ang);
  Serial.print("\t");
  Serial.print(revs);
  Serial.println();
  old_ang = ang;
}

In my tests, I use the motor at constant speed and log the values seen in the Serial.print part of the loop via PuTTY. The problem can be seen if the logged data is plotted. At all of the red markers in the plot, the angle value jumps about 45° from on measurment to the next. The normal delta between two back-to-back measurements is 4° to 5°. jumps Detail view of a jump: jumps_detail This problem is also seen in the plot of the raw sensor angle and the angle in degrees: raw_signal and angle The strange thing is, that the distance in time respectively the number of measuring points between two jumps is always almost identical. (This can be seen by the x values in the first picture. The distance between two red markers is about 2945 points every time).

Has somebody experienced the same problem? Or can somebody give me a hint what the cause for the jumps is?

I also tried different motor speeds but the distance between the jumps in the angle stays at about 2945 measuring points.

RobTillaart commented 1 year ago

Thanks for the issue, Will look into it asap, i hope this week.

RobTillaart commented 1 year ago

Thanks for the detailed info, that gives food for thought (no solution yet)

Questions

I'm interested in the absolute position of the motor

OK, maybe this functionality should be build in the library to hold a cumulative counter of the steps.

Has somebody experienced the same problem?

Not seen before or heard of.

Or can somebody give me a hint what the cause for the jumps is?

Need some test to get a better understanding what happens.

Code

OK, code looks straightforward, I see no error in that.

(Note: adding cpp to the backquotes in your post gives syntax highlighting)

Observations from the graphs

  1. The jump is real, it is not a communication error with a bit flipped or so as then there would be a spike in the slope.
  2. As the angle jumps up it is not a mechanical slip - very unlikely
  3. The jump is at regular intervals. ==> word that pops up is interference but of what? how?
  4. The jump is simultaneous in rawAngle() and readAngle() which points to the sensor.

Tests

Can you run the following tests?

  1. read the sensor (same frequency) with the motor off - does the problem occur too? same interval? same jump size? you may need to run for 15-20 minutes or so.
  2. test at very slow speed e.g 10 RPM if possible - does the problem occur too? same interval? same jump size?
  3. if not increase the speed to see at what level it starts to occur.

so far my first thoughts

RobTillaart commented 1 year ago

I also tried different motor speeds but the distance between the jumps in the angle stays at about 2945 measuring points.

OK that answers test 2 and 3 (at least enough for now).

dead5ailor commented 1 year ago

I just tested it with a different AS5600. The issue still exists. I also tried the Seeed AS5600 library and with this library the same jumps are present.

The jump is at regular intervals. ==> word that pops up is interference but of what? how?

This is the detail that surprises me the most.

I will also try another microcontroller (Esp8266) tomorrow. I think its a problem related to the ESP32 because its also present with the Seeed library.

RobTillaart commented 1 year ago

Q: how much time is 2945 measuring points? ~2.9 seconds?

Q: are there other processes running e.g. webserver? (no expert) The ESP needs to call yield() every so much time to give background processes some runtime. Could be a source of interference.

RobTillaart commented 1 year ago

There is a simple test you can do.

Make a plot of the rawAngle() and millis() % 1000

It might be that at there is also a jump in time at the moment of jump in angle. (because of whatever).

It should show a broken line for millis() if this is the case.

(the % 1000 is to keep it inside a graph)

RobTillaart commented 1 year ago

I think its a problem related to the ESP32 because its also present with the Seeed library.

Could be, still interested in the cause.

dead5ailor commented 1 year ago

image top is millis() % 1000 bottom is rawAngle()

your assumption was right. It seems like something stops the processor so that like 8 measurement points are skipped during the stop.

dead5ailor commented 1 year ago

Q: how much time is 2945 measuring points? ~2.9 seconds?

I have to check this, but when i use delay(1) at the end of loop(), there are still jumps. The number of measurement points between the jumps is then smaller (around 1995)

Q: are there other processes running e.g. webserver? (no expert) The ESP needs to call yield() every so much time to give background processes some runtime. Could be a source of interference.

No, the loop() from post 1 is all that is running

RobTillaart commented 1 year ago

Can you add a call to yield() after every call to rawAngle().

RobTillaart commented 1 year ago

hypothesis

The printing of the data fills the send buffer and blocks when it is full.

Printing is a background process, that is driven by interrupts. As it roughly prints a same amount of bytes every iteration of loop() it continues to send data. If the output buffer is not empty when new prints start it gradually fills.

tests

  1. Reduce the frequency of measurements with a factor two.
  2. Print less data per iteration
  3. Only print data every 100 ms

Do these affect the anomalie?

(Back to sleep )

RobTillaart commented 1 year ago

Another test Increase speed of Serial port to e.g. 500000

dead5ailor commented 1 year ago

I made some tests.

I used this statement to only print every 3rd value:

if (counter == 3)
    {Serial.print(ang);
    Serial.print("\t");
    Serial.print(raw_ang);
    Serial.print("\t");
    Serial.print(abs_ang);
    Serial.print("\t");
    Serial.print(revs);
    Serial.print("\t");
    t = millis();
    Serial.print(t);
    Serial.println();
    counter = 0;
    }
  counter++; 

But due to printing the time in millis:

    t = millis();
    Serial.print(t);

I now can check the time in millis between two jumps. Here you see the millis() over the measuring points for the test were I put yield() after every raw angle: millis_yield So by calculating the difference in Y values for two back-to-back jumps in the figure we now have the time between two jumps. For the first two points in the above figure we get 17751 ms - 15750 ms = 2001 ms. There is a two second time period between every pair of jumps.

I also checked it for the test were I only printed every third value. Here the time span is also two seconds between every jump: millis every 3

The same goes for the test with baudrate = 1 000 000. There is also a interrupt every 2 seconds.

It seams like some background task is blocking the ESP every 2 seconds for about 5 ms. In this 5 ms some measurements get skipped and therefore the jumps in angle occur.

RobTillaart commented 1 year ago

I made some tests. Baudrate 500 000 -> jumps still present Baudrate 1 000 000 -> jumps still present Baudrate back to 115 200 and yield() after every rawAngle() -> jumps still present Only printing every 3rd value -> jumps still present

==> So that eliminates the printing as cause of the jump.

RobTillaart commented 1 year ago

there is a two second time period between every pair of jumps.

Perfect observation! ==> so this looks something timer controlled.

Googling "Xiao ESP32C3 2 seconds" did not provide any clue.

just a guess - maybe it relates to wifi?

add this line in setup() to disable it. (found on a forum, not tested)

esp_err_t results = esp_wifi_stop();
dead5ailor commented 1 year ago

esp_err_t results = esp_wifi_stop(); did not compile in the arduino IDE. Maybe it needs some libraries. I tried this code:

void disableWiFi(){

    WiFi.disconnect(true);  // Disconnect from the network
    WiFi.mode(WIFI_OFF);    // Switch WiFi off
}

from https://www.mischianti.org/2021/03/06/esp32-practical-power-saving-manage-wifi-and-cpu-1/.

It compiled but the problem is still present. Also i don't use WiFi in my current sketch. I also think that the WiFi is off until i turn it on explicitly.

RobTillaart commented 1 year ago

Thanks for testing - sorry for wrong code if other ideas / insights pop up I'll let you know. For now pretty clueless.

dead5ailor commented 1 year ago

Thank you too for your hints and help, I appreciate it. I will post these investigations in the Seeed forum - maybe someone there can help.

RobTillaart commented 1 year ago

Please post a link to your post on the Seeed forum here (so people can follow it)

RobTillaart commented 1 year ago

@dead5ailor

just a question, does the function getAngularSpeed() works well for you? It is a function that has been tested minimally (not at such speed) so additional feedback is welcome.

Test program: AS5600_angular_speed.ino Expected output: 1400 RPM ==> 8400 degrees per second

dead5ailor commented 1 year ago

I just tried it out. In the first figure you can see the rawAngle over the time in millis(). For ten revolutions the duration is (27793 - 27350) ms = 0.443 s/10revs. This equals to 1363 rpm or 8178°/s. The second figure shows the values of getAngularSpeed() over the time. It's quite fluctuating but the time span shown is only 0.5 s. I calculated the mean speed over 3000 measuring points of getAngularSpeed() and it lead to an average of 8212°/s. image image

EDIT: here is the log file if you want to check it out yourself. speed_test.txt

RobTillaart commented 1 year ago

Thanks, looks not too bad on first sight - I'll dive into it later.

I am working today on a cumulative counter in the library based upon rawAngle() .
It has been asked for before, you use it too and it is not difficult to implement.

First tests failed hopelessly 😎 but the 3rd or 4th gave reasonable results.

POS     REV
3644    0
3640    0
4100    1
4458    1
4458    1
4458    1
...
7818    1
8075    1
8346    2
8368    2
...
// and reverse

217 0
134 0
78  0
2   0
-116    -1
-213    -1
-248    -1

This is the additional interface - function names can change - what names do you prefer?

  //  EXPERIMENTAL CUMULATIVE POSITION
  //  not working yet.
  int32_t  getCounter();     //  getPosition() ?
  int32_t  getRotations();  //  getRevolutions() ?
  int32_t  resetCounter();  //  resets counter returns last value.   //  resetCumulativePosition.

What is missing is an offset to set position to zero. now it just takes over rawAngle() so there is an offset difference. For a first (experimental) version this is quite acceptable imho.

RobTillaart commented 1 year ago

Average result is acceptable 8212°/s - 8178°/s = 34°/s 34 / 8178 ==> 0.416 %

the variation of 2400 (highest - lowest) is not good (bad).

RobTillaart commented 1 year ago

@dead5ailor

what makes more sense to you when the cumulative position / counter goes from positive to negative

POS     REV
217 0
134 0
78  0
2   0
-116    -1
-213    -1
-248    -1
...
-4096   -2

or

POS     REV
217 0
134 0
78  0
2   0
-116    0
-213    0
-248    0
...
-4096   -1
RobTillaart commented 1 year ago

the variation of 2400 (highest - lowest) is not good (bad).

Looking at the test log, this is probably caused by the high frequency of the calls ~3 ms apart relative to e.g. fluctuations in I2C communications, and the speed of the internal sensor read out.

At 1400 RPM = 23.33 rev/s = 8400°/s ==> 8400 / 300 is about 28° per measurement in theory. Sampling 4x less often would reduce the impact of the I2C fluctuations. e.g. a delay of 10 ms between calls.

Thanks for testing.

RobTillaart commented 1 year ago

@dead5ailor https://github.com/RobTillaart/AS5600/tree/develop has the experimental getCounter() if you want to "play"

RobTillaart commented 1 year ago

I will also try another microcontroller (Esp8266) tomorrow.

Still interested in this test ..

dead5ailor commented 1 year ago

For the cumulative angle (for example 732°) i would use something like getPosition() or getCumulativePosition() or getAbsolutePosition() maybe. To get the number of Revolutions (for example 2 at 721°) i would use something like getNumberRevolutions().

what makes more sense to you when the cumulative position / counter goes from positive to negative

If the rawAngle goes in positive direction then the counter for the revolutions should also go in positive direction. Vice versa for rawAngle in negative direction.

RobTillaart commented 1 year ago

For the cumulative angle (for example 732°) i would use something like getPosition() or getCumulativePosition() or getAbsolutePosition() maybe. To get the number of Revolutions (for example 2 at 721°) i would use something like getNumberRevolutions().

OK, will update those in develop branch asap

If the rawAngle goes in positive direction then the counter for the revolutions should also go in positive direction. Vice versa for rawAngle in negative direction.

That already works, but the question is more subtle, If you go negative does the revolutions

  1. become -1 immediately (which makes sense if you are coming from a positive value)
  2. become 0 first (which makes sense if you start from zero)

So there is a rationale for both, the first makes sense if you want to do math with them (crossing zero) The second is feels more logical but math will fail

So I have a preference for model 1

dead5ailor commented 1 year ago

I will also try another microcontroller (Esp8266) tomorrow.

Still interested in this test ..

I just tested it with a Wemos D1 Mini (ESP8266). No jumps are present with this controller: image Due to the lower speed (i guess), the sampling rate is also lower compared to the ESP32 and so the angle distance between two measuring points is higher with around 25°: image

But no repeatable jumps every 2 seconds

RobTillaart commented 1 year ago

@dead5ailor

I merged the develop branch into master with the experimental getRevolutions() and getCumulativePosition() functions. It time permits, please verify if these works for you.

RobTillaart commented 1 year ago

found a bug in the experimental code - https://github.com/RobTillaart/AS5600/issues/26 Not evaluated all scenario's.😒 Fix will be available later today.

dead5ailor commented 1 year ago

Sorry I was busy the last days.

That already works, but the question is more subtle, If you go negative does the revolutions

  1. become -1 immediately (which makes sense if you are coming from a positive value)
  2. become 0 first (which makes sense if you start from zero)

How do you calculate the cumulative angle? If you do it like me: abs_ang = revs * 360 + ang; //cumulative angle over several revolutions then you need to have revs (the number of revolutions) go from 1 to 0 and start at zero. With this, the math will always be correct. For example: you start the microcontroller and the position is 20° and revs is zero because we just started. Then you go back 30°, so the cumulative angle should state -10°. Because of the angle jumping from 0° to 360° revs goes to -1. The current sensor reading is 350° at this position. Abs angle is then -1 * 360° + 350° = -10° so the math is correct.

I will check the new getRevolutions() and getCumulativePosition() functions asap.

RobTillaart commented 1 year ago

No need to apologize,

Please note that the cumulative position is not based upon degrees but the raw values 0..4095. That is (1) fastest and (2) allows people to convert to degrees, radians or even RPM whatever they need.

Furthermore it does provide negative angles for CCW rotation

Looking forward to your feedback,

RobTillaart commented 1 year ago

@dead5ailor What is the status (your opinion) of this issue? As it is not a library problem I prefer to close it. However if you have found the cause I would like to know to update in the documentation.

Found only this as possible cause - https://esp32.com/viewtopic.php?t=22955

dead5ailor commented 1 year ago

Hi @RobTillaart, I think you can close the issue here. I posted in the seeed studio forum but there is no solution yet: https://forum.seeedstudio.com/t/xiao-esp32c2-5-ms-pause-freeze-every-2-seconds/267707

Due to limited time, I couldn't do further testing but i will inform you, if I found a solution. Maybe I will try another ESP32 from another brand to check if this problem is manufacturer related.