0

For my project I am creating a RISC processing architecture on an FPGA that can perform various basic instructions like adding, multiplying, subtracting, storing and fetching from memory etc. To prove my architecture I am using these instructions to control the speed of a motor using a PID control loop.

So the system has a potentiometer to set a speed setpoint, a direction and start/stop inputs, a motor driver to drive the motor with PWM, and an encoder to read the speed back. Its pretty simple.

I at the stage where everything has been tested individually and can now start controlling the motor speed with the PID loop. I am new to PID loops but have been reading up on them.

I read the speed setpoint from potentiometer and rotational speed from the encoder every 1ms. However in my program that I write with instructions that is ran at 100MHz, I keep calculating the speeds as they are refreshed every 1ms. So I calculate the same answer a lot of times before it is refreshed. This is no problem currently as it is only for display on 7 segments.

From my PID loop understanding, you need a fixed scan time as the integral and derivative accumulations will be skewed. Currently I have no means to do this as my processor will be calculating at 100MHz but the variables are only refreshing at 1ms.

I have two questions:

  1. Should I add an interrupt capability to my processor to only run the PID code every 1ms as the speeds are refreshed?

  2. Also in your opinions, is every 1ms too fast or too slow to refresh the values? This is a motor and encoder on a bench with no load so it probably doesn't even matter, I am just unsure.

If there is anything unclear, please ask :)

EDIT

Some additional process information:

Motor speed: Approx 1000 to 4000 rpm max

Motor no load, full speed current: 200mA

Motor load: Virtually nothing, just an encoder

Motor driver: L298n (PWM is created in FPGA logic)

Encoder type: Rotary incremental encoder, 2 12V channels 90 degree phase shifted, 1000ppr (doubled to 2000ppr)

David777
  • 1,548
  • 12
  • 29
  • Since you are asking about timing, it would be helpful to know a few more things about what you are actually controlling (the plant) in your control loop, things like the anticipated motor speed, encoder type and counts per revolution etc. – gcr Mar 30 '21 at 20:32
  • @gcr Ah ok, I will edit the question with more information. – David777 Mar 30 '21 at 20:38
  • Is your encoder 1000 _lines_ per revolution, or are you getting 1000 _counts_ per revolution? If it's 1000 lines per revolution, you should be getting 4000 counts per revolution out of it, using the correct way to decode a shaft encoder. – TimWescott Mar 30 '21 at 21:06
  • @TimWescott The encoder is 1000 pulses per channel, per rotation. I'm only counting the rising edges of the pulses and not using the pulses to determine direction. So the FPGA gets 2000 pulses per rotation between it's two inputs from encoder. – David777 Mar 30 '21 at 21:09
  • 1
    "I'm only counting the rising edges of the pulses and not using the pulses to determine direction." OK -- 30 years of experience speaking here. Don't do that. Bad Things inevitably happen. The state machine to count both rising and falling edges is dead easy -- it'll probably fit in one LUT, then you hook it up to an up/down counter and life is a joy. – TimWescott Mar 30 '21 at 21:35
  • 2
    Unless you're absolutely positively only ever going to rotate in one direction. But even then -- one LUT, why expend effort in doing things in a suboptimal way, when you can expend the same or less effort and do it well, and sweetly? – TimWescott Mar 30 '21 at 21:38
  • @TimWescott Well I do run the motor in both directions. What problems does this cause? I'm guessing at high RPM speeds you will lose pulses – David777 Mar 30 '21 at 21:39
  • @TimWescott Also have you any examples of how a rotary encoder should be decoded, I'm interested in changing the way I am doing it now to. – David777 Mar 30 '21 at 21:47
  • 1
    Ask in a separate question. Short story -- you get extra pulses that no amount of kludging will clear up. And -- just one LUT to fix it _all_. – TimWescott Mar 30 '21 at 21:47
  • @TimWescott Will do tomorrow, thank you. – David777 Mar 30 '21 at 21:48
  • 1
    Quadrature decoder up/dn counter may be found on the web in s/w or h/w to compute velocity direction even position with an index pulse. – Tony Stewart EE75 Mar 30 '21 at 22:16
  • Ok, I have a question. Currently I’m counting the pulses ever millisecond, storing it in a register and then resetting the counter value to 0. I then use this register value (pulses per millisecond) to calculate RPM. With an up/down counter, how can this value be used to calculate RPM and when is it reset? – David777 Mar 30 '21 at 22:55

4 Answers4

2

1st write a spec with the Current vs acceleration vs RPM then repeat with an inertial load as the optimal PID parameters will change especially with reverse. Create an algorithm for it.

Also determine the power dissipation for such duty cycles of acceleration and create an algorithm for that. You ought to know that no-load = kV or RPM/V is a constant and current is proportional to force of acceleration which depends on load and there is a stall stiction start current.

You those two feedbacks with your encoder velocity and current feedback to create two loops and determine the error with your target profile and use PID feedback to optimize stability and minimize 1 or more parameter choices. Eg. Max/rated current, seek time, etc. So the signal bandwidth changes and the error tolerance on each control variable including overshoot and settling time are your specs. The lighter the load, the more bandwidth from faster response and samples per second you will need to support the dynamic range of speed control and tolerance error.

BW=0.35/Tr 10~90z handles only 3dB of BW but your step changes might span from 3 dB to 20 dB or more, thus depending on your goal to make the fastest settling (Tbd BW) time with minimal TBD overshoot determines your sampling rate.

