We’ve been having some issues with discs getting jammed in our shooter and the motors stalling… So far, its only been in scenarios where someone is standing next to the shooter so we e-stop or whatever to avoid damage.
Does anyone have some code they can point me to that will detect a stall condition in software and shutdown the motor? Right now we don’t have encoders installed (hope to soon) so I’m thinking of something that relates requested voltage detects amp draw and shutdown if amp draw stays too high for the given voltage for too long. Using CAN & Labview.
What would the code look like if we have encoders ? (Although I’d like to have both ‘safety nets’ in case the encoder fails and we have to go back to running without)
Under a stall condition, current will be relatively high after the motor has been free spinning for a while (if the motor is starting from stop, it will have a similar current as a stall, briefly). With CAN, you should be able to get the current output on the motors. Someone with more experience with CAN might be able to help you there.
If you’re using an encoder, you should get a reading of # ticks / timeframe with a certain motor output. If you get near zero ticks / timeframe, the wheel isn’t spinning and you could be in a stall condition.
Specifically, with encoders, you’d want to implement something like an integrated error check. Integrate your error, and if the integrated error exceeds a certain amount, you’ve stalled. You’ll want to tune it so you don’t trip the limit while you’re spinning up, obviously. Probably a good idea to have something in there that reduces the integrated error over time so you don’t have a whole lot of error built up from spinning up.
Implement a software circuit breaker. Circuit Breakers operate on an I^2T curve. Basically, average the I^2 value over some number of samples and if it exceeds a certain value, stop the motor. Don’t set this value too low or it will trip whenever you start the motor or fire a disc.
I don’t know a thing about Labview, but I am familiar with CAN. The non-hardware solution is to monitor the output current to your shooter motors. In c++, the function is GetOutputCurrent. Our method: if the current exceeds a defined limit (say 20A) for a defined time (1.5 s) then you know the motor is stalled. Then you can take action to either shut it down to save the motor, or run it backwards if you think you can clear the jam.
To determine the current limit and time above, you’ll need to know what a baseline current is for your shooter and how it reacts under normal conditions. Easiest way to do that is to chart the current on the dashboard while you are doing normal tasks like startup and shooting. These will create spikes in current. You don’t want these normal activities to get confused with a stall condition.
Assuming I already had an encoder for speed control or monitoring, I would use a low speed threshold and acceleration threshold to declare stall. A rapid deceleration of the wheel combined with a low wheel speed when wheel power is on will correlate to a stalled wheel.
wss_stall_cal_lt is low, probably about 50% normal running speed
wss_stall_accel_cal is a negative number. I would play with it but I would guess something like -12 for a 10ms loop time. This number should be calibrated based on data traces of stalling the wheel and normal free running.
wss_stall_on_cal is probably around 1/2 the time it takes to spin up to full speed measured in loop iterations (this is determined by fixed iteration time).
No normal conditions except firing a frisbee should create significant negative acceleration. The low speed cal prevents the normal firing from tripping it, and the on time cal prevents battery voltage spikes/dips or other startup oddities from tripping it even if you don’t think they would produce a large negative acceleration.
-You are literally copying each value back an entry in the table with a For loop which is really really inefficient. A more efficient solution while still retaining the table of history would be to use a circular buffer where you set an arbitrary array index as the start and work back from there, looping around at one end to the other. Then, you can move the ‘front’ of the buffer forward and overwrite the oldest history element each time you sample the value. Then the process of inserting a sample requires only a memory write to a known array index, an increment of the circular buffer index, and a case to wrap it back to 0 at the end.
-It would be significantly more efficient to use a racheting error counter than a large RAM array which you For through each iteration. A racheting error counter woud use a single threshold on the current sample each time you sampled, and a counter of failures. A failure would increment the counter, a success would decrement the counter (limited to 0 and a number above your threshold). A certain counter value would be thresholded to declare stall. The comparison on the sample is only done once per sample (ever), instead of multiple times on the same sample, and the function call to read the stall state only has to perform a comparison on the counter. The reset is also simple, as you just set the counter to 0.
That’s what we’re doing. Although we did not have it implemented in Orlando when we smoked our shooter with a jam, in an early qualifier match :rolleyes:
Those accel/decel calcs from encoders will likely need a bit of filtering.
Easiest way I can think of fixing that is to use a current sensor. As per the rules it has to be low impedance. The one we used this year was: https://www.sparkfun.com/products/9028. There is also a 45amp version.
As for implementation: in Java we simply set it up as an AnalogButton that called a StopMotor command when it hit a current higher than a set amount. (We are also planning to re-implement that for if it jumps a certain amount, aka current spike, even if its low current to start with).
Seeing we jam after we shoot, and we shoot when the rpm of the wheel is 5% of the target speed, we condition the jam detection that we have shot, and the wheel encoder is less than 150 rpm. Only allowing to detect when you shoot, masks all of the startup issues.
After the jam is detected, we reverse our flipper motor and shooter wheel. The flipper motor will retrip the home photoeye, and stop the shoot routine. The shooter wheel is run reverse at full power for 1.5 seconds, then allowed go back to normal shoot mode.
Code works, we will test at Livonia in a couple of days if it actually can correct a jam condition.
We have a 700MHz processor. That particular class was written by a freshman who has never programmed prior to her first year of FIRST.
Well it isn’t that efficient, I never proclaimed it to be (just easy to integrate into a pre-existing project). We’ve never actually run into any performance issues with it, and honestly, it works, right? (reason why we used a for loop was actually to teach her a for loop)
It definitely can be improved, but during build season my team has more important things to worry about. And let’s be honest here, I don’t think any practical application of a cRio for FRC really pushes the processor to its bounds…
I have seen the 400mhz PowerPC pushed to its bounds by many teams. Lots of bad things happen when nearing CPU bandwidth limits, the first things is the LV bootloader stops working (many LV teams know of the ‘no-app’ switch for this reason). Then, tasks will start jittering and low priority tasks will start missing or be very late, and eventually all of the timing gets so bad that nothing is usable.
I agree that we shouldn’t be running up near the limits of a 400mhz PowerPC but many teams are.
A lot of this has to do with horribly inefficient code in the WPIlib, massively inefficient algorithm design, and too many layers of execution. The nature of the LV execution system for LV teams is also a huge contributor to CPU loading, as it optimizes and builds code in a way that might seem strange to OO or procedural programmers. It makes a lot of sense for LV, but the WPIlib and framework is written in a way that seriously hurts LV optimization and fast execution.
I am also generally an opponent of the For loop in embedded software. Unlike the Goto they do occasionally have a use, but they are almost always the wrong answer.
I wouldn’t get too worried about the perf of that particular for loop, since the array only has three elements. It’s very possible the compiler unrolls that loop anyways.
In an FRC robot, big picture choices like your vision processing strategy will have a much, much bigger impact on CPU utilization than tuning a small loop.