# Programming Arm states (Java, Command-Based)

Hi! Our team has built an arm that has 180degrees of movement (front to back axis). We need to program it to keep the angle that we want (preferably pre-programmed angle states). We wanted to do it by PID and encoder. I calculated the ratios and have accurate angle of arm itself but when testing PID to setPoint it to 15deg it flew all the way back and slammed floor. The encoder values were good i think. The arm is somewhat heavy but motors have more than enough power to power it. (20% is fast enough for moving it)

I need help with programming it to angle states and making it keep angle with no driver input. Iâ€™m kinda worried with testing it, bcs i dont want to slam it again. If you know some good way to protect it when tesing that would help.

(the code is here: GitHub - SpiceGears/FRC-2023 at develop
develop branch is failed pid attempt, and main is working code on simple â€ścan power when encoder is not over the topâ€ť)

1 Like

Physical protectionâ€¦ tape a pool noodle to it.

Coding protectionâ€¦ use a clamping function to make sure you limit it while tuning it. As you get dialed in relax the clamp.

``````  public void useOutput(double output, TrapezoidProfile.State setpoint) {

// Calculate the feedforward from the sepoint
double feedforward = m_feedforward.calculate(setpoint.position, setpoint.velocity);

// Add the feedforward to the PID output to get the motor output
double volts = MathUtil.clamp(output + feedforward, -6.0, 6.0);
m_motor.setVoltage(volts);
}
``````

This based on the WPILIB arm example.

Ok, I havenâ€™t really worked with feedforward before, and i didnâ€™t do TrapeziodProfile thingy for setpoint and constraints, i just had simple angles of arm.

How do I find the velocity and all of the `kS` `kV` `kA` values for feedforward?
And what is this `double output`?
And if i implement it and change the setpoint going into this function, will the arm will smoothly translate to new setpoint?
I donâ€™t understand the code in `super()`

``````  public ArmSubsystem() {
super(
new ProfiledPIDController(
ArmConstants.kP,
0,
0,
new TrapezoidProfile.Constraints(
0);
m_encoder.setDistancePerPulse(ArmConstants.kEncoderDistancePerPulse);
// Start arm at rest in neutral position
}
``````

First of all, set kI to 0. That alone probably caused what you saw due to integral windup if you changed your setpoint directly from 0 to 180 or whatever.

Feedforward is the correct answer. You can use the ArmFeedForward class in WPILib (this is a direct link, but i highly recommend reading the whole page). You can get the necessary constants (kG, kS, kV) empirically, or by using sysID (which will also give you kA)

2 Likes

In the example it is the result of the profiled (trapezoid) PID Controller.

A trapezoid profile allows you to control the maximum velocity and acceleration of the output from the PID. You donâ€™t need to do itâ€¦ you can control the maximum with the clamp too. However, the trapezoid profile allows â€śsmoothâ€ť increase in velocity of your arm.

It is also possible that your motor or your encoder are inverted so that the feedback is actively causing the error to get worse, instead of better. Before running your feedback loop, trying running your motor with positive power and ensure that the encoder readings are going up, not down.

I have something like this for now

`````` private final ArmFeedforward m_feedforward =
new ArmFeedforward(

public ArmSubsystem() {

leftArmMaster = new VictorSP(PortMap.ARM.LEFT_MASTER_PORT);
rightArmMaster = new VictorSP(PortMap.ARM.RIGHT_MASTER_PORT);
leftArmSlave = new VictorSP(PortMap.ARM.LEFT_SLAVE_PORT);
rightArmSlave = new VictorSP(PortMap.ARM.RIGHT_SLAVE_PORT);

armGroup = new MotorControllerGroup(leftArmMaster, rightArmMaster, leftArmSlave, rightArmSlave);

armEncoder = new Encoder(PortMap.ARM.ENCODER_PORT_A, PortMap.ARM.ENCODER_PORT_B);
armEncoder.setDistancePerPulse(Constants.ARM.ENCODER_ANGLES_PER_ROTATION / Constants.ARM.ENCODER_TICK_RATE);
// armEncoder.setMaxPeriod(Constants.ARM.ENCODER_MIN_RATE);
armEncoder.setReverseDirection(Constants.ARM.ENCODER_REVERSE);
armEncoder.setSamplesToAverage(Constants.ARM.ENCODER_SAMPLES_TO_AVERAGE);

frontLimitSwitch = new DigitalInput(6);
backLimitSwitch = new DigitalInput(7);

ProfiledPIDController profiledController = new ProfiledPIDController(
Constants.ARM.kP,
Constants.ARM.kI,
Constants.ARM.kD,
new TrapezoidProfile.Constraints(
0.35, // in radians 0.35 == 20deg
0.35)); // in radians 0.35 == 20deg

}

//* Rotate arm to specific angle */
public void rotateArmByAngle(double angle) {

}
``````

How I understand it, I donâ€™t need to worry about the thetas, and just figure out the Kâ€™s in this formula for feedforward.
(Kg isnt going to be 9.81, it would be too easy right?)

And I donâ€™t know how to implement it into a method. would something like this work and move the arm? :

``````  armGroup.setVoltage(6* feedforward.calculate(0.35, 2, 2));
``````

No; you can tell this because the units would not make any sense. That equation needs to yield volts. The acceleration of gravity has units of meters per second squared.

so for testing i can assume and set Kg to 1
as 1volt is going to counterbalance the gravity

is it right?

(ofc I dont know how much itâ€™d be)

Iâ€™m not sure. What test have you run to determine how much voltage is needed to counteract gravity?

for now none, i just tested the arm with completely failed pid

and tapping 20% power at motor is making it go up fairly quickly.

You will have to either use manual calculation, an online calculator like ReCalc, or physical test routines to figure out the relevant gains. I suggest doing something a bit more precise than tapping on a joystick - do you have working telemetry so you can read the applied voltage on your dashboard?

not really, but reading voltage to dashboard should be doable easily.
I tested the robot yesterday, today i program it in home and tommorow iâ€™ll have a session of testing it and changing gains etc.

1 Like

`kG` and `kS` can be estimated easily via manual control. The others will probably require theoretical estimation or a `SysId` routine.

Iâ€™m not sure about the weight and CoM distance and efficiency (andymark toughbox â†’ chain reduction)

And are the start angle and end angle from horizontal ground? so -15 to like 90 or 180 to the back of it?

From this calculator kG should be roughly around .8V to 1.5V

Those are what really determine your gains, so if youâ€™re not sure about those your gains probably wonâ€™t be correct. You should try to measure them as accurately as possible.

Efficiency is usually well-estimated by a 10% loss per reduction stage (a little less for gears/belt, a little more for chain).

Are you logging your encoders values? Before we run PID we always log our encoders and check they move to desired values when we move the subsystem manually (to rule out incorrect gear ratio entered in code, encoders inverted, etc).

yeah, that time it wasnt encoders reading fault, because they were showing good angle. I think it was due to kI value.

how do i use SysId?
I want to get ready before tonight so i can manage time efficiently.
From what i expect, I hook it up to robot move the arm up and down, and record the data to .JSON and there in the middle i put this .JSON and the values should be ok?

And from the programming side, I donâ€™t know if this will move the arm parallel to the floor or to zero position,
and how does it know where it is, when it doesnt even use encoder?