Not sure How to Code Actuonix L16 Actuator

Hello, I’m trying to code an Actuonix L16 but I can’t seem to find any example code for it. I’m using it like a piston to stop balls from going into the shooter on our robot. I was hoping someone would be able to either give me some example code or tell me how I could write code for it. all I need it to do is stay extended, then retract temporarily when I press a button.

Here’s our team’s code if anyone needs it for any reason

Here is the code we used for our 2021 robot should be able to get you going:

3 Likes

Would I be able to just put the example code into a command, or is it not meant for a command?

Sorry, I meant to say put it in a subsystem

This code creates an object, somewhat like a speed controller or normal servo. Then at the top of your subsystem class you can create a new instance of the servo like you would any other object. Will look something like this but instead of using the “Servo” class use LinearServo:

Are there any specific libraries I need to install to make it work?

We used the L16R in 2017 on our shooter hood.

I dropped links to our 2017 subsystem and command code in this thread:

You can use the wpilib servo class to command the actuator to different distances (see the subsystem for reference)

No, it’s just PWM, so no vendor libraries. You should be able to put the code nearly anywhere, but you need to be sure the constructor code runs once (at initialization), the periodic code is run periodically, etc. Putting this in a subsystem is a good approach.

(Edit: This assumes you are using L16-R, which seems likely. Here’s a link to the options. For the L16-R, the electrical hookup is just the same as for any other servo. The PWM signal controls the position of the actuator.)

I’m curious how the bounds were determined. The upper and lower bounds make sense based on the datasheet, but the neutral range seems very large. It’s consistent with the AndyMark example, but not with the 2168 code.

Iirc a few of the bounds can be different? We just copied the andymark data sheet and everything seemed to work perfectly.

I have added a pull request to the github of what I’ve done so far based on @jjsessa 's sample code but I’m still having errors and I don’t know how to solve them. I might have just not added everything I needed to, but I’m still not sure how to fix it.

I’m going to test out using that code when I get a chance. I know that you can program the servo with scaled values instead of angles, but would I be able to use angles for what I’m trying to do?

If you only used the ends or the exact middle, there wouldn’t be a difference. To test, set the bounds so there isn’t a deadband (as shown below), and then send a value of 0.0 and after it stops moving send 0.5. If it doesn’t move, then the deadband you have is probably correct (although you could sweep it to verify the smallest value at which it moves). 0.5 would correspond to a PWM pulse of 1.75 which is inside your neutral deadband (1.8).

setBounds(2.0, 1.5, 1.5, 1.5, 1.0);
1 Like

The normal use case for PWM deadband is for velocity control, when zero velocity is in the middle. In such a case, one wants to be sure that commanding zero velocity does not result in a small forward or reverse velocity.

With position control, such as on a regular servo or (as here) on a linear actuator, having no deadband probably makes sense almost all of the time. This is why motor controllers allow for configuration of a deadband, but normal servos do not normally do so. I don’t have one of these to try out, but the data sheet doesn’t mention any deadband. So my guess is that it is normally going to be better to not have setBounds() parameters specify a deadband.

would I be able to use angles for what I’m trying to do?

Short answer, yes.

Check out the code within the wpilib Servo library. You’ll see that the setAngle method ends up just converting the angle you supplied into a rate. This rate is determined simply from the linear map of the angle range to the rate or pwm range.

  public void setAngle(double degrees) {
    if (degrees < kMinServoAngle) {          //dont let command exceed min value
      degrees = kMinServoAngle;
    } else if (degrees > kMaxServoAngle) {   //dont let command exceed max value
      degrees = kMaxServoAngle;
    }

    setPosition(((degrees - kMinServoAngle)) / getServoAngleRange());
  }

So using the setAngle (input of degrees) method ends up calling the setPosition method (input is normalized from 0.0 to 1.0).

In the stock Servo code, these two ranges are defined by default here:

  private static final double kMaxServoAngle = 180.0;
  private static final double kMinServoAngle = 0.0;

  protected static final double kDefaultMaxServoPWM = 2.4;
  protected static final double kDefaultMinServoPWM = 0.6;

Note that the range defined by the min and max pwm values is wider than what he L16R will respond to. Per the data sheet the L16R has a min - max PWM range of 1.0 to 2.0 mS

So using the default range could work, but you wouldn’t be able to command “angles” below ~40 degrees or above ~140 degrees.
The code here (and referenced variables) makes this range change so that 0 - 180 degrees (or in our code the range is changed to 0-360) maps into the right range for the L16R

I’m pretty new to coding, so this sounds super helpful, I just don’t understand most of what you said. If I were to just copy the code in your subsystem and put it in my servo subsystem, would it work? How would I program it to where it defaults to extended as soon as the robot turns on; Also, what would I need to do to program a button to make it retract? I apologize if this is a lot to ask at once.

The minimum you want is probably this:

// Somewhere in init code…
Servo exampleServo = new Servo(1); // “1” is the PWM port where you have things plugged in, may need to be changed!
exampleServo.setBounds(2.0, 1.5, 1.5, 1.5, 1.0);

// Somewhere in code that runs periodically…
exampleServo.set(.5); // 0.0 - 1.0 to specify position for actuator

Yes, if you copy the right stuff into the right places. :smirk: /S

As with everything, the devil’s in the details

As you suspected, these are pretty broad stroke questions. So I can point you in the right direction, but can’t give you a copy/paste answer (wouldn’t do you good in the long run anyways).

I’d suggest you start by taking a look at the example robot projects that are built into the wpilib plugin in vscode. Specifically how command based robot projects work. The go-to reference reading materials are here:

To have the hood extend on startup you could kick off a command in Robot.java within the TeleopInit() or AutoInit(). Alternatively you could define the raised position to be the default command for the subsystem… Depends on how you want things to work.

Linking a command to a button should be covered well by the example code and above links into the docs.

Note that my team’s code is from 2017, and while it’s largely applicable still, there have been some design changes to wpilib, so organization might be slightly different than you’re used to (e.g. commands are mapped to buttons in OI.java vs Robot container.java).

If you run into problems getting things running please feel free to ask follow up questions (typically is helpful to share your project/code on GitHub)

I tried to use this but I wasn’t sure how to implement this:
public class LinearServo extends Servo{}

Normally when I make a subsystem it has SubsystemBase where Servo is and I don’t know how to add that in.

I was actually able to figure it out but now I don’t know how to assign it to a button, which I definitely need help with. I only know how to program a button if it uses a command, but not how to do it when there isn’t a command.