Open In Colab

5.2. Motion Model for the Differential Drive Robot#

By rotating the two wheels independently, we can control both the linear and angular velocity of the DDR.

Splash image with steampunk differential drive robot

The motion model for the logistics robot of the previous chapter was fairly simple; we assumed that the robot moved with constant linear velocity \(v\) for a time interval \(\Delta T\), and therefore we expressed the motion model as \(x_{k+1} = x_k + v \Delta T\). Further, by assigning the robot’s body-attached frame to be parallel to the world frame, we were able to simply ignore the body-attached frame, and reason directly in the world frame without difficulty. Things are more complex for our DDR, due to the role of orientation.

When describing the motion of our DDR, the orientation of the robot enters in two ways. First, because the robot wheels roll without slipping, the linear velocity of the robot is always instantaneously in the steering direction. Second, because the robot can rotate, we must take account of its angular velocity, in addition to the linear velocity. This is illustrated in the figure below. Suppose the robot is following a path \(\gamma(s)\) (where \(s\) parameterizes the path). The instantaneous linear velocity expressed with respect to the body frame is given by:

\[\begin{split} v^{\mathrm{body,linear}} \begin{bmatrix} v_x \\ 0 \end{bmatrix} \end{split}\]

Note that the velocity is tangent to the curve \(\gamma\) at \(s\), and that in the body-attached frame the y-component of the velocity is zero (i.e., in the body-attached frame, \(v_y = 0\)). The steering direction is determined by the angle \(\theta\) as \([\cos \theta, \sin \theta]^T\), so that the linear velocity with respect to the world frame is given by

\[\begin{split} v^{\mathrm{world,linear}} \begin{bmatrix} v_x \cos \theta \\ v_x \sin \theta \end{bmatrix} \end{split}\]

Because our robot moves in the plane, the z-axis of the body-attached frame is always parallel to the z-axis of the world frame. This greatly simplifies the description of angular velocity, which in this case we may define as \(\omega = \dot{\theta}\), the instantaneous rate of change of the robot’s orientation.

It is common to combine the angular and linear velocity into a single vector,

\[\begin{split} v^{\mathrm{body}}= \begin{bmatrix} v_x \\ 0 \\ \dot{\theta} \end{bmatrix} ~~~~~~~ v^{\mathrm{world}}= \begin{bmatrix} v_x \cos \theta \\ v_x \sin \theta \\ \dot{\theta} \end{bmatrix} \end{split}\]
The linear velocity is always in the steering direction.

5.2.1. The Relationship Between Wheel Rotation and Robot Velocity#

Because the wheels of the DDR are independently actuated, it is possible to achieve a large range of robot velocities. We can derive the relationship between wheel rotation and robot velocity by considering first the motion of a single wheel, and then considering the effect of coupling the two wheels along a single axis of rotation.

The figure below shows a side-view of the right wheel. We denote by \(\phi_R\) the instantaneous orientation of the right wheel with respect to the world z-axis (Note that we measure the angle \(\phi_R\) by attaching a distinguished point to the wheel, so that we can uniquely identify its orientation. In the figure, a red star is used to denote this point.) As we have seen in the previous chapter when modeling the forward motion of an omni-wheel, the relationship between the forward linear speed of the wheel and the rotation speed is given by

\[ v_\mathrm{right} = r \dot{\phi}_R \]

The same reasoning can be applied to the left wheel to obtain

\[ v_\mathrm{left} = r \dot{\phi}_L \]
The linear velocity is always in the steering direction.

Suppose now that both wheels spin at the same speed, \(\dot{\phi}_R = \dot{\phi}_L\). In this case, the forward speed of the wheels will also be equal, \(v_\mathrm{left} = v_\mathrm{right}\), and the robot will move in purely translational motion (i.e., \(\omega = 0\)), with \(v_x = v_\mathrm{left} = v_\mathrm{right}\), since all points on the robot move with exactly the same velocity for pure translational motion. Using the results above, we obtain

\[ \dot{\phi}_L = \dot{\phi}_R = \frac{v_x}{r} \]
When the wheels spin in the same direction with the same speed, the robot moves with pure translation.

