Python Multi Processing

Hello All,

I need some help with multi-processing in python on the bot. I would use multi threading but because of the global lock in CPython that isn’t doable, or at least that is what i assume is what is blocking its use i could be just completely wrong and miss using the threading.

None-the-less, here is a class i wrote up to get rate from a infinite turn pot hooked up to a roller, it works as long as it is ran fast enough, other wise it will miss a turn or two and give out some funky data, it isn’t very clean and doesn’t work on the bot because of no multi-threading support.

If i could get some help it would be great, I would prefer to get multi-processing working, but if i could have it run under a timed event properly instead that would be fine as well.

Thank you all very much the code is below, feel free to take and use or re-write it any way you feel.

class potRate(): 
    def __init__(self, potObj, timerObj):
        self.rate = 0.0
        self.stopThread = False
        self.lastRate = 0.0
        self.lastPotVal = potObj.GetVoltage()
        self.potObj = potObj
        self.timerObj = timerObj
        self.timerObj.Start()
        self.timerObj.Reset()

    def calcValue(self): 
        while not self.stopThread:  
            try:
                #This tells us how much it has traveled since it was last looked at by comparing last to current
                Rate = 0.0
                lastRate = self.rate
                potVal = self.potObj.GetVoltage()
                potDiff = (self.potObj.GetVoltage() - self.lastPotVal)
                
                #The pots give some jumpy data so this is a very basic filter that seprates out any garbage in readings
                if (abs(potDiff) >= 0.02):
                    #here we actually calulate out the rate, rate is just distance travled over time, we time how fast the pot moved
                    #Becuase of the way the shooter works, we don't want negitive values
                    if (potDiff > 0):
                        Rate = potDiff/self.timerObj.Get()
                        Rate = (Rate + lastRate)/2
                    else:
                        pass
            except ZeroDivisionError:
                Rate = 0.0
                print("RPM error zero divison")
            except:
                print("RPM error unhaneled")
                self.stopThread = True
            self.rate = Rate       
            self.lastPotVal = potVal     
            self.timerObj.Reset()

    def Start(self):
        self.stopThread = False
        self.rateThread = threading.Thread(target=self.calcValue, args = ())
        self.rateThread.daemon = True
        self.rateThread.start()
    def Stop(self):
        self.stopThread = True
        self.rateThread.join()
    def getRate(self):
        return self.rate

Recommend surrounding your code with

 tags, it's pretty much impossible to read without them. :)


Code goes here. :slight_smile:



I'm surprised threads didn't work out for you... they should work in RobotPy. What error did you get?

On the other hand, I would be *very* surprised if multiprocessing worked. I'm surprised we didn't pull the module out.

For periodic tasks like this, I actually tend to prefer a polling approach where the main loop keeps calling my function, without an actual while loop in there. The key to this is to make sure the main loop is controlled tightly without too much variance (using something like our [PreciseDelay](https://github.com/frc1418/2014/blob/master/robot/robot/src/common/delay.py) object). Once you control the loop, you can even do things like [adjust the robot angle](https://github.com/frc1418/2014/blob/master/robot/robot/src/components/drive.py#L75) with pretty good accuracy. 

What rate are you trying to reach?

I am attempting to keep it under 0.018 sec per iteration. I currently have it under under that time and have it just running once per loop, but I would like to be able to just have it forked out so that it would never accidentally become an issue in the future with code revisions.

I could just have it called multiple times through out the main loop, but I just don’t really like the way that would look. I also would like to know what the best method of having multiple threads running as it would just be useful. I couldn’t get it running myself but on some test code i had made ran well on my pc so i just assumed the bot didn’t support it as it just froze on the start() method running forever in the while loop instead of just having it spawn out as a separate thread and continuing on.

Well, we run our main loop at 15ms, so that sounds reasonable to achieve with polling.

Now that your code is formatted, the error is easy to spot. It’s good to remember that FRC code on the cRio runs in the kernel, and not in userspace like it does on the desktop. As a consequence of vxWorks being a real time operating system, what’s happening is you’re never yielding to other threads using something like sleep() (or even doing some I/O would do the trick), so vxWorks is just running your thread infinitely and eating 100% CPU, and none of the other threads (like your main loop) ever get activated. You should be able to verify this by sticking a print statement at the beginning of the calcValue function (you can use NetConsole to view the output. RobotPy ships with a netconsole.py which also implements the functionality). Stick a wpilib.Wait() in there, and your other threads should start working again.

This type of problem is very easily avoided by never using threads directly. Alternatively, you can use the wpilib.Notifier object, which will call a function continuously at a specified interval.

Also, I wonder if GetAverageVoltage() would work better for you instead of just GetVoltage().

Additionally, if you care about uniform performance, I would just deal with the divide by zero case and make sure the exception never happens. Exceptions are relatively expensive in python, or at least expensive enough if you care about jitter in your calculations.