Experiment with hall effect angle sensors

Over the last few months I’ve been experimenting with hall effect angle sensors attached to the back of stepper motors. This is a follow up to some of the work I did on the Mechaduino ( Mechaduino experiment ). However, in the mechaduino code the goal was to be able to react to changing sensor readings with low-latency (eg, for closed-loop support). In contrast, the goal of my recent work with angle sensors is to gather large amounts of data for off-line analysis. A similar type of development approach that the adxl345 accelerometer code uses. (And, indeed, some interesting results were found comparing the data and timing between the angle sensors and accelerometer.)

The code for this experiment is at https://github.com/KevinOConnor/klipper-dev/tree/work-angle-20210722. It currently supports a1333, as5047d, and tle5012b angle sensors.

The main reason I have not proposed merging this into the main Klipper repository is that I’m uncertain about the current angle calibration code. These angle sensors are surprisingly accurate, but they do need calibration to get to that accuracy (likely because the magnet glued to the back of the stepper shaft is never 100% centered). The current calibration code seems to work okay, but it does require numpy to be installed.

The test setup I’ve been using with this code is on my Voron Zero printer using pcbs described at GitHub - KevinOConnor/nema14tle: PCB test board for TLE5012B sensor on Nema14 stepper . However, I would not recommend deploying those boards (as the wiring is fragile).

-Kevin

2 Likes

Hi @koconnor,

any luck with pushing this (or at least part of it) to master?
I’m concerned about closed-loop support because with 1.5mm nozzle and 1.1mm layer height there are often problems with missing steps because of nozzle hitting obstacles from previous layers.
Small bubbles becomes huge monster godzilla bubbles with such nozzle and even infill can pop up causing obstacles for nozzle when getting cold.

Also I’m very against using BTT/MKS driver shields:

  • they have built in driver (I don’t like what I can’t replace)
  • it’s a lot of useless parts there, we need only sensor and magnet, klipper can handle everything else
  • they often create “salmon skin” artefacts on side surfaces

So I’d be super glad (and guess many other users) if klipper would have native support for a1333, as5047d, and tle5012b (maybe even cheap as5600?) sensors.
I can order bunch of those sensors and help with testing and some coding if needed.

Best regards,
Alexander

P.s.: that URL is 404:
https://github.com/KevinOConnor/klipper-dev/tree/work-angle-20210722

upd: found this:
https://www.klipper3d.org/Config_Reference.html#angle

any luck adding AS5600 sensor?
though it’s 12 bits, it might handle microstepping up to 1/16…

The work-angle-20210722 branch mentioned above was merged into the Klipper master branch ( Support for magnetic hall angle sensors (for motion analysis) by KevinOConnor · Pull Request #5369 · Klipper3d/klipper · GitHub ).

Note, however, that it does not have closed-loop support. It is not a goal of that code.

Can’t say much about the as5600 sensor as I haven’t looked at it. Someone would need to code it up and submit it if desired.

Cheers,
-Kevin

Hi Kevin.

I finally ordered a bunch of sensors and magnets, couple that you mentioned and couple new.
Guess it won’t be a problem for me to add calibration routine for them (AS5600, MT6701),
but adding closed loop support might be challenging, because I never looked deeply into your architecture.
Good thing is it looks like >50% already implemented there (calibration and logging).
Do you know anyone making closed loop errors fixing watch process or are you going to implement it?

P.s.: still thinking those MKS/BTT servo boards are huge overkill because klipper can do the same and even better (because it can be editted and modified easily).

I spent some time looking at closed loop a couple of years ago (see the thread at Mechaduino experiment ). I believe Arksine also further extended that code ( GitHub - Arksine/klipper at work-mechaduino-updated ?).

Unfortunately, my experience led me to believe that implementing a traditional “closed loop” driver would be high effort and have a high risk of failure. My rough conclusion is that minor overshoot on typical stepper motors is common and correcting it with a closed loop feedback system will likely lead to “salmon skin” effects. That is, I fear correcting motor position is likely to result in worse quality.

That said, I think it may be possible to use the sensors to characterize a stepper motor driver and use that to calibrate the motion system to send slightly modified commands - so as to improve positional accuracy in a predictive way. (A kind of “feed forward” system.) Alas, this is likely very complex.

I’m not currently working on this and don’t have any short or medium term plans to work on it. I don’t know of anyone else currently working on it.

Cheers,
-Kevin

I’m thinking about rough compensation in case motor missed steps - if it hit something, in my case layers gets switched up to 10mm by X\Y, so need to try get back to something that could be called valid position and continue.

It’s not correcting motor position all the time, it’s “watch for some specified delta in what we see on motor shaft and what it should be and if it happens - get it back until that delta is eliminated”.

So watchdog process looks for errors printing in usual way, if error occurs - it’s emergency situation, stop printing and eliminate discrepancy in supposed position and current drive shaft position.
When done - continue normal printing.

As I see klipper has enough quality of prints when mechanics are OK and these sensors should be used only to fix specified discrepancy delta. Because when obstacle occurs, it’s not couple missed steps there, motor misses a lot of steps and looses torque untill it rebounds back. Usually it happens on start of movement with huge acceleration and low speed. With constant low speed and no acceleration same place can be crossed OK.

You are very welcome to share your thoughts, because someone of us will be doing this code I guess, if not you - then me :slight_smile:

Well, for what it’s worth, I fear that may be a “lot of development effort to enable a low quality result”. If the steppers lose steps (which always occurs in multiples of 4 full steps) then almost certainly the print quality will suffer dramatically. I’d personally prefer to work on “development that facilitates excellent quality results”.

That said, I understand that some machines and processes require compromises. If that’s something you want to work on, then go for it!

Cheers,
-Kevin

Hi Kevin.

It took a while, but I got a bunch of sensors working (in arduino for now).
Ordered TLE5012 and AS5047D as bare MCU’s, because PCB’s with them were unreasonably expensive, so made PCB’s myself.

What was surprising is that MT6701 is way more precise than any of TLE5012 and AS5047D,
it doesn’t even tremble in 0.01 degree, really smooth (14bits), while TLE5012 trembles around 0.02 deg from time to time on turned off motor.
AS5047D had worst documentation, and it trembles a lot, though I did everything I could find about it’s PCB:


it works nearly as bad as AS5600, which is dirt cheap, but only I2C\PWM.

so got a bunch of questions here:)

  • which sensors\boards did you use? any pictures or PCB schemes?
  • what about tremble/accuracy on still motor? 0.02 deg for all or less, or …?
  • looks like you use only SPI/SSI, without I2C, is it because it’s too slow for this? (I could make MT6701 to work in SSI, need to add code for it to klipper, so asking a lot here)
  • can I send you my code for review to add MT6701 support? Or create pull request? It cost like 5-6 times less than TLE5012 and AS5047D on their PCB’s, nearly as low as 3$, it can add more attention to these sensors. Though it doesn’t have any ready to use libs or code examples, but it works really smooth.


