Skip to content

Commit 25cd5af

Browse files
authored
Add Python to mecanum kinematics and odometry (#2435)
* add python to mecanum kin and odom * address review * more review items * fixed spacing and link
1 parent 7f0cb47 commit 25cd5af

File tree

2 files changed

+113
-6
lines changed

2 files changed

+113
-6
lines changed

source/docs/software/kinematics-and-odometry/mecanum-drive-kinematics.rst

+62-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ The ``MecanumDriveKinematics`` class accepts four constructor arguments, with ea
2121
m_frontLeftLocation, m_frontRightLocation, m_backLeftLocation, m_backRightLocation
2222
);
2323
24-
2524
.. code-block:: c++
2625

2726
// Locations of the wheels relative to the robot center.
@@ -35,9 +34,25 @@ The ``MecanumDriveKinematics`` class accepts four constructor arguments, with ea
3534
m_frontLeftLocation, m_frontRightLocation, m_backLeftLocation,
3635
m_backRightLocation};
3736

37+
.. code-block:: python
38+
39+
from wpimath.geometry import Translation2d
40+
from wpimath.kinematics import MecanumDriveKinematics
41+
42+
# Locations of the wheels relative to the robot center.
43+
frontLeftLocation = Translation2d(0.381, 0.381)
44+
frontRightLocation = Translation2d(0.381, -0.381)
45+
backLeftLocation = Translation2d(-0.381, 0.381)
46+
backRightLocation = Translation2d(-0.381, -0.381)
47+
48+
# Creating my kinematics object using the wheel locations.
49+
self.kinematics = MecanumDriveKinematics(
50+
frontLeftLocation, frontRightLocation, backLeftLocation, backRightLocation
51+
)
52+
3853
Converting Chassis Speeds to Wheel Speeds
3954
-----------------------------------------
40-
The ``toWheelSpeeds(ChassisSpeeds speeds)`` (Java) / ``ToWheelSpeeds(ChassisSpeeds speeds)`` (C++) method should be used to convert a ``ChassisSpeeds`` object to a ``MecanumDriveWheelSpeeds`` object. This is useful in situations where you have to convert a forward velocity, sideways velocity, and an angular velocity into individual wheel speeds.
55+
The ``toWheelSpeeds(ChassisSpeeds speeds)`` (Java / Python) / ``ToWheelSpeeds(ChassisSpeeds speeds)`` (C++) method should be used to convert a ``ChassisSpeeds`` object to a ``MecanumDriveWheelSpeeds`` object. This is useful in situations where you have to convert a forward velocity, sideways velocity, and an angular velocity into individual wheel speeds.
4156

4257
.. tab-set-code::
4358

