First, I’m not an expert in algorithms. I’ve never taken algorithms classes, nor did I learned Calculus when I’m writing it. This passage describes an algorithm that works. It may not have a strong theoretical background, but it just works. I guess when I finish college, I may have a new understanding on this, either figuring out why it works or thinking “Oh, this sucks. What the hell I was doing?”
You may have already known that the primary goal of our VEX Robotics Competition of 2016 is to put as many balls as possible into the basket. Obviously, you want to collect them from the field as fast as possible. But apart from that, you also want to throw the balls into the basket rapidly with high accuracy. There’re two ways to make balls fly. Punchers, though accurate without hard programming, requires loads of engineering and maintenance, as the elastic may decay over time. You need to replace the elastic frequently and readjust the force it exerts after replacements. Punchers are also delicate, meaning that some variables like temperature may affect the range it shoots significantly.
For the reasons above, we choose flywheels. Two groups of them are installed at the end of the transporter. A set of motors rotate them at an extremely high speed. An encoder, mounted on the shaft of the flywheels, measures the angular speed of it. When a ball makes contact with the flywheels, it will be taken out by friction, which was induced by the elastic deformation of the softballs.
It sounds easy, but multiple problems emerge.
1. The weight of wheels
Should the wheels be lighter or heavier? Heavy wheels take a longer time to accelerate, but when a ball was shoot, it also has a lower decrease in speed. According to the conservation of energy, this should not be an issue: as the ball was shoot at the same initial speed with the same amount of energy, the flywheels should take approximately same length of time to return to its normal speed, whether they are light or heavy, given that the power of motors is constant. However, massive flywheels are more steady against disturbance, meaning that the velocity can be more easily controlled, whereas light flywheels can be quickly accelerated initially.
So, we built two different types of robots. The first kind of robot has a high-power chassis, light flywheels and large angle of attack, which makes it a good field-cleaner. The second type of robot, mainly used at the far end of the field for the human-load balls, utilize the higher mass of flywheels to stabilize the rotational speed. It also decreased the number of motors on the chassis down to four, using the saved motors for the elevation mechanism.
2. Limitations of the encoder
The sensor for the measurement of the flywheels’ rotational speed, the encoder, has some minor limitations on its accuracy. When I set the motor to a fixed power, I can observe some random fluctuations in the readings of the encoder. The solution is the Kalman Filter. It significantly reduced the statistic noise and other inaccuracies involved in the measurement, producing a reasonable estimate of the real value. It also reacts swiftly when a sudden change occurs – something other filters lack.
3. The controlling algorithm
As I’ve mentioned before, the initial speed of the ball was determined by the rotational speed of the flywheels. So, the most important job here is to stabilize and maintain that speed.
When we want to create a closed-loop controlling system, the PID algorithm is almost always the best choice. However, in this case, a straight-forward PID does not work. Every time you shoot a ball, the rotational speed of the flywheels will always recover, go above where it should be for a few seconds, and then go down again. The process waste a lot of time, which is significant in a competition. At first, I thought I could solve the problem by adjusting the parameters appropriately, but after some failures, I realized that a simple PID is just impossible to work. The proportional&derivative part was fine, but the integral part will always accumulate some errors during the shoot, as it is an “abnormal” phenomenon in the PID controller’s perspective, who assume that the decrease in speed, or the increased friction induced by the ball, is permanent. After the velocity is recovered, the accumulated errors will push the power up, until enough negative errors counteract them.
So I decided to go with the easy way. I abandoned the PID algorithm and went with a simple mathematic calculation: The offset power is error squared, multiplied by a coefficient, k. A larger k indicates a more sensitive system. Why I want the error squared? When the error is small, we don’t want the controller to adjust the power too much, so that fluctuations could be removed. But when the error gets big, we know that a ball was just shot, so we need a sudden increase in power to make that up.
Now, the program looks like this:
This work well when the error is greater than zero – that is, when the actual speed is lower than the target speed. If, for some reason, the actual speed exceeds the target speed? We don’t want the motors to decrease its power significantly because otherwise fluctuations will occur. So:
if(error >= 0) offset=error*error*k;The offset was then added to a target power, on which the motors rotate the flywheels at the target speed.
else if(error < 0) offset=error*k;
Hitherto, everything works fast and accurately, except…
4.Variations of the target power
You may have noticed that the target power here is really awkward. You want the flywheels run at a specific speed? Okay, go ahead and test the exact power that matches the rate. If the friction of the flywheels changes, you need to do it again. If the target power doesn’t match, you end up having a lot of fluctuations.
That’s why a calibration system was needed. It basically runs the flywheels using a standard PID controller. Once the flywheels were stabilized, it records the power and shut them down. Now, we have the exact power that matches our target speed.
I used the method above for quite a period. But then an idea came to me: the reason integral part in a PID controller ruins the entire thing, is that the data collected during balls shootings obfuscated the real data. So, why not disable the integral part when the shooting button was pressed? Integrals can eliminate static errors, which is exactly what I want. So, finally, the program looks like this:
else if(error < 0) offset=error*k;
if(!buttonPressed) targetPower += error * ki;
outputPower = targetPower + offset;
I pushed the entire project to GitHub. Click here to check it out if you want.