← Back to VEX Robotics
Oct 2026 · Journal & Opinion · VEX Robotics

On X-Drives, PID, and HoloLib

I have joined the 2026–2027 VEX Robotics season with 12790X Entropic Alpacas. For this season, we decided to adopt an X-drive configuration, which allows the robot to move in any direction without first rotating to face that direction. We chose it because this year’s game, Override, requires a lot of rotation and dexterity while picking up and stacking Pins and Cups.

Working with an X-drive was new for most of us. On the software side, our goal was to use libraries to control the robot more reliably, especially during autonomous. I quickly discovered that the standard libraries we knew—LemLib, JAR-Template, and EZ-Template—did not have the X-drive support we needed for PID control and curved paths. While searching through GitHub repositories, we found HoloLib, which runs on PROS.

HoloLib turned out to be very useful. We can create quick and efficient routes while using the full movement capabilities of the X-drive. The robot can rotate while moving in a direction that is not parallel to its heading, removing the need for some time-consuming curves. It also resolved our biggest concern: spending weeks working out how to build several PID loops that run in parallel.

I wanted to understand how HoloLib does this, and whether it is really that different from controlling a differential drive.

PID Loops

A PID—proportional, integral, derivative—controller is a negative-feedback loop. It uses the current position, the target position, and the difference between them to calculate a motor command. In one dimension, it can be written as

\[ u(t)=K_P e(t)+K_I\int_0^t e(\tau)\,d\tau+K_D\frac{de(t)}{dt}. \]

Here, \(e(t)=x_{\mathrm{target}}-x_{\mathrm{current}}(t)\), and \(K_P\), \(K_I\), and \(K_D\) are constants that have to be tuned for the actual robot.

The proportional term responds to the current error, the derivative term damps rapid changes and helps limit overshoot, and the integral term accumulates error over time. The integral term can correct a small error that persists after the proportional and derivative terms have become too small, although too much integral action can cause windup.

The world is not one-dimensional, of course. Most VEX path-following systems work with the robot’s pose \((x,y,\theta)\).

Parallel PID Loops (Curves): Differential Drive

A standard tank or differential drive has two controllable motions: forward velocity \(v\) and angular velocity \(\omega\). Each can have a separate PID loop running in parallel.

When working with \((x,y,\theta)\), the two-dimensional position error must first be converted into a linear error that the drivetrain can use. Let

\[ \mathbf e_p= \begin{bmatrix} x_t-x_c\\ y_t-y_c \end{bmatrix}. \]

Projecting this vector onto the robot’s forward unit vector gives

\[ e_{\mathrm{forward}} =e_x\cos\theta+e_y\sin\theta. \]

This scalar can be used in the PID loop for \(v\). The rotational loop uses the wrapped angular error

\[ e_\theta=\operatorname{wrap}(\theta_t-\theta_c) \]

to calculate \(\omega\). The left and right wheel speeds are then linear combinations of \(v\) and \(\omega\):

\[ \omega_L=\frac{1}{r}\left(v-\frac{B}{2}\omega\right), \qquad \omega_R=\frac{1}{r}\left(v+\frac{B}{2}\omega\right), \]

where \(B\) is the drivetrain width and \(r\) is the wheel radius. A differential drive cannot directly correct sideways error, so it must rotate until its forward direction points toward the desired motion. This is what produces a curve.

Parallel PID Loops (Curves): X-Drive

An X-drive has three controllable motions: sideways velocity \(v_x\), forward velocity \(v_y\), and angular velocity \(\omega\). This means that separate PID loops can be used for \(x\), \(y\), and \(\theta\).

If the translational command is calculated in field coordinates, it first has to be rotated into the robot’s coordinate frame:

\[ \begin{bmatrix} v_x^{(r)}\\ v_y^{(r)} \end{bmatrix} = \begin{bmatrix} \cos\theta & \sin\theta\\ -\sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} v_x^{(f)}\\ v_y^{(f)} \end{bmatrix}. \]

For a 45° X-drive, the wheel commands can then be found through linear superposition:

\[ \begin{aligned} \omega_1 &= \frac{1}{r}\left(v_x^{(r)}+v_y^{(r)}+R\omega\right),\\ \omega_2 &= \frac{1}{r}\left(-v_x^{(r)}+v_y^{(r)}-R\omega\right),\\ \omega_3 &= \frac{1}{r}\left(v_x^{(r)}+v_y^{(r)}-R\omega\right),\\ \omega_4 &= \frac{1}{r}\left(-v_x^{(r)}+v_y^{(r)}+R\omega\right). \end{aligned} \]

Here, \(R\) is the effective distance from the robot’s center to each wheel’s contact direction. The signs depend on the wheel numbering, motor orientation, and coordinate convention, but the principle is the same: each wheel receives a combination of sideways motion, forward motion, and rotation.

The robot now runs three motion loops instead of two, but the basic idea is more direct than I expected. There is an error and a commanded velocity for each degree of freedom, followed by a coordinate transformation and a wheel mixer. HoloLib handles these calculations along with the other parts of autonomous control.

So, although the X-drive is mechanically more flexible, its core control model is not necessarily more complicated. In some ways, it is simpler because the software can treat the \(x\), \(y\), and heading errors independently.