@@ -69,6 +84,18 @@ The ``toWheelSpeeds(ChassisSpeeds speeds)`` (Java) / ``ToWheelSpeeds(ChassisSpee
6984
// struct into it's individual components
7085
auto [fl, fr, bl, br] = kinematics.ToWheelSpeeds(speeds);
7186

87+
.. code-block:: python
88+
89+
from wpimath.kinematics import ChassisSpeeds
90+
91+
# Example chassis speeds: 1 meter per second forward, 3 meters
92+
# per second to the left, and rotation at 1.5 radians per second
93+
# counterclockwise.
94+
speeds = ChassisSpeeds(1.0, 3.0, 1.5)
95+
96+
# Convert to wheel speeds
97+
frontLeft, frontRight, backLeft, backRight = self.kinematics.toWheelSpeeds(speeds)
98+
7299
Field-oriented drive
73100
~~~~~~~~~~~~~~~~~~~~
74101
:ref:`Recall <docs/software/kinematics-and-odometry/intro-and-chassis-speeds:Creating a ChassisSpeeds object from field-relative speeds>` that a ``ChassisSpeeds`` object can be created from a set of desired field-oriented speeds. This feature can be used to get wheel speeds from a set of desired field-oriented speeds.
@@ -101,6 +128,23 @@ Field-oriented drive
101128
// Now use this in our kinematics
102129
auto [fl, fr, bl, br] = kinematics.ToWheelSpeeds(speeds);
103130

131+
.. code-block:: python
132+
133+
from wpimath.kinematics import ChassisSpeeds
134+
import math
135+
from wpimath.geometry import Rotation2d
136+
137+
# The desired field relative speed here is 2 meters per second
138+
# toward the opponent's alliance station wall, and 2 meters per
139+
# second toward the left field boundary. The desired rotation
140+
# is a quarter of a rotation per second counterclockwise. The current
141+
# robot angle is 45 degrees.
142+
speeds = ChassisSpeeds.fromFieldRelativeSpeeds(
143+
2.0, 2.0, math.pi / 2.0, Rotation2d.fromDegrees(45.0))
144+
145+
# Now use this in our kinematics
146+
wheelSpeeds = self.kinematics.toWheelSpeeds(speeds)
147+
104148
Using custom centers of rotation
105149
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106150
Sometimes, rotating around one specific corner might be desirable for certain evasive maneuvers. This type of behavior is also supported by the WPILib classes. The same ``ToWheelSpeeds()`` method accepts a second parameter for the center of rotation (as a ``Translation2d``). Just like the wheel locations, the ``Translation2d`` representing the center of rotation should be relative to the robot center.
@@ -111,7 +155,7 @@ For example, one can set the center of rotation on a certain wheel and if the pr
111155

112156
Converting wheel speeds to chassis speeds
113157
-----------------------------------------
114-
One can also use the kinematics object to convert a ``MecanumDriveWheelSpeeds`` object to a singular ``ChassisSpeeds`` object. The ``toChassisSpeeds(MecanumDriveWheelSpeeds speeds)`` (Java) / ``ToChassisSpeeds(MecanumDriveWheelSpeeds speeds)`` (C++) method can be used to achieve this.
158+
One can also use the kinematics object to convert a ``MecanumDriveWheelSpeeds`` object to a singular ``ChassisSpeeds`` object. The ``toChassisSpeeds(MecanumDriveWheelSpeeds speeds)`` (Java / Python) / ``ToChassisSpeeds(MecanumDriveWheelSpeeds speeds)`` (C++) method can be used to achieve this.
115159

116160
.. tab-set-code::
117161

@@ -137,3 +181,18 @@ One can also use the kinematics object to convert a ``MecanumDriveWheelSpeeds``
137181
// feature to automatically break up the ChassisSpeeds struct into its
138182
// three components.
139183
auto [forward, sideways, angular] = kinematics.ToChassisSpeeds(wheelSpeeds);
184+
185+
.. code-block:: python
186+
187+
from wpimath.kinematics import MecanumDriveWheelSpeeds
188+
189+
# Example wheel speeds
190+
wheelSpeeds = MecanumDriveWheelSpeeds(-17.67, 20.51, -13.44, 16.26)
191+
192+
# Convert to chassis speeds
193+
chassisSpeeds = self.kinematics.toChassisSpeeds(wheelSpeeds)
194+
195+
# Getting individual speeds
196+
forward = chassisSpeeds.vx
197+
sideways = chassisSpeeds.vy
198+
angular = chassisSpeeds.omega

source/docs/software/kinematics-and-odometry/mecanum-drive-odometry.rst

+51-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The mandatory arguments are:
1212

1313
* The kinematics object that represents your mecanum drive (as a ``MecanumDriveKinematics`` instance)
1414
* The angle reported by your gyroscope (as a ``Rotation2d``)
15-
* The initial positions of the wheels (as ``MecanumDriveWheelPositions``). In Java, this must be constructed with each wheel position in meters. In C++, the :doc:`units library </docs/software/basic-programming/cpp-units>` must be used to represent your wheel positions.
15+
* The initial positions of the wheels (as ``MecanumDriveWheelPositions``). In Java / Python, this must be constructed with each wheel position in meters. In C++, the :doc:`units library </docs/software/basic-programming/cpp-units>` must be used to represent your wheel positions.
1616

1717
The fourth optional argument is the starting pose of your robot on the field (as a ``Pose2d``). By default, the robot will start at ``x = 0, y = 0, theta = 0``.
1818

@@ -74,6 +74,38 @@ The fourth optional argument is the starting pose of your robot on the field (as
7474
},
7575
frc::Pose2d{5_m, 13.5_m, 0_rad}};
7676

