Command-based help

I am trying to control our motor with command-based which follows the wpilib doc and examples. It just a simple control way with subsystem. However, I can’t run the arm, could anyone help? Below are my codes.

This looks like it should work - are you sure you have your joysticks configured correctly?

Yeh, and I also check the id. I have test it with time robot code, and the motor can run.

If you add some print statements to your methods, commands, triggers, you can isolate where the trouble may be. This saves everyone from going through the 1000’s of things that could go wrong and saves you from having to answer each one with “Yes, I checked that.”

You also need to check the riolog or driver station console for errors (two of the 1000’s of items on the check list).

We are also assuming you meant to write that you “can’t” run the arm. (I leave off the “not” almost all the time and have to go back and fix everything I write.)

You really should upload all of your code as the full robot project. This code won’t work as posted to github cause it doesn’t have any of the right project stuff and it doesn’t have the right folder structure.

I would recommend the structure of having your subsystem actually do the action and then inline a command rather than having your subsystem return a command.

Is there a reason why you are doing whileTrue rather than onTrue? You don’t need to keep sending the command to the motor.

I had tried to do that but failed. But I will keep try it tomorrow.

Because the doc show this is a great way to run simple command. If I do that, I will have lots of command?
Thanks for your suggestions, I will try it.

I just follow wpilib examples, I will change it, thanks!

Ok, I will add it.

Yes, I do.

It is somewhat contradictory to use whileTrue in your controller binding, but runOnce for your command. It makes me think you intended to use run so it would continuously tell your motor to run while the button is held.

More detail… whileTrue runs a command as long as the button is held down, cancelling it when the button is released. runOnce constructs a command that runs your code only once, in it’s initialize method, and then it ends. So ultimately, your code arm_motor.set(speed) only runs one time, regardless of using whileTrue. If you intended it to keep running arm_motor.set(speed) on every iteration while the button is held, you should use run to construct the command in your subsystem. run will construct a command that continuously runs your code in it’s execute method, until the command is cancelled by releasing the button.

In any case, I downloaded your code and ran it in the simulator and it works. What behavior are you seeing, what are the lights on the Spark Max doing? What messages are printing in the log?

3 Likes

You may be on to something with your analysis. If the single quick pulse of a set(0.5) wasn’t seen then the motor won’t operate. We could assume the simulator isn’t going to miss that set but what about the real motor controller? Was the controller ready to accept a set so soon after construction? Supposed to be but… . Best practice is to refresh the set at each cycle then you know what you it got there and you point out that isn’t happening.

1 Like

My first thought was motor safety but as far as I know, CANSparkMax does not implement this.

I agree with @SLAB-Mr.Thomas, it’s still a best practice to continuously tell your actuators what to do.

I appreciate your suggestions, and after I transform runOnce into run, it run.

Actually, my original idea is to return command for only one system in subsystem, and return command for more than two systems in command. However, it seem like most of teams’ subsystem don’t return the command. I wonder what is the best structure to do this in order to decide what is my next goal to program.

1 Like

Returning single-subsystem commands from the subsystem is by far the cleanest way to use the library; it’s not common in team code yet because it’s a fairly new feature.

Multi-subsystem commands can be composed in RobotContainer.

3 Likes

This does require quite a bit of a mind-shift for teams, especially mentors that have been doing it their way for years.

Over the years that I have mentored (since 2015 or so), we’ve had far fewer buggy autos, or dead robots, or anything like that software related. Part of that was getting a firm understanding of command-based programming and orchestrating them through the lifecycle methods (initialize, execute, isfinished, etc).

Re-mapping that sort of thing from subclassing Command and doing all the work in the single object and lifecycle methods, into the new paradigm of method-based command composition is not always entirely a clear path forward.

I think it’s the correct path forward, but I understand why it’s not common in team code quite yet.

2 Likes

I agree and want my team to fully embrace Command-Based. I pointed out to my team that the structure of the Robot.java that they learned well was an “init, periodic, exit” (and exit is nearly useless). There is only one set for “auto” and one set for teleop.

A robot is actually composed of several components with many actions each of which need their own dedicated set of “init, periodic, exit.” It requires many tortuous if, else, switch, case, timer statements to force all those components and actions into the single set of “init, periodic, exit” that they know.

Enter the command-based that readily gives each action its own set of (now called) “initialize, execute, end.” There was resistance but they get it. Good riddance to all those flow control statements. Life is so much better. So where do we get badly stuck and resist command-based? I’ll point out a problem that I and others - exemplified by this thread’s OP misusing run/runOnce - find disconcerting.

