paper: Take-Back-Half Shooter Wheel Speed Control

Thread created automatically to discuss a document in CD-Media.

Take-Back-Half Shooter Wheel Speed Control
by: Marvin Kraska

Control algorithm used by Team 123 for shooter wheel speed control. Simpler than PID, robust to changes.

An integrating algorithm originally described for temperature control has proven stable and reliable over many competitions. Algorithm description, LabView implementation, mesured response, and simple .xls model (no macros) to explore algorithm. (114 KB)

This is a simple and proven algorithm we have used to control shooter wheel RPM, it is a comprimise between simple bang-bang and a full dynamic PID controller. It only has one parameter to adjust, is very forgiving and easy to adjust. As long as the gain is small enough, the controler is stable and will converge to near zero error.

The Bisect Ctl code shows a while loop, and you have that in the teleop code. Isn’t having a while loop in the Teleop code a bad thing? Or am I missing something…

Also, does the controller need a fixed time base between iterations? It would seem that if the delta t’s are not consistent, there would be issues similar to if they are not consistent in a PID loop?

We actually have the code running on our bot, but I am doing a software review, and had some questions. Not 100% FRC and Labview savvy.

They’re using a while loop for glorified feedback nodes. The loop never executes more than once in a single iteration due to the True constant being wired to the Stop if True terminal.

That being said, I like the use of a while loop in this case since it makes the logic flow easier to follow.

Thanks John, I see the stop button now, not used to seeing that done. I did just take out the while loop, and rewired it all with the “normal” feed back loops, and even after the “broom of death” the code was pretty much un-readable. I see the reason for the one iteration loop, it made the code pretty.

As to the other question, what happens to the control if there isn’t a consistent time between teleop iterations.


Wow this is a cool algorithm. I think we’ll try it next chance we get. Thanks for sharing!

For shooter speed control, what are the advantages of PID or take-back-half control over bang-bang control? It seems like bang-bang control provides the shortest spin-up time, as the motor is run at full power, and, if run with a fast enough update time, accurately holds shooter speed in a small tolerance.


  1. your motor is directly connected to the shooter wheel (so there is no gearbox free play and minimal “drivetrain” friction), and

  2. your wheel is reasonably balanced (so excessive vibration does not cause rapid deceleration of the wheel when power is removed), and

  3. your speed feedback has good resolution and minimal phase lag (as would a properly coded counter object using the getPeriod() method), and

  4. your shooter has sufficient moment of inertia (most do), and

  5. you run the algorithm fast enough (20ms or faster)

… then it’s hard to beat bang-bang control.

Even if some of the above criteria are not met, bang-bang can be the best method, on a case-by-case basis depending on your design.

“You must spread some reputation around before giving it to Ether again”

*I think the following is a correct C implementation of Take-Back-Half:


e = S-P;                            // calculate the error;
Y += G*e;                           // integrate the output;
if (Y>1) Y=1; else if (Y<0) Y=0;    // clamp the output to 0..+1;
if (signbit(e)!=signbit(d)){        // if zero crossing,
  Y = b = 0.5*(Y+b);                // then Take Back Half
  d = e;}                           // and save the previous error;


S is the setpoint (target RPM)
P is the process variable (measured RPM)
G is the integral gain (the tuning parameter)
Y is the output command to the motor controller
e is the error
d is the previous error
b is the TBH variable

Y, d, and b should be initialized.

Would someone be willing to test this and make any necessary corrections and re-post for the benefit of C language teams who might want to try TBH?

^Following up on the above^

You can improve the spinup response of TBH by clever selection of the initial values before you change the setpoint “S” from 0 to your desired speed.

Do a simple open-loop test to establish the approximate value of motor command (in the range 0 to +1) required to hold your wheel speed at the target value. Call this experimentally determined motor command value “M”. It doesn’t have to be exact.

To start your spinup do the following:

