Paper · 2026-06-30 · cs.RO
Improving path-tracking performance of an articulated tractor-trailer system using a non-linear kinematic model
A kinematic model of an articulated tractor with front steering and a central joint, with a hitched trailer, is embedded in an NMPC that tracks the trailer's xy-position. In ROS/Gazebo simulation on a 1600 m2 seeding field, trailer error stays below 16 cm along x after start-up and below 12 cm along y in headland turns (author-reported, simulation only).
Model predictive control (MPC)Direct collocationKinematic bicycle modelpath-trackingnonlinear-mpcarticulated-vehiclesagricultural-robotics
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
A tractor pulling a trailer does not follow the same path as its trailer. In turns the trailer cuts a different line, so a row of seeds or a spray pass can end up with gaps or overlaps. The usual remedy is to steer the tractor, but this paper argues that what matters is where the trailer itself goes. The authors write a mathematical model of a tractor that can steer in two ways, with its front wheels and with a joint in its middle, plus a trailer hitched behind it. The model ignores forces and works only with positions, angles and speeds, which the authors consider accurate enough for vehicles at low acceleration.
They put that model inside a controller that looks ahead over a short window, plans the steering that best keeps the trailer on the path, applies only the first step, and then repeats. In a computer simulation the trailer follows a seeding path closely, including the turns at the end of each row. All results are the authors' own, and they come from simulation only.
TL;DR
- A kinematic model with no forces, derived under a no-slip assumption, describes an articulated tractor with front steering and a central joint pulling a trailer §2.
- A nonlinear MPC uses this model to control the trailer's xy-position rather than the tractor's §3 §4. Horizon 6 s, sampling 0.1 s, three collocation points §5.
- In ROS/Gazebo on a 1600 m² seeding field, trailer error stays below 16 cm along x once moving and below 12 cm along y in headland turns. A tractor-front controller reaches a front-block error below 3.8 m in headland turns §5.1 §5.2. All author-reported.
- The authors publish code under GPL-3.0. It differs from the paper in state order, cost terms and the articulation joint limit; see Paper vs code.
- Simulation only, with no field or hardware test. Use it as a modelling reference, not as a tuned controller.
Problem
Trailers behind agricultural tractors drift from the tractor's path in curves and headland turns, which causes gaps and overlaps §1. Earlier work either runs separate controllers for tractor and implement, which ignores their interaction §1 §6, or uses models limited to front-steering tractors without an articulation joint §1 §6. Kinematic models in the literature mostly describe front-steering tractors and leave out the implement §1. The authors take the view that kinematic models are accurate enough for vehicles that operate at low accelerations, and that their target vehicles run slowly §1.
Assumptions built into the model §2:
- No lateral slip. Each axle is treated as a single wheel at its centre (bicycle simplification), and each block moves only along its own heading.
- Pitch and roll are not modelled. Future work targets non-flat terrain §7.
- The simulator uses physics the model does not contain, and the authors say the two differ §5.
"Headland turns" here means the turns at the end of each row §5.2.
Method
The tractor has a front axle steered by an angle φ and a central articulation joint with angle γ between its rear and front blocks. The trailer hitches at distance d_1 behind the rear axle and has its own yaw θ_t §2. Each block obeys its non-holonomic constraint, and solving those constraints gives the rear yaw rate and the trailer yaw rate. The inputs are the front speed v_f and the two angular rates ω_1 = γ̇ and ω_2 = φ̇. The paper chooses v_f rather than the rear speed v_r as the speed input, to make the control problem harder §2.
Steps, in the order the paper derives and uses them:
- Geometry. Five frames (world, trailer, rear, front, front wheels). Lengths L_r (rear axle to joint), L_f (joint to front axle), d_1 (rear axle to hitch) and d_2 (hitch to trailer axle); angles θ_t, θ_r, γ, φ §2.
- Constraints. Each block's velocity lies along its heading. The front block uses θ_r + γ + φ, which accounts for the front steering §2 eq. 4 eq. 5.
- Rear yaw rate. Eliminating the front velocity gives θ̇_r (eq. 7). The denominator is nonzero because |γ| < π/2 §2 eq. 8.
- Trailer yaw rate. Eliminating the rear constraints gives θ̇_t (eq. 9) §2.
- Speed relation and position. The rear speed v_r is written in terms of v_f (eq. 14). The trailer position rates are given by eq. 12.
- Full model. Stacking these gives ẋ = F(x, u) (eq. 13), with x = [x_t, y_t, θ_r, θ_t, γ, φ] and u = [v_f, ω_1, ω_2] (eqs. 15–16) §2.
- Discretisation. Collocation with three points per sampling period §4 §5. The paper names Gauss–Legendre points as one way to obtain such polynomials §4.
- NMPC. At each sampling instant, solve the finite-horizon problem of eq. 17 with the cost of eq. 19 (quadratic in the trailer xy error and the inputs, plus a terminal term). Apply only the first input and shift the horizon §3 §4. The terminal weight P equals Q §5.
- Two scenarios. Section 5.1 controls the front-block xy-position with the tractor-only model. Section 5.2 controls the trailer xy-position with the full model. The cost is the same in both §5.
Key equations, written in the paper's notation:
\dot\theta_r = \frac{v_f\sin(\gamma+\phi) - \dot\gamma\,L_f\cos\gamma}{L_r + L_f\cos\gamma} eq. 7
\dot\theta_t = \frac{v_r}{d_2}\sin(\theta_r-\theta_t) - \frac{d_1}{d_2}\dot\theta_r\cos(\theta_r-\theta_t) eq. 9
v_r = v_f\cos(\gamma+\phi) + L_f(\dot\theta_r+\dot\gamma)\sin\gamma eq. 14
\dot{\mathbf{x}} = \begin{bmatrix} v_r\cos\theta_r + d_1\dot\theta_r\sin\theta_r + d_2\dot\theta_t\sin\theta_t \\ v_r\sin\theta_r - d_1\dot\theta_r\cos\theta_r - d_2\dot\theta_t\cos\theta_t \\ \dot\theta_r \\ \dot\theta_t \\ \omega_1 \\ \omega_2 \end{bmatrix} eq. 13, with θ̇_r and θ̇_t as in eqs. 7 and 9
The model as code-shaped pseudocode, following eqs. 7, 9, 12, 13 and 14:
# Kinematic model F(x, u) of the articulated tractor-trailer [eq. 13]
# State: x = [x_t, y_t, theta_r, theta_t, gamma, phi] [eq. 15]
# Input: u = [v_f, omega_1, omega_2] [eq. 15]
# Output: state derivative, same shape as x
def model_rhs(state, control):
# Unpack the state and the input vectors [eq. 15]
x_t, y_t, theta_r, theta_t, gamma, phi = state
v_f, omega_1, omega_2 = control
# Yaw rate of the rear block, from the constraints [eq. 7]
theta_r_dot = (
(v_f * sin(gamma + phi) - L_f * cos(gamma) * omega_1)
/ (L_r + L_f * cos(gamma))
)
# Speed of the rear axle centre [eq. 14]
v_r = (
v_f * cos(gamma + phi)
+ L_f * (theta_r_dot + omega_1) * sin(gamma)
)
# Yaw rate of the trailer [eq. 9]
theta_t_dot = (
v_r / d_2 * sin(theta_r - theta_t)
- d_1 / d_2 * theta_r_dot * cos(theta_r - theta_t)
)
# Velocity of the trailer axle centre [eq. 12]
x_t_dot = (
v_r * cos(theta_r)
+ d_1 * theta_r_dot * sin(theta_r)
+ d_2 * theta_t_dot * sin(theta_t)
)
y_t_dot = (
v_r * sin(theta_r)
- d_1 * theta_r_dot * cos(theta_r)
- d_2 * theta_t_dot * cos(theta_t)
)
# Gamma and phi integrate their rates directly [eq. 10] [eq. 11]
return [x_t_dot, y_t_dot, theta_r_dot, theta_t_dot, omega_1, omega_2]The receding-horizon loop, from §3 and §4. The paper does not describe a runtime loop, so this is a generic form of its scheme:
# Receding-horizon loop that tracks the trailer's xy path [§3] [§4]
# Input: xy reference points (M, 2), horizon length in steps N [§5]
# Output: applied inputs (M, 3), one row per sampling period
def track_trailer_path(reference_xy, horizon_steps):
# The previous input is zero before the first period [§5]
previous_input = zeros(3)
applied_inputs = []
# One pass per sampling period T_s = 0.1 s [§5]
for period in range(number_of_periods):
# Measure the state; the paper does not say how [§3]
state = measure_state()
# Solve the finite-horizon problem with the cost and bounds [eq. 17]
# Cost: quadratic in the trailer xy error and the inputs [eq. 19]
inputs = solve_nmpc(state, reference_xy, previous_input, horizon_steps)
# Apply only the first input, then re-solve next period [§3]
previous_input = inputs[0]
applied_inputs.append(inputs[0])
return applied_inputsHyperparameters
Values from §5 unless marked. Code values are from the authors' repository at code_commit. Angles in the paper are in degrees, and the code converts them to radians internally export_articulated_tractor_trailer_model.py.
| Name | Value | Where | Notes |
|---|---|---|---|
| L_f, front axle to joint | 0.8 m | §5 | export_articulated_tractor_trailer_model.py |
| L_r, rear axle to joint | 1.3 m | §5 | export_articulated_tractor_trailer_model.py |
| d_1, rear axle to hitch | 0.5 m | §5 | export_articulated_tractor_trailer_model.py |
| d_2, hitch to trailer axle | 1.3 m | §5 | export_articulated_tractor_trailer_model.py |
| State weights Q | diag(150, 300, 1, 100, 1, 100) | §5 | Paper order differs from code order; see Paper vs code |
| Terminal weight P | equal to Q | §5 | export_articulated_tractor_trailer_model.py |
| Input weights R | diag(25, 1, 1) | §5 | export_articulated_tractor_trailer_model.py |
| Sampling period T_s | 0.1 s | §5 | Loop at 10 Hz in code: articulated_tractor_trailer_trajectory.py |
| Horizon N | 6 s | §5 | 60 steps in code: export_articulated_tractor_trailer_model.py |
| Collocation points | 3 per period | §5 | export_articulated_tractor_trailer_model.py |
| Articulation angle bound | 60° in absolute value | §5 | Code uses ±π/3 export_articulated_tractor_trailer_model.py; joint limit differs, see Paper vs code |
| Steering angle bound | 60° in absolute value | §5 | Same code lines as above |
| Front speed bound | 2 m/s in absolute value | §5 | code: export_articulated_tractor_trailer_model.py |
| Speed change bound | 0.5 m/s per step | §5 | Code applies it as a bound on the change between consecutive samples: export_articulated_tractor_trailer_model.py |
| Joint rate bounds | 15 deg/s | §5 | ω_1 and ω_2; code: export_articulated_tractor_trailer_model.py |
| Joint rate change bounds | 10 deg/s per step | §5 | ω_1 and ω_2; code: export_articulated_tractor_trailer_model.py |
| Initial previous input | zero | not stated | Code sets it to zero: articulated_tractor_trailer_trajectory.py |
| Wheel radius | not stated | not stated | Code uses 0.2 m: articulated_tractor_trailer_trajectory.py; matches URDF model_properties.xacro |
| Field and reference rows | 1600 m² field | §5 | Code: 40 m wide, 10 m row spacing, five passes: trajectory_generation.py trajectory_generation.py |
| Reference speeds | not stated | not stated | Code spaces reference points at 1.9 m/s on rows and 1.3 m/s on arcs: trajectory_generation.py |
| Solver stack | CasADi, MPCTools, HSL | §5 | HSL linear solver call is commented out: export_articulated_tractor_trailer_model.py |
| Simulation host | Intel Core i7-8700, 3.20 GHz, 16 GB RAM | §5 | Not a robot or field test |
Results
Author-reported, in simulation only. Setup: ROS with Gazebo, controller in Python with CasADi, MPCTools and HSL, on the host listed above §5. The baseline is the tractor-front controller of §5.1, which uses the same cost and horizon and differs only in the embedded model §5. The paper has no tables; the numbers come from the text and figures.
| Setting | Metric | Result | Source |
|---|---|---|---|
| Trailer controlled (§5.2) | e_x along x | Larger at start, below 16 cm once moving | §5.2 fig. 7 |
| Trailer controlled, straight rows | e_y along y | Below 1 cm | §5.2 fig. 7 |
| Trailer controlled, headland turns | e_y along y | Below 12 cm | §5.2 fig. 7 |
| Trailer controlled, row entry | Alignment | Enters the next row with almost no deviation (qualitative) | §5.2 |
| Tractor-front controlled (§5.1), straight rows | Front-block e_y | "Very small" (no number given) | §5.1 fig. 5 |
| Tractor-front controlled, headland turns | Front-block e_y | Below 3.8 m | §5.1 fig. 5 |
| Tractor-front controlled | Trailer path | Trailer turns on a tighter circle than the reference (no number) | §5.1 fig. 4 |
| Trailer controlled | Front speed v_f | Rises from 0 to 2 m/s; 1.75 m/s to the maximum on rows; 1.15–1.55 m/s in turns | §5.2 fig. 9 |
| Trailer controlled | γ and φ | Near zero on rows; move together in turns | §5.2 fig. 8 |
Not stated: solve times, number of runs, noise or disturbance settings, and any comparison with published controllers other than §5.1.
Paper vs code
Official repository at code_commit, GPL-3.0 (copyleft). Code is cited by path and line only, and nothing is quoted. The equations in the trailer model match the paper term by term export_articulated_tractor_trailer_model.py eq. 7 eq. 9 eq. 12 eq. 13 eq. 14.
- State order and Q weights. Eq. 15 orders the state as [x_t, y_t, θ_r, θ_t, γ, φ] §2. The code orders it as [x_t, y_t, θ_r, γ, θ_r − θ_t, φ], using the yaw difference instead of θ_t export_articulated_tractor_trailer_model.py articulated_tractor_trailer_trajectory.py. Read in the code's order, the weight 100 in Q falls on γ, not θ_t export_articulated_tractor_trailer_model.py §5. The paper does not say which ordering the reported runs used.
- Cost terms. Eq. 19 and its text consider only the trailer xy setpoints §4. The code also sets a setpoint for θ_r, the heading of the reference path articulated_tractor_trailer_trajectory.py. It leaves the setpoints for γ, θ_r − θ_t and φ at zero, and those states enter the quadratic cost with weights 100, 1 and 100 export_articulated_tractor_trailer_model.py §5. So the implemented cost is larger than eq. 19 as written.
- Front-block scenario (§5.1). The paper says the controller knows nothing about the trailer §5.1. The tractor script still reads the trailer yaw, builds the θ_r − θ_t state from it, and the cost keeps that state with weight 1 articulated_tractor_trajectory.py articulated_tractor_trajectory.py export_articulated_tractor_model.py. The front-block outputs do not depend on that state, so the effect is limited to the cost.
- Articulation joint limit. The paper's Definition 1 gives ±π/4 (45°) for the articulation joint §5. The simulated URDF uses the same ±π/4 articulated_tractor_trailer.xacro. The MPC bound is ±60° §5 export_articulated_tractor_trailer_model.py. The optimiser can request angles the simulated joint cannot reach. The paper does not discuss this.
- Steering. The paper has one front steering angle φ §2. The code sends the same command to both front steering joints articulated_tractor_trailer_trajectory.py. The URDF defines steering joints per side from one macro steering.xacro. The paper does not mention this.
- Commands. The paper's inputs are the rates ω_1 and ω_2 §2. The code sends the predicted next-step positions of γ and φ as joint position targets articulated_tractor_trailer_trajectory.py articulated_tractor_trailer_trajectory.py. It converts v_f to a wheel angular speed by dividing by 0.2 m articulated_tractor_trailer_trajectory.py.
- Sensing. The paper does not describe state estimation. The code reads simulator ground truth for every pose and angle articulated_tractor_trailer_trajectory.py articulated_tractor_trailer_trajectory.py.
- Solver. The paper says HSL is used §5. Both model files have the HSL linear solver call commented out export_articulated_tractor_trailer_model.py export_articulated_tractor_model.py. The README describes HSL as optional README.md.
- Geometry constants. The MPC constants are hard-coded export_articulated_tractor_trailer_model.py. They are not read from the simulator model. The URDF lengths are front 1.0 m, rear 1.5 m and trailer 0.5 m model_properties.xacro model_properties.xacro model_properties.xacro. The paper says the Gazebo model differs from the mathematical one §5.
- Speed-change bound. The code applies the 0.5 m/s and 10 deg/s bounds to changes between consecutive samples export_articulated_tractor_trailer_model.py. The paper does not say whether its numbers are per step or per second.
- Yaw conversion. The quaternion-to-yaw conversion negates its result articulated_tractor_trailer_trajectory.py. The sign convention against the paper's θ was not checked.
No code was run. The discrepancies above come from reading the code only.
Pitfalls
- Equation numbers. They were counted from the LaTeX source, not from a rendered PDF. The geometric pair is 2a and 2b. Eq. 6 holds four sub-equations written as (a) to (d) in the text. Check against the rendered PDF before citing a number in a published page.
- N is ambiguous. §5 gives N = 6 s §5, but eqs. 17 and 19 use N as a count of steps §3 §4. The code uses 60 steps at 0.1 s export_articulated_tractor_trailer_model.py.
- Q is six-dimensional but the cost is written in xy. Eq. 19 takes the trailer position error as its argument §4, while Q is 6×6 §5. A position-only reading needs a 2×2 block that the paper does not give. The code uses all six states (see Paper vs code).
- θ_t versus θ_r − θ_t. The paper's state holds θ_t §2. The code's state holds θ_r − θ_t. Recover θ_t as θ_r minus that state.
- Singularity margin. The denominator L_r + L_f cos γ needs |γ| < π/2 §2. The code bound of 60° gives cos γ ≥ 0.5 export_articulated_tractor_trailer_model.py.
- Units. The paper gives rates and bounds in deg/s and deg §5. The code works in radians export_articulated_tractor_trailer_model.py. Read as a per-step bound with 0.1 s steps, it implies a rate of 5 m/s²; the paper does not say which reading it means.
- Collocation nodes. The paper gives the count (three) §5 and names Gauss–Legendre points as one option §4. The code sets only the count export_articulated_tractor_trailer_model.py. Node placement is not stated.
- Input oscillation. The paper reports violent oscillation in v_f, ω_1 and ω_2 §5.2. It proposes first-order filters for γ and φ with constants k_1 to k_4 §5.2, which are not given and are not in the code. Smooth joints need new design work.
- Solver failure. The control loop stops at the first non-success solver status, with no fallback articulated_tractor_trailer_trajectory.py. The paper does not discuss failure.
- Errors in §5.1 use the trailer. The §5.1 error definitions use the trailer position x_t §5.1, but the controlled output there is the front block. The §5.1 text also reads e_x as the trailer being ahead or behind. Treat the 3.8 m figure as front-block error, as Fig. 5's caption states fig. 5.
- Qualitative claims. "Very small" in §5.1 and "almost no deviation" in §5.2 have no numbers §5.1 §5.2.
- Simulation gap. The authors say the simulator uses physics the model does not contain and that they do not know how it works internally §5. No model-error measurement is reported.
- Scope. No slip, no pitch or roll, flat ground, no noise, no hardware, and no field test §2 §5 §7.
- Date. The LaTeX date field reads May 2022 (
main.texline 34). The arXiv record is v1 dated 2026-06-30. The code header is dated 17/08/2021 articulated_tractor_trailer_trajectory.py. Check which version the implementation follows. - Licence of the code. GPL-3.0 is copyleft. Cite it, but do not copy code into a permissively licensed project.
Lineage
- Builds on: The NMPC framework cited as
rawlings2017model§3, with no card here. - Special cases: The authors say the model reduces to the articulated-vehicle model in
gustaf2015effectwhen the front direction is fixed and the trailer is dropped, and to the model inlavalle2006planningwhen the hitch sits on the rear axle and the articulation is removed §2. Neither has a card here. - Compared against: The authors contrast their work with the NMPC tractor-trailer papers "Navigation system for agricultural machines: Nonlinear model predictive path tracking" (
backman2012navigation) and "Learning in centralized nonlinear model predictive control: Application to an autonomous tractor-trailer system" (kayacan2014learning), which they say cover front steering only and no articulation joint §6. Neither has a card here. - Superseded by: Nothing known from the wiki.
Source and license
Adapted from “Improving path-tracking performance of an articulated tractor-trailer system using a non-linear kinematic model” by Marina Murillo, Guido Sanchez, Nestor Deniz, Lucas Genzelis, Leonardo Giovanini (arXiv:2606.31889), used under CC BY-SA 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. This page is licensed under CC BY-SA 4.0. Code citations refer to https://github.com/marinahmurillo/articulated_tractor_trailer_paper at commit 4e8931b2ebee (GPL-3.0). Any quoted code is shown under that license.