77+
.. code-block:: python
78+
79+
from wpimath.geometry import Translation2d
80+
from wpimath.kinematics import MecanumDriveKinematics
81+
from wpimath.kinematics import MecanumDriveOdometry
82+
from wpimath.kinematics import MecanumDriveWheelPositions
83+
from wpimath.geometry import Pose2d
84+
from wpimath.geometry import Rotation2d
85+
86+
# Locations of the wheels relative to the robot center.
87+
frontLeftLocation = Translation2d(0.381, 0.381)
88+
frontRightLocation = Translation2d(0.381, -0.381)
89+
backLeftLocation = Translation2d(-0.381, 0.381)
90+
backRightLocation = Translation2d(-0.381, -0.381)
91+
92+
# Creating my kinematics object using the wheel locations.
93+
self.kinematics = MecanumDriveKinematics(
94+
frontLeftLocation, frontRightLocation, backLeftLocation, backRightLocation
95+
)
96+
97+
# Creating my odometry object from the kinematics object and the initial wheel positions.
98+
# Here, our starting pose is 5 meters along the long end of the field and in the
99+
# center of the field along the short end, facing the opposing alliance wall.
100+
self.odometry = MecanumDriveOdometry(
101+
self.kinematics,
102+
self.gyro.getRotation2d(),
103+
MecanumDriveWheelPositions(
104+
self.frontLeftEncoder.getDistance(), self.frontRightEncoder.getDistance(),
105+
self.backLeftEncoder.getDistance(), self.backRightEncoder.getDistance()
106+
),
107+
Pose2d(5.0, 13.5, Rotation2d())
108+
)
77109
78110
Updating the robot pose
79111
-----------------------
@@ -114,12 +146,28 @@ The ``update`` method of the odometry class updates the robot position on the fi
114146
m_pose = m_odometry.Update(gyroAngle, wheelPositions);
115147
}
116148

149+
.. code-block:: python
150+
151+
from wpimath.kinematics import MecanumDriveWheelPositions
152+
153+
def periodic(self):
154+
# Get my wheel positions
155+
wheelPositions = MecanumDriveWheelPositions(
156+
self.frontLeftEncoder.getDistance(), self.frontRightEncoder.getDistance(),
157+
self.backLeftEncoder.getDistance(), self.backRightEncoder.getDistance())
158+
159+
# Get the rotation of the robot from the gyro.
160+
gyroAngle = gyro.getRotation2d()
161+
162+
# Update the pose
163+
self.pose = odometry.update(gyroAngle, wheelPositions)
164+
117165
Resetting the Robot Pose
118166
------------------------
119167
The robot pose can be reset via the ``resetPosition`` method. This method accepts three arguments: the current gyro angle, the current wheel positions, and the new field-relative pose.
120168

121169
.. important:: If at any time, you decide to reset your gyroscope or encoders, the ``resetPosition`` method MUST be called with the new gyro angle and wheel positions.
122170

123-
.. note:: A full example of a mecanum drive robot with odometry is available here: `C++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/MecanumBot>`_ / `Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot>`_.
171+
.. note:: A full example of a mecanum drive robot with odometry is available here: `C++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/MecanumBot>`_ / `Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot>`_ / `Python <https://github.com/robotpy/examples/tree/main/MecanumBot>`_
124172

125-
In addition, the ``GetPose`` (C++) / ``getPoseMeters`` (Java) methods can be used to retrieve the current robot pose without an update.
173+
In addition, the ``GetPose`` (C++) / ``getPoseMeters`` (Java / Python) methods can be used to retrieve the current robot pose without an update.

0 commit comments

Comments
 (0)