Skip to content

Drive types ​

The chassis type decides how the base drives. It fixes the wheel mounts a program must fill, which of them drive, what the base can do, and what the targets emit to drive it.

Chassis typeWheelsDrivenSteeredSpins in placeHolonomicController
Diff_two_caster2 Standard, a casterbothnoneyesnodiff_drive_controller
Tracked2 Trackbothnoneyesnothe runtime's track drive
Diff_drive4 Standardall; one motor may turn a side's twononeyesnodiff_drive_controller, two wheels a side
Mecanum_four_wheel4 Mecanumallnoneyesyesmecanum_drive_controller
Omni_drive4 Omni_wheelallnoneyesyesthe runtime's omni drive
Omni_three_wheel3 Omni_wheelallnoneyesyesthe runtime's omni drive
Tricycle3 Standardfrontfrontnonotricycle_controller
Ackermann4 Standardrear, and the front pair when it is driven too; one motor may turn an axle's twofront pairnonothe runtime's steering drive
Leggednone: it stands on the feet of its legs, its own or Leg parts placed on itits legs, on servos of their ownnoneeach way its gait turnsto each side its gait moves asidethe mjcf target's walking controller, the policy or the gait its part states; the ROS targets refuse it

Every base the ROS targets write takes geometry_msgs/msg/TwistStamped on /cmd_vel and publishes odometry on /odom, whatever its controller. The controller's kinematics, wheel radius, track and wheelbase, come from the placed wheels, not from numbers you type.

Motors ​

A wheel on a mount the chassis type drives is driven. Declare the motor that drives it, a Motor glued to the chassis like any part with the wheel glued onto its shaft connector, and the targets hold the wheel joint to what the motor's part states: its stall torque is the joint's effort limit and its no-load speed its velocity limit, and MuJoCo clamps the wheel's actuator to both.

robotdsl
Motor motor_l(ref = "gearmotor_37d", glue = (0, 0.1, -0.02, 0, 1, 0), command = @core/driver)
Standard wheel_l(ref = "standard_80", glue = (0, 0.12, -0.02, 0, 1, 0))

The motor here seats 0.02 m inside the wheel's position, and its shaft reaches out to where the wheel's glue is. A motor is declared, not required. A wheel glued straight onto a drive mount, with no motor anywhere for it, is driven as the chassis type says, within no stated limits: the kit's motors are then part of its chassis, as in every library built before motors were parts, and the output records a wheel joint without effort or velocity limits with the reason. A wheel glued straight onto a mount the type does not drive rolls freely, as a tricycle's rear wheels do. A motor on a mount the chassis type does not drive, a wheel beside a motor instead of on its shaft, and a motor with nothing on its shaft are each refused by assembly.wheel_mounts_filled.

The motor is driven by the MotorDriver its command names and fed through it by a Battery, both placed like any part (Robot). Its speed and torque hold at the voltage its part states, rated_voltage_v, and the targets use them as they are. On a supply that never reaches that voltage, the real wheel is slower and weaker than the simulated one, and power.rated_voltage_in_supply warns of it.

One motor for several wheels ​

A wheel need not sit on its motor's shaft. A car's axle turns both of its wheels from one motor through a differential, and a skid-steer kit often turns both wheels of a side from one motor. The chassis part says so with a driveline on the connector where that motor seats, naming the connectors whose wheels it turns. The program glues each wheel onto its own mount and the motor onto its seat:

robotdsl
Ackermann chassis(ref = "car_4x4")
Motor motor_rear(ref = "gearmotor_37d", glue = (-0.07, 0, -0.02, -1, 0, 0), command = @core/driver)
Standard wheel_rl(ref = "standard_80", glue = (-0.15, 0.13, -0.02, 0, 1, 0))
Standard wheel_rr(ref = "standard_80", glue = (-0.15, -0.13, -0.02, 0, -1, 0))

The chassis type decides which wheels one motor may turn together: the two of a car's axle, or both axles from one driveline; the two of a skid-steer base's side; never two wheels of a mecanum or an omni base, which turns each at its own speed. The targets limit each of those wheels' joints to the motor's no-load speed over the driveline's reduction, and to an equal share of its stall torque times the reduction. A driveline that states no reduction leaves the joints without limits, and compile.json says so.

What the task may ask ​

Spin needs a base that turns in place, and Strafe a holonomic one. The rules kinematics.spin_supported and kinematics.strafe_supported check the task against the base at compile time.

Two-wheel and skid-steer bases ​

