WPILib中的PID控制

备注

This article focuses on in-code implementation of PID control in WPILib. For a conceptual explanation of the working of a PIDController, see PID简介

备注

关于通过以下指南来实现PID控制 command-based framework,参见 通过PID子系统和PID指令进行PID控制.

WPILib supports PID control of mechanisms through the PIDController class (Java, C++, Python). This class handles the feedback loop calculation for the user, as well as offering methods for returning the error, setting tolerances, and checking if the control loop has reached its setpoint within the specified tolerances.

使用PIDController类

构造一个PIDController

备注

虽然``PIDController``可以异步使用,但它不提供任何线程安全功能-确保线程安全操作完全留给用户使用,因此仅建议高级团队使用异步方法。

为了使用WPILib的PID控制功能,用户必须首先构造具有所需增益的“ PIDController”对象:

// Creates a PIDController with gains kP, kI, and kD
PIDController pid = new PIDController(kP, kI, kD);
// Creates a PIDController with gains kP, kI, and kD
frc::PIDController pid{kP, kI, kD};
from wpimath.controller import PIDController

# Creates a PIDController with gains kP, kI, and kD
pid = PIDController(kP, kI, kD)

可以向构造函数提供一个可选的第四个参数,指定控制器将运行的时间段。 PIDController对象主要用于与机器人主循环同步使用,因此该值默认为20ms。

使用反馈回路输出

备注

PIDController假定以与配置的周期一致的时间间隔定期调用calculate()方法。否则,将导致意外的循环行为。

使用构造的PIDController很简单:只需从机器人主循环中调用calculate()方法(例如,机器人的autonomousPeriodic()方法):

// Calculates the output of the PID algorithm based on the sensor reading
// and sends it to a motor
motor.set(pid.calculate(encoder.getDistance(), setpoint));
// Calculates the output of the PID algorithm based on the sensor reading
// and sends it to a motor
motor.Set(pid.Calculate(encoder.GetDistance(), setpoint));
# Calculates the output of the PID algorithm based on the sensor reading
# and sends it to a motor
motor.set(pid.calculate(encoder.getDistance(), setpoint))

检查错误

备注

getPositionError()和getVelocityError()的命名是假设循环正在控制位置-对于控制速度的循环,它们分别返回速度误差和加速度误差。

测量的过程变量的当前错误由getPositionError()函数返回,而其派生由getVelocityError()函数返回:

指定和检查公差

备注

如果仅指定位置公差,则速度公差默认为无穷大。

备注

如上所述,“位置”是指过程变量的测量值,“速度”是指其导数-因此,对于速度环,它们实际上分别是速度和加速度。

有时,知道控制器是否跟踪了设定值到给定的容忍范围内是有用的——例如,确定一个命令是否应该结束,或者(在遵循一个运动轮廓时)是否运动被阻碍,需要重新规划。

为此,我们首先必须使用’ ‘ setTolerance() ‘ ‘方法指定公差;然后,我们可以用’ ‘ atSetpoint() ‘ ‘方法检查它。

// Sets the error tolerance to 5, and the error derivative tolerance to 10 per second
pid.setTolerance(5, 10);

// Returns true if the error is less than 5 units, and the
// error derivative is less than 10 units
pid.atSetpoint();
// Sets the error tolerance to 5, and the error derivative tolerance to 10 per second
pid.SetTolerance(5, 10);

// Returns true if the error is less than 5 units, and the
// error derivative is less than 10 units
pid.AtSetpoint();
# Sets the error tolerance to 5, and the error derivative tolerance to 10 per second
pid.setTolerance(5, 10)

# Returns true if the error is less than 5 units, and the
# error derivative is less than 10 units
pid.atSetpoint()

重置控制器

有时需要清除``PIDController 的内部状态(最重要的是,积分累加器),因为它可能不再有效(例如,``PIDController ``已禁用,然后重新启用)。这可以通过调用 reset() ``方法来完成。

设置最大积分器值

备注

积分器将不稳定性和迟滞引入反馈回路系统。强烈建议团队避免使用积分增益除非绝对没有其他解决方案——通常,问题可以解决一个积分器可以更好的通过使用更为精确的解决 feedforward.

A typical problem encountered when using integral feedback is excessive “wind-up” causing the system to wildly overshoot the setpoint. This can be alleviated in a number of ways - the WPILib PIDController class enforces an integrator range limiter to help teams overcome this issue.

By default, the total output contribution from the integral gain is limited to be between -1.0 and 1.0.

The range limits may be increased or decreased using the setIntegratorRange() method.

// The integral gain term will never add or subtract more than 0.5 from
// the total loop output
pid.setIntegratorRange(-0.5, 0.5);
// The integral gain term will never add or subtract more than 0.5 from
// the total loop output
pid.SetIntegratorRange(-0.5, 0.5);
# The integral gain term will never add or subtract more than 0.5 from
# the total loop output
pid.setIntegratorRange(-0.5, 0.5)

Disabling Integral Gain if the Error is Too High

Another way integral “wind-up” can be alleviated is by limiting the error range where integral gain is active. This can be achieved by setting IZone. If the error is more than IZone, the total accumulated error is reset, disabling integral gain. When the error is equal to or less than IZone, integral gain is enabled.

By default, IZone is disabled.

IZone may be set using the setIZone() method. To disable it, set it to infinity.

// Disable IZone
pid.setIZone(Double.POSITIVE_INFINITY);

// Integral gain will not be applied if the absolute value of the error is
// more than 2
pid.setIZone(2);
// Disable IZone
pid.SetIZone(std::numeric_limits<double>::infinity());

// Integral gain will not be applied if the absolute value of the error is
// more than 2
pid.SetIZone(2);
# Disable IZone
pid.setIZone(math.inf)

# Integral gain will not be applied if the absolute value of the error is
# more than 2
pid.setIZone(2)

设置连续输入

警告

如果你的机械装置不能完全连续旋转运动(例如,一个没有滑环的炮塔,它的电线在旋转时扭曲),*不要*开启连续输入,除非你安装了额外的安全装置,以防止机械装置移动超过其限制!

警告

连续输入函数*不会*自动包装您的输入值-请确保您的输入值,在使用此功能时,永远不会超出指定的范围!

Some process variables (such as the angle of a turret) are measured on a circular scale, rather than a linear one - that is, each “end” of the process variable range corresponds to the same point in reality (e.g. 360 degrees and 0 degrees). In such a configuration, there are two possible values for any given error, corresponding to which way around the circle the error is measured. It is usually best to use the smaller of these errors.

要配置一个`` PIDController ``来自动执行此操作,请使用``enableContinuousInput() ``方法:

// Enables continuous input on a range from -180 to 180
pid.enableContinuousInput(-180, 180);
// Enables continuous input on a range from -180 to 180
pid.EnableContinuousInput(-180, 180);
# Enables continuous input on a range from -180 to 180
pid.enableContinuousInput(-180, 180)

钳位控制器输出

// Clamps the controller output to between -0.5 and 0.5
MathUtil.clamp(pid.calculate(encoder.getDistance(), setpoint), -0.5, 0.5);
// Clamps the controller output to between -0.5 and 0.5
std::clamp(pid.Calculate(encoder.GetDistance(), setpoint), -0.5, 0.5);
# Python doesn't have a builtin clamp function
def clamp(v, minval, maxval):
    return max(min(v, maxval), minval)

# Clamps the controller output to between -0.5 and 0.5
clamp(pid.calculate(encoder.getDistance(), setpoint), -0.5, 0.5)