We are working on learning Java as a precursor to next years season by programming this years bot in Java. We have tank drive working just fine, and the pneumatic kicker works just fine. The one problem we have is that one kick (out and in) takes about a second to complete. (Half second delay each way is programmed in to provide time for the cylinder to fully extend and retract). During that second, the we have no control over the bot. If it was driving (ie the joystick was moved forward or backwards) when the kicker is activated, the bot will continue to move at that speed and direction until the kicker cycle is done, even if we release the stick or change directions. How should we solve that?
Use Timers
You’re going to have to give us more than that. How?
We got stuck in a goal in Connecticut, twice, because of this very issue. We stopped kicking while moving for the rest of the event to correct it. Hope you find a solution.
I assume you are using Wait or some similar statement (for some odd reason I can never remember what the thing is called) just like you would in Auton?
Robototes2412 is correct if that is your problem and that using Timers is your problem. I am not familiar enough with Java to know exactly how but a quick bit of research should show you the solution.
Sorry I couldn’t be more help.
We’re using Timer.delay(xnumberofseconds). Is that what you mean?
It sounds like your “driving” code has to wait for your “kicking” code to complete.
If this is the case, there are two (at least) different ways to solve this:
- put your kicking code and driving code into separate tasks so that the OS can multitask them for you,
or
- use a state machine for your kicker code, so that it doesn’t bring everything to a halt while it’s waiting for the delay
What is happening is that your code is stopping there and is not checking for new values for the joystick.
I don’t know much about java but what will probably work is assigning the time at the beginning of the kick to a variable. When the time is equal to or greater than the variable plus .5 move on with the kick.
This will let your code loop and read the joysticks.
I believe so… There should be a way of setting a timer to go off in X period of time. In C++ these would be the Timer.Start Timer.Stop and Timer.HasPeriodPassed (Referenced Timer Class Reference) Java should have an equivalent. This should not delay the loop you are in which is your problem. (If I am assuming your problem correctly)
We’re trying option one right now, but having never had to program multiple threads, could you explain how to do it? We’re trying various things we’re finding on the internet, but none of it is exactly what we need.
If it matters, we’re using SimpleRobotTemplate (so like Independent from LabView ) as opposed to IterativeRobot.
As far as I can tell, it’s literally a delay; if you set a state for something (ie motor 1 forward 50%) and then use that delay method for 2 seconds, motor 1 will continue to operate at 50% until the timer is done and you send a command to change the state.
I wish I could give you more specific help, but I am unfamiliar with Java. I’m sure there’s a way to do it. There are lots of good Java programmers on CD. One fellow is even currently writing a Java programming manual for FIRST:
http://www.chiefdelphi.com/forums/showthread.php?t=85836
maybe you could ask him.
~
Another option is to create a kickerLoops variable, and allow a certain loops to go by before operating your cylinders. We did this for our kicker and it worked great, but getting the initial setup/timing down might take a while.
Give me a few minutes, and I can pull up our code, which an on-hand programming helper at the regional assisted us with. We had that exact same issue.
Notes:
This is setup so that a button on a USB gamepad triggers the kicker.
Replace the sFire/sLatch/etc parts with your actual kicking code.
Depending on how your kicker works, you may need more or less “sections”.
kickTime.start in the first block is required, and kickTimer.stop(); and kickTimer.reset();.
// Set kicking loop to start when button 7 on gamepad is pressed and not in loop already
Watchdog.getInstance().feed();
if(gamePad.getRawButton(7) == true && kickTimer.get() == 0.0)
{
kickTimer.start();
sFire.set(true);
sLatch.set(false);
sExt.set(true);
sRet.set(false);
}
if (kickTimer.get() > 0.75 && kickTimer.get() < 1.05)
{
sFire.set(true);
sLatch.set(false);
sExt.set(false);
sRet.set(true);
}
if (kickTimer.get() > 1.05 && kickTimer.get() < 1.35)
{
sFire.set(false);
sLatch.set(true);
sExt.set(false);
sRet.set(true);
}
if (kickTimer.get() > 1.35)
{
sFire.set(false);
sLatch.set(true);
sExt.set(true);
sRet.set(false);
kickTimer.stop();
kickTimer.reset();
}
What you’ve posted is essentially a state machine, where the state variable is the value of kickTimer.
A generic state machine might look like this:
// Set kicking loop to start when button 7 on gamepad is pressed and not in loop already
switch(kickState) {
case 0: // kicking is not in progress
if(gamePad.getRawButton(7) == true) {
start_the_kick_sequence_and_initialize_variables_as_necessary();
kickState = 1;
}
break;
case 1:
if case_1_timer_or_event_has occurred(){
perform_case_1_action();
initialize_variables_for_case_2_as_necessary();
kickState=2;
}
break;
case 2:
if case_2_timer_or_event_has occurred(){
perform_case_2_action();
initialize_variables_for_case_3_as_necessary();
kickState=3;
}
break;
.
.
.
case n:
if case_n_timer_or_event_has occurred(){
perform_casen2_action();
initialize_variables_for_case_0_as_necessary();
kickState=0;
}
break;
}
The above has the advantage that it’s a little easier to see how to use events (like limit switches etc) to change states, instead of being strictly timer-based.
~
Thanks for the suggestions. I should have also stated:
I’m a non-student member (I guess a mentor, in some ways), and our team is extremely small. I couldn’t even coerce any students to learn programming, so I had to learn Java with some small help from the team mentor (who knew very little more about Java than I did) and the internet starting 2 days from Kick-Off.
We were just getting the bare minimum working. But I’ll keep that in mind for the future, if/when we teach the new programmers.
One solution for solving this in Java is threads. Team 997’s kicker this year takes 4.5 seconds to reload. It was extremely important for us to make sure the driver maintained control during this time. I’ve copied in some of our code below and removed most of the extra stuff it for you to see how we did threading on our robot.
A couple things to keep in mind.
- It is vital your each thread of execution have either a Thread.yield() and/or a Timer.delay(). These are required to ensure each thread gets time on the CPU. Otherwise one thread could starve (not get any time on the CPU).
- Put things like kicking that take a long time in separate threads, but keep quick things in the main thread. I accidentally put our gear shift in the kicker thread and the driver couldn’t shift while the kicker was reloading. :o We fixed this after regionals.
Main thread that starts kicker thread:
public class RobotMain extends SimpleRobot {
final int Preload = 1;
final int Retract = 0;
final int Latch = 1;
final int Unlatch = 0;
private Joystick leftStick = new Joystick(1);
private Joystick rightStick = new Joystick(2);
public RobotMain() {
}
public void operatorControl() {
getWatchdog().setEnabled(true);
KickerThread kickerThread = new KickerThread(this);
kickerThread.start();
while (true && isOperatorControl() && isEnabled()) {
drivetrain.tankDrive(leftStick, rightStick);
getWatchdog().feed();
// DO NOT remove this yield. Required for kicker thread to execute.
Thread.yield();
}
try {
kickerThread.join();
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
}
/**
* Gets the right joystick
* @return The right joystick
*/
public Joystick getRightStick() {
return rightStick;
}
/**
* Gets the robot ready for the big kick.
*/
private void makeReadyForKick() {
//Not latched and needs to be preloaded.
if ((preloaded == false) && (latched == false)) {
driveLatchSolenoid(Latch);
Timer.delay(1);
driveShooterSolenoid(Preload);
Timer.delay(1);
}
//Latched, but needs to preload.
if ((preloaded == false) && (latched == true)) {
driveShooterSolenoid(Preload);
Timer.delay(1);
}
//Preloaded, but needs to latch.
if ((preloaded == true) && (latched == false)) {
driveShooterSolenoid(Retract);
Timer.delay(2);
driveLatchSolenoid(Latch);
Timer.delay(1);
driveShooterSolenoid(Preload);
Timer.delay(1);
}
}
/**
* Peforms the kick routine. First loads the kicker if needed.
* After kicking it loads the kicker for the next big kick.
*/
public void performKick() {
makeReadyForKick();
//Kick!
driveLatchSolenoid(Unlatch);
Timer.delay(.5); // If this changed, change the delay in autoKick!!!
//Reload for big kick.
driveShooterSolenoid(Retract);
Timer.delay(2);
driveLatchSolenoid(Latch);
Timer.delay(1);
driveShooterSolenoid(Preload);
Timer.delay(1);
System.out.println("Kick Performed");
}
}
Kicker Thread:
public class KickerThread extends Thread {
private RobotMain mRobot997;
private Joystick mRightStick;
public KickerThread(RobotMain robot) {
mRobot997 = robot;
mRightStick = mRobot997.getRightStick();
}
public void run() {
while (mRobot997.isOperatorControl() && mRobot997.isEnabled()) {
if (mRightStick.getTrigger()) {
mRobot997.performKick();
}
Thread.yield();
}
}
}
In order to allow us to drive to the next ball in autonomous while reloading, we also used threads. The main thread just set a flag on the autonomous kicker thread’s class to let it know when to go ahead and perform the kick. Does require being sure to write thread safe code.
One point of clarification, since multithreading can be a confusing topic for some. Event though the performKick() method is located in the RobotMain class, the call to performKick() located in the KickerThread class’ run() method still executes on the second thread. Any methods called during the While loop in the operatorControl() method are executing on the main thread.
Hey, welcome aboard frasnow. Check your Private Message inbox, I had a question about your post.