@koconnor
That was a lot of questions, good ones. It seems you fully understand the goals I wanted to achieve.
( actual precision ),
As mentioned before, the hardware development of my driver took place at the exact same time as the Mechaduino one, but at the time I didn’t want to release hardware / software that I deemed not ready…
The whole setup is very similar ( the sensor identical btw ) to the mechaduino, It’s only 2 years since I ported it to the TMC’s, which means in my code it’s just a # define
to switch between ‘dumb’ H-bridge drive (4 pins bridge + 2 AD) and SPI… I figured the TMC’s would do a better job as a dual current controller…
The PWM code is interchangeable with that of SPI.
( in the end it’s just symmetrical amplitude modulation, symmetrical across coils )
Portability:
All math is Integer, I’m using a magic nr. for the wave pointer (819200), that allows for sufficient integer compute headroom. ( 4096usteps / 200/400steps ) and 15bit sensor position. It’s almost all boolean, so it runs fast even on 8bit avr’s. I’m not using dma, there’s no need.
Long story short, it will run just fine on the mechaduino without overheating the a4950…
Sine table is hardcoded uint16_t wave_sine[4096], that’s 8k, which might be to much for some 8bit mcu’s. ( I have another waveform too, but that can wait )
Lets’ do this, since you’re obviously interested
I’ve reviewed your sample code / changes for the mechaduino, and have a couple of suggestions to make the configuration more generic and backwards compatible, with few changes to klippy and the mcu code.
Let’s agree on the following:
An MCU will have following operation modes and conforms to following conditions.
step / dir OR wavedrive > wavedrive 2 submodes > pwm / spi
if wavedrive > either open- closedloop.
if open-loop > support for multiple steppers / heaters / endstops etc…
if closed-loop > single stepper / dual endstop.
whe’re not going to support step/dir driven closed loop aren’t we ? I strongely object because of the inherent latency this interface has caused by consecutive pulses needed for correction.
This means configuration changes to klippy can be implemented as follows with an additional stepper definition.
[stepper_{n}]
interface:
#interface defaults to stepdir, make it backwards compatible, doesn’t need to be provided.
interface: pwm # requires one to provide 4 control pins of which 2 are pwm capable + at least 1 AD pin
interface: spi # required pins for interface are already defined in the specific driver section [tmc2130][tmc2209] etc…
(optionally)
[closed_loop stepper_{n}]
sercom: sercom4
miso_pin:
mosi_pin:
clk_pin:
ss_pin:
sensor_type:
pid_values: p,i,d #(*) this is a tricky one, I’ll elaborate later, it’s actually PIIP(s) or PI2P(s) don’t know what to name it yet…
filter_values: n,p # (**) same here…
Since closed-loop mcu’s have only 1 stepper, there’s no problem in running the complete feedback control ( the other kind of pid ) inside the mcu.
A good other reason for not running control software on a host is, usb/can bus latency and it’s associated chatter. pid ( or whatever whe’re going to call it ) cannot be queued/planned. It’s all very lightweight and portable anyway…
This way all currently existing ( including my own ) drivers can be supported. One can then seamlessly switch to wavedrive if the drivers support it. Can we agree on this?
virtual_stepper.c / h
For now this is usable, however since it basically transforms a time/step into a steps/timeframe, it’s kind of wastefull on interrupts inside the mcu just to do that.
Eventually, any time one switches to wavedrive, planner / kinematics should switch to steps/timeframe.
Ideal interval is 1/10000 sec.
Load on mcu’s will become (practically) constant and easier to predict, and allow for crazy speeds and/or usteps…
So ideally either, klippy/mcu protocol should be extended or altered alltogether to steps/timeframe…
It’s propably not something you’re keen on considering, but please do, replace it altogether, it won’t make any difference for the step/dir users.
(*) Why PID is not suited for fast feedback, I mentioned it before, here it is.
Noted as follows ( can’t use mathematical notation here, that’s unfortunate )
where Kp Ki Kd are it’s coefficients e(t) the positional error… simplified it’s
P = Kp * e(t)
I += Ki * e(t)
de = e(t) - e(t-1)
D = Kd * de
One of the misconceptions of tuning a PID for stepper motors is the misuse of Kp, which shouldn’t be a tunable parameter, but a constant matching your hardware.
if Ki = 0, Kd = 0
then Kp should match the size of the smallest step (1 ustep), zeroing Ki/Kd should make a motor run like it has no control loop at all…( simplest closed_loop )
if Ki != 0 Kd != 0
The derivative allows for damping during velocity changes.
At a constant velocity, ( I ) will wind up/down until P=0, since velocity is constant D = 0 is also true.
This makes I the sole factor for overcoming friction @ a constant velocity.
if Kd=0:
Since I is an integral of e(t), to large of Ki will make it oscillate, to low Ki, will make it even slower to react. If Kd = 0 this windup will cause a PID to always overshoot on decelleration and vice versa, one has to tune Kd to overcome that…
Problem with D and Kd is that the faster one is sampling for errors the lower D will become, at very fast sample rates D will become binary… and that is bad as it creates a lot of noise in the output.
The overshoot / or the noise introduced by the control algorithm will make a PID unsuitable for closed loop, to make matters worse, the AMS 5047D chip advertised to have 14bit resolution, has in reality only a stable 10/11 bit output which is not even close to the 6400 steps required for the 32usteps/200steps resolution, and so will introduce even more noise…
oversampling doesn’t work as it creates lag, and therefore limits RPM…
kalman filtering is too heavy for an 8-bit MCU…
No doubt this is your conclusion too,
next post is about PIIP(s), and filtering
Cheers
Jan