Topics#
This page gives each topic that the ROS 2 bridge publishes and each topic that it subscribes to, for the Polaris and for the Maxxum. The Polaris topics have the names, the types, the frames and the QoS of the real Polaris.
The node sim_bridge makes the topics from the Sensor Stream and the Vehicle Bridge of the simulator.
The topics are the same for the game and for ACRES Core. Services and Actions gives the other interfaces of the bridge.
Messages gives the fields of the acres_interfaces messages.
| Item | Type | Function |
|---|---|---|
| Names and Namespaces | Convention | Relative and absolute topic names. |
| QoS Profiles | Convention | The reliability, the durability and the depth of each topic. |
| Frames | Convention | The frame names in the message headers and in TF. |
| Time | Convention | The simulation time of the stamps and of /clock. |
| Clock and Transforms | Polaris, Maxxum | /clock, /tf, /tf_static. |
| Pose and INS | Polaris | /vehicle/odom, /oxts/*, /sim/ground_truth/odom. |
| LiDAR and Camera | Polaris, Maxxum | /lidar/points, /camera/image_raw, /camera/camera_info. |
| Drive-by-Wire Reports | Polaris | /vehicle/*/report, /vehicle/vehicle_velocity, /vehicle/dbw_enabled. |
| Drive-by-Wire Commands | Polaris | /vehicle/*/cmd, /vehicle/enable, /vehicle/disable. |
| Reset Pose | Polaris, Maxxum | /sim/reset. |
| State of the Maxxum | Maxxum | odom, imu/data, gnss/fix, engine_rpm, implement/state. |
| Commands of the Maxxum | Maxxum | cmd_vel, the guidance topics, the hitch and the PTO. |
| CAN Bus | Maxxum | can/rx, can/tx. |
| Episode Clock | Session | /sim/episode. |
| Channels of the Episode Log | File | /sim/agents, /sim/agent_states and the other channels of the MCAP file. |
| Several Agents | Session | The namespace and the frame prefix of each agent. |
| Topics of the Other Nodes | Table | The description, the path, the demo driver and the substitutes. |
| Differences in ACRES Core | Table | The topics without data in a Core session. |
| Parity with the Real Polaris | Check | The comparison with bags of the real vehicle. |
WARNING
The command topics have the names of the command topics of the real Polaris.
Publish on them only in a shell that has the DDS loopback fence (source ROS/Env/setup_env.sh).
Conventions#
Names and Namespaces#
The topic names in the tables are for a bridge in the root namespace, which is the default.
In a different namespace, each name gets the namespace as a prefix, for example /polaris2/vehicle/odom.
Four names are absolute and do not change with the namespace: /clock, /tf, /tf_static and /sim/episode.
The Maxxum topics have no vehicle name in them. Use a namespace for the Maxxum, for example namespace:=maxxum.
The tables of the Maxxum show the names without the namespace.
QoS Profiles#
The column QoS of each table gives one of these profiles.
| Profile | Reliability | Durability | History Depth | Topics |
|---|---|---|---|---|
| vehicle | reliable | volatile | 10 | All topics of a vehicle and /clock. |
| tf | reliable | volatile | 100 | /tf |
| static | reliable | transient local | 1 | /tf_static |
| latched | reliable | transient local | 1 | /sim/episode |
| can | reliable | volatile | 500 | can/rx, can/tx |
The bridge subscribes to the command topics with the profile "vehicle". A publisher with reliable or best-effort reliability is compatible.
A subscriber of /sim/episode must request transient local durability to get the last message.
Frames#
| Frame | Vehicle | Definition |
|---|---|---|
utm |
Both | UTM zone 16N. x is the easting and y is the northing, in metres. The parameter utm_frame changes the name. |
base_footprint |
Both | The point on the ground below the centre of the rear axle. x forward, y left, z up. |
lidar |
Both | The LiDAR. The sensor stream gives the name. |
reolink_camera_optical |
Polaris | The optical frame of the camera: z forward, x right, y down. |
imu_link |
Both | The IMU. On the Polaris, the axes are those of the OxTS unit: x left, y forward, z down. |
oxts_link |
Polaris | The frame of /oxts/velocity. |
navsat_link |
Polaris | The frame of /oxts/fix. |
camera, camera_optical |
Maxxum | The camera body and its optical frame. |
gnss_link |
Maxxum | The GNSS antenna. |
can |
Maxxum | The frame name of the CAN frames. |
world |
Both | The frame of the services: east, north, up in metres, with the origin at the centre of the tile. No topic uses it. |
The parameter frame_prefix adds a prefix to the frame names of one vehicle, for example polaris2/base_footprint.
The frames utm, can and world get no prefix. On the Polaris, imu_link and oxts_link also get no prefix.
Time#
All stamps are simulation time: the time of the physics step that made the data. The physics step is 1/120 s.
The bridge publishes /clock for each physics step. Set use_sim_time:=true on the nodes that use the topics.
In lockstep, the time advances only when a client requests steps. Refer to Lockstep Stepping.
Polaris#
| Name | Type | Direction | Rate | QoS | Frame | Description |
|---|---|---|---|---|---|---|
/clock |
rosgraph_msgs/msg/Clock |
publish | 120 Hz | vehicle | The simulation time. | |
/tf |
tf2_msgs/msg/TFMessage |
publish | 100 Hz | tf | utm |
The transform from utm to base_footprint. |
/tf_static |
tf2_msgs/msg/TFMessage |
publish | one time | static | No transform from the bridge. The Polaris description sends the sensor frames. | |
/vehicle/odom |
nav_msgs/msg/Odometry |
publish | 100 Hz | vehicle | utm |
The pose of base_footprint and the velocity, with the INS errors. |
/oxts/imu |
sensor_msgs/msg/Imu |
publish | 100 Hz | vehicle | imu_link |
The attitude, the angular rate and the specific force. |
/oxts/velocity |
geometry_msgs/msg/TwistStamped |
publish | 100 Hz | vehicle | oxts_link |
The velocity in the body frame. |
/oxts/fix |
sensor_msgs/msg/NavSatFix |
publish | 100 Hz | vehicle | navsat_link |
The latitude, the longitude and the height. |
/sim/ground_truth/odom |
nav_msgs/msg/Odometry |
publish | 100 Hz | vehicle | utm |
The exact pose of base_footprint, without the INS errors. |
/lidar/points |
sensor_msgs/msg/PointCloud2 |
publish | 10 Hz | vehicle | lidar |
The LiDAR cloud, 1800 columns and 32 rings. |
/camera/image_raw |
sensor_msgs/msg/Image |
publish | 10 Hz | vehicle | reolink_camera_optical |
The camera image, 896 x 512, bgr8. |
/camera/camera_info |
sensor_msgs/msg/CameraInfo |
publish | 10 Hz | vehicle | reolink_camera_optical |
The camera matrix and the distortion. |
/vehicle/steering/report |
ds_dbw_msgs/msg/SteeringReport |
publish | 50 Hz | vehicle | The steering wheel angle and the steering command. | |
/vehicle/throttle/report |
ds_dbw_msgs/msg/ThrottleReport |
publish | 50 Hz | vehicle | The throttle input, command and output. | |
/vehicle/brake/report |
ds_dbw_msgs/msg/BrakeReport |
publish | 50 Hz | vehicle | The brake pressure input, command and output. | |
/vehicle/gear/report |
ds_dbw_msgs/msg/GearReport |
publish | 50 Hz | vehicle | The gear, the gear command and the gear lever. | |
/vehicle/ulc/report |
ds_dbw_msgs/msg/UlcReport |
publish | 50 Hz | vehicle | The reference and the measured speed of the ULC. | |
/vehicle/vehicle_velocity |
ds_dbw_msgs/msg/VehicleVelocity |
publish | 50 Hz | vehicle | The vehicle speed from the brake and the propulsion systems. | |
/vehicle/dbw_enabled |
std_msgs/msg/Bool |
publish | 50 Hz | vehicle | true while the drive-by-wire is on. |
|
/vehicle/steering/cmd |
ds_dbw_msgs/msg/SteeringCmd |
subscribe | vehicle | The steering command. | ||
/vehicle/throttle/cmd |
ds_dbw_msgs/msg/ThrottleCmd |
subscribe | vehicle | The throttle command. | ||
/vehicle/brake/cmd |
ds_dbw_msgs/msg/BrakeCmd |
subscribe | vehicle | The brake command. | ||
/vehicle/gear/cmd |
ds_dbw_msgs/msg/GearCmd |
subscribe | vehicle | The gear command. | ||
/vehicle/ulc/cmd |
ds_dbw_msgs/msg/UlcCmd |
subscribe | vehicle | The speed command of the ULC. | ||
/vehicle/enable |
std_msgs/msg/Empty |
subscribe | vehicle | Enables the drive-by-wire. | ||
/vehicle/disable |
std_msgs/msg/Empty |
subscribe | vehicle | Disables the drive-by-wire. | ||
/sim/reset |
geometry_msgs/msg/PoseStamped |
subscribe | vehicle | utm |
Moves the vehicle to a pose. |
Clock and Transforms#
/clock has one message for each physics step. The bridge of agent 0 publishes it.
A second bridge of the same session must have publish_clock:=false.
/tf has one transform for each INS sample: from utm to base_footprint.
The translation is the UTM position and the height. The rotation is the grid heading with roll and pitch.
The Polaris description (robot_state_publisher) publishes the transforms from base_footprint to lidar, reolink_camera and reolink_camera_optical on /tf_static.
The description needs the package purdue_ranger. Refer to Build the ROS 2 Workspace.
Pose and INS#
The four INS topics come from one INS sample of the sensor stream. INS and GNSS gives the error model.
/vehicle/odom
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
pose.pose.position |
point | m | x easting, y northing in UTM 16N. z is the ellipsoidal height. |
|
pose.pose.orientation |
quaternion | The attitude of base_footprint in the utm frame. The yaw is counter-clockwise from grid east. |
||
pose.covariance |
number[36] | m² | Only the diagonal for east, north and up. | |
twist.twist.linear |
vector | m/s | The velocity in the body frame: x forward, y left, z up. |
|
twist.twist.angular |
vector | rad/s | The angular rate in the body frame. |
/oxts/imu
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
orientation |
quaternion | The attitude of the IMU axes in the frame east, north, up. | ||
angular_velocity |
vector | rad/s | In the IMU axes: x left, y forward, z down. |
|
linear_acceleration |
vector | m/s² | The specific force in the IMU axes. At rest, z is -9.81. |
/oxts/velocity
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
twist.linear |
vector | m/s | The velocity in the body frame: x forward, y left, z up. |
|
twist.angular |
vector | deg/s | The angular rate in the body frame. The unit is degrees, as on the real vehicle. |
/oxts/fix
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
latitude, longitude |
number | deg | The geodetic position. | |
altitude |
number | m | The ellipsoidal height. | |
status.status |
integer | 2 |
2 = a fix with ground-based corrections (RTK). | |
position_covariance |
number[9] | m² | Only the diagonal for east, north and up. |
/sim/ground_truth/odom has the exact pose in the same utm frame. twist.twist.linear.x is the exact forward speed.
The other twist fields are zero. The real vehicle has no such topic.
The bridge publishes /vehicle/odom, /tf, /oxts/fix and the ground truth only when the sample has a UTM position.
The first samples after the start of the simulator can be without a position.
header:
stamp:
sec: 27
nanosec: 250001421
frame_id: utm
child_frame_id: base_footprint
pose:
pose:
position:
x: 500438.12220755406
y: 4479963.951710937
z: 181.76825509173662
orientation:
x: -0.01584030324089254
y: 0.009055761779796622
z: 0.7071480518888165
w: 0.7068300380638741
covariance: '<array type: double[36]>'
twist:
twist:
linear:
x: -0.0066012713313529404
y: 0.008960991459550805
z: 0.0077292981674181305
angular:
x: 0.0009210506604661536
y: 0.00010572694792926153
z: 0.0006802870432511093
covariance: '<array type: double[36]>'
---
LiDAR and Camera#
/lidar/points is an organised cloud. LiDAR gives the sensor model.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
height |
integer | 1800 |
The number of azimuth columns (Polaris). | |
width |
integer | 32 |
The number of rings (Polaris). | |
fields |
list | m | x, y, z, intensity, each float32. |
|
point_step |
integer | bytes | 16 |
The size of one point. |
is_dense |
boolean | false |
A beam without a return has NaN in x, y and z. |
/camera/image_raw has the encoding bgr8. Camera gives the camera model.
/camera/camera_info has the matrix K, the distortion D (plumb_bob) and the projection P, which the bridge makes from K.
The sensor stream gives these values when the session starts.
The rates are those of the sensor rig: 10 Hz for the LiDAR and 10 Hz for the camera of the Polaris. In a game session on the workstation of the project, the measured rates were 10.0 Hz and 9.9 Hz. In lockstep, a step waits for each cloud and each image that is due. The bridge drops a cloud or an image when its queue of 4 frames is full.
header:
stamp:
sec: 33
nanosec: 108335060
frame_id: lidar
height: 1800
width: 32
fields: '<sequence type: sensor_msgs/msg/PointField, length: 4>'
is_bigendian: false
point_step: 16
row_step: 512
data: '<sequence type: uint8, length: 921600>'
is_dense: false
---
header:
stamp:
sec: 30
nanosec: 108334904
frame_id: reolink_camera_optical
height: 512
width: 896
distortion_model: plumb_bob
d:
- -0.5538868812746999
- 0.40841873696727643
- -0.009978535293476691
- 0.004423926017374143
- -0.16673270849069252
k:
- 599.5261980366653
- 0.0
- 440.1304622044219
- 0.0
- 592.3778194293697
- 270.80532448208777
- 0.0
- 0.0
- 1.0
...
Drive-by-Wire Reports#
The simulator sends one report line at 50 Hz of simulation time. The bridge publishes the seven topics from this line.
The messages have the definitions of ds_dbw_msgs release 2.3.11. Their frame name is empty. Drive-by-Wire and ULC gives the model.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
steering_wheel_angle |
number | deg | steering/report: the steering wheel angle. |
|
cmd, cmd_type |
number, integer | by type | steering/report: the last steering command and its type. |
|
percent_input, percent_cmd, percent_output |
number | % | throttle/report: the pedal input, the command and the output of the actuator. |
|
pressure_input, pressure_cmd, pressure_output |
number | bar | brake/report: the brake pressures. |
|
percent_cmd, percent_output |
number | % | brake/report: the brake command and output in percent. |
|
gear, cmd, driver |
Gear |
gear/report: 1 = P, 2 = R, 3 = N, 4 = H, 5 = L. |
||
vel_ref, vel_meas |
number | m/s | ulc/report: the reference speed and the measured speed. |
|
accel_ref, accel_meas |
number | m/s² | ulc/report: the reference and the measured acceleration. |
|
vehicle_velocity_brake, vehicle_velocity_propulsion |
number | m/s | vehicle_velocity: the speed from the two systems. |
|
enabled, override_active, timeout |
boolean | Each report: the state of the subsystem. |
The bridge fills some fields with the constant values of the real vehicle.
ready is true. The steering limits are 1000 deg/s and 487 deg. The actuator temperatures are -40 °C.
The torque and acceleration fields of the brake report are NaN.
header:
stamp:
sec: 137
nanosec: 183300000
frame_id: ''
cmd_type: 1
vel_ref: 1.5
vel_meas: 1.5082000494003296
accel_ref: 0.0
accel_meas: 0.04100000113248825
coast_decel: false
ready: true
enabled: true
override_active: false
override_latched: false
preempted: false
timeout: false
bad_crc: false
bad_rc: false
---
Drive-by-Wire Commands#
The bridge sends each command to the simulator immediately. The field names and the units are those of ds_dbw_msgs.
Vehicle Bridge gives the effect of each field in the simulator.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
steering/cmd |
number | by type | cmd with cmd_type 2 = angle of the steering wheel (deg), 3 = curvature (1/m), 4 = yaw rate (rad/s), 14 = percent. |
|
throttle/cmd |
number | % | cmd with cmd_type 14 = percent, 13 = pedal units. |
|
brake/cmd |
number | by type | cmd with cmd_type 14 = percent, 1 = pressure (bar). |
|
gear/cmd |
integer | cmd.value: 1 = P, 2 = R, 3 = N, 4 = H, 5 = L. The bridge does not send the value 0. |
||
ulc/cmd |
number | by type | cmd with cmd_type 1 = speed (m/s), 2 = acceleration (m/s²). |
|
enable |
boolean | Set to true in each steering, throttle, brake and ULC command. |
Send /vehicle/enable one time. Then send the commands at 20 Hz to 100 Hz.
The simulator stops a subsystem 0.1 s after its last command, as the real vehicle does.
In lockstep, the bridge sends all commands that it received before a step request, and then the step request. A command thus applies from the first physics step of that request.
import rclpy
from ds_dbw_msgs.msg import GearCmd, SteeringCmd, UlcCmd
from std_msgs.msg import Empty
rclpy.init()
node = rclpy.create_node("dbw_commands")
enable = node.create_publisher(Empty, "/vehicle/enable", 10)
gear = node.create_publisher(GearCmd, "/vehicle/gear/cmd", 10)
steer = node.create_publisher(SteeringCmd, "/vehicle/steering/cmd", 10)
ulc = node.create_publisher(UlcCmd, "/vehicle/ulc/cmd", 10)
def tick():
enable.publish(Empty())
low = GearCmd()
low.cmd.value = low.cmd.LOW
gear.publish(low)
steer.publish(SteeringCmd(cmd=0.02, cmd_type=SteeringCmd.CMD_CURVATURE, enable=True))
ulc.publish(UlcCmd(cmd=1.5, cmd_type=UlcCmd.CMD_VELOCITY, enable=True))
node.create_timer(0.02, tick)
rclpy.spin(node)
Reset Pose#
/sim/reset moves the vehicle to a pose and starts a new episode of the vehicle. The speed after the reset is zero.
The marks on the fields stay.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
pose.position.x, pose.position.y |
number | m | The UTM easting and northing of base_footprint. |
|
pose.orientation |
quaternion | The yaw, counter-clockwise from UTM east. |
The bridge changes the pose into the coordinates of the simulator with the georeference of the last INS sample.
The vehicle thus goes to the position that /vehicle/odom then reports.
The bridge ignores the message until the first INS sample with a position arrives.
To reset the full session, use the service /reset_simulation.
Maxxum#
The bridge publishes the Maxxum topics with vehicle:=maxxum, or with vehicle:=auto when the session has a Maxxum.
The names in the table are relative to the namespace of the bridge.
| Name | Type | Direction | Rate | QoS | Frame | Description |
|---|---|---|---|---|---|---|
/clock |
rosgraph_msgs/msg/Clock |
publish | 120 Hz | vehicle | The simulation time. | |
/tf |
tf2_msgs/msg/TFMessage |
publish | 100 Hz | tf | utm |
The transform from utm to base_footprint. |
/tf_static |
tf2_msgs/msg/TFMessage |
publish | one time | static | The sensor mounts below base_footprint. |
|
odom |
nav_msgs/msg/Odometry |
publish | 100 Hz | vehicle | utm |
The pose of base_footprint and the velocity from the INS. |
ground_truth/odom |
nav_msgs/msg/Odometry |
publish | 100 Hz | vehicle | utm |
The exact pose of base_footprint. |
imu/data |
sensor_msgs/msg/Imu |
publish | 100 Hz | vehicle | imu_link |
The attitude, the angular rate and the specific force in the body axes. |
gnss/fix |
sensor_msgs/msg/NavSatFix |
publish | 100 Hz | vehicle | gnss_link |
The latitude, the longitude and the height. |
lidar/points |
sensor_msgs/msg/PointCloud2 |
publish | 10 Hz | vehicle | lidar |
The LiDAR cloud of the sensor rig. The default is 180 columns and 8 rings. |
camera/image_raw |
sensor_msgs/msg/Image |
publish | 10 Hz | vehicle | camera_optical |
The camera image. |
camera/camera_info |
sensor_msgs/msg/CameraInfo |
publish | 10 Hz | vehicle | camera_optical |
The camera matrix and the distortion. |
engine_rpm |
std_msgs/msg/Float32 |
publish | 10 Hz | vehicle | The engine speed in r/min. | |
steering_angle |
std_msgs/msg/Float32 |
publish | 10 Hz | vehicle | The steering angle of the front wheels in rad. Positive = left. | |
implement/state |
std_msgs/msg/String |
publish | 10 Hz | vehicle | The state of the implement as JSON text. | |
can/rx |
can_msgs/msg/Frame |
publish | 13 frames each 0.1 s | can | can |
The frames that the tractor sends. |
cmd_vel |
geometry_msgs/msg/Twist |
subscribe | vehicle | The speed and the yaw rate. | ||
guidance/curvature_cmd |
std_msgs/msg/Float32 |
subscribe | vehicle | The path curvature in 1/m. Positive = left. | ||
speed_cmd |
std_msgs/msg/Float32 |
subscribe | vehicle | The speed in m/s. | ||
hitch/lever_cmd |
std_msgs/msg/Float32 |
subscribe | vehicle | The hitch lever, 0 to 1. | ||
hitch/raise_cmd |
std_msgs/msg/Bool |
subscribe | vehicle | true lifts the implement. |
||
pto/cmd |
std_msgs/msg/Bool |
subscribe | vehicle | true engages the PTO. |
||
hand_throttle_cmd |
std_msgs/msg/Float32 |
subscribe | vehicle | The hand throttle, 0 to 1. | ||
implement/cmd |
std_msgs/msg/String |
subscribe | vehicle | A JSON object with implement controls. | ||
sim/reset |
geometry_msgs/msg/PoseStamped |
subscribe | vehicle | utm |
Moves the vehicle to a pose. | |
can/tx |
can_msgs/msg/Frame |
subscribe | can | The frames that a controller sends to the tractor. |
State of the Maxxum#
The fields of odom, ground_truth/odom and gnss/fix are the same as for the Polaris.
imu/data uses the body axes: x forward, y left, z up. There is no oxts/velocity topic.
engine_rpm, steering_angle and implement/state come from the observation of the vehicle bridge, each 0.1 s of simulation time.
implement/state has these keys:
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
implement |
string | The name of the implement. | ||
implement_depth_m |
number | m | The working depth. | |
pto_kw |
number | kW | The PTO power. | |
pto_on |
boolean | true while the PTO is on. |
||
pto_rpm |
number | r/min | The PTO speed. | |
hitch_lever |
number | The hitch lever, 0 to 1. | ||
hitch_lift_deg |
number | deg | The lift angle of the hitch. | |
hitch_mode |
string | position, draft or float. |
||
raised |
boolean | The implement is in the lifted position. | ||
draft_n |
number | N | The draft force. | |
difflock |
integer | 0 = open, 1 = rear, 2 = rear and front. | ||
rpm |
number | r/min | The engine speed. | |
stale |
boolean | true while the safety brake of the vehicle bridge holds the vehicle. |
The static transforms go from base_footprint to lidar, camera, imu_link and gnss_link, and from camera to camera_optical.
They come from the mount poses of the sensor rig. The sensor stream gives them when the session starts.
Commands of the Maxxum#
The bridge has a speed controller for the Maxxum. The curvature goes to the simulator without a change.
The controller sets the pedal from the speed error with the parameters speed_kp and speed_ki.
It operates at 20 Hz of simulation time, thus its behaviour is the same in lockstep.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
cmd_vel |
number | m/s | linear.x: the speed. The limit is max_speed_mps. A negative value gives zero curvature and a full brake. |
|
cmd_vel |
number | rad/s | angular.z: the yaw rate. The bridge divides it by the speed to get the curvature. The minimum speed in this calculation is 0.1 m/s. |
|
guidance/curvature_cmd |
number | 1/m | The curvature, from -0.5 to 0.5. | |
speed_cmd |
number | m/s | The speed, from 0 to max_speed_mps. Below 0.05 m/s the controller applies the full brake. |
|
implement/cmd |
JSON | The object of the implement message of the vehicle bridge. |
Send the guidance commands continuously. When no command arrives for cmd_timeout_s (0.5 s), the bridge stops its commands.
The simulator then applies its safety brake.
import rclpy
from std_msgs.msg import Bool, Float32
rclpy.init()
node = rclpy.create_node("maxxum_commands")
curvature = node.create_publisher(Float32, "/maxxum/guidance/curvature_cmd", 10)
speed = node.create_publisher(Float32, "/maxxum/speed_cmd", 10)
lower = node.create_publisher(Bool, "/maxxum/hitch/raise_cmd", 10)
def tick():
curvature.publish(Float32(data=0.05))
speed.publish(Float32(data=2.0))
lower.publish(Bool(data=False))
node.create_timer(0.05, tick)
rclpy.spin(node)
CAN Bus#
With can_udp_port, the bridge connects to the CAN transport of the game (-RlCanUdp=).
can/rx and can/tx have the type and the QoS of the receiver and the sender of the ros2_socketcan driver.
A node for a real tractor bus thus operates with the remaps from_can_bus:=can/rx and to_can_bus:=can/tx.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
id |
integer | The 29-bit identifier, without the flag bits. | ||
is_extended |
boolean | true |
All frames of the tractor have 29-bit identifiers. | |
dlc |
integer | bytes | 8 |
The data length. |
data |
integer[8] | The data bytes. | ||
header.stamp |
time | The simulation time. |
Wheel and CAN Signals gives the frames and their fields.
The Python module acres_sim.j1939 and the header acres_sim/j1939.hpp encode and decode the frames.
In lockstep, the game sends a barrier frame after each step. The bridge does not publish this frame.
The game sends a group of 13 frames each 0.1 s of simulation time. A subscriber must have a history depth of 13 or more.
The tools ros2 topic echo and ros2 topic hz keep only the last 5 frames of each group and thus show 50 Hz.
Session#
Episode Clock#
/sim/episode has the type acres_interfaces/msg/Episode. The bridge publishes it only when it has a control port.
The simulator sends a message at the start, on each reset and on each change of the simulation state.
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
episode_id |
string | The start time of the session in UTC and the episode number. | ||
episode |
integer | 0 |
The number of episodes since the start. | |
reset_epoch |
integer | The number of resets of agent 0. | ||
step |
integer | steps | The physics steps since the start. | |
episode_time_s |
number | s | The simulation time since the start of the episode. | |
state |
integer | 1 |
0 = stopped, 1 = playing, 2 = paused. | |
lockstep |
boolean | true when the world advances only on step requests. |
||
seed |
integer | The seed of the sensor noise. | ||
source |
string | unreal for the game, core for ACRES Core. |
||
map |
string | The map, for example V03ACRE. |
||
agents |
string list | The agent names. Agent 0 is first. |
A node can use source to make sure that its data comes from a simulator and not from the real vehicle.
import rclpy
from rclpy.qos import DurabilityPolicy, QoSProfile, ReliabilityPolicy
from acres_interfaces.msg import Episode
rclpy.init()
node = rclpy.create_node("episode_reader")
latched = QoSProfile(depth=1, reliability=ReliabilityPolicy.RELIABLE,
durability=DurabilityPolicy.TRANSIENT_LOCAL)
episodes = []
node.create_subscription(Episode, "/sim/episode", episodes.append, latched)
while not episodes:
rclpy.spin_once(node, timeout_sec=1.0)
print(episodes[-1].episode_id, episodes[-1].source)
Channels of the Episode Log#
The other /sim/* names are channels of the Episode Log. The bridge does not publish them as topics.
The simulator writes them into the MCAP file. The command ros2 bag play -s mcap <file> publishes them from a file.
| Name | Type | Direction | Rate | QoS | Frame | Description |
|---|---|---|---|---|---|---|
/sim/episode |
acres_interfaces/msg/Episode |
file and topic | on change | latched | The episode clock. | |
/sim/agents |
acres_interfaces/msg/Agents |
file | each episode | One AgentInfo for each agent: name, vehicle, implement, wheelbase, tyre radii. |
||
/sim/agent_states |
acres_interfaces/msg/AgentStates |
file | 120 Hz | world |
One AgentState for each agent, with its EnergyLedger. |
|
/sim/farm_stamps |
acres_interfaces/msg/FarmStamps |
file | steps with contacts | world |
One FarmStamp for each contact of a tyre or the body with the farm. |
|
/sim/farm_events |
acres_interfaces/msg/FarmEvents |
file | steps with events | world |
One FarmEvent for each change of the crop or the soil. |
|
/sim/conditions |
acres_interfaces/msg/Conditions |
file | on change and each 1 s | The clock, the weather and the soil water. | ||
/sim/shifts |
acres_interfaces/msg/VehicleShifts |
file | on change | One VehicleShift for each agent with a hardware shift. |
||
/sim/task |
acres_interfaces/msg/TaskStatus |
file | by the task | The status and the reward terms of a task. The task code writes it, not the simulator. |
The message FieldState is part of the service /acres/farm_state. Refer to Services and Actions.
Several Agents#
With agents:=auto, one bridge process serves all agents of a session. It reads the agents and their ports from the control channel.
| Agent | Namespace | Frame Prefix | /clock |
|---|---|---|---|
| Agent 0 | The namespace of the bridge (default /) |
none | publish |
| Each other agent | /<name> below the namespace of the bridge |
<name>/ |
no |
A Polaris that is the second agent with the name polaris2 thus has the topics /polaris2/vehicle/odom and /polaris2/vehicle/ulc/cmd.
Its odometry has the child frame polaris2/base_footprint. All agents use the same topic /tf.
The bridge changes each character of an agent name that is not a letter, a digit or _ into _.
The services and the action stay in the root namespace. They apply to the session, not to one agent. Run Several Vehicles gives the procedure.
Topics of the Other Nodes#
| Name | Type | Direction | Rate | QoS | Frame | Description |
|---|---|---|---|---|---|---|
/robot_description |
std_msgs/msg/String |
publish | one time | latched | robot_state_publisher: the URDF of the Polaris description. |
|
/tf_static |
tf2_msgs/msg/TFMessage |
publish | one time | static | robot_state_publisher: the sensor frames below base_footprint. |
|
/vehicle/odom/path |
nav_msgs/msg/Path |
publish | 100 Hz | reliable, depth 10 | odom_origin |
odom_path: the path of the vehicle for RViz. |
/Demo/route |
nav_msgs/msg/Path |
publish | one time | latched | demo_route_origin |
dbw_demo_driver: the route. |
/Demo/status |
std_msgs/msg/String |
publish | 50 Hz | reliable, depth 10 | dbw_demo_driver: the route index, the curvature and the speed. |
|
/safety_stop |
std_msgs/msg/Bool |
publish | 20 Hz | reliable, depth 10 | lab_stubs: true during a safety stop. |
|
/obstacle_speed_limit_mps |
std_msgs/msg/Float32 |
publish | 20 Hz | reliable, depth 10 | lab_stubs: the speed limit in m/s. |
Differences in ACRES Core#
A session with core_sim has the same Polaris topics. These are the differences:
| Topic | ACRES Core |
|---|---|
/camera/image_raw, /camera/camera_info |
The topics exist, but no message arrives. ACRES Core has no camera. |
/lidar/points |
The cloud comes from the ray-cast LiDAR of ACRES Core. It contains the terrain, the buildings and the other agents. It does not contain the crop. |
/sim/episode |
source is core. |
| Maxxum topics | ACRES Core has only the Polaris. |
Parity with the Real Polaris#
The tool ROS/Tools/check_topic_parity.py compares a bag of the simulator with bags of the real Polaris.
For each topic that the two sides record, it compares the type, the reliability, the durability and the frame names.
The tool ROS/Tools/check_msg_defs.py examines the bytes of the ds_dbw_msgs messages.
The result below is from a session with core_sim, sim_bridge and dbw_demo_driver, without the Polaris description.
The vehicle bags are dbw_direct_test_01 and human_20260813_172445.
OK /vehicle/brake/report ds_dbw_msgs/msg/BrakeReport qos (1, 2) / (1, 2) frames [''] / ['']
OK /vehicle/steering/cmd ds_dbw_msgs/msg/SteeringCmd qos (1, 2) / (1, 2) frames ['base_footprint'] / ['base_footprint']
OK /lidar/points sensor_msgs/msg/PointCloud2 qos (1, 2) / (1, 2) frames ['lidar'] / ['lidar']
OK /oxts/imu sensor_msgs/msg/Imu qos (1, 2) / (1, 2) frames ['imu_link'] / ['imu_link']
OK /vehicle/odom nav_msgs/msg/Odometry qos (1, 2) / (1, 2) frames ['utm'] / ['utm']
DIFF /tf_static tf2_msgs/msg/TFMessage qos (1, 1) / (1, 1) frames ['base_footprint->lidar', ...] / ['utm->demo_route_origin']
...
18 common topic checks, 1 differences
The one difference is /tf_static. Without the Polaris description, the session has no transform from base_footprint to lidar.
The description needs the package purdue_ranger.
These are the known differences between the simulator and the real vehicle:
| Topic | Real Polaris | Simulator |
|---|---|---|
| Drive-by-wire reports | Steering and velocity at 100 Hz, throttle and brake at 50 Hz, dbw_enabled at 20 Hz, ULC at 15 Hz, gear at 12.5 Hz. |
All reports at 50 Hz. |
/tf |
Also contains map to oxts_link and the axle frames of the OxTS driver. |
Only utm to base_footprint. |
/oxts/fix, /clock, /sim/* |
The bags of the vehicle do not contain them. | Published. |
/camera/segformer_*, /learning/route/local_path |
Published by nodes of the lab. | Not published. |
| Camera frame | The vehicle publishes a camera at (1.2, 0, 1.3) m. | The description uses the camera mount of the simulator: (2.745, 0.606, 1.887) m. |