(for I2C/SPI R2 had to be moved to R1 position and R3, R4 have to be removed).

Best regards,
Alexander.

1 Like

The kicad pcb files for the boards I built are at the link above ( GitHub - KevinOConnor/nema14tle: PCB test board for TLE5012B sensor on Nema14 stepper ). I went with the tle5012b because it was low cost at the time I had the boards manufactured (from jlcpcb) - if I recall correctly, they were only a few dollars per chip back then. (For example Search by "tle5012b" ).

I didn’t observe any issues with precision or reliability with the tle5012b - the least significant bit can fluctuate a little, but I’ve seen that on all the sensors I’ve tested.

I’m not familiar with the MT6701. I don’t see an issue with adding support for it if someone wants to do that work.

Cheers,
-Kevin

FLY’s new CAN tool header board supports as5047 and can provide you with a board to test。
image

Support for mt6701, needed so much, if anyone can implement those, it will be huge celebration for your honor))

sorry, but I won’t have time to add this into klipper till summer or even autumn(

just sharing my code for MT6701 (for arduino, SPI mode), might help someone,
because it took quite time to find it’s small datasheet and make something from it (because it’s a mess with errors).

code
const int CLOCK_PIN = 13;
const int DATA_PIN = 12;
const int CS_PIN = 10;
const int BIT_COUNT = 24;

void setup() {
  //setup our pins
  pinMode(DATA_PIN, INPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(CS_PIN, OUTPUT);
  
  //give some default values
  digitalWrite(CS_PIN, LOW);
  digitalWrite(CLOCK_PIN, HIGH);

  Serial.begin(115200);
}


void loop() {
  unsigned long reading = readPosition();

  if (reading != 0) {
      Serial.print("Reading: ");
      Serial.println(reading);
       
  }

  delay(500);
}

//read the current angular position
unsigned long readPosition() {
  // Read the same position data twice to check for errors
  unsigned long sample1 = shiftIn(DATA_PIN, CLOCK_PIN, BIT_COUNT);
  delayMicroseconds(25);  // Clock mus be high for 20 microseconds before a new sample can be taken
  
  unsigned long sample2 = shiftIn(DATA_PIN, CLOCK_PIN, BIT_COUNT);

  if (sample1 != sample2)
    return 0;

  return sample1;
}

//read in a byte of data from the digital input of the board.
unsigned long shiftIn(const int data_pin, const int clock_pin, const int bit_count) {
  unsigned long data = 0;

  digitalWrite(CS_PIN, LOW);
  delayMicroseconds(1);

  for (int i=0; i<bit_count; i++) {
    data <<= 1;
    digitalWrite(clock_pin,LOW);
    delayMicroseconds(1);
    digitalWrite(clock_pin,HIGH);
    delayMicroseconds(1);

    data |= digitalRead(data_pin);
  }

  digitalWrite(CS_PIN, HIGH);

  return data;
}
1 Like