The prewritten commands and factories are shortcuts. Shortcuts in general are very hard to remember, if you don’t use them much. Google “shortcuts are dangerous” and you’ll get a million hits about never use shortcuts - they can kill you. Google “shortcuts are useful” and another million hits show the fantastic productivity gains.

Anything Command-Based authors can do to mistake-proof the use of shortcuts - easy names, predictable actions associated with a shortcut name, training and documentation materials, etc. - would go a long way to Command-Based acceptance.

1 Like

WPILib skews heavily towards eagerly breaking things to improve ergonomics. If anyone has an idea for a better name for run, they can open a github issue or PR. We take those seriously; the modern Promise-like API emerged from many incremental changes introduced in just this way.

Documentation is open-source “all the way down,” and we accept contributions eagerly. Part of the reason there’s no single coherent set of documentation for the “modern” usage pattern is that it emerged rather than being designed all at once. No single WPILib developer has the time to keep all of the documentation up to date with what we feel is the best way to use the library - we really need the community to pitch in.

I winced badly when I wrote my post knowing I was failing to heed one of my good bosses directives - “don’t bring me problems without a suggested solution.”

I’d encourage OP @J.F to comment on what could have been done to get them running quicker and what can be done to help others succeed in the example where they struggled. Maybe they could help annotate the example with possible alternatives that would also work or show how they wouldn’t work.

I’m (still) working on my own Command-Based Summary that isn’t fit to share now. I keep thinking about the article Worse Is Better and how there are too many choices or ways to do the same thing (as usual - that’s a bane of living, in general, and especially software development). (Other CD posts have explained the evolution of “addition for improvement” and understandable maintenance of the old for backward compatibility and the fine detailed structure for “advanced” usage.)

I think most students (and mentors) and teams are better served with a small set of prescriptions that are easy to remember how to use correctly - “do it this way and you’ll be fine.” Command-based seems so hard when all you want to do is get the robot moving for the first competition when actually it’s easy to make it dance somewhat with few carefully chosen commands.

1 Like

I couldn’t agree more. It’s even harder because a lot of the most-expressive and best-behaved “verbs” are fairly recent innovations; the language we were using even two years ago was a lot clunkier.

It’ll get better over time as some of the deprecated naming overloads finally reach removal, but I think we’re also kind of stuck with a representation overload as long as APCS insists on focusing on parsing all problems exclusively into class hierarchies. I think we’ve reached a good compromise under the constraints as far as language features go, but the documentation is definitely lagging.

Thank you!
I will then start to test PID with command-based. Will you suggest me to use single-command to program or use PIDSubsystems and PIDCommands (I saw on doc)?

No, I’d avoid those and just compose the PIDController inside the Subsystem or Command scope manually, as necessary. Here are some simplified examples:

// in subsystem scope

// we need a motor and sensor for feedback
private final MotorController motor = new FooMotor();
private final Encoder encoder = new Encoder(...);

// we could have a PIDController as a field in the subsystem itself...
private final PIDController controller = new PIDController(...);

// this command captures the subsystem's PIDController, like PIDSubsystem
public Command moveToPosition(double position) {
    return runOnce(controller::reset)
        .andThen(run(() -> {
            motor.set(controller.calculate(
                encoder.getPosition(),
                position
            ));
         }).finallyDo(motor::stop);
}

// if we don't want to persist the controller in the subsystem after the command ends...

// this command captures its *own* controller, like PIDCommand
public Command moveToPosition(double position) {
    PIDController controller = new PIDController(...);
    // we don't have to reset a fresh controller
    return run(() -> {
            motor.set(controller.calculate(
                encoder.getPosition(),
                position
            ));
         }).finallyDo(motor::stop);
}
5 Likes

The nice thing about these two examples is they show that the code for the action is exactly what has been used for a few years. Just add a couple of lines of simple code to make a command and all the scheduling of the action is done for you!

The sad thing of these examples is the “don’t use the documented way.”

Comment about the 2024 documents that I just finished reading. I did like the documents as they are always improving. It would be much more hospitable to me and I think get the students attention better, if the Java code and the C++ code weren’t interleaved. The C++ stuff is a lot of noise and the large quantity adds to the intimidation of the Java programmer.

For the WPILib documenter’s sanity some sort of generator is needed to create two separate threads from the interleaved version.

1 Like