|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
|
|
Thread Tools | Rate Thread | Display Modes |
|
|
|
#1
|
|||
|
|||
|
Oscillating Motor - PID Subsystem
Hello CD,
Yet another PID help thread, I apologize, but my team and I were having troubles with implementation of the PID Subsystem to properly work with our elevator lift. Originally we had the lift just stop once it went over a certain setpoint(which worked to some degree) but we now wanted a more accurate and elegant solution. Setup: We have two standard CIM motors as the output, and one encoder as the input. There is no CAN, just PWM and regular victors being used. Issue: -The implementation of the PID Subsystem results in the motor reaching the desired setpoint; however, oscillates when it reaches it. -When the Robot is disabled quickly after the fear that the oscillation will break the elevator, when it is re-enabled it continues the PID command right when enabled without any buttons pressed. When the robot code is re-deployed or reset, this does not happen. Attempted Solutions: -Followed the GearBots Example, seems to have had some success, except for the oscillation. -Lower P constant (I and D have remained at 0). Seems to not work, it has went as low as 0.02. -Change the isFinished() method within the command to instead of detect for onTarget() method to a custom method that implemented our old code of the lift stopping after a certain setpoint. Did not work as well. -Changed Absolute Tolerance and even tried Percent Tolerance, these were set to 200 ticks for the former and 30% for the latter. Still oscillation. The Code: Subsystem Code:
package org.usfirst.frc.team4716.robot.subsystems;
import org.usfirst.frc.team4716.robot.RobotMap;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.SpeedController;
import edu.wpi.first.wpilibj.CounterBase.EncodingType;
import edu.wpi.first.wpilibj.Victor;
import edu.wpi.first.wpilibj.command.PIDSubsystem;
/**
*
*/
public class PIDElevator extends PIDSubsystem {
private DigitalInput limitSwitch;
private Encoder elevEncoder;
private SpeedController m1,m2;
// Initialize your subsystem here
public PIDElevator() {
super("PIDElevator",0.1,0.0,0.0);
setAbsoluteTolerance(5);
getPIDController().setContinuous(false);
limitSwitch = new DigitalInput(1);
m1 = new Victor(4);
m2 = new Victor(5);
elevEncoder = new Encoder(RobotMap.ELEVATOR_ENCODER_PORT_1, RobotMap.ELEVATOR_ENCDER_PORT_2,
false, EncodingType.k4X);
// Use these to get going:
// setSetpoint() - Sets where the PID controller should move the system
// to
// enable() - Enables the PID controller.
}
public void initDefaultCommand() {
// Set the default command for a subsystem here.
//setDefaultCommand(new MySpecialCommand());
}
protected double returnPIDInput() {
// Return your input value for the PID loop
// e.g. a sensor, like a potentiometer:
// yourPot.getAverageVoltage() / kYourMaxVoltage;
return elevEncoder.pidGet();
}
protected void usePIDOutput(double output) {
// Use output to drive your system, like a motor
// e.g. yourMotor.set(output);
m1.set(-output);
m2.set(-output);
}
public boolean getLimit(){
return limitSwitch.get();
}
public boolean isUp(double setpoint){
return (elevEncoder.get() >= setpoint) ? true:false;
}
public void setMotor(double x){
m1.set(x);
m2.set(x);
}
}
Command Code:
package org.usfirst.frc.team4716.robot.commands;
import org.usfirst.frc.team4716.robot.Robot;
import edu.wpi.first.wpilibj.command.Command;
/**
*
*/
public class SetElevatorSetpoint extends Command {
private double setpoint;
public SetElevatorSetpoint(double setpoint) {
// Use requires() here to declare subsystem dependencies
// eg. requires(chassis);
this.setpoint = setpoint;
requires(Robot.pidelevator);
}
// Called just before this Command runs the first time
protected void initialize() {
Robot.pidelevator.enable();
Robot.pidelevator.setSetpoint(setpoint);
}
// Called repeatedly when this Command is scheduled to run
protected void execute() {
}
// Make this return true when this Command no longer needs to run execute()
protected boolean isFinished() {
return Robot.pidelevator.onTarget();
}
// Called once after isFinished returns true
protected void end() {
Robot.pidelevator.disable();
}
// Called when another command which requires one or more of the same
// subsystems is scheduled to run
protected void interrupted() {
Robot.pidelevator.disable();
}
}
Sorry if the post is a bit lengthy, I just wanted to give as much information as possible to reduce the amount of replies to clarify information. |
|
#2
|
|||||
|
|||||
|
Re: Oscillating Motor - PID Subsystem
I haven't looked at your details yet, but in general a larger magnitude for D (that is, derivative of the error, or velocity for a mechanical system going for a position) will act like a dashpot/shock absorber and dampen your system.
|
|
#3
|
|||
|
|||
|
Re: Oscillating Motor - PID Subsystem
Quote:
How would the tuning work if the system has an issue with just a P-closed loop essentially? Would that not continue having error in the sense of a continued oscillation? Last edited by Asymons : 25-11-2015 at 20:56. |
|
#4
|
|||||
|
|||||
|
Re: Oscillating Motor - PID Subsystem
Quote:
A P-only PID feedback system is (assuming that the mechanical system is linear) is mathematically identical to an undamped spring-and-mass; you would expect it to oscillate for a long time. When the system is far from the target, it is accelerated towards the target. By the time it reaches the target, it has significant speed, but there is no frictional term to slow it down, so it overshoots. You have essentially made a system in which F=kx (Hooke's Law). In terms of a differential equation, it becomes the simple x''=(k/m)x, where k/m is proportional to your P term. If you have done calculus 1 it is easy to verify that one solution to this equation is x=sin(sqrt(k/m)t), that is displacement is described as an undamped sine wave. Some sort of friction term is required to dissipate the initial "potential energy"; neither P nor I do this. Both D and mechanical friction will dampen the oscillations. Last edited by GeeTwo : 25-11-2015 at 21:32. |
|
#5
|
|||
|
|||
|
Re: Oscillating Motor - PID Subsystem
Quote:
![]() |
|
#6
|
|||||
|
|||||
|
Re: Oscillating Motor - PID Subsystem
Quote:
--Gus |
|
#7
|
||||
|
||||
|
Re: Oscillating Motor - PID Subsystem
Regarding the command continuing to run between the robot being enabled/disabled:
Your command will only finish if the onTarget method returns true. Code:
protected boolean isFinished() {
return Robot.pidelevator.onTarget();
}
Add something like: Code:
Scheduler.getInstance().removeAll(); Scheduler.getInstance().disable(); |
|
#8
|
|||
|
|||
|
Re: Oscillating Motor - PID Subsystem
In that case, Gus' suggestion, as well as James worked!
So my solution was as followed: -Added the D value, set it to 1.0 and eventually lowered this value to 0.1 due to the lift "skipping". -Absolute Tolerance was adjusted from 200 to 20, which it finally remained at 50 to keep the lift moving smoothly. -P value was adjusted to 0.05 -Added Robot.elevator.disable(); at end of command Doing all this, I was able to get the lift to stop at a set point, or have it skip once and get to the set point. I'm most likely going to learn common tuning practices;however, I've mostly seen people use the "guess and check" method. Last edited by Asymons : 26-11-2015 at 12:11. |
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|