A two-wheel base turns by driving its wheels at different speeds; its caster only supports it. A skid-steer base drags its wheels sideways to turn, so the targets give its wheels less grip across their rolling direction than along it. In Gazebo the sideways grip also gives way in proportion to the sideways force, scaled alike for every wheel, so the base turns in place about its middle while every wheel carries at least half the average wheel load; one with more of its weight on an axle than that turns toward it. In MuJoCo, whose contact friction is the same in every direction, a skid-steer base turns in place at about a third of the commanded rate; the target declares this.

Tracked bases ​

A Tracked base runs on two tracks, each driven on its own, and steers by their speed difference like a skid-steer base. A Track sits where a wheeled base has a wheel: on the shaft of the motor on its mount, or on the mount itself when a motor on a seat of the chassis turns it through a driveline.

robotdsl
Tracked chassis(ref = "tracked_base")
Motor motor_l(ref = "gearmotor_37d", glue = (0.08, 0.06, -0.01, 0, 1, 0), command = @core/driver)
Track track_l(ref = "track_unit", glue = (0.08, 0.08, -0.01, 0, 1, 0))

A track part is the whole unit, belt, sprockets and road wheels, and states three things of its belt: how far it travels per turn of the hub the part mates by, how wide it is, and where it lies on the ground. The base's speed follows from the travel, its turning from the distance between the two runs, and its footprint and its stability from the runs themselves. The same unit turned half round serves the other side when the two motors sit at opposite ends; a unit mirrored for the other side is a part of its own.

Neither simulator models a belt's links. The targets give each track one box on its run on the ground, as wide as the belt, and the simulator moves the surface of that box: the floor's grip on it carries the base. The rest of the track unit collides with nothing. A belt is not a joint, so nothing of ros2_control runs for a tracked base: runtime/run_track_drive.py holds /cmd_vel to what the two belts' motors reach, each belt running at the speed of its side, hands the simulator that motion, and publishes the odometry from the two speeds.

This serves driving and navigation. It does not tell what a turn or a slope asks of a tracked robot's motors. Three things follow, each recorded in compile.json:

  • The belts carry the base about the centre its command turns it about. A real belt runs along its track only and scrubs sideways in a turn, by an amount that follows from its pressure along the run and its grip, which a part does not state. A tracked base therefore turns as far as it is commanded here, and its odometry agrees with where it is.
  • A belt takes a speed, not a force: it is held to the speed its motor reaches unloaded, and its pull is bounded by the floor's grip, not by the motor's stall torque.
  • A belt's hub does not turn, so Check_vel, Check_acc, Check_jerk and Check_effort on a Track are refused at compile time.

The two tracks lie equally far to the two sides of the chassis part's origin; a chassis whose origin is not midway between its track mounts is refused.

Steering bases ​

A tricycle or an Ackermann base steers its front wheels about kingpins. The chassis part states each steered mount's kingpin axis and how far it turns:

yaml
wheel_mount_front:
  pos: [0.15, 0, -0.02]
  dir: [0, 1, 0]
  steering: {axis: [0, 0, 1], max_angle_rad: 0.6}

The targets hang the part on that mount, the motor of a driven wheel or a free wheel itself, from a light knuckle that turns about that axis, and plan with Smac Hybrid-A* at the base's minimum turning radius, following the path with MPPI's Ackermann model. That radius is the tightest circle the kingpins allow: a kingpin a wheelbase ahead of the rear axle and some way off the centre line holds the turn that has it on the inside to the wheelbase over the tangent of its limit, plus that distance. A steering base is not held to a final heading, since it cannot turn in place to correct one, and it cannot Spin.

An Ackermann base drives its rear wheels and may drive its front wheels too, both or neither: with a motor on each steered mount and the wheel on its shaft, or with a motor on a seat whose driveline turns the two front hubs. Both targets drive every wheel the program drives. ros2_controllers' Ackermann controller takes exactly two traction joints, so the runtime's steering drive stands in its place for every car: it turns /cmd_vel into the speed of each driven wheel along its own circle about the turning centre and the angle of each kingpin, and takes the odometry from the measured speeds of the rear wheels. In a turn the front wheels travel further than the rear ones and are driven that much faster.

Omni bases ​

An omni wheel grips along its rolling direction and slides along its axle. The runtime's omni drive turns /cmd_vel into each wheel's speed from its position and rolling direction, and computes the odometry back from the measured wheel speeds. That works for any layout, including the rectangles most four-wheel omni bases are built on, as long as the wheels' rolling directions span the plane; the rule kinematics.omni_wheels_span_the_plane checks that.

Mecanum bases ​

A mecanum wheel's rollers let it slide along one diagonal. In Gazebo, each wheel is a sphere that grips along one diagonal and slides along the other. MuJoCo cannot yet model that sliding, so the ros-mujoco-nav2 target refuses mecanum and omni bases.

Chassis types in the vocabulary lists the mounts of each type.