Excellent job, guys! A very impressive machine put together in so little time, especially at the end of your school year when so much else is going on. I can’t wait to see what you’ve achieved after you spend 5000 hours on this.
Excellent robot! I do believe that I saw your 85 point programming skills video earlier. Hopefully you can bring that up to about 250 (all stacks into the high goal) later in the season!
Hopefully our balls and goals will come in soon. I thought that they weren’t being shipped out until after July 1st.
I see you have 49:3 gearing on the flywheels (7:3 followed by 7:1) with two motors on each side. You also have IMEs on the flywheel motors, are you using velocity PID control ?
Take Back Half. It’s a very specialized control algorithm. It is essentially just a big I controller with a special case for when the controller “overshoots” the target. It only works well when you have a system where output correlates mostly linearly with input., like a flywheel, or certain temperature controls.
Here is some java code I wrote to test it out on an FRC robot last FRC offseason. It worked remarkably well and requires minimal tuning. Spin up time was impossible to beat and it kept within 30 or 40 rpm of the target, all the way up to 9000 RPM!
package edu.wpi.first.wpilibj.templates;
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.Joystick;
import edu.wpi.first.wpilibj.Talon;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj.Counter;
import edu.wpi.first.wpilibj.CounterBase;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.DigitalSource;
import edu.wpi.first.wpilibj.Timer;
/**
* The VM is configured to automatically run this class, and to call the
* functions corresponding to each mode, as described in the IterativeRobot
* documentation. If you change the name of this class or the package after
* creating this project, you must also update the manifest file in the resource
* directory.
*/
public class RobotTemplate extends IterativeRobot {
double flyWheelSetSpeed = 0.0;
Encoder flywheelEncoder = new Encoder(4, 5);
Talon flywheelMotoTalon1 = new Talon(5);
Talon flywheelMotorTalon2 = new Talon(2);
Joystick j = new Joystick(1);
Timer timer = new Timer();
double targetRPM =0.0;
double motorPower =0.0;
double kMaxSpeed =9000.0;
double tbh = 0.0;
double lastError = 10.0;
double gain = 1E-5;
double error;
double lastTime = 0;
double time = 0;
double lastDistance = 0;
double distance = 0;
double speed = 0;
boolean lastButton1 = false;
boolean lastButton2 = false;
/**
* This function is run when the robot is first started up and should be
* used for any initialization code.
*/
public void robotInit() {
flywheelEncoder.setDistancePerPulse(.01);
flywheelEncoder.setReverseDirection(true);
flywheelEncoder.setSamplesToAverage(ROBOT_TASK_PRIORITY);
flywheelEncoder.start();
timer.reset();
timer.start();
}
/**
* This function is called periodically during autonomous
*/
public void autonomousPeriodic() {
}
/**
* This function is called periodically during operator control
*/
public void teleopPeriodic() {
//yes the gain needs to be extremely small. This value generally works
gain = SmartDashboard.getNumber("Gain", 1E-5);
// use joysticks to incremement and decrement RPM
if(j.getRawButton(1) && !lastButton1){
targetRPM += 1000;
lastError =1; //when incrementing, lastError MUST be 1
tbh = (2* targetRPM/kMaxSpeed)-1;
}else if(j.getRawButton(2) && !lastButton2){
targetRPM-=1000;
lastError = -1; //when decrementing it MUST be -1
tbh = (2* targetRPM/kMaxSpeed)-1;
// tbh ranges from 1 to -1 between maxRPM and 0 respectively.
}
if(targetRPM <0){
targetRPM= 0;//don't want to go backwards do we?
}
lastButton1 = j.getRawButton(1);
lastButton2 = j.getRawButton(2);
lastTime = time;
time = timer.get();
lastDistance = distance;
distance = flywheelEncoder.getDistance();
//high speed flywheels are better measured in ticks per second than time between ticks.
speed = (distance-lastDistance)/(time-lastTime) *60; //getting actual RPM.
lastError = error;
error = targetRPM-speed;
motorPower +=gain*error;
motorPower = Util.constrain(motorPower, -1, 1); //not allowed to give %110
/*The fun bit. Upon overshooting, the motor cuts severely back based on its current speed and tbh variable
(determined by setRPM, maxRPM and past overshoots). Upon zero steady state error, tbh=motorPower
and neither variable changes. Upon any deviation, it brings it back.
*/
if(lastError>0 != error>0){
motorPower = 0.5 *(motorPower+tbh);
tbh = motorPower;
}
//set the motors to the powers determined before.
flywheelMotoTalon1.set(motorPower);
flywheelMotorTalon2.set(motorPower);
//send results to custom dashboard.
SmartDashboard.putNumber("Plot 0",speed);
SmartDashboard.putNumber("Plot 1", targetRPM);
SmartDashboard.putNumber("Plot 2", motorPower*9000);
}
Once my kids get the hang of PID for driving, I’ll see if they want to code up a PROS implementation.
Great job guys! Really impressive robot! What is your auton? Can you high elevate the alliance? And how much time does it take your flywheels to “get back up to speed” after launching a shot? Thanks.
I am confused too. What does this mean? The vex motor control value certainly does not correspond linearly with the motor speed. Or am I reading this wrong?
Thanks!
-our auton is simply 4 preloads into the high goal
-we cannot elevate our alliance (although with 12 motors, the sky’s the limit!)
-it takes about 1-2 seconds to get back up to speed.
-high speed motors.
A flywheel is a mostly linear system. If your motors don’t give linear output, that’s something that is easily fixable through mapping of input and output values to make them linear.
The general correlation behind a mechanically “good” flywheel is that giving 20% power as opposed to 10% power should give 2000 rpm vs 1000 rpm. Of course, flywheels don’t actually act like that. In fact, they are far from linear when they get close to topping out. They are however, close enough to be considered “linear” for our purposes.
The actual important part isn’t that it be perfectly linear though, it’s that the system correlate directly with your input, and that it have a large inertia (simply doubling power might take a second or two to get the steady state error down to nearly zero). Both temperature controls and flywheels share that characteristic.
Really nice robot thanks for posting it! I have a few questions; why do you have an omni wheel on the top and a traction wheel on the bottom? Also how much does your system bend, and flex when it encounters a ball. Basically does it warp structure or is it solid. I don’t know if you can tell that without some type of high speed camera though. What’s the spacing between your flywheels? Lastly, have you tried different angles of the flywheel? It seems to me that a higher angle would give a larger area that the ball could hit, while a low straighter shot would only give the ball a small ‘window’ to hit. Again, really great robot, thanks for posting all about it :D.
We use an omni wheel on top because we found that they grip the balls better than the other wheels that we tried. We added the second wheel (non-omni) in order to increase the mass of the flywheels and thereby increase their momentum when running. This decreases the time needed to spin the fly wheels back up to speed after shooting, allowing us to shoot balls faster.
From what I can see our flywheels are pretty solid however I agree that the best way to properly tell would probably be with some sort of high speed camera.
I believe that there are 16 holes between the axles on each wheel however I am not positive on this.
We have only tried the angle that the flywheels are at now. Due to time constraints we only adjusted the angle until the ball went in. We will definitely be testing this sort of thing in more detail now that we have the whole summer in front of us.