Set S to your desired wheel speed; initialize Y=1, d=1, and b=2*M-1; and turn your speed controller on.

Since Y=1, you will be applying full voltage to the motor to spin it up (just like bang-bang). Y will remain equal to 1 (applying full voltage) during the spinup, because there will be no zero crossings until you reach the target speed S.

When you reach the first zero crossing (at the target speed), the TBH algorithm will set Y (and b) equal to (Y+b)/2 = (1+(2*M-1))/2 = M, which is the experimentally-determined motor command value required to maintain the wheel at the target speed. You will immediately have the correct (or approximately correct) motor command for your target speed. This will reduce overshoot and oscillation.

In case anyone is interested, we have implemented a Take-Back-Half speed controller in Java here.
It is working well for controlling the shooter wheels on our robot.

A little data on the TBH and wheel performance. Match 54, Livonia competition, TORC went 26 for 28 on 2-point full court shots. 1 for 3 on 3 pointers while hanging, but as you can see from video, the angle while hanging needs to be adjusted up.

Video of match on FB page at:

Excel data can be downloaded from here:

Data logger on the bot, is recording every 100ms to csv text file on cRio.

We think the two misses are from the driver shooting when there was no disk to shoot, and the disk probably landed on top of the arm instead of in front of the arm pushing a disk that is laying flat. From data you can see driver shot 57 times, yet the only 33 disks touched the wheel. 24 times the arm spun without a disk in the hopper.

Based on the data and time sequence, we can shoot a disk at with angle set and wheel at speed every 1.5 seconds.

This data logger is one of the reasons we won the innovation in control award.

We Love Data.


RPMCommand = RPM we were telling the wheel to spin.
RPMActual = RPM the actual from feedback photoeye.
MotorControl = Output of TBH to Motor Controller, 0 to 1 on right axis.
RPMControl = How driver was controlling wheel speed, 1 = preset for whole match.
DidShoot = Shooter arm advances when 1 and goes to 0 when it completes it’s revolution.
CounterValue = number of disks in hopper, but we removed the photoeye that used to count, as it interfered with disks going to the hopper.

Thanks for posting that.

What speed sensor are you using, and how are you decoding the signal?

What motor, and gear ratio from motor to wheel?

What language are you using?

How fast are you running your TBH algorithm?

The speed sensor, is a Banner DS18VN6LP photoeye.

The 8" pneumatic wheel is direct mounted to a CIM with Hex shaft and hub.

There is a piece of reflector tape on every other face of the hex shaft, so 3 pulses per revolution.

The TBH algorithm runs is in a 10ms timed tasked loop, with metronome timer.

The code is labview, more info from this link.

We are not using gear tooth mode on the sensor, but now we are averaging over 3 pulses, because of the change to the hex shaft.

Doh. I remember our conversation now. I am starting to show my age. Sorry 'bout that.

:slight_smile: Me too. NP.

I have found this to be an acceptable take-back-half algorithm:

cval -= (cval - goodval) / 2;

Where cval is the current value of [whatever] and goodval is the ideal point (what you want it to be at)

cval += constrain(-(cval - goodval) / 2,-max,max);

This also works, but is constrains the value to within a maximum speed, the variable max. Function constrain below:

float constrain(val,min,max) {
          return val>max?max:val<min?min:val;

This seems a lot simpler than the previously posted code, and I was wondering if they are equivalent?

Where’s the feedback here? How do you find goodval? The fact that you’re setting cval suggests it’s an output, and if you somehow knew the ideal output (namely goodval) then you wouldn’t need closed loop control in the first place.

Now, if you instead have

cval -= (sensorval - goodval) / 2;

which seems to be what you were getting at, then what you secretly have is an integral controller (just the I from PID) with coefficient 1/2.


And, just because it’s an integrator, and its gain is 1/2, does not make it a TBH algorithm. The hallmark of TBH is the unique way that the output is reset at each zero crossing.