PID -Hardware needed for a software fix

New Home Forum Mostly Printed CNC – MPCNC Hardware Development – MPCNC PID -Hardware needed for a software fix

Tagged: 

This topic contains 466 replies, has 29 voices, and was last updated by  Ryan 2 months, 1 week ago.

Viewing 30 posts - 31 through 60 (of 467 total)
  • Author
    Posts
  • #52755

    Jeffeb3
    Participant

    That code runs on the nano, and pin 3 controls the output?

    So you will need to set up these things on the Arduino:

    1) a way to set the desired speed
    2) a way to sense the current speed
    3) a PID loop to take the error, and generate an output value.
    Adding a separate interface for controlling the gains and enabling the anti-chatter feature can come right after this works.

    There will be some nuance in each one of these. If you can pull any part from someone else’s attempts, that will save some trouble. Each one of these can sort of be worked on independently. If you keep the serial debug, you should print the state of each of these components, so you can find errors quicker.

    For part 1) you can read in PWM. Can you configure Marlin to use PPM (servos) or even better something like a serial or i2c connection? It would be more accurate, which would help to keep a steady speed. PWM will be moving a little bit due to noise. PPM will only be marginally better.

    For part 3) The Kp, Ki, and Kd will need to be tuned. Also good is a max integrator. Also also, you need to reset it at the right time, so I won’t build up when it’s not in control. Two common gotchas in PID.

    This is a great project for you to stretch your software skills. A little science, a little original code, and some clear subsystems. As usual, let me know if you hit a wall, or if you want me to review something.

    #52757

    Ryan
    Keymaster

    Whoa whoa whoa, slow your role. Step 2 basic “dumb” speed control. Step 3 add in the screen and speed sense optical. Step 4 the PID equations, step 5 take over the world, if step 5 fails just hope I don’t get shocked too many times.

    I’m just stoked that I could find it since I order it early October.

    Really the next step is how does it handle the heat. That heat sink got pretty hot but I am pretty sure the heat gun and fan really took it to it’s limits. The dewalt shouldn’t, fingers crossed.

    I forgot to order one of those oled screens so I have to figure out how to use one of the other ones I have. I think the 2004 style will be best no library to add and it has a encoder with 2 buttons.

    #52778

    Ryan
    Keymaster

    Marlin 2.0, making things easy.

     

    //#define SPINDLE_LASER_ENABLE
    #if ENABLED(SPINDLE_LASER_ENABLE)

     

    #define SPINDLE_LASER_ENABLE_INVERT false // set to "true" if the on/off function is reversed
    #define SPINDLE_LASER_PWM true // set to true if your controller supports setting the speed/power
    #define SPINDLE_LASER_PWM_INVERT true // set to "true" if the speed/power goes up when you want it to go slower
    #define SPINDLE_LASER_POWERUP_DELAY 5000 // delay in milliseconds to allow the spindle/laser to come up to speed/power
    #define SPINDLE_LASER_POWERDOWN_DELAY 5000 // delay in milliseconds to allow the spindle to stop
    #define SPINDLE_DIR_CHANGE true // set to true if your spindle controller supports changing spindle direction
    #define SPINDLE_INVERT_DIR false
    #define SPINDLE_STOP_ON_DIR_CHANGE true // set to true if Marlin should stop the spindle before changing rotation direction

     

    /**
    * The M3 & M4 commands use the following equation to convert PWM duty cycle to speed/power
    *
    * SPEED/POWER = PWM duty cycle * SPEED_POWER_SLOPE + SPEED_POWER_INTERCEPT
    * where PWM duty cycle varies from 0 to 255
    *
    * set the following for your controller (ALL MUST BE SET)
    */

     

    #define SPEED_POWER_SLOPE 118.4
    #define SPEED_POWER_INTERCEPT 0
    #define SPEED_POWER_MIN 5000
    #define SPEED_POWER_MAX 30000 // SuperPID router controller 0 - 30,000 RPM

     

    //#define SPEED_POWER_SLOPE 0.3922
    //#define SPEED_POWER_INTERCEPT 0
    //#define SPEED_POWER_MIN 10
    //#define SPEED_POWER_MAX 100 // 0-100%
    #endif

     

    #52779

    Ryan
    Keymaster

    Well on second thought. That math is just going to need to get reversed on the other end….That might actually get in the way.

    #52780

    Ryan
    Keymaster

    Pretty sure that equation works out to,

    pwm value = (desired RPM-intercept) * (1/Slope)

    That’s fine, reverse it on the other end easy enough, the rest seems to check out. Finally easy math and not all over the place.

    Also also, you need to reset it at the right time, so I won’t build up when it’s not in control.

    The enable pin could come in handy to stop the PID?

    Rambo pins-

    #define SPINDLE_LASER_PWM_PIN 45 // MUST BE HARDWARE PWM
    #define SPINDLE_LASER_ENABLE_PIN 31 // Pin should have a pullup!
    #define SPINDLE_DIR_PIN 32

    #52786

    Jeffeb3
    Participant

    That equation is used for making the speed easier for the “user” so they can put in 20k and think they are getting 20k. PWM still only has 0-255, and that’s not terribly accurate to read on the other end. So it’s a start, but you’re not going to get 20,000 written on your display, it will be 20,136 and suddenly change to 20,925 and back. Something to think about. It sure would be easier if Marlin could just send “S20000” digitally to the slave Arduino. For now. Leave in the equation, and let you dumb speed control just transfer that 0-255 right to the output.

    You don’t want direction, right? I’m not sure what that will enable.

    An enable would be a great idea. Just so that a little noise on the line won’t try to spin up the router to 1krpm. You would definitely reset the PID in disable. You would also reset it if the sensor was measuring zero speed, because that would mean someone forgot to flip the switch. Reset won’t stop the P component, just the I and D parts.

    #52788

    Ryan
    Keymaster

    So PWM will only go in increments of 1, so 255 levels of speed only? If that is the case it won’t be the end of the world but I wouldn’t be happy until there was another way.

    Jeez reading pwm in is not so straight forward, rising and falling…

    I figure the display will read actual RPM unless you are in “manual mode” setting the rpm with a knob.  Or Desired and actual RPM on screen would be cool.

    I hoped to use M05 to kill the enable pin and M03 should trigger an enable. It would just be used as a master switch. Right now it does register but the value bounces back and forth, gotta dig deeper on that one. M05 seems to switch it though.

    #52791

    Jeffeb3
    Participant

    Jeez reading pwm in is not so straight forward, rising and falling…

    Use pulseIn:

    https://www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/

    So PWM will only go in increments of 1, so 255 levels of speed only

    Now that I think about it, it’s 0-1023… Is that right? I am also thinking it will have 1-2% noise, but that might be pessimistic too.

    #52792

    Jeffeb3
    Participant

    AnalogWrite is 0-255. It’s probably 256.

    #52793

    Ryan
    Keymaster

    PulseIn looks better, I read it wrong initially. That also gives me a little better control of how often to check the value. In over my head but learning, I hope. Took out the element of danger and unplugged the mains….I’m a sissy.

    #52794

    Ryan
    Keymaster

    Both ways worked a bit, PulseIn is easier for me to understand. Falling and Rising I just had it loop and pull the current reading when I needed it.

    M05 turns off the enable pin and silences the PWN pin. Good I think.

    M03 /4 is erratic and backwards, lower speed cause higher pwm numbers. Re-flashing with reverse logic. I am getting odd numbers 12-1697, instead of 0-255 (or 1023). Maybe because of the reverse? Marlin takes forever to flash. Changing the nano baud rate doesn’t seem to effect the readings. I have it set to match at 250000, but lower seems to work fine.

    #52798

    Ryan
    Keymaster

    Ahhhh learning, that is micro seconds…see not just a pretty face.

    #52801

    Ryan
    Keymaster

    Oh dam it is looking good now super clean I swapped my inter board Grounds and now it is looking good. 0-2012 ms with a range of about 7ms in each reading. Pretty sure it registers it all, I can see a difference of 50-100rpm command in the numbers If I run an average maybe even finer.

    Pretty stoked this is getting good.

    #52802

    SquidPlan
    Participant

    Ah, waiting with fingers crossed.

    #52803

    SquidPlan
    Participant

    Heh, I was on page 1 when I said that.  I hadn’t noticed there was another page. 🙂

    #52804

    Ryan
    Keymaster

    So close now, tomorrow is try to rig up the RPM sensor side. Either way we at least have dumb control now.

    #52812

    Jeffeb3
    Participant

    The baud rate only affects how fast characters get to the debug console. Faster would mean less time printing.

    Those are microseconds, so about 2 milliseconds. You can set the timeout to be 5000 then. Do you get anything funny when it’s always on our always off? I would expect it to return zero if the pwm is set to 255, because there’s no pulse. That’s easily checked with a digitalRead. So if it returns 0, check if it’s high. If it is, then pretend it returned max.

    Awesome progress though. Do you have any idea what speed you’d like to be able to run the control loop? That would include reading the sensor, and making an adjustment in the output. 10Hz is too slow. 100Hz might be too slow too. 1kHz seems too fast.

    #52813

    Jeffeb3
    Participant

    Proportional-Integral-Derivative (PID) Controller

    Some light reading, in case you haven’t written a PID loop in C before. It’s pretty simple, actually.

    #52814

    Ryan
    Keymaster

    Once I cleaned up the ground issue it all worked as expected. Not sure if it was loose or dirty or the stupid breadboard. Super clean numbers.

    I didn’t see any issues with 255. I had it running as I read and tried to figure other things out and it seemed to hold up fine.

    I remember reading a snippit in there about the right speed. At least I hope so. Or brute force trial and error. I think the speed all comes down to the control equations right and rpm sensor, like how often the PID needs an update to work well. Thinking adaptive/tricoidal milling and all the variances that is gunna cause.

    Lcd, Rpm sensor, PID equations left. All of those intimidate me but we’ll see. PID was my last actual math class, control systems. I really hope it doesn’t get that deep. There has to be some baby equations I can decipher somewhere.

    Need to contact the maker and see if I can get a giant batch so we can all play with the AC triac controller thing, even cheaper.

    I have high hopes this will work.

    Side note Marlin 2 makes using Lasers really easy now, no more pin tweaking, and the marlin zero extruder stuff is getting pushed soon so dual endstops will be easier as well! Seems like the perfect time to be running marlin and pushing boundaries.

    #52815

    Ryan
    Keymaster

    Dude! perfect link, I will sleep better for sure not worrying about that math.

    #52816

    Ryan
    Keymaster

    His windup guard and zeroizer is where I figure the enable pin comes in as well as maybe a power relay…

    #52829

    Jeffeb3
    Participant

    I have high hopes this will work.

    Oh, it will.

    About timing. Is the pulse in for the input takes up to 2ms, that’s probably ok. But at 1krpm, one rotation of the collet will be 60ms. We need to stay under 10ms for 100Hz. So we will probably need to use interrupts.

    https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

    Getting the interrupt on the right pin will make things a bit more complicated, but you can do the same thing as pulseIn without stopping the program.

    Just interrupt on change. If it’s high, then set a “begin” variable to be the current microsecond value, and if it’s low, then subtract “begin” from the current microsecond value and set a global (and volatile) variable with that value. In your code, you can then check this global value in your loop. It’s probably shorter than this paragraph 🙂

    PulseIn is going to be fine for a first draft, but as soon as you’re to the stage of tuning the PID, we should probably switch. Having a slow loop will make the PID harder. Plus, it’s silly to have the CPU just waiting for the router to turn. We will have plenty of headroom if we use the interrupts.

    For the sensor, you will probably just want to look for a rising edge, and just subtract the previous time you saw a rising edge.

    #52839

    Ryan
    Keymaster

    I thought the raising a falling thing was out side of the control loop and did similar things as the interrupt version? Either way, should be no big deal right now. From what I saw last night was the nano has 2 capable pins pins and I am already on 1 of them just to be safe in case this came up. Other boards have more.

    Using the rising thing was cool with a serial.print (of course it wouldn’t normally be there) because you could see all the readings going by and every once and a while you would see the control loop spit out its numbers. Cool basic debugging.

    I am kind assuming the rpm sensor and the PWM input with both need to be on separate interrupt type loops and the control loop will just check them as often as it can ( I have no idea)? I understand the basics of the interrupt stuff but have never used it, can’t wait to learn. The signals seem very similar, it is just looking for how often a high signal is detected.

    Your insight is invaluable…I am sure you have had very similar things to deal with before.

    #52846

    Jeffeb3
    Participant

    I thought the raising a falling thing was out side of the control loop and did similar things as the interrupt version

    I’m not sure what the rising falling thing is. There are many ways to skin this cat, for sure. The benefit of doing the timing in a quick ISR is that it won’t be bothered by timing in the control loop and it won’t be bothering the speed of the control loop. If you have the ISRs measuring the plus width of the input and the period of the rpm sensor, then the main loop can just capture those values at the beginning, compute and set the output, even print status to serial, and then wait (sleeping or polling) until it’s reached it’s desired frequency.

    I am sure you have had very similar things to deal with before.

    I haven’t done this in Arduino before. So some of this advice might be terrible ;). I have done PID loops in C++ in real time operating systems and in Linux before. I definitely don’t want to dictate the solution. It will be better for you if you’re the one who does that. Then you won’t have trouble fixing it.

    #52848

    Ryan
    Keymaster

    Trying it out now, get the inturupts working now while it is simple. Trying to do the right thing and get byte, sort, int put in correctly. Slightly foreign to me but arduino is well documented.

    I got it from here. Each time I read this stuff I understand it a bit more. http://www.benripley.com/diy/arduino/three-ways-to-read-a-pwm-signal-with-arduino/

    #52849

    Jeffeb3
    Participant

    Yep, that’s a good write up.

    #52850

    Ryan
    Keymaster

    I tried HIGH, and Change. Both worked but just slowed it down to a crawl updated the values once a minute maybe.

    snip

    So then I tried the other thing and it seems to work well. See anything fishy or a reason to go one way over the other?

    snip-2

    Attachments:
    #52855

    Ryan
    Keymaster

    The PWM In number is stable to +-2 now and the range is from 0-2024. With the range set in marlin up to 30k a change of 100 rpms seems to show in the output, the math says 0-255 should be about 117 between steps so pretty good right?

     

    #52864

    Jeffeb3
    Participant

    Very good. Using pulseIn in the interrupt routine won’t work. The rising falling is perfect.

    For the rpm reading, you can just do a (separate) rising function, and compute the difference between rising edges.

    int now = microseconds;
    rpm_value = now – prev_rpm;
    prev_rpm = now:

    #52872

    Ryan
    Keymaster

    I had to take a break, Al sent me a lit acrylic sign, pretty fun.

    I have my electronics goody box out and I have two kinds of uv led sets I can try for the RPM thing. I am going to get that running on a separate nano for now, just to try and read the RPM. Then get them on the same board.

    Good to hear about the other code. So using the other interrupt, is on it’s own “circuit/channel” I guess you would call it. So each interrupt runs independently of each other and the main loop?

Viewing 30 posts - 31 through 60 (of 467 total)

You must be logged in to reply to this topic.