In this post, we will take a look out how to draw the force vector diagrams for an omni-directional mobile robot with Mecanum wheels. But before we do that, let’s take a look at a few real-world applications of these vehicles.
Industrial forklifts: Moving long loads sideways through narrow aisles and doors.
Military: Rescue missions and hazardous environment exploration in tight spaces.
Medical: Powered wheelchairs that can maneuver in congested areas.
Aviation: Transport of helicopters or large pieces of aircraft
Below is a video of Mecanum-wheeled robots helping to move a piece of a passenger train.
Here is a Mecanum-wheeled robot transporting a helicopter.
Here is a sports car design that uses Mecanum wheels. Makes parallel parking a breeze!
Force Vector Diagrams
The robot schematic diagram below consists of four Mecanum wheels. Each wheel is driven by a motor and contains a set of passive rubber cylinders that are oriented at an angle of 45° to the axis of rotation of the wheel (the axis of rotation of the wheel is the imaginary line that goes through the center of the wheel).
The 45° orientation of the rollers is what gives the robot the ability to move in directions other than just forwards and backwards.
Another thing to note is that the rubber rollers of the robot are oriented as an ‘X’ (i.e. the red line below). This X configuration enables the Mecanum-wheeled robot to be able to move in any direction (I’ll show why this is the case in the force/rotation diagrams).
The rollers could also be oriented as an ‘O’ as seen from the top, but in this case, we will assume an X configuration.
Before I draw the force/rotation diagrams that cause the different directions of motion of the robot, let’s take a look at the concept of friction because it plays an important role in how the Mecanum wheels do what they do.
Below we have a person trying to push a box across a floor.
The person exerts a pushing force on the box. As the box moves to the right, the surface of the floor exerts a frictional force that acts in the opposite direction of the motion of the box. The person must make sure he pushes hard enough on the box to overcome that frictional force; otherwise, the box won’t move.
Similarly, in the case of the omni-directional Mecanum-wheeled robot, as the wheels rotate, each rubber roller on each wheel makes contact with the surface of the ground at an angle of 45° to the axis of rotation of the wheel.
At the moment each rubber roller makes contact with the ground, the surface of the ground exerts a frictional force that acts in the opposite direction of the force exerted on the ground surface by the rubber roller. There are four frictional forces that act on the robot simultaneously (one for each wheel). It is these forces that cause the robot to move.
In what direction does the robot move? The direction the robot moves is determined by the magnitude and direction of each of the four frictional forces.
Below are the vehicle diagrams (as shown from above the robot) that show how all of this works. The diagrams show how each wheel must move, both direction and angular rate (long vector for high angular rate, short vector for small), so that the indicated motion results. The diagrams also show which way force is applied on the robot when the wheels rotate along the surface of the ground, with the key point being that the sum of the four force vectors (i.e. resultant force) equals the direction of motion of the reference point (which is located in the center of the robot).
The red arrows show the magnitude and direction of the rotation of the wheels.
The black and orange arrows show the frictional force vectors that act when the rubber roller makes contact with the ground surface. Although these vectors are shown above the robot, they act underneath the robot. They are the frictional forces that act on each rubber roller when it touches the ground surface.
The blue arrow shows the motion of the vehicle reference point.
Now, we add up the force vectors on the front and rear of the robot to calculate the resultant force (i.e. motion of the robot/vehicle reference point).
Using the head-to-tail method to add the force vectors (black and orange arrows above), we see that the x-components of these force vectors on both the front and rear of the vehicle cancel out, so all that is left are the y-components. It is this net, resultant force in the positive y-direction, which results in straight-ahead, forward motion of the robot (i.e. vehicle reference point).
Now, we add up the force vectors on the front and rear of the robot to calculate the resultant force (i.e. motion of the robot/vehicle reference point).
Similar to the linear forward case, the x-components of the force vectors on both the front and rear of the vehicle cancel out, so all that is left are the y-components. The net force is in the negative y-direction, causing the robot (i.e. vehicle reference point) to move in reverse.
Now, we add up the force vectors on the front and rear of the robot to calculate the resultant force (i.e. motion of the robot/vehicle reference point).
Using the head-to-tail method to add the force vectors (black and orange arrows above), we see that the y-components of these force vectors on both the front and rear of the vehicle cancel out, so all that is left are the x-components. It is this net, resultant force in the negative x-direction, which causes the robot to move linearly to the left.
Now, we add up the force vectors on the front and rear of the robot to calculate the resultant force (i.e. motion of the robot/vehicle reference point).
Using the head-to-tail method to add the force vectors (black and orange arrows above), we see that the y-components of these force vectors on both the front and rear of the vehicle cancel out, so all that is left are the x-components. It is this net, resultant force in the positive x-direction which causes the robot to move in a sideways direction to the right.
Finally, here are the force vector diagrams for when the robot spins in-place, either to the left or to the right.
Before we get into how to describe the rotation of a robot in three dimensions, it is important you understand how to describe the rotation of a robot in two dimensions.
In my previous post, we learned how to calculate the coordinates of a point (or vector) in the 2D global reference frame given the coordinates of that point (or magnitude and direction of that vector) in the 2D robot (local) reference frame (i.e. the rotated coordinate system).
Here is the equation we use to convert a point in the 2D local reference frame to a point in the 2D global reference frame (If you don’t know how to multiply matrices together, there are a bunch of videos on YouTube that walk through the process):
Here is the equation we use to convert velocity in the 2D local reference frame to velocity in the 2D global reference frame:
The thing that makes all this possible is the two-dimensional rotation matrix:
To describe the rotation of a robot in three dimensions, we need to use the three-dimensional rotation matrix. Let’s explore this now.
Converting a Point (Or Vector) in the 3D Local Reference Frame to a Point in the 3D Global Reference Frame
In two dimensions, the only angle we had to worry about was γ. γ represents the amount of rotation (in degrees or radians) of the robot x-axis from the global x-axis, going in the counterclockwise direction.
γ is often called the yaw angle. It is the angle of rotation about the z-axis.
So, how do we derive the three-dimensional rotation matrix? What we need to do is calculate the rotation matrix for all rotations a robot can do in a three-dimensional space. Since there are three axes, there are three different rotations we need to account for: rotation about the x-axis, rotation about the y-axis, and rotation about the z-axis.
Let’s start with rotation about the z-axis. Guess what? We already know that rotation matrix.
This is the two-dimensional case I explained at the beginning of this post. The x and y coordinates of a point in the robot reference frame will change when converted to coordinates in the global reference frame. However, the z coordinate will remain constant.
Here is the rotation matrix that takes care of rotation of a robot in 3D about the global z-axis:
When we rotate a point about the y-axis, the x and z coordinates of the point will change, but the y-coordinate will remain the same.
Here is the rotation matrix that enables us to convert a point (or vector) in the local reference frame to a point (or vector) in the global reference frame when all we have is rotation of the robot about the global y-axis.
When we rotate a point about the x-axis, the y and z coordinates of the point will change, but the x-coordinate will remain the same.
Here is the rotation matrix that enables us to convert a point (or vector) in the local reference frame to a point (or vector) in the global reference frame when all we have is rotation of the robot about the global x-axis.
Ok, so now we need to combine the three matrices above to calculate the full three-dimensional rotation matrix.
Three-Dimensional Rotation Matrix in Python Code
Here is a NumPy-based method that converts angles into a 3×3 rotation matrix like the one above. You can use this method in whatever code you want to write.
import numpy as np # NumPy library
def euler_rotation_matrix(alpha,beta,gamma):
"""
Generate a full three-dimensional rotation matrix from euler angles
Input
:param alpha: The roll angle (radians) - Rotation around the x-axis
:param beta: The pitch angle (radians) - Rotation around the y-axis
:param alpha: The yaw angle (radians) - Rotation around the z-axis
Output
:return: A 3x3 element matix containing the rotation matrix.
This rotation matrix converts a point in the local reference
frame to a point in the global reference frame.
"""
# First row of the rotation matrix
r00 = np.cos(gamma) * np.cos(beta)
r01 = np.cos(gamma) * np.sin(beta) * np.sin(alpha) - np.sin(gamma) * np.cos(alpha)
r02 = np.cos(gamma) * np.sin(beta) * np.cos(alpha) + np.sin(gamma) * np.sin(alpha)
# Second row of the rotation matrix
r10 = np.sin(gamma) * np.cos(beta)
r11 = np.sin(gamma) * np.sin(beta) * np.sin(alpha) + np.cos(gamma) * np.cos(alpha)
r12 = np.sin(gamma) * np.sin(beta) * np.cos(alpha) - np.cos(gamma) * np.sin(alpha)
# Third row of the rotation matrix
r20 = -np.sin(beta)
r21 = np.cos(beta) * np.sin(alpha)
r22 = np.cos(beta) * np.cos(alpha)
# 3x3 rotation matrix
rot_matrix = np.array([[r00, r01, r02],
[r10, r11, r12],
[r20, r21, r22]])
return rot_matrix
def rotate(p1,alpha,beta,gamma):
"""
Rotates a point p1 in 3D space in the local reference frame to
a point p2 in the global reference frame.
Input
:param p1: A 3 element array containing the position of a point in the
local reference frame (xL,yL,zL)
:param alpha: The roll angle (radians) - Rotation around the x-axis
:param beta: The pitch angle (radians) - Rotation around the y-axis
:param alpha: The yaw angle (radians) - Rotation around the z-axis
Output
:return: p2: A 3 element array containing the position of a point in the
global reference frame (xG,yG,zG)
"""
p2 = euler_rotation_matrix(alpha, beta, gamma) @ p1
return p2
def main():
# Point that we want to rotate from local frame to global frame
p1 = np.array([5,6,7])
# Rotation angles
alpha = (30/180.0)*np.pi
beta = (40/180.0)*np.pi
gamma = (70/180.0)*np.pi
print(f'local coordinates p1: {p1}')
print(f'Rotated by Roll {alpha}, Pitch {beta}, Yaw: {gamma}')
p2 = rotate(p1,alpha,beta,gamma)
print(f'global coordinates p2:{p2}')
if __name__ == '__main__':
main()
Converting a Point (Or Vector) in the 3D Global Reference Frame to a Point in the 3D Local Reference Frame
Below we are going to derive the equation that will enable you to convert a point (or vector) in the 3D global reference frame to a point (or vector) in the 3D local reference frame. In other words, we are going to calculate the three-dimensional inverse rotation matrix.
If you remember, when we derived the three-dimensional rotation matrix earlier in this post, we started out with this equation.
You’ll notice that the first operation performed on a point in the local frame is to account for rotation about the x-axis (i.e. roll). Then we do pitch (rotation about the y-axis), and then we do yaw (rotation about the z-axis). This sequence of operations enables us to convert any point (or vector) in the 3D local reference frame to a point (or vector) in the 3D global reference frame.
To calculate the inverse three-dimensional rotation matrix, we need to find the inverse of each of the three matrices and then perform the operations in reverse order. Let’s walk through this together.
First, we need to calculate the inverse for each of the three matrices. The inverse of a rotation matrix R is equal to the rotation matrix’s transpose (RT = R-1), where T means “transpose” and -1 means “inverse”. If you don’t know how to take the transpose of a 3×3 matrix, take a look at this article.
And since the last operation performed is yaw, we need to take its transpose, then take the transpose of the pitch matrix, and then take the transpose of the roll matrix.
Here are the steps of the matrix multiplication:
And there you have it. This matrix below is the full three-dimensional inverse rotation matrix. Use it when you want to convert a point (or vector) in the 3D global reference frame to a point (or vector) in the 3D local reference frame:
Three-Dimensional Inverse Rotation Matrix in Python Code
Here is a NumPy-based method that converts angles into a 3×3 inverse rotation matrix like the one above. You can use this method in whatever code you want to write.
import numpy as np
def euler_rotation_matrix(alpha,beta,gamma):
"""
Generate a full three-dimensional rotation matrix from euler angles
Input
:param alpha: The roll angle (radians) - Rotation around the x-axis
:param beta: The pitch angle (radians) - Rotation around the y-axis
:param alpha: The yaw angle (radians) - Rotation around the z-axis
Output
:return: A 3x3 element matix containing the rotation matrix.
This rotation matrix converts a point in the local reference
frame to a point in the global reference frame.
"""
# First row of the rotation matrix
r00 = np.cos(gamma) * np.cos(beta)
r01 = np.cos(gamma) * np.sin(beta) * np.sin(alpha) - np.sin(gamma) * np.cos(alpha)
r02 = np.cos(gamma) * np.sin(beta) * np.cos(alpha) + np.sin(gamma) * np.sin(alpha)
# Second row of the rotation matrix
r10 = np.sin(gamma) * np.cos(beta)
r11 = np.sin(gamma) * np.sin(beta) * np.sin(alpha) + np.cos(gamma) * np.cos(alpha)
r12 = np.sin(gamma) * np.sin(beta) * np.cos(alpha) - np.cos(gamma) * np.sin(alpha)
# Third row of the rotation matrix
r20 = -np.sin(beta)
r21 = np.cos(beta) * np.sin(alpha)
r22 = np.cos(beta) * np.cos(alpha)
# 3x3 rotation matrix
rot_matrix = np.array([[r00, r01, r02],
[r10, r11, r12],
[r20, r21, r22]])
return rot_matrix
def rotate(p1,alpha,beta,gamma):
"""
Rotates a point p1 in 3D space in the local reference frame to
a point p2 in the global reference frame.
Input
:param p1: A 3 element array containing the position of a point in the
local reference frame (xL,yL,zL)
:param alpha: The roll angle (radians) - Rotation around the x-axis
:param beta: The pitch angle (radians) - Rotation around the y-axis
:param alpha: The yaw angle (radians) - Rotation around the z-axis
Output
:return: p2: A 3 element array containing the position of a point in the
global reference frame (xG,yG,zG)
"""
p2 = euler_rotation_matrix(alpha, beta, gamma) @ p1
return p2
def inverse_rotation(p2,alpha,beta,gamma):
"""
Inverse rotation from a point p2 in global 3D reference frame
to a point p1 in the local (robot) reference frame.
Input
:param p2: A 3 element array containing the position of a point in the
global reference frame (xG,yG,zG)
:param alpha: The roll angle (radians) - Rotation around the x-axis
:param beta: The pitch angle (radians) - Rotation around the y-axis
:param alpha: The yaw angle (radians) - Rotation around the z-axis
Output
:return: A 3 element array containing the position of a point in the
local reference frame (xL,yL,zL)
"""
# First row of the inverse rotation matrix
r00 = np.cos(gamma) * np.cos(beta)
r01 = np.sin(gamma) * np.cos(beta)
r02 = -np.sin(beta)
# Second row of the inverse rotation matrix
r10 = np.cos(gamma) * np.sin(beta) * np.sin(alpha) - np.sin(gamma) * np.cos(alpha)
r11 = np.sin(gamma) * np.sin(beta) * np.sin(alpha) + np.cos(gamma) * np.cos(alpha)
r12 = np.cos(beta) * np.sin(alpha)
# Third row of the inverse rotation matrix
r20 = np.cos(gamma) * np.sin(beta) * np.cos(alpha) + np.sin(gamma) * np.sin(alpha)
r21 = np.sin(gamma) * np.sin(beta) * np.cos(alpha) - np.cos(gamma) * np.sin(alpha)
r22 = np.cos(beta) * np.cos(alpha)
# 3x3 inverse rotation matrix
inv_rot_matrix = np.array([[r00, r01, r02],
[r10, r11, r12],
[r20, r21, r22]])
return inv_rot_matrix @ p2
def main():
# Point that we want to rotate from local frame to global frame
p1 = np.array([5,6,7])
# Rotation angles
alpha = (30/180.0)*np.pi
beta = (40/180.0)*np.pi
gamma = (70/180.0)*np.pi
print(f'local coordinates p1: {p1}')
print(f'Rotated by Roll {alpha}, Pitch {beta}, Yaw: {gamma}')
p2 = rotate(p1,alpha,beta,gamma)
print(f'global coordinates p2:{p2}')
p1_ = inverse_rotation(p2,alpha,beta,gamma)
print(f'inverse rotation back into local frame p1:{p1_}')
if __name__ == '__main__':
main()
In this post, we’ll take a look at how to describe the rotation of a robot in a two-dimensional (2D) space.
Consider the robot below. It’s moving around in a 2D coordinate frame. Its position in this coordinate frame can be described at any time by its x-coordinate and its y-coordinate.
We’ll call this coordinate frame the “global” reference frame because it defines the world that the robot is moving around in.
There is also another coordinate frame: the local reference frame. The local reference frame is the coordinate frame from the perspective of the robot (as opposed to the world). Local reference frame…robot reference frame…robot axes…all ways of saying the same thing.
Let’s draw the y and x-axes of the local reference frame. The y-axis points towards the front of the robot, and the x-axis is perpendicular to the y-axis. The local reference frame is labeled with the subscript L, and the global reference frame is labeled with the subscript G.
Notice that the local reference frame is rotated at an angle from the global reference frame. We’ll call this angle γ.
γ = Angular difference between the global x-axis and the local x-axis
You’ll also note that the angular difference between the global y-axis and the local y-axis is also equal to γ.
The rate at which the angle γ is changing (in the counterclockwise direction) is known as the angular velocity, and is often represented by the Greek letter ω. It’s the change in gamma divided by the change in time.
ω = (change in γ) / (change in time) = (γfinal – γinitial) / (tfinal – tinitial)
Now our robot isn’t very useful if all it does is rotate. Let’s suppose the robot is an omnidirectional robot…it can move in any direction. How do we describe the velocity of the robot?
Velocity is a measure of how fast something is moving in a particular direction. It is speed (e.g. miles per hour, meters per second, etc.) + direction.
The velocity of our 2D robot can be broken down into two components: velocity in the x-direction and velocity in the y-direction. The sum of both of those vectors defines the velocity of the robot.
Let’s draw the two velocity vectors right now from the perspective of the local reference frame.
Suppose the robot is moving due north (as shown by the red arrow below) with respect to the global reference frame, while maintaining the same angle of rotation. In other words, the front of the robot is still pointed towards the “northwest,” while the robot is moving directly north, parallel to the global y-axis.
How do we calculate the magnitude of the robot’s velocity (i.e. its speed)?
We need to add the two velocity vectors together, using the head-to-tail method. The head of the first vector points to the tail of the next vector.
You probably notice that the angle between Vx and Vy is 90 degrees, so we can use the Pythagorean Theorem to calculate the speed (i.e. magnitude of the velocity) of the robot.
This velocity above is the velocity of the robot in the local reference frame. In other words, both Vx and Vy are parallel to the robot’s x-axis and y-axis respectively.
How do we convert these velocities in the local reference frame to velocities in the global reference frame? And why would we want to do that?
The reason for doing this conversion is so that we can know where in the world the robot will be at a particular time in the future. Just knowing the robot velocity vectors doesn’t help us. We need to know how the position of the robot is changing with respect to time in the global reference frame.
So let’s figure that out now. To solve this problem, we’ll need to use some trigonometry.
Let’s start from the graphic you are already familiar with.
Assume the center of the robot above is currently at the origin in the global reference frame.
What is VxG, the velocity in the x-direction on the global reference frame? To answer that, we need to recognize that both the x and y components of the robot’s velocity in the local reference frame can be broken down into global reference frame components (i.e. the pink and green arrows below).
Let’s call the velocity of the robot in the global reference frame VxG and VyG. You can see from the above diagram of the pink and green vectors that:
VxG = (vector 1) – (vector 4) … Minus sign because vector 4 is pointing in the negative global x-axis direction
VyG = (vector 2) + (vector 3) … Plus sign because both vectors are pointing in the positive global y-axis direction
Now, how do we find vectors 1, 2, 3, and 4?
Do you remember the trigonometric ratios? Back in middle school, we used the acronym, SOHCAHTOA:
Sine = Opposite / Hypotenuse (SOH)
Cosine = Adjacent / Hypotenuse (CAH)
Tangent = Opposite / Adjacent (TOA)
In the right triangle above: sin A = a/c; cos A = b/c; tan A = a/b.
Using these ratios as our guide, we get:
vector 1 = VxL * cos(γ)
vector 2 = VxL * sin(γ)
vector 3 = VyL * cos(γ)
vector 4 = VyL * sin(γ)
Plugging this information back in, we get the two equations that enable us to convert local velocities to global velocities.
VxG = (VxL * cos(γ)) – (VyL * sin(γ))
VyG = (VxL * sin(γ)) + (VyL * cos(γ))
If you’re familiar with linear algebra, you’ll notice that we can convert the system of equations above into matrix form:
The matrix above with the cosines and sines is often referred to in robotics as the rotation matrix (or two-dimensional rotation matrix). It helps us convert either vectors or a point in the local reference frame to a vector or a point in the global reference frame.
Here is how we convert a point in the local reference frame to a point in the global reference frame:
At this stage, you might now be wondering…how do I convert velocities in the global reference frame to velocities in the local reference frame? Say you were looking down on the robot from up above (i.e. aerial view) and had a radar gun that could measure the velocity of the robot…what would the velocity be from the perspective of the robot’s own x and y axes?
To answer that, let’s draw our local and global velocity vectors.
Now let’s write out equations for the local velocities in the x and y directions:
VxL = (vector 1) + (vector 4) … Plus sign because both vectors are pointing in the positive local x-axis direction
VyL = -(vector 2) + (vector 3) … Minus sign because vector 2 is pointing in the negative local y-axis direction
Using the trigonometric ratios we discussed earlier, we have the following equations:
vector 1 = VxG * cos(γ)
vector 2 = VXG * sin(γ)
vector 3 = VyG * cos(γ)
vector 4 = VyG * sin(γ)
Plugging this information back in, we get the two equations that enable us to convert global velocities to local velocities.
VxL = (VxG * cos(γ)) + (VyG * sin(γ))
VyL = -(VxG * sin(γ)) + (VyG * cos(γ))
In matrix form, we have:
And there you have it. That’s how to describe the rotation of a robot in two dimensions.
In a future post, I’ll show you how to describe the rotation of a robot in three dimensions.