car wheel magnetic speed sensors for example using Infineon IC measures velocity from 0.5 to over 150 kph for smart cornering and braking with something like 100 magnets per rev as the wheel encoder which is excessive at high RPM but this is how it measure speed. So the LSB’s are truncated at low speed and DSP interpolates with a Biphase digital output in addition with the ~ 100ish pulses per rev. the ECU handles all the PID etc.

You might want to be more ambitious in Your project.

p.. don’t use a BJT driver, use a Full bridge NCh driver that can handle your motor. Like an Arduino CNC shield. Look for PMIC software.

Tony Stewart EE75
  • 1
  • 3
  • 54
  • 182
  • I don't measure voltage or current within this project, or do you mean analysing these parameters just to get a process model to base the PID constants off? – David777 Mar 30 '21 at 20:53
  • Exactly.......... control the voltage with PWM not measure it but measure current for force and RPM. Unless you sense RPM some other way. – Tony Stewart EE75 Mar 30 '21 at 20:55
  • I measure the RPM of the motor with the encoder, I don't measure current at all. – David777 Mar 30 '21 at 21:01
  • Also, do you say not to use the L298n BJT driver due to the internal voltage drop and terrible efficiency? Just wondering? – David777 Mar 30 '21 at 21:16
  • You got it, unless it were used for 24V, don’t use it, this way the Rce(sat) won’t affect Vout with acceleration where stall current is 10x rated current. – Tony Stewart EE75 Mar 30 '21 at 22:04
  • The motor is actually rated for 24V, I cannot get a mosfet h bridge driver in the UK without having to wait 2 weeks delivery time. Will have to use the L298n in the meantime. – David777 Mar 30 '21 at 22:46
  • 24V loss is only 10% just use 26V or so. a good exercise is compute a ramp of velocity with rated current with BEMF until target RPM then CV but don’t exceed rated current like a battery charger, CC till CV – Tony Stewart EE75 Mar 30 '21 at 23:22
  • you wont’ need much more than P gain with a tach and current with limits – Tony Stewart EE75 Mar 30 '21 at 23:28
2

Should I add an interrupt capability to my processor to only run the PID code every 1ms as the speeds are refreshed?

Yes.

Also in your opinions, is every 1ms too fast or too slow to refresh the values? This is a motor and encoder on a bench with no load so it probably doesn't even matter, I am just unsure.

That sounds about right for a motor control. Unless it's an absolutely awesome motor, it will limit the loop closure frequency to no more than 20Hz or so -- that's well below the \$f_s \ge 10 B_w\$ rule that I go by.

You say you'll be sampling the loop at 1000Hz, and you've got a 1000 count encoder, yet you expect many samples without change -- that implies a pretty slow-moving motor.

TimWescott
  • 44,867
  • 1
  • 41
  • 104
  • "You say you'll be sampling the loop at 1000Hz, and you've got a 1000 count encoder, yet you expect many samples without change -- that implies a pretty slow-moving motor." Sorry, I mean I will be sampling the INPUTS at 1kHz, but will be running the PID loop at 100MHz without interrupt capabilities, so that's what I meant by many samples without change. Sorry, my bad. – David777 Mar 30 '21 at 21:14
1

Re: 2.: This is a question that control theory and the properties of your system will have to define. We can't tell you that!

Re: 1.: Yeah, that sounds like the standard way of doing that: add a timer unit, give it a couple easy registers (or memory-mapped registers) to control it, and an interrupt controller.

That thing doesn't have to be fancy, honestly - if you add a "SUSPEND" instruction you can simply stall your CPU until the interrupt generator fires. That's not really an interrupt controller (you can't interrupt anything while your CPU is actually doing anything), but for your single use case, it would certainly be easier than thinking about how to save and restore CPU state upon entering and leaving interrupt routines!

Marcus Müller
  • 88,280
  • 5
  • 131
  • 237
  • Re: 2 : I assumed that, just thought was asking where you folks might start at. – David777 Mar 30 '21 at 20:42
  • Re : 1 : Yeah, my thinking was that I have JUMP instructions, so if I got an interrupt input to my control unit, I could jump to a different sub code consisting of the PID loop code so it would only be ran every 1ms (or whatever I choose). However I could only jump to another instruction at the start of an instruction. Every instruction takes 6 clock cycles by the way in my processor. – David777 Mar 30 '21 at 20:44
1

There is a rule of thumb for digital control loops : $$ f_s >= 20 B_w $$ Basically, the sampling rate should be at least 20 times the desired closed-loop bandwidth.

So in your case, the closed-loop bandwidth should be less than 50 Hz. For example, if you want a stabilization time of 1 ms, you need more bandwidth. It's up to you to determine what your requirements. From these requirements, you should be able to estimate a desired bandwidth.

Edit :

As @TimWescott mentionned the, rule of thumb $$ f_s >= 10 B_w $$ usually works well, so my rule of thumb is a bit conservative. However, the closer you get to the Nyquist frequency, the more you need to model accurately your plant, you need to consider all delays (computation delays, actuator delays, sampling delays). You need to make sure all your dynamics are properly modeled in the 0 to f_s/2 range, etc.

Ben
  • 645
  • 4
  • 13
  • 1
    Well, \$10 B_w \$ usually works. It depends on how precise you need to be, and how robust. I've closed loops where \$f_s \simeq 1 B_w\$ but that's in special cases where you have very well-defined plant behavior, and where your sampling rate is determined by external factors. – TimWescott Mar 30 '21 at 21:08
  • If you use fs=64 BW then it is trivial to LPF the PWM current with a 2nd order filter rather than an 6th or 8th order filter for Nyquist noise reduction, which demands a lot more. BW than RPM, otherwise Integrate S&H ADC and dump to measure average current. – Tony Stewart EE75 Mar 30 '21 at 22:08