Suppose instead that the two wheels spin in opposite directions, so that \(\dot{\phi}_R = -\dot{\phi}_L\). In this case, \(v_\mathrm{left} = -r\dot{\phi}_L \) and \(v_\mathrm{right} = r\dot{\phi}_R\). Because the two wheels are constrained by the physical mechanism to remain in a fixed geometric relationship to one another, these opposite but equal forward wheel speeds cause the robot to rotate, with both \(v_\mathrm{left}\) and \(v_\mathrm{right}\) tangent to a circle of diameter \(L\) centered at the origin of the body-attached frame. Note that the linear velocity of the robot, \(v_x\), is zero in this case, since \(v_\mathrm{left}\) and \(v_\mathrm{right}\) “cancel one another out” with respect to the linear velocity of the robot. Applying the equation of circular motion yields

\[ \frac{L}{2} \omega = -v_\mathrm{left} = -r\dot{\phi}_R ~~~~~~~ \frac{L}{2} \omega = v_\mathrm{right} = r\dot{\phi}_R \]

which leads to

\[ \dot{\phi}_L= -\frac{L}{2} \frac{\omega}{r} ~~~~~~~ \dot{\phi}_R= \frac{L}{2} \frac{\omega}{r} \]
When the wheels spin in the opposite direction with the same speed, the robot moves with pure rotation..

We have now considered the two special cases of pure translation and pure rotation. Because instantaneous velocities lie in a vector space, we can add these in the same way that we would add any vectors. Therefore, by spinning the wheels at different rates, we can obtain various linear combinations of the above pure translations and rotations. Adding the equations for linear and angular velocities of the two wheels, we obtain

\[ \dot{\phi}_L = \frac{v_x}{r} -\frac{L}{2} \frac{\omega}{r} ~~~~~~~ \dot{\phi}_R = \frac{v_x}{r} + \frac{L}{2} \frac{\omega}{r} \]

These two equations define the inverse velocity kinematics for our DDR: Given a desired output specified by \(v\) and \(\omega\), determine the required \(input\) specified as \(\dot{\phi}_R\) and \(\dot{\phi}_L\). These equations can be used to determine the required wheel actuation to achieve the desired linear and angular velocities of the robot.

The forward velocity kinematics are easily obtained from the above equations via simple algebra:

\[ v_x = \frac{r}{2} (\dot{\phi}_R + \dot{\phi}_L) ~~~~~~~~ \omega = \frac{r}{L} (\dot{\phi}_R - \dot{\phi}_L) \]

This leads immediately to the system equations with respect to the body-attached frame and with respect to the world frame

\[\begin{split} v^{\mathrm{body}}= \begin{bmatrix} v_x \\ 0 \\ \dot{\theta} \end{bmatrix} = \begin{bmatrix} \frac{r}{2} (\dot{\phi}_R + \dot{\phi}_L) \\ 0 \\ \frac{r}{L} (\dot{\phi}_R - \dot{\phi}_L)\end{bmatrix} ~~~~~~~ v^{\mathrm{world}}= \begin{bmatrix} v_x \cos \theta \\ v_x \sin \theta \\ \dot{\theta} \end{bmatrix} =\begin{bmatrix} \frac{r}{2} (\dot{\phi}_R + \dot{\phi}_L) \cos\theta \\ \frac{r}{2} (\dot{\phi}_R + \dot{\phi}_L) \sin\theta \\ \frac{r}{L} (\dot{\phi}_R - \dot{\phi}_L)\end{bmatrix} \end{split}\]

5.2.2. Kinematics in Code#

The equations above can be easily implemented in code. We do so below:

def ddr_ik(v_x, omega, L=0.5, r=0.1):
    """DDR inverse kinematics: calculate wheels speeds from desired velocity."""
    return (v_x - (L/2)*omega)/r, (v_x + (L/2)*omega)/r
def ddr_fk(phidot_L, phidot_R, L=0.5, r=0.1):
    """DDR inverse kinematics: calculate wheels speeds from desired velocity."""
    return gtsam.Point3((phidot_R+phidot_L)*r/2, 0, (phidot_R-phidot_L)*r/L)

As an example, let us try to move forward with a velocity of 20 cm/s, while turning counterclockwise at 0.3 rad/s:

phidot_L, phidot_R = ddr_ik(v_x=0.2, omega=0.3)
print(phidot_L, phidot_R)
1.25 2.75

As expected, the left wheel rotates less quickly, making us turn counter-clockwise. To sanity-check, let us put these same wheel speeds through the forward kinematics:

print(ddr_fk(phidot_L, phidot_R))
[0.2 0.  0.3]

The velocities are as desired, validating both the equations and their implementation. Feel free to experiment with other values using the code above!