From 635e971a0283e3773bdca19d1481e7bd61af3fbf Mon Sep 17 00:00:00 2001 From: Thad House Date: Fri, 29 May 2026 16:31:48 -0700 Subject: [PATCH] [wpilib] Add a default deadband to all gamepads (#8897) The FTC DS had a deadband. Additionally, the FRC one had an implicit deadband due to the only 8 bit resolution. We need a deadband by default now with the high resolution gamepads. --- .../frc2/command/button/CommandGamepad.cpp | 4 +- .../main/native/cpp/driverstation/Gamepad.cpp | 53 +++++++-- .../include/wpi/driverstation/Gamepad.hpp | 79 +++++++++++++ wpilibc/src/main/python/semiwrap/Gamepad.yml | 6 + .../org/wpilib/driverstation/Gamepad.java | 107 +++++++++++++++++- 5 files changed, 233 insertions(+), 16 deletions(-) diff --git a/commandsv2/src/main/native/cpp/frc2/command/button/CommandGamepad.cpp b/commandsv2/src/main/native/cpp/frc2/command/button/CommandGamepad.cpp index 2f0bf2aa69..7d50affddd 100644 --- a/commandsv2/src/main/native/cpp/frc2/command/button/CommandGamepad.cpp +++ b/commandsv2/src/main/native/cpp/frc2/command/button/CommandGamepad.cpp @@ -127,14 +127,14 @@ Trigger CommandGamepad::Misc6(wpi::EventLoop* loop) const { Trigger CommandGamepad::LeftTrigger(double threshold, wpi::EventLoop* loop) const { return Trigger(loop, [this, threshold] { - return m_hid.GetLeftTriggerAxis() > threshold; + return m_hid.GetAxis(wpi::Gamepad::Axis::LEFT_TRIGGER) > threshold; }); } Trigger CommandGamepad::RightTrigger(double threshold, wpi::EventLoop* loop) const { return Trigger(loop, [this, threshold] { - return m_hid.GetRightTriggerAxis() > threshold; + return m_hid.GetAxis(wpi::Gamepad::Axis::RIGHT_TRIGGER) > threshold; }); } diff --git a/wpilibc/src/main/native/cpp/driverstation/Gamepad.cpp b/wpilibc/src/main/native/cpp/driverstation/Gamepad.cpp index 2347984f6e..0519b99553 100644 --- a/wpilibc/src/main/native/cpp/driverstation/Gamepad.cpp +++ b/wpilibc/src/main/native/cpp/driverstation/Gamepad.cpp @@ -4,39 +4,71 @@ #include "wpi/driverstation/Gamepad.hpp" +#include +#include + #include "wpi/event/BooleanEvent.hpp" #include "wpi/hal/UsageReporting.hpp" +#include "wpi/math/util/MathUtil.hpp" #include "wpi/util/sendable/SendableBuilder.hpp" using namespace wpi; +static double ClampDeadband(double deadband) { + if (std::isnan(deadband)) { + return 0.0; + } + return std::clamp(deadband, 0.0, std::nextafter(1.0, 0.0)); +} + Gamepad::Gamepad(int port) : GenericHID(port) { HAL_ReportUsage("HID", port, "Gamepad"); } double Gamepad::GetLeftX() const { - return GetAxis(Axis::LEFT_X); + return wpi::math::ApplyDeadband(GetAxis(Axis::LEFT_X), m_leftXDeadband); +} + +void Gamepad::SetLeftXDeadband(double deadband) { + m_leftXDeadband = ClampDeadband(deadband); } double Gamepad::GetLeftY() const { - return GetAxis(Axis::LEFT_Y); + return wpi::math::ApplyDeadband(GetAxis(Axis::LEFT_Y), m_leftYDeadband); +} + +void Gamepad::SetLeftYDeadband(double deadband) { + m_leftYDeadband = ClampDeadband(deadband); } double Gamepad::GetRightX() const { - return GetAxis(Axis::RIGHT_X); + return wpi::math::ApplyDeadband(GetAxis(Axis::RIGHT_X), m_rightXDeadband); +} + +void Gamepad::SetRightXDeadband(double deadband) { + m_rightXDeadband = ClampDeadband(deadband); } double Gamepad::GetRightY() const { - return GetAxis(Axis::RIGHT_Y); + return wpi::math::ApplyDeadband(GetAxis(Axis::RIGHT_Y), m_rightYDeadband); +} + +void Gamepad::SetRightYDeadband(double deadband) { + m_rightYDeadband = ClampDeadband(deadband); } double Gamepad::GetLeftTriggerAxis() const { - return GetAxis(Axis::LEFT_TRIGGER); + return wpi::math::ApplyDeadband(GetAxis(Axis::LEFT_TRIGGER), + m_leftTriggerDeadband); +} + +void Gamepad::SetLeftTriggerDeadband(double deadband) { + m_leftTriggerDeadband = ClampDeadband(deadband); } BooleanEvent Gamepad::LeftTrigger(double threshold, EventLoop* loop) const { return BooleanEvent(loop, [this, threshold] { - return this->GetLeftTriggerAxis() > threshold; + return this->GetAxis(Axis::LEFT_TRIGGER) > threshold; }); } @@ -45,12 +77,17 @@ BooleanEvent Gamepad::LeftTrigger(EventLoop* loop) const { } double Gamepad::GetRightTriggerAxis() const { - return GetAxis(Axis::RIGHT_TRIGGER); + return wpi::math::ApplyDeadband(GetAxis(Axis::RIGHT_TRIGGER), + m_rightTriggerDeadband); +} + +void Gamepad::SetRightTriggerDeadband(double deadband) { + m_rightTriggerDeadband = ClampDeadband(deadband); } BooleanEvent Gamepad::RightTrigger(double threshold, EventLoop* loop) const { return BooleanEvent(loop, [this, threshold] { - return this->GetRightTriggerAxis() > threshold; + return this->GetAxis(Axis::RIGHT_TRIGGER) > threshold; }); } diff --git a/wpilibc/src/main/native/include/wpi/driverstation/Gamepad.hpp b/wpilibc/src/main/native/include/wpi/driverstation/Gamepad.hpp index a7a004481b..c253f41fba 100644 --- a/wpilibc/src/main/native/include/wpi/driverstation/Gamepad.hpp +++ b/wpilibc/src/main/native/include/wpi/driverstation/Gamepad.hpp @@ -116,39 +116,99 @@ class Gamepad : public GenericHID, /** * Get the X axis value of left side of the controller. Right is positive. * + * A deadband of 0.1 is applied by default. Use SetLeftXDeadband() to change + * it. + * * @return the axis value. */ double GetLeftX() const; + /** + * Set the deadband for the left X axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetLeftXDeadband(double deadband); + /** * Get the Y axis value of left side of the controller. Back is positive. * + * A deadband of 0.1 is applied by default. Use SetLeftYDeadband() to change + * it. + * * @return the axis value. */ double GetLeftY() const; + /** + * Set the deadband for the left Y axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetLeftYDeadband(double deadband); + /** * Get the X axis value of right side of the controller. Right is positive. * + * A deadband of 0.1 is applied by default. Use SetRightXDeadband() to change + * it. + * * @return the axis value. */ double GetRightX() const; + /** + * Set the deadband for the right X axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetRightXDeadband(double deadband); + /** * Get the Y axis value of right side of the controller. Back is positive. * + * A deadband of 0.1 is applied by default. Use SetRightYDeadband() to change + * it. + * * @return the axis value. */ double GetRightY() const; + /** + * Set the deadband for the right Y axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetRightYDeadband(double deadband); + /** * Get the left trigger axis value of the controller. Note that this axis * is bound to the range of [0, 1] as opposed to the usual [-1, 1]. * + * A deadband of 0.01 is applied by default. Use SetLeftTriggerDeadband() to + * change it. + * * @return the axis value. */ double GetLeftTriggerAxis() const; + /** + * Set the deadband for the left trigger axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetLeftTriggerDeadband(double deadband); + /** * Constructs an event instance around the axis value of the left trigger. * The returned trigger will be true when the axis value is greater than @@ -175,10 +235,22 @@ class Gamepad : public GenericHID, * Get the right trigger axis value of the controller. Note that this axis * is bound to the range of [0, 1] as opposed to the usual [-1, 1]. * + * A deadband of 0.01 is applied by default. Use SetRightTriggerDeadband() to + * change it. + * * @return the axis value. */ double GetRightTriggerAxis() const; + /** + * Set the deadband for the right trigger axis. + * + * The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + void SetRightTriggerDeadband(double deadband); + /** * Constructs an event instance around the axis value of the right trigger. * The returned trigger will be true when the axis value is greater than @@ -1090,6 +1162,13 @@ class Gamepad : public GenericHID, private: double GetAxisForSendable(Axis axis) const; bool GetButtonForSendable(Button button) const; + + double m_leftXDeadband = 0.1; + double m_leftYDeadband = 0.1; + double m_rightXDeadband = 0.1; + double m_rightYDeadband = 0.1; + double m_leftTriggerDeadband = 0.01; + double m_rightTriggerDeadband = 0.01; }; } // namespace wpi diff --git a/wpilibc/src/main/python/semiwrap/Gamepad.yml b/wpilibc/src/main/python/semiwrap/Gamepad.yml index 667bac9e67..ce6e39b1c8 100644 --- a/wpilibc/src/main/python/semiwrap/Gamepad.yml +++ b/wpilibc/src/main/python/semiwrap/Gamepad.yml @@ -8,15 +8,21 @@ classes: methods: Gamepad: GetLeftX: + SetLeftXDeadband: GetLeftY: + SetLeftYDeadband: GetRightX: + SetRightXDeadband: GetRightY: + SetRightYDeadband: GetLeftTriggerAxis: + SetLeftTriggerDeadband: LeftTrigger: overloads: double, EventLoop* [const]: EventLoop* [const]: GetRightTriggerAxis: + SetRightTriggerDeadband: RightTrigger: overloads: double, EventLoop* [const]: diff --git a/wpilibj/src/main/java/org/wpilib/driverstation/Gamepad.java b/wpilibj/src/main/java/org/wpilib/driverstation/Gamepad.java index 406efce371..1680218012 100644 --- a/wpilibj/src/main/java/org/wpilib/driverstation/Gamepad.java +++ b/wpilibj/src/main/java/org/wpilib/driverstation/Gamepad.java @@ -8,6 +8,7 @@ import org.wpilib.driverstation.internal.DriverStationBackend; import org.wpilib.event.BooleanEvent; import org.wpilib.event.EventLoop; import org.wpilib.hardware.hal.HAL; +import org.wpilib.math.util.MathUtil; import org.wpilib.util.sendable.Sendable; import org.wpilib.util.sendable.SendableBuilder; @@ -23,6 +24,8 @@ import org.wpilib.util.sendable.SendableBuilder; * party controllers. */ public class Gamepad extends GenericHID implements Sendable { + private static final double MAX_DEADBAND = Math.nextDown(1.0); + /** Represents a digital button on a Gamepad. */ public enum Button { /** Face Down button. */ @@ -139,6 +142,20 @@ public class Gamepad extends GenericHID implements Sendable { } } + private double m_leftXDeadband = 0.1; + private double m_leftYDeadband = 0.1; + private double m_rightXDeadband = 0.1; + private double m_rightYDeadband = 0.1; + private double m_leftTriggerDeadband = 0.01; + private double m_rightTriggerDeadband = 0.01; + + private static double clampDeadband(double deadband) { + if (Double.isNaN(deadband)) { + return 0.0; + } + return Math.clamp(deadband, 0.0, MAX_DEADBAND); + } + /** * Construct an instance of a controller. * @@ -149,50 +166,126 @@ public class Gamepad extends GenericHID implements Sendable { HAL.reportUsage("HID", port, "Gamepad"); } + /** + * Set the deadband for the left X axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setLeftXDeadband(double deadband) { + m_leftXDeadband = clampDeadband(deadband); + } + + /** + * Set the deadband for the left Y axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setLeftYDeadband(double deadband) { + m_leftYDeadband = clampDeadband(deadband); + } + + /** + * Set the deadband for the right X axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setRightXDeadband(double deadband) { + m_rightXDeadband = clampDeadband(deadband); + } + + /** + * Set the deadband for the right Y axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setRightYDeadband(double deadband) { + m_rightYDeadband = clampDeadband(deadband); + } + + /** + * Set the deadband for the left trigger axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setLeftTriggerDeadband(double deadband) { + m_leftTriggerDeadband = clampDeadband(deadband); + } + + /** + * Set the deadband for the right trigger axis. + * + *

The deadband is clamped to [0, 1). + * + * @param deadband The deadband to apply. + */ + public void setRightTriggerDeadband(double deadband) { + m_rightTriggerDeadband = clampDeadband(deadband); + } + /** * Get the X axis value of left side of the controller. Right is positive. * + *

A deadband of 0.1 is applied by default. Use {@link #setLeftXDeadband} to change it. + * * @return The axis value. */ public double getLeftX() { - return getAxis(Axis.LEFT_X); + return MathUtil.applyDeadband(getAxis(Axis.LEFT_X), m_leftXDeadband); } /** * Get the Y axis value of left side of the controller. Back is positive. * + *

A deadband of 0.1 is applied by default. Use {@link #setLeftYDeadband} to change it. + * * @return The axis value. */ public double getLeftY() { - return getAxis(Axis.LEFT_Y); + return MathUtil.applyDeadband(getAxis(Axis.LEFT_Y), m_leftYDeadband); } /** * Get the X axis value of right side of the controller. Right is positive. * + *

A deadband of 0.1 is applied by default. Use {@link #setRightXDeadband} to change it. + * * @return The axis value. */ public double getRightX() { - return getAxis(Axis.RIGHT_X); + return MathUtil.applyDeadband(getAxis(Axis.RIGHT_X), m_rightXDeadband); } /** * Get the Y axis value of right side of the controller. Back is positive. * + *

A deadband of 0.1 is applied by default. Use {@link #setRightYDeadband} to change it. + * * @return The axis value. */ public double getRightY() { - return getAxis(Axis.RIGHT_Y); + return MathUtil.applyDeadband(getAxis(Axis.RIGHT_Y), m_rightYDeadband); } /** * Get the left trigger axis value of the controller. Note that this axis is bound to the range of * [0, 1] as opposed to the usual [-1, 1]. * + *

A deadband of 0.01 is applied by default. Use {@link #setLeftTriggerDeadband} to change it. + * * @return The axis value. */ public double getLeftTriggerAxis() { - return getAxis(Axis.LEFT_TRIGGER); + return MathUtil.applyDeadband(getAxis(Axis.LEFT_TRIGGER), m_leftTriggerDeadband); } /** @@ -225,10 +318,12 @@ public class Gamepad extends GenericHID implements Sendable { * Get the right trigger axis value of the controller. Note that this axis is bound to the range * of [0, 1] as opposed to the usual [-1, 1]. * + *

A deadband of 0.01 is applied by default. Use {@link #setRightTriggerDeadband} to change it. + * * @return The axis value. */ public double getRightTriggerAxis() { - return getAxis(Axis.RIGHT_TRIGGER); + return MathUtil.applyDeadband(getAxis(Axis.RIGHT_TRIGGER), m_rightTriggerDeadband); } /**