Control · PID
A pair of PID loops, one per plate axis, that map the tracking error to a scalar tilt command and route it through wiring, inverse kinematics and smoothing to a joint trajectory for the arm.
Two right-handed Cartesian frames are used: the world frame $\{W\}$ at the UR7e base and the plate frame $\{P\}$ at the geometric centre of the plate. The ball state $X = (x, y, \dot x, \dot y)$ and the reference trajectory $r(t) = (x_r, y_r, \dot x_r, \dot y_r)$ are both expressed in $\{P\}$, in metres and metres per second. Two errors are derived from them and consumed by the controller:
The relative orientation of the two frames is encoded in a rotation matrix $R_{WP}(q) \in SO(3)$, with components of a vector $v$ transforming as $v^{W} = R_{WP}(q)\, v^{P}$. The inverse mapping is $R_{PW}(q) = R_{WP}(q)^{\top}$.
In general, the pose of the plate frame in the world frame is a homogeneous transform
and a full plate-pose controller would have to regulate both the rotation $R_{WP}(q)$ and the translation $t(q)$ as functions of the six joints $q \in \mathbb{R}^6$.
In the deployed setup (see § 5) the upper-arm joints $q_1, q_2, q_3$ are held at their home values, so the plate centre is fixed in $\{W\}$ throughout operation and the translation
drops out of the control problem. The task reduces to tracking only the rotation $R_{WP}(q) \in SO(3)$. Its first two columns are the world-frame coordinates of the plate's local axes $\hat x_P, \hat y_P$, and feed the wiring stage below.
The PID block takes the tracking error and emits a pair of scalar tilt angles in radians,
where $u_x$ is the desired tilt of the plate about the local $\hat y_P$ axis and $u_y$ is the desired tilt about $\hat x_P$. The sign convention is fixed by the rolling-without-slipping equation derived on the physics page: $u_x > 0$ corresponds to a tilt that drives the ball toward $-\hat x_P$.
One scalar PID controller is instantiated per plate axis. The two run independently with shared gains. Each consumes the tracking error along its axis and emits the corresponding tilt angle:
The integrator state is anti-windup-clamped to a symmetric bound and the output is clipped to a maximum tilt amplitude. The deployed gains were tuned by hand on the live arm.
The scalars $(u_x, u_y)$ are tilt angles about the plate's local axes $\hat x_P, \hat y_P$. The downstream inverse-kinematics step needs the corresponding rotation 3-vector expressed in the world frame $\{W\}$, because the manipulator Jacobian maps joint motion to world-frame plate rotations. The wiring stage performs that change of basis.
Writing $\hat x_P^{\,W}(q)$ for the plate's local $\hat x_P$ axis with its components expressed in $\{W\}$,
where $\hat e_x = (1,0,0)^{\top}, \hat e_y = (0,1,0)^{\top}$, the two vectors $\hat x_P^{\,W}(q)$ and $\hat y_P^{\,W}(q)$ are the first two columns of $R_{WP}(q)$.
For a small angle $\alpha$, a rotation by $\alpha$ about a unit axis $\hat n$ is represented in axis-angle form by the 3-vector $\alpha \hat n$. Stacking the two scalar tilts, the target rotation in $\{W\}$ is
The minus sign on the $u_x$ term comes from the rolling-equation convention fixed on the physics page: $u_x > 0$ must drive the ball toward $-\hat x_P$, which corresponds to a rotation about $-\hat y_P$.
The next stage is the inverse kinematics, where the rotational Jacobian $J_r(q) \in \mathbb{R}^{3 \times 6}$ relates joint displacements $\Delta q$ to plate rotations: in the small-displacement regime, $J_r(q)\, \Delta q \approx \Delta\theta$. This relation is a first-order Taylor expansion around $q$ and is accurate only when $\Delta\theta$ is genuinely small. Feeding the IK an absolute target rotation produces large residual joint motion and breaks the linearisation as soon as the plate is no longer near home.
To avoid this, the current plate rotation $\theta_{\text{cur}}$ is read from the transform tree (encoding the current $R_{WP}(q)$ in axis-angle form) and subtracted from the absolute target:
The IK then solves for the joint displacement that produces $\Delta\theta$, not $\theta_{\text{tar}}$; the linearisation is exercised only on small residuals.
The geometric Jacobian of the manipulator decomposes into a positional block and a rotational block:
$J_p$ relates joint velocities to the linear velocity of the wrist and is unused here, because the position of the plate centre is fixed by the locked upper-arm joints. $J_r$ relates joint velocities to the angular velocity of the plate; in the small-displacement regime, $J_r(q)\, \Delta q \approx \Delta\theta$.
The system has three equations and six unknowns and is therefore underdetermined. With the upper-arm joints $q_1, q_2, q_3$ locked, the corresponding three columns of $J_r$ are dropped; only the wrist columns contribute. The remaining $3 \times 3$ subsystem is square and well-conditioned at the home configuration. The minimum-norm solution is computed via the Moore-Penrose pseudoinverse:
In practice the pseudoinverse is computed via the SVD-based least-squares routine, which is numerically stable when the wrist subsystem approaches singularity (e.g. at gimbal-lock configurations).
The raw $\Delta q$ is clipped per-joint to a safe maximum tilt $\Delta q_{\max}$ and then passed through a first-order low-pass filter to attenuate high-frequency content that would excite the structural modes of the arm:
The filtered correction is added to the current configuration to produce the target,
which is published as a joint trajectory message at the controller rate. The vendor's joint-space firmware takes over from there: it interpolates between successive waypoints and runs an internal joint-level loop with gravity compensation. This boundary is described in more detail on the ROS page.