A modular differential-drive robot simulator designed for control, estimation,
and active-learning gain tuning research.
This README explains:
- Installation
- What each subsystem does
- Which YAML fields configure it
- The equations used in every module
- How all components interact in the simulation loop
- The planner, controller, robot model, estimator, and visualization
# Clone repo
git clone git@github.com:IMRCLab/wmr-simulator.git
# Create and activate a virtual env (using Python >=3.11,<3.13)
cd wmr-simulator
python3.11 -m venv .venv
source .venv/bin/activate
# Install dependecies
# uv must be installed, see https://docs.astral.sh/uv/getting-started/installation/
uv sync
uv pip install -e . # Current package in editable modeThe simulator is driven by a YAML file such as empty.yaml.
sim_time: 5.0
time_step: 0.01
environment:
min: [-5, -5]
max: [5, 5]
obstacles: []
start: [0.0, 0.0, 0.0]
goal: [2.0, 1.0, 1.57]
planner:
waypoints:
- [0.5, 0.0, -1.57]
- [1.0, 0.5, 1.57]
time: 4.0
controller:
gains: [5.0, 5.0, 3.0, 0.4, 0.4, 0.2, 0.2]
robot:
wheel_radius: 0.016
base_diameter: 0.089
max_wheel_speed: 40.0
slip_r: 0.4
slip_l: 0.5
estimator:
type: "kf"
wheel_radius: 0.015
base_diameter: 0.09
noise_pos: 0.0001
noise_angle: 0.07
enc_angle_noise: 0.01
proc_pos_std: 0.7
proc_theta_std: 0.7
start: [0.0, 0.0, 0.0]| Subsystem | YAML fields | What it configures |
|---|---|---|
| Planner | planner.waypoints, planner.time |
Path & time scaling |
| Controller | controller.gains |
Geometric gains + PI wheel controllers |
| Robot | robot.wheel_radius, robot.base_diameter, robot.slip_r, robot.slip_l, robot.max_wheel_speed |
True simulation dynamics |
| Estimator | estimator.type, noise params |
DR or EKF |
| Simulation | sim_time, time_step, start/goal |
Integration horizon & boundary conditions |
To run a complete simulation:
python3 scripts/simulator.py --problem problems/empty.yaml --output simulation_visualizationOutputs:
- PDF of plots
- MeshCat 3D animation
- Logs in
results/
Produces:
- Multi-page PDF
- MeshCat 3D animation
- True, estimated, and reference trajectories
wheel_radiusbase_diameterslip_r,slip_lmax_wheel_speed
State evolution:
Robot state update uses the effective speeds.
In addition to the kinematic mapping from wheel speeds to ((v,\omega)), the robot model includes a simple first-order motor dynamics model that smooths the wheel commands and approximates actuator delay.
The relevant YAML field is:
robot:
wheel_radius: 0.016
base_diameter: 0.089
max_wheel_speed: 40.0
time_constant: 0.05 # [s] motor time constant
slip_r: 0.4
slip_l: 0.5The continuous-time motor dynamics for each wheel are modeled as:
where
-
$u_r^{cmd}, u_l^{cmd}$ are the commanded wheel speeds from the PI controllers, -
$u_r^{eff}, u_l^{eff}$ are the effective wheel speeds seen by the kinematics, -
$\tau = \texttt{robot.time_constant}$ is the motor time constant.
In discrete time with simulator timestep
and updates:
If
which reduces to
i.e. no motor lag (instantaneous response).
These effective wheel speeds are then used in the kinematics (and slip model):
- First apply motor dynamics to get
$u_r^{eff}, u_l^{eff}$ . - Then apply slip: $$ u_r^{slip} = u_r^{eff}(1-\varepsilon_r),\qquad u_l^{slip} = u_l^{eff}(1-\varepsilon_l), $$
- Finally compute $$ v = \frac{r}{2}(u_r^{slip}+u_l^{slip}), \qquad \omega = \frac{r}{L}(u_r^{slip}-u_l^{slip}). $$
The planner produces the reference trajectory:
It performs:
- Quintic spline fitting in space
- Time-scaling to match total duration
- Trajectory sampling
planner:
waypoints:
- [...]
- [...]
time: 4.0Waypoints can be:
[x, y][x, y, theta]Length of the waypoint determines whether heading is enforced.
where
Velocities:
Heading:
Curvature:
Angular velocity:
To debug waypoints without running the simulator:
python3 planner.pyModify the __main__ block inside planner.py:
intermediate_waypoints = [
[x1, y1], # no θ constraint
[x2, y2, theta2], # θ constrained
]Running the module plots:
- spline path
- heading
- curvature
- velocities
controller:
gains: [k_x, k_y, k_θ, k_pr, k_pl, k_ir, k_il]Inputs:
- Reference: ((x_d,y_d,\theta_d,v_d,\omega_d))
- Estimate: ((\hat{x},\hat{y},\hat{\theta}))
Pose errors in robot frame:
Control law:
Errors:
Commands:
Outputs
type:"dr"or"kf"wheel_radius,base_diameter- sensor noise:
noise_pos,noise_angle - encoder noise:
enc_angle_noise - process noise:
proc_pos_std,proc_theta_std - initial estimate:
start
Speeds:
State prediction:
Jacobian:
Covariance:
Measurements:
Angle wrapping:
Residual:
Update:
Subsystems:
- Planner → smooth reference trajectory
- Trajectory sampler
- Estimator → produces (\hat{x},\hat{y},\hat{\theta})
- Controller
- geometric pose control
- wheel-speed PI loops
- Robot
- slip
- differential-drive kinematics
- Visualization and logging