Vehicles
This section describes vehicle definitions, dynamics models, and control systems in MVSim. Vehicles are mobile robots or platforms that can move through the simulated world using various locomotion systems.
1. Overview
Vehicles in MVSim are defined through XML configuration files that specify:
Dynamics model - The type of locomotion (differential drive, Ackermann steering, etc.)
Physical properties - Mass, dimensions, wheel configurations
Motor controllers - How commands translate to wheel torques
Friction models - Ground interaction and traction characteristics
Sensors - Optional sensor attachments (LiDAR, cameras, IMU, etc.)
Visual models - 3D meshes for rendering
2. Vehicle Definition Structure
Basic XML Structure
Vehicles are defined using <vehicle:class> tags for reusable templates, and <vehicle>
tags for specific instances:
<!-- Define a reusable vehicle class -->
<vehicle:class name="my_robot">
<dynamics class="differential">
<!-- Dynamics parameters -->
</dynamics>
<friction class="default">
<!-- Friction parameters -->
</friction>
<!-- Additional parameters -->
</vehicle:class>
<!-- Create a vehicle instance -->
<vehicle name="robot1" class="my_robot">
<init_pose>0 0 0</init_pose> <!-- x, y, yaw(deg) -->
<init_vel>0 0 0</init_vel> <!-- vx, vy, omega(deg/s) -->
</vehicle>
Common Vehicle Parameters
Chassis Configuration:
<chassis mass="15.0" zmin="0.05" zmax="0.6" color="#0080ff">
<shape>
<pt>-0.4 -0.5</pt>
<pt>-0.4 0.5</pt>
<pt> 0.4 0.5</pt>
<pt> 0.4 -0.5</pt>
</shape>
</chassis>
Chassis Attributes:
mass - Vehicle chassis mass in kilograms
zmin, zmax - Minimum and maximum Z coordinates for collision detection
color - RGB color in hex format (e.g., #FF0000)
shape - 2D polygon defining the chassis footprint (list of <pt> elements)
shape_from_visual - Automatically extract collision shape from 3D visual model
Odometry Configuration:
<odometry
x_multiplier="1.02"
y_multiplier="1.01"
yaw_multiplier="1.03"
/>
Adds realistic noise to odometry by multiplying actual pose increments. Values are typically
drawn from $f{1.0 + 0.02*randn()} to simulate sensor imperfections.
Visual Models:
<visual>
<model_uri>path/to/model.dae</model_uri>
<model_scale>0.001</model_scale>
<model_offset_x>0.0</model_offset_x>
<model_offset_y>0.0</model_offset_y>
<model_offset_z>0.0</model_offset_z>
<model_yaw>0.0</model_yaw>
<model_pitch>0.0</model_pitch>
<model_roll>90.0</model_roll>
</visual>
3. Dynamics Models
MVSim supports several vehicle dynamics models representing different locomotion systems.
Differential Drive
Class: differential, differential_3_wheels, differential_4_wheels
Two independently driven wheels (or sets of wheels) that control vehicle motion through differential speeds. Common in mobile robots like TurtleBot.
<dynamics class="differential">
<l_wheel pos="0.0 0.5" mass="4.0" width="0.20" diameter="0.40" />
<r_wheel pos="0.0 -0.5" mass="4.0" width="0.20" diameter="0.40" />
<chassis mass="15.0" zmin="0.05" zmax="0.6" />
<controller class="twist_pid">
<KP>5</KP>
<KI>10</KI>
<I_MAX>1</I_MAX>
<KD>0</KD>
<max_torque>100</max_torque>
</controller>
</dynamics>
Wheel Parameters:
pos - Position (x, y) relative to chassis center [meters]
mass - Wheel mass [kg]
width - Wheel width [meters]
diameter - Wheel diameter [meters]
Kinematics:
The vehicle angular velocity and linear velocity are computed from wheel speeds:
where \(\omega_i\) is wheel angular velocity, \(R_i\) is wheel radius, and \(y_i\) is the lateral position of wheel \(i\).
Ackermann Steering
Class: ackermann
Four-wheeled vehicle with front-wheel steering geometry. The two front wheels turn to steer the vehicle, while rear wheels remain parallel to the chassis axis.
<dynamics class="ackermann">
<!-- Rear wheels -->
<rl_wheel pos="0 1" mass="6.0" width="0.30" diameter="0.62" />
<rr_wheel pos="0 -1" mass="6.0" width="0.30" diameter="0.62" />
<!-- Front wheels -->
<fl_wheel mass="6.0" width="0.30" diameter="0.62" />
<fr_wheel mass="6.0" width="0.30" diameter="0.62" />
<f_wheels_x>1.3</f_wheels_x> <!-- Wheelbase -->
<f_wheels_d>2.0</f_wheels_d> <!-- Track width -->
<max_steer_ang_deg>30.0</max_steer_ang_deg>
<chassis mass="800.0" zmin="0.15" zmax="1.00" />
<controller class="twist_front_steer_pid">
<KP>1500</KP>
<KI>50</KI>
<I_MAX>20</I_MAX>
<KD>0</KD>
<max_torque>600</max_torque>
</controller>
</dynamics>
Ackermann-Specific Parameters:
f_wheels_x - Distance from rear to front axle (wheelbase) [meters]
f_wheels_d - Distance between front wheel centers (track width) [meters]
max_steer_ang_deg - Maximum steering angle for each front wheel [degrees]
Steering Geometry:
The Ackermann geometry ensures wheels trace correct turning circles:
where \(\alpha\) is the desired steering angle, \(w\) is track width, and \(l\) is wheelbase.
Ackermann with Drivetrain
Class: ackermann_drivetrain
Enhanced Ackermann model with realistic powertrain simulation including differentials. A single “motor” output is distributed to wheels through differential mechanisms.
<dynamics class="ackermann_drivetrain">
<!-- Same wheel configuration as ackermann -->
<drivetrain type="torsen_4wd">
<front_rear_split>0.5</front_rear_split>
<front_rear_bias>1.5</front_rear_bias>
<front_left_right_split>0.5</front_left_right_split>
<front_left_right_bias>1.5</front_left_right_bias>
<rear_left_right_split>0.5</rear_left_right_split>
<rear_left_right_bias>1.5</rear_left_right_bias>
</drivetrain>
</dynamics>
Drivetrain Types:
open_front- Front-wheel drive with open differentialopen_rear- Rear-wheel drive with open differentialopen_4wd- All-wheel drive with open differentialstorsen_front- Front-wheel drive with Torsen limited-slip differentialtorsen_rear- Rear-wheel drive with Torsen limited-slip differentialtorsen_4wd- All-wheel drive with Torsen limited-slip differentials
Differential Parameters:
xxx_split - Torque distribution ratio (0.5 = 50/50 split)
xxx_bias - Torsen bias ratio (how much more torque can go to wheel with traction)
Open Differential:
Torque is split according to the split coefficients:
Torsen Differential:
Self-locking behavior based on wheel speed differences. The bias ratio \(b\) (default 1.5) determines locking strength:
Torque distribution changes dynamically to favor the wheel with more traction.
4. Motor Controllers
Controllers translate high-level commands (velocity, steering) into wheel torques.
Raw Controllers
Class: raw (differential), front_steer (Ackermann)
Direct control of wheel torques and steering angles. User provides:
Wheel torques directly
Steering angle (Ackermann only)
<controller class="raw">
<!-- No parameters needed -->
</controller>
Twist PID Controllers
Class: twist_pid (differential), twist_front_steer_pid (Ackermann)
PID control of linear and angular velocities. Automatically computes required wheel torques to achieve desired twist commands.
<controller class="twist_pid">
<KP>5</KP> <!-- Proportional gain -->
<KI>10</KI> <!-- Integral gain -->
<I_MAX>1</I_MAX> <!-- Integral windup limit -->
<KD>0</KD> <!-- Derivative gain -->
<V>0.0</V> <!-- Initial linear velocity setpoint -->
<W>0</W> <!-- Initial angular velocity setpoint -->
<max_torque>100</max_torque>
</controller>
For Differential Drive:
Desired wheel velocities are computed from commanded twist \((\nu, \omega)\):
where \(w\) is the lateral distance between wheels.
For Ackermann:
Steering angle is computed from turn radius, and PID controls wheel torques to achieve desired speed while accounting for different wheel speeds in turns.
Ideal Controllers
Class: twist_ideal
Perfect velocity control without dynamics. Instantly achieves commanded velocities. Useful for testing high-level algorithms without worrying about low-level control.
<controller class="twist_ideal" />
Exactly Reproducible Trajectories
Class: trajectory
Drives the vehicle along an EXACT, closed-form (t, x, y) polyline given directly in
the XML, instead of being teleoperated or controlled by a planner. This is useful for
offline experiments that need repetitive or predefined motion: benchmarking a
perception/SLAM pipeline against the very same path run after run, regression-testing
sensor data, or reproducing a reference trajectory from a dataset.
Like twist_ideal, the controller works for any non-holonomic vehicle type
(differential, differential_3_wheels, differential_4_wheels and ackermann)
and imposes the resulting twist directly on the vehicle state, bypassing the physical
wheel torque model.
Internally, mvsim::PoseTrajectoryFollower implements the algorithm and has no
dependency on the rest of the simulator, so it can be tested standalone (see
tests/test_pose_trajectory_follower.cpp). At each simulation step it:
Evaluates the waypoint list at the current simulation time to get the reference position and a feedforward speed (computed automatically as the distance between consecutive waypoints divided by their time difference – so you never specify speed directly, only where the vehicle should be and when).
Looks ahead
lookahead_distancefurther along the path geometry from that reference position, and computes the local-frame(vx, omega)twist that steers the vehicle towards that lookahead point using the classic pure-pursuit curvature formula \(\kappa = 2y/L^2\).Optionally clamps
|omega|tomax_angular_speed, slowingvxdown (instead of just capping the turn rate) so the curvature actually driven matches what pure-pursuit asked for – this is what makes the vehicle round off sharp polyline corners realistically rather than either teleporting or cutting them short.
Because the pure-pursuit lookahead search is anchored on the time schedule (not on the vehicle’s actual position), tracking remains stable even for self-intersecting paths (e.g. a figure-eight) and even if the vehicle temporarily lags behind schedule.
<controller class="trajectory" loop="true">
<lookahead_distance>0.6</lookahead_distance> <!-- [m], default 0.5 -->
<max_angular_speed>1.5</max_angular_speed> <!-- [rad/s], default 2.0. 0 = unlimited -->
<viz_height>0.5</viz_height> <!-- [m], default 0.5 -->
<!-- At least 2 waypoints are required. Times must be strictly increasing,
but need not start at 0 nor be given in order. -->
<waypoint t="0.0" x="0.0" y="0.0" />
<waypoint t="4.0" x="4.0" y="0.0" />
<waypoint t="8.0" x="4.0" y="4.0" />
<waypoint t="12.0" x="0.0" y="4.0" />
<waypoint t="16.0" x="0.0" y="0.0" />
</controller>
Parameters:
loop (attribute,
<controller>tag) -true(default) to repeat the trajectory forever, wrapping the simulation time back to the first waypoint’s time each period.falseto run it once and then stop (hold the last waypoint’s position) – a “play once” trajectory.lookahead_distance - Pure-pursuit lookahead distance [m] (default 0.5). Larger values round off sharp corners more gently but track the geometric path less tightly.
max_angular_speed - Maximum
|omega|[rad/s] the controller will ever command (default 2.0). Set to 0 to disable the clamp.waypoint (one or more) -
t[s],x/y[m] in global coordinates. A segment with two waypoints at the same(x,y)makes the vehicle pause there for the elapsedtdifference.viz_height - Height [m] above ground (default 0.5) at which the trajectory polyline is drawn in the 3D view (see below). Purely cosmetic, does not affect tracking.
The full path polyline can be shown in the GUI as a 3D line (a mrpt::opengl line set),
toggled with the “View trajectories” checkbox next to “View forces” in the control
window. Whether it starts shown or hidden is controlled per-world via the <gui> block:
<gui>
<show_trajectories>true</show_trajectories> <!-- default: false -->
</gui>
Tip
Since trajectories are plain XML, a set of predefined ones can be kept as separate
files (e.g. under definitions/trajectories/) and swapped in via <include> and a
<variable>, without touching the rest of the world file. See the
demo_trajectory.world.xml and demo_trajectory_ackermann.world.xml demos in
Demo worlds for a complete example (square loop, figure-eight, and
point-to-point presets, selectable from a single TRAJECTORY variable).
5. Friction Models
Friction models simulate tire-ground interaction, determining forces and wheel slip.
Note
The mu and C_rr parameters of any friction model can be overridden spatially
using PropertyRegion world elements with the property names friction_mu and
friction_C_rr. See Physics Models for details.
Default Friction
Class: default
Basic friction model with Coulomb friction and viscous damping. Suitable for most indoor robotics applications.
<friction class="default">
<mu>0.8</mu> <!-- Friction coefficient -->
<C_damping>1.0</C_damping> <!-- Viscous damping [N·m·s/rad] -->
<C_rr>0.01</C_rr> <!-- Rolling resistance coefficient -->
</friction>
Model Equations:
Maximum friction force:
Lateral friction (prevents side slip):
Longitudinal friction (accelerates/decelerates wheel):
Ward-Iagnemma Friction
Class: wardiagnemma
Advanced model with rolling resistance based on Ward & Iagnemma (2008) [WI08]. Includes velocity-dependent rolling resistance for more realistic off-road simulation.
<friction class="wardiagnemma">
<mu>0.7</mu>
<C_damping>10</C_damping>
<A_roll>50</A_roll> <!-- Rolling resistance shape parameter -->
<R1>0.0075</R1> <!-- Static rolling resistance coefficient -->
<R2>0.02</R2> <!-- Dynamic rolling resistance coefficient -->
<C_rr>0.0</C_rr> <!-- Rolling resistance torque coefficient -->
</friction>
Rolling Resistance:
This force opposes motion and depends on normal force \(N\) and velocity \(v_x\).
Ellipse Curve Method
Class: ellipse
Physics-based tire model using elliptical friction curves. Models slip angle and slip ratio effects, suitable for vehicle dynamics research.
<friction class="ellipse">
<C_damping>0.05</C_damping>
<C_rr>0.01</C_rr> <!-- Rolling resistance coefficient -->
<!-- Lateral slip parameters -->
<C_alpha>8.5</C_alpha> <!-- Lateral coefficient -->
<slip_angle_saturation>0.1</slip_angle_saturation> <!-- rad -->
<C_alpha_s>0.5</C_alpha_s> <!-- Coupling coefficient -->
<!-- Longitudinal slip parameters -->
<C_s>7.5</C_s> <!-- Longitudinal coefficient -->
<slip_ratio_saturation>0.1</slip_ratio_saturation>
<C_s_alpha>0.5</C_s_alpha> <!-- Coupling coefficient -->
</friction>
Slip Angle \(\alpha\) - Angle between wheel heading and velocity direction
Slip Ratio \(s\):
Friction Forces:
6. Predefined Vehicle Classes
The following predefined vehicle classes are available in the definitions/ directory.
TurtleBot3 Burger
File: turtlebot3_burger.vehicle.xml
Small differential-drive educational robot.
Specifications:
Dynamics: Differential drive
Mass: 1.0 kg (chassis), 0.1 kg (each wheel)
Wheel diameter: 0.066 m
Wheel separation: 0.16 m
Default sensors: RPLidar A2, camera, IMU
Jackal UGV
File: jackal.vehicle.xml
Four-wheel differential-drive robot for outdoor applications.
Specifications:
Dynamics: Differential (4 wheels)
Mass: 10.0 kg (chassis)
Wheel diameter: 0.20 m
Wheelbase: 0.26 m, Track width: 0.32 m
Default sensors: 2D LiDAR, 3D LiDAR, camera
Small Robot
File: small_robot.vehicle.xml
Generic small differential-drive robot template.
Specifications:
Dynamics: Differential drive
Mass: 15.0 kg (chassis), 4.0 kg (each wheel)
Wheel diameter: 0.40 m
Wheel separation: 1.0 m
Ackermann Car
File: ackermann.vehicle.xml
Generic car-like vehicle with Ackermann steering.
Specifications:
Dynamics: Ackermann steering
Mass: 800.0 kg
Wheel diameter: 0.62 m
Wheelbase: 1.3 m, Track width: 2.0 m
Max steering: ±30°
Drivetrain: Torsen 4WD
Pickup Truck
File: pickup.vehicle.xml
Large Ackermann vehicle representing a pickup truck.
Specifications:
Dynamics: Ackermann steering
Mass: 2000.0 kg
Wheel diameter: 0.8 m
Wheelbase: 3.5 m
Max steering: ±40°
AgriCobiot2
File: agricobiot2.vehicle.xml
Agricultural robot with custom 3D model and Ackermann drivetrain.
Specifications:
Dynamics: Ackermann with drivetrain
Mass: 97.0 kg
Wheel diameter: 0.406 m
Wheelbase: 0.62 m
Open rear-wheel drive
7. Creating Custom Vehicles
Step 1: Define Vehicle Class
Create a new XML file in the definitions/ directory:
<vehicle:class name="my_custom_robot">
<dynamics class="differential">
<!-- Define wheels -->
<l_wheel pos="0.0 0.3" mass="2.0" width="0.10" diameter="0.20" />
<r_wheel pos="0.0 -0.3" mass="2.0" width="0.10" diameter="0.20" />
<!-- Define chassis -->
<chassis mass="20.0" zmin="0.0" zmax="0.5">
<shape>
<pt>-0.3 -0.3</pt>
<pt>-0.3 0.3</pt>
<pt> 0.3 0.3</pt>
<pt> 0.3 -0.3</pt>
</shape>
</chassis>
<!-- Choose controller -->
<controller class="twist_pid">
<KP>10</KP>
<KI>5</KI>
<I_MAX>2</I_MAX>
<max_torque>50</max_torque>
</controller>
</dynamics>
<!-- Choose friction model -->
<friction class="default">
<mu>0.8</mu>
<C_damping>0.5</C_damping>
</friction>
</vehicle:class>
Step 2: Instantiate in World File
<mvsim_world>
<!-- Include your vehicle definition -->
<include file="definitions/my_custom_robot.vehicle.xml" />
<!-- Create instances -->
<vehicle name="robot1" class="my_custom_robot">
<init_pose>0 0 0</init_pose>
</vehicle>
</mvsim_world>
Step 3: Add Sensors (Optional)
<vehicle name="robot1" class="my_custom_robot">
<init_pose>0 0 0</init_pose>
<!-- Add LiDAR -->
<include file="definitions/lidar2d.sensor.xml"
sensor_x="0.2" sensor_z="0.3"
sensor_name="lidar1"
/>
<!-- Add camera -->
<include file="definitions/camera.sensor.xml"
sensor_x="0.2" sensor_z="0.25"
sensor_name="cam1"
/>
</vehicle>
8. Advanced Features
Linked Visual Objects
For Ackermann vehicles with visible steering mechanisms:
<fl_wheel mass="6.0" width="0.30" diameter="0.62"
linked_yaw="support-left-front-wheel"
linked_yaw_offset_deg="-90">
<!-- This wheel's yaw angle controls a named visual object -->
</fl_wheel>
The linked_yaw attribute names a visual object whose yaw angle will track the
wheel’s steering angle, offset by linked_yaw_offset_deg.
Shape from Visual Model
Automatically extract collision shape from 3D mesh:
<chassis mass="10.0" zmin="0.0" zmax="0.5">
<shape_from_visual />
</chassis>
This computes the chassis polygon and height from the bounding box of visual models.
Multiple Visual Models
Add multiple 3D models to a vehicle:
<visual>
<name>base</name>
<model_uri>models/base.dae</model_uri>
</visual>
<visual>
<name>sensor_mast</name>
<model_uri>models/mast.dae</model_uri>
<model_offset_z>0.5</model_offset_z>
</visual>
9. Debugging and Logging
Enable vehicle logging for debugging:
<log_path>/path/to/logs/</log_path>
This creates CSV files:
mvsim_<name>_pose.csv- Vehicle pose and velocity over timemvsim_<name>_wheel_<N>.csv- Per-wheel forces, torques, and velocities
10. ROS 2 Topics
MVSim publishes the following ROS 2 topics for each vehicle. Topic names are prefixed with the vehicle name when multiple vehicles exist in the simulation, or published directly when only one vehicle is present.
Note
Simulation time. MVSim is the ROS time source: it publishes the global
/clock topic and stamps every outgoing message header with simulation
time (the wall-clock time at simulation start plus the elapsed simulated
seconds). This keeps all stamps coherent regardless of the real-time factor
or transient CPU load. Run your downstream nodes with use_sim_time:=true
so they consume /clock; the mvsim_node itself drives the clock and
normally runs with use_sim_time:=false. Set the disable_sim_time_clock
parameter to true to opt out and revert to plain wall-clock stamps (no
/clock publication), as done before simulation time support was added.
Global Topics
/clock(rosgraph_msgs/Clock) - Simulation time, for nodes running withuse_sim_time:=true
Standard Vehicle Topics
Always published for each vehicle:
<VEH>/odom(nav_msgs/Odometry) - Odometry computed from wheel encoders with configurable noise<VEH>/base_pose_ground_truth(nav_msgs/Odometry) - Perfect ground truth pose and velocity<VEH>/collision(std_msgs/Bool) - Collision detection flag, true when vehicle collides<VEH>/chassis_markers(visualization_msgs/MarkerArray) - RViz markers for chassis and wheel visualization<VEH>/chassis_polygon(geometry_msgs/Polygon) - 2D footprint polygon of the vehicle<VEH>/tf(tf2_msgs/TFMessage) - Dynamic transforms (odom→base_link, sensor frames)<VEH>/tf_static(tf2_msgs/TFMessage) - Static transforms (base_link→base_footprint)
Fake Localization Topics
Published when do_fake_localization parameter is enabled:
<VEH>/amcl_pose(geometry_msgs/PoseWithCovarianceStamped) - Fake AMCL localization output<VEH>/particlecloud(geometry_msgs/PoseArray) - Single-particle fake particle filter
Sensor Topics
Published dynamically based on sensors attached to the vehicle:
2D LiDAR:
<VEH>/<SENSOR_LABEL>(sensor_msgs/LaserScan) - 2D laser scan data
3D LiDAR:
<VEH>/<SENSOR_LABEL>_points(sensor_msgs/PointCloud2) - 3D point cloud (XYZ, XYZRGB, XYZIRT)
Camera:
<VEH>/<SENSOR_LABEL>/image_raw(sensor_msgs/Image) - Camera image<VEH>/<SENSOR_LABEL>/camera_info(sensor_msgs/CameraInfo) - Camera calibration parameters
Depth Camera (RGBD):
<VEH>/<SENSOR_LABEL>_image(sensor_msgs/Image) - RGB image<VEH>/<SENSOR_LABEL>_image_camera_info(sensor_msgs/CameraInfo) - RGB camera calibration<VEH>/<SENSOR_LABEL>_depth(sensor_msgs/Image) - Depth image (16UC1, values in millimeters)<VEH>/<SENSOR_LABEL>_depth_camera_info(sensor_msgs/CameraInfo) - Depth camera calibration<VEH>/<SENSOR_LABEL>_points(sensor_msgs/PointCloud2) - Point cloud (XYZ or XYZRGB)
IMU:
<VEH>/<SENSOR_LABEL>(sensor_msgs/Imu) - Inertial measurement data
GNSS/GPS:
<VEH>/<SENSOR_LABEL>(sensor_msgs/NavSatFix) - GPS position with optional covariance
Coordinate Frames
MVSim uses the REP-105 standard coordinate frames:
map- World fixed frame (only with fake_localization)odom- Odometry frame, drift-free over short periodsbase_link- Robot body frame at the center of rotationbase_footprint- Projection of base_link onto the ground plane<sensor_name>- Individual sensor frames (camera, lidar, etc.)
CSV Logger → ROS 2 publishing
If publishing log topics is enabled via publish_log_topics:=True:
ros2 launch mvsim launch_world.launch.py \
world_file:=my.world.xml \
publish_log_topics:=True
these topics will be created (per vehicle, lazily on first data):
<VEH>/log/pose/Timestamp(Float64)<VEH>/log/pose/q0x(Float64)<VEH>/log/pose/q1y(Float64)<VEH>/log/pose/dqx(Float64)
…
<VEH>/log/wheel_1/torque(Float64)<VEH>/log/wheel_1/friction_x(Float64)
…
11. ROS 2 Subscribed Topics
Command Topics
Each vehicle subscribes to:
<VEH>/cmd_vel(geometry_msgs/Twist) - Velocity commands (linear.x, linear.y, angular.z)
The command is processed by the vehicle’s controller (twist_pid, twist_ideal, etc.) and converted to wheel torques. Commands older than 1 second are automatically discarded for safety.