Skip to content

Simulator Control Channel#

The simulator control channel is a JSON socket that controls one session: the simulation state, lockstep steps, resets, entities, conditions and the episode log. This page gives the protocol of the game and the differences of the Core server.

The three local sockets of one session: the simulator control channel for the session, and a vehicle bridge and a sensor stream for each agent, with the clients and the data that flows on each socket. CLIENTS SOCKETS ON 127.0.0.1 GAME OR CORE SERVER Session client sim_control.py ROS 2 bridge: services and the step action Simulator control channel JSON lines on TCP, one client one socket for the session -SimControl=5600 Session state, lockstep steps, resets, conditions, episode log requests replies, events Controller dbw_client.py ROS 2 bridge: command and report topics Vehicle bridge JSON lines on TCP, one client -RlPort=5556 J1939 CAN frames, UDP or SocketCAN -RlCanUdp= -RlCan= commands, resets reports, barrier ROS 2 bridge sim_bridge publishes the sensor topics and the clock Sensor stream binary frames on TCP, one client INS, LiDAR, camera, clock -SensorStream=5601 frames, barrier Agent 0 one vehicle: Polaris or Maxxum sensor rig of the vehicle More clients one controller for each agent Sockets of agent 1, 2, ... -RlPorts=5556,5557 -SensorStreams=5601,5602 Agent 1, 2, ... own vehicle bridge and sensor stream
The sockets of one session. The simulator control channel serves the session. Each agent has its own vehicle bridge and sensor stream. Open the diagram

The game opens the channel with -SimControl=<port>. The game has no default port. The Core server, the ROS 2 launch files and the tools use port 5600. The ROS 2 bridge connects to this channel and gives each operation as a ROS 2 service: refer to Services and Actions. The other two sockets are the Vehicle Bridge and the Sensor Stream.

Item Type Function
-SimControl= Option Opens the channel.
-Lockstep Option Starts the session in the paused state.
-EpisodeLog= Option Records an episode log from the start.
-EpisodeReplay= Option Shows an episode log in the game.
Transport Protocol TCP on 127.0.0.1, one JSON object in each line.
Request and Reply Protocol The fields that all operations use.
Result Codes Protocol The values of result.
Events Protocol The lines that the simulator sends without a request.
Simulation States Protocol Stopped, playing, paused and quitting.
Frames and Poses Protocol The world frame, the utm frame and the pose format.
Python Client Tool The classes SimControl and JsonBridge.
hello Operation Gives the session, the agents and their ports.
get_state Operation Gives the simulation state and the physics step.
set_state Operation Plays, pauses, stops or quits the simulation.
step Operation Advances the paused simulation by N physics steps.
reset Operation Starts a new episode.
get_entities Operation Lists the agents and the spawned entities.
get_entity_state Operation Gives the pose and the twist of an entity.
set_entity_state Operation Moves an entity to a pose.
spawn_entity Operation Adds a prop or a worker.
delete_entity Operation Removes a spawned entity.
get_spawnables Operation Lists the entity types that a client can spawn.
get_named_poses Operation Lists the named places of the tile.
set_conditions Operation Sets the clock, the weather and the soil water.
set_vehicle_shift Operation Sets the hardware shift of one agent.
get_vehicle_shifts Operation Lists the hardware shifts.
record Operation Starts or stops the episode log.
farm_state Operation Gives the ground truth of the fields.
field_query Operation Gives the ground truth at one point.
screenshot Operation Writes the main view to a PNG file.
Differences in ACRES Core Table The behaviour of the Core server.

The protocol has no shutdown operation. To stop the simulator, send set_state with the state 3.

Options#

-SimControl=#

Opens the simulator control channel on 127.0.0.1 at the given port. The game writes ACRES_SIM_CONTROL_LISTENING port=<port> to its log when the socket is open.

Name Type Unit Default Description
-SimControl= integer no channel The TCP port, from 1 to 65535.
Packaged/Linux/Acres.sh -VehicleDemo -Vehicle=polaris \
    -SimControl=5600 -RlPort=5556 -SensorStream=5601
ACRES_SIM_CONTROL_LISTENING port=5600
ACRES_SIM_CONTROL_READY port=5600 lockstep=0 replay=0 log=

-Lockstep#

Starts the session in the paused state. The world advances only when a client sends a step request. The frame time becomes exactly one physics step (1/120 s).

CAUTION

Use -Lockstep together with -SimControl=. Without the channel, no client can release the world.

Packaged/Linux/Acres.sh -VehicleDemo -Vehicle=polaris \
    -SimControl=5600 -Lockstep -RlPort=5556
ACRES_LOCKSTEP on
ACRES_SIM_CONTROL_READY port=5600 lockstep=1 replay=0 log=

-EpisodeLog=#

Records an Episode Log from the first physics step. The game makes the folder of the file. The recording stops when the session ends or when a client sends record with the action stop.

Name Type Unit Default Description
-EpisodeLog= path no log The MCAP file. The game replaces a file that exists.
Packaged/Linux/Acres.sh -VehicleDemo -Vehicle=polaris \
    -EpisodeLog=/data/run0.mcap
ACRES_EPISODE_LOG_START /data/run0.mcap
ACRES_EPISODE_LOG_STOP /data/run0.mcap messages=8563

-EpisodeReplay=#

Loads an episode log and shows it. Each vehicle follows its logged states, and the physics models do not run. When the command line has no -Vehicle=, -Vehicles= or -Agents=, the game makes the agents of the log. The procedure is in Replay.

Name Type Unit Default Description
-EpisodeReplay= path no replay The MCAP file. The chunks must not use compression.
-EpisodeReplayExit flag off Stops the game after the last logged step.
Packaged/Linux/Acres.sh -VehicleDemo \
    -EpisodeReplay=/data/core-episode.mcap -EpisodeReplayExit
ACRES_REPLAY_LOADED /data/core-episode.mcap states=3600
    agents=1 farm_events=0 farm_stamps=3600 duration_s=29.99
ACRES_SIM_CONTROL_READY port=0 lockstep=0 replay=1 log=
ACRES_REPLAY_DONE steps=3600

Each of the options -SimControl=, -Lockstep, -EpisodeLog= and -EpisodeReplay= starts the simulator control of the session. The Core server has the equivalent options --control-port, --lockstep and --episode-log.

Protocol#

Transport#

The channel is a TCP socket on the loopback address 127.0.0.1. A program on a different computer cannot connect. Each message is one JSON object in one line of UTF-8 text. A newline character ends the line.

The socket accepts one client at a time. A second client waits until the first client disconnects. When a client connects, the simulator sends an episode event first.

The game reads the socket one time in each frame, thus a reply comes one frame after the request or later. The game disconnects a client that does not read when 8 MiB of replies wait.

The Python client is SimControl in Tools/SimControl/sim_control.py. It uses only the standard library. The shell function simctl in the example sends one request and prints the last line that it receives. Its second argument is the time to wait for the reply, in seconds.

simctl() {
    (printf '%s\n' "$1"; sleep "${2:-1}") \
        | nc 127.0.0.1 5600 | tail -n 1
}
simctl '{"id":1,"op":"get_state"}'
import sys
sys.path.insert(0, "Tools/SimControl")
from sim_control import SimControl

ctl = SimControl(5600)
print(ctl.call("get_state"))
{"state":2,"lockstep":true,"step":0,"time_s":0,
 "id":1,"ok":true,"result":1}

Request and Reply#

A request is an object with the key op and the fields of the operation. The simulator sends one reply for each request. The reply contains the reply fields of the operation and the fields below.

Request

Name Type Unit Default Description
op string The name of the operation.
id integer none An identifier that the client selects, 0 or larger. The reply contains the same value.

Reply

Name Type Unit Default Description
id integer The id of the request. The key is absent when the request had no id.
ok boolean true when result is 1.
result integer The result code.
error string A message. The key is absent when there is no message.

A reply with ok equal to true can contain an error text. The text is then a warning. A line that is not a JSON object gets a reply with the result 4 and without an id.

{"id": 7, "op": "step", "steps": 12}
{"step":12,"time_s":0.10000000521540642,"barrier":12,
 "wall_s":0.22548372589517385,"id":7,"ok":true,"result":1}
{"id": 8, "op": "frobnicate"}
{"id":8,"ok":false,"result":0,
 "error":"unknown op \"frobnicate\""}

Result Codes#

The codes are the codes of the ROS 2 package simulation_interfaces, thus the ROS 2 bridge passes them without a change. The codes from 0 to 4 apply to all operations. The codes above 100 apply to one operation.

Code Operation Meaning
0 all The simulator does not have this operation.
1 all The operation was successful.
2 all The entity or the agent does not exist.
3 all The simulation is not in the correct state.
4 all The operation failed. The key error gives the cause.
101 set_state The simulation is already in this state.
101 spawn_entity An entity has this name and allow_renaming is false.
102 spawn_entity The name is empty and allow_renaming is false.
103 spawn_entity The uri is not in the list of get_spawnables.
104 spawn_entity The uri is empty.
107 spawn_entity The mesh of the prop is not in this build.
{"id": 9, "op": "get_entity_state", "entity": "nobody"}
{"id":9,"ok":false,"result":2,
 "error":"no entity \"nobody\""}

Events#

An event is a line with the key event. The simulator sends it without a request.

The episode event has the fields of the message acres_interfaces/msg/Episode: refer to Messages. The simulator sends it when a client connects, after each reset and after each change of the simulation state.

Name Type Unit Default Description
episode_id string The UTC start time of the session and the episode number.
episode integer The number of resets through this channel.
reset_epoch integer The number of resets of agent 0.
step integer The physics steps since the start of the world.
time_s number s The simulation time.
episode_time_s number s The simulation time since the start of the episode.
state integer The simulation state.
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 names of the agents. Agent 0 is first.

The progress event belongs to a step request with progress equal to true. It contains the id of the request, thus a client must not use it as the reply.

Name Type Unit Default Description
id integer The id of the step request.
completed integer steps The physics steps that are complete.
remaining integer steps The physics steps that remain.
{"event":"episode","episode_id":"20261002T070414-0000",
 "episode":0,"reset_epoch":0,"step":0,"time_s":0,
 "episode_time_s":0,"state":2,"lockstep":true,"seed":42,
 "source":"unreal","map":"V03ACRE","agents":["polaris"]}
{"event":"progress","id":900,"completed":12,"remaining":24}

Simulation States#

The states are the states of simulation_interfaces/msg/SimulationState.

State Name Behaviour
0 Stopped The simulator does a full reset and then holds the world.
1 Playing The world runs in real time. The physics runs at 120 Hz.
2 Paused The simulator holds the world. This is the state of lockstep.
3 Quitting The simulator closes the episode log and stops.

In the paused state the world does not advance: no physics step, no clock, no weather, no traffic. The game continues to show frames. The sensors complete their work and the vehicle bridges send their reports. A session starts in the playing state. With -Lockstep it starts in the paused state.

{"id": 2, "op": "set_state", "state": 2}
{"state":2,"id":2,"ok":true,"result":1}

Frames and Poses#

The default frame is world: the grid of the tile in metres with x east, y north and z up. The origin is the centre of the tile. The frame utm is UTM zone 16N with the datum NAD83(2011).

A pose is an object with a position and an orientation quaternion. A twist is an object with two vectors. The pose of a vehicle is the pose of its frame base_footprint: the point on the ground below the centre of the rear axle. The body axes are x forward, y left and z up.

Name Type Unit Default Description
position number list m [x, y, z]. In the frame utm: easting, northing, height.
orientation number list [0, 0, 0, 1] The quaternion [qx, qy, qz, qw].
linear number list m/s The linear velocity [vx, vy, vz] in the world frame.
angular number list rad/s The angular velocity in the world frame.
{"pose": {"position": [-38.71, -138.67, 0.0],
          "orientation": [0, 0, 0.7071, 0.7071]},
 "twist": {"linear": [0, 2.0, 0], "angular": [0, 0, 0]}}

Python Client#

The file Tools/SimControl/sim_control.py has two classes. They accept only a loopback address.

Member Function
SimControl(port=5600) Connects to the simulator control channel. It tries again for 300 s when the simulator is not ready.
SimControl.call(op, **fields) Sends one request and returns the reply as a dictionary.
SimControl.events The list of the events that arrived before a reply.
SimControl.close() Closes the socket.
JsonBridge(port) Connects to a vehicle bridge and starts a thread that reads it.
JsonBridge.send(message) Sends one command object.
JsonBridge.observation The last observation.
JsonBridge.report, JsonBridge.reports The last drive-by-wire report and the list of all reports.
JsonBridge.wait_barrier(step) Waits until the bridge sent the barrier of this step.

call() waits for the final reply, including when progress is true. Progress events stay in SimControl.events.

import sys
sys.path.insert(0, "Tools/SimControl")
from sim_control import JsonBridge, SimControl

ctl = SimControl(5600)
hello = ctl.call("hello")
dbw = JsonBridge(hello["agents"][0]["rl_port"])
print(ctl.events[0]["event"])
episode

Operations#

The examples use the client ctl and the function simctl of the section Transport. Each reply below is a real reply of the game with one Polaris.

hello#

Gives the description of the session. A client uses the ports of the agents to connect to the other sockets.

Reply

Name Type Unit Default Description
protocol integer The version of the protocol: 1.
map string The map.
physics_hz integer Hz The rate of the physics: 120.
state integer The simulation state.
lockstep boolean true in the paused state and during a step request.
step integer The physics steps since the start of the world.
time_s number s The simulation time.
episode integer The episode number.
episode_id string The identifier of the episode.
seed integer The seed of the sensor noise.
headless boolean true when the simulator has no renderer.
replay boolean true during an episode replay.
agents object list One object for each agent: see the table below.
features integer list The identifiers of simulation_interfaces/msg/SimulatorFeatures.

Agent

Name Type Unit Default Description
name string The name of the agent.
vehicle string polaris or maxxum.
index integer The index of the agent. Agent 0 is the first vehicle.
implement string The implement of a Maxxum. Empty for a Polaris.
sensor_stream integer The port of the sensor stream. 0 without a sensor stream.
rl_port integer The port of the vehicle bridge. 0 without a vehicle bridge.
can_udp integer The UDP port of the CAN transport. 0 without a CAN transport.
simctl '{"id":1,"op":"hello"}'
hello = ctl.call("hello")
print(hello["agents"])
{"protocol":1,"map":"V03ACRE","physics_hz":120,"state":2,
 "lockstep":true,"step":0,"time_s":0,"episode":0,
 "episode_id":"20261002T070414-0000","seed":42,
 "headless":false,"replay":false,
 "agents":[{"name":"polaris","vehicle":"polaris","index":0,
   "implement":"","sensor_stream":0,"rl_port":5556,
   "can_udp":0}],
 "features":[0,1,2,10,11,14,20,22,23,24,25,26,31,32,33],
 "id":1,"ok":true,"result":1}

get_state#

Gives the simulation state and the physics clock.

Reply

Name Type Unit Default Description
state integer The simulation state.
lockstep boolean true in the paused state and during a step request.
step integer The physics steps since the start of the world.
time_s number s The simulation time.
simctl '{"id":2,"op":"get_state"}'
ctl.call("get_state")
{"state":2,"lockstep":true,"step":0,"time_s":0,
 "id":2,"ok":true,"result":1}

set_state#

Changes the simulation state. The simulator sends an episode event after the change. The state 0 does a reset with the scope 255 and then holds the world. The state 3 stops the simulator. The reply comes before the simulator stops.

Name Type Unit Default Description
state integer The new state, from 0 to 3.

The reply contains state: the state after the operation. The result is 101 when the simulation is already in the state. The result is 3 during a step request.

simctl '{"id":3,"op":"set_state","state":1}'
ctl.call("set_state", state=1)   # play
ctl.call("set_state", state=2)   # pause
ctl.call("set_state", state=3)   # quit
{"state":1,"id":3,"ok":true,"result":1}
{"state":2,"id":4,"ok":false,"result":101,
 "error":"already in that state"}

step#

Advances the paused simulation by steps physics steps and then holds it again. One physics step is 1/120 s. The reply comes when all steps are complete and all sensors are idle. Before the reply, the simulator sends a barrier on each vehicle bridge and each sensor stream. The procedure and the timing are in Lockstep Stepping.

Name Type Unit Default Description
steps integer steps 1 The number of physics steps, 1 or more.
progress boolean false true to get a progress event after each 12 steps.

Reply

Name Type Unit Default Description
step integer The physics step that the world reached.
time_s number s The simulation time after the steps.
barrier integer The step number in the barriers. It is equal to step.
wall_s number s The time that the request used on the wall clock.

The result is 3 when the simulation state is not 2 or when a step request is in progress. The result is 4 when steps is less than 1.

simctl '{"id":5,"op":"step","steps":12}' 5
reply = ctl.call("step", steps=12)
print(reply["step"], reply["time_s"])
{"step":12,"time_s":0.10000000521540642,"barrier":12,
 "wall_s":0.22548372589517385,"id":5,"ok":true,"result":1}

With "progress": true and 36 steps:

{"event":"progress","id":900,"completed":12,"remaining":24}
{"event":"progress","id":900,"completed":24,"remaining":12}
{"step":48,"time_s":0.40000002086162567,"barrier":48,
 "wall_s":0.6043131760088727,"id":900,"ok":true,"result":1}

reset#

Starts a new episode. The episode number increases by 1 and the episode clock starts at zero. The physics clock does not go back. The simulator sends an episode event.

Name Type Unit Default Description
scope integer 255 A sum of the scope bits of the table below. 0 is the same as 255.
restore_fields boolean true for scope 255 true to remove the marks and the crop damage of all fields.
Scope Bit Effect
1 No effect. The reply contains a warning in error, and ok stays true.
2 Puts each agent at its spawn pose with zero speed.
4 Removes all spawned entities.

Reply

Name Type Unit Default Description
episode integer The new episode number.
episode_id string The new episode identifier.
reset_epoch integer The number of reset requests of agent 0.
restored_fields integer The number of fields that the simulator restored.

The pose of an agent changes at the start of the next physics step.

simctl '{"id":45,"op":"reset","scope":2}'
ctl.call("reset", scope=2)   # agents to their spawn poses
ctl.call("reset")            # full reset
{"episode":1,"episode_id":"20261002T070414-0001",
 "reset_epoch":2,"restored_fields":0,
 "id":45,"ok":true,"result":1}
{"episode":3,"episode_id":"20261002T070414-0003",
 "reset_epoch":3,"restored_fields":59,"id":47,"ok":true,
 "result":1,"error":"the physics clock is not reset
 (SCOPE_TIME): the episode clock restarts instead"}

get_entities#

Lists the names of the entities. The agents are first, then the entities that a client spawned.

Reply

Name Type Unit Default Description
entities string list The names.
simctl '{"id":19,"op":"get_entities"}'
ctl.call("get_entities")["entities"]
{"entities":["polaris","cone1","cone1_1","w1"],
 "id":19,"ok":true,"result":1}

get_entity_state#

Gives the pose and the twist of an agent or of a spawned entity in the world frame.

Name Type Unit Default Description
entity string The name of the entity.

Reply

Name Type Unit Default Description
time_s number s The simulation time of the state.
frame string world.
pose object The pose. For a vehicle: base_footprint.
twist object The velocities in the world frame. Zero for a prop.
utm.easting number m The UTM easting of the position.
utm.northing number m The UTM northing of the position.
utm.height number m The z coordinate of the position.
utm.heading_deg number deg The heading, clockwise from UTM grid north.

The result is 2 when no entity has the name.

simctl '{"id":8,"op":"get_entity_state","entity":"polaris"}'
state = ctl.call("get_entity_state", entity="polaris")
x, y, z = state["pose"]["position"]
{"time_s":0.40000002086162567,"frame":"world",
 "pose":{"position":[-38.7087,-141.6659,0.5935],
  "orientation":[-0.0181,0.0080,0.7068,0.7071]},
 "twist":{"linear":[-0.0230,0.0403,0.0590],
  "angular":[-0.0134,-0.0771,0.0004]},
 "utm":{"easting":500438.0926,"northing":4479958.1254,
  "height":0.5935,"heading_deg":0.0958},
 "id":8,"ok":true,"result":1}

set_entity_state#

Moves an agent or a spawned entity to a pose. For a vehicle, the pose is the pose of base_footprint, and the simulator uses the position x, y and the yaw angle. The simulator puts the vehicle on the ground. The change applies at the start of the next physics step.

Name Type Unit Default Description
entity string The name of the entity.
pose object The pose. The position is necessary.
frame string world world or utm.
twist object zero A vehicle starts with the horizontal speed of linear, 8 m/s maximum.

In the frame utm, the yaw angle of the orientation is counter-clockwise from grid east. The result is 4 when the position is absent or the frame is unknown. The result is 2 when no entity has the name.

simctl '{"id":25,"op":"set_entity_state","entity":"polaris",
 "pose":{"position":[-38.71,-138.67,0],
 "orientation":[0,0,0.7071,0.7071]}}'
ctl.call("set_entity_state", entity="polaris",
         pose={"position": [-38.71, -138.67, 0],
               "orientation": [0, 0, 0.7071, 0.7071]})
ctl.call("step", steps=1)
{"id":25,"ok":true,"result":1}

spawn_entity#

Adds a prop or a worker to the world. A prop has collision, and the sensors see it. A client cannot spawn a vehicle: the agents come from the command line of the session.

Name Type Unit Default Description
name string The name of the new entity.
uri string The type, from get_spawnables.
allow_renaming boolean false true to let the simulator add a number to a name that exists.
pose object origin The pose. With z equal to 0, the entity is on the ground.
frame string world world or utm.

The reply contains entity: the name that the simulator used. The type person is available only when the session has -NpcWorkers or -NpcVehicles.

simctl '{"id":13,"op":"spawn_entity","name":"cone1",
 "uri":"prop:cone","pose":{"position":[-33.71,-141.67,0]}}'
ctl.call("spawn_entity", name="cone1", uri="prop:cone",
         pose={"position": [-33.71, -141.67, 0]})
{"entity":"cone1","id":13,"ok":true,"result":1}

The same request again, then with "allow_renaming": true:

{"id":14,"ok":false,"result":101,
 "error":"\"cone1\" exists"}
{"entity":"cone1_1","id":15,"ok":true,"result":1}

delete_entity#

Removes an entity that a client spawned.

Name Type Unit Default Description
entity string The name of the entity.

The result is 2 when no spawned entity has the name. The result is 4 for an agent.

simctl '{"id":22,"op":"delete_entity","entity":"cone1_1"}'
ctl.call("delete_entity", entity="cone1_1")
{"id":22,"ok":true,"result":1}
{"id":24,"ok":false,"result":4,
 "error":"agents cannot be deleted at run time"}

get_spawnables#

Lists the entity types of spawn_entity.

URI Entity
prop:cone A traffic cone, 0.7 m high.
prop:box A box with sides of 1 m.
prop:bale_round A round bale with a diameter of 1.5 m.
prop:bale_square A square bale, 2.4 m by 1.2 m by 0.9 m.
person A worker that stands.

The reply contains spawnables: a list of objects with the keys uri and description.

simctl '{"id":10,"op":"get_spawnables"}'
for item in ctl.call("get_spawnables")["spawnables"]:
    print(item["uri"], "-", item["description"])
{"spawnables":[
 {"uri":"prop:cone","description":"traffic cone, 0.7 m tall"},
 {"uri":"prop:box","description":"1 m box"},
 {"uri":"prop:bale_round",
  "description":"round bale, 1.5 m diameter"},
 {"uri":"prop:bale_square",
  "description":"square bale, 2.4 x 1.2 x 0.9 m"},
 {"uri":"person","description":"a standing worker
  (needs -NpcWorkers or -NpcVehicles)"}],
 "id":10,"ok":true,"result":1}

get_named_poses#

Lists the point places of the file places.json as poses in the world frame. A place without a heading points north. The z coordinate is the height of the ground.

Reply

Name Type Unit Default Description
poses object list One object for each place, with the keys below.
name string The identifier of the place.
description string The name that the menu shows.
tags string list The category of the place. heading when the place has a heading.
pose object The pose.
simctl '{"id":11,"op":"get_named_poses"}'
for place in ctl.call("get_named_poses")["poses"]:
    print(place["name"], place["pose"]["position"])
{"poses":[
 {"name":"spawn-icsc-garage",
  "description":"Spawn: ICSC garage",
  "tags":["spawn","heading"],
  "pose":{"position":[-38.7097,-140.5131,0.6392],
   "orientation":[0,0,0.7071,0.7071]}},
 {"name":"spawn-beck-lot", "...": "..."}],
 "id":11,"ok":true,"result":1}

set_conditions#

Sets the clock, the weather, the soil water and the state of the fields. Each part is optional. The simulator applies the correct parts and gives the errors of the other parts in error.

Name Type Unit Default Description
clock.date string The local date as YYYY-MM-DD. The weather starts again at this time.
clock.hour number h 12 The local hour of the day.
weather.preset string keep the sky clear, fair, overcast, rain, storm or fog.
weather.rain_mm_h number mm/h preset value The rain rate. A negative value selects the value of the preset.
soil_water.wetness number 0 is the wilting point, 1 is saturation.
soil_water.fields integer list all fields The field numbers, from 1 to 59.
restore_fields.fields integer list all fields The fields that lose their marks and crop damage.

The reply contains message: a text with the parts that the simulator applied.

simctl '{"id":30,"op":"set_conditions",
 "clock":{"date":"2026-07-04","hour":15.5},
 "weather":{"preset":"rain","rain_mm_h":4},
 "soil_water":{"wetness":0.6,"fields":[48]}}'
ctl.call("set_conditions",
         clock={"date": "2026-07-04", "hour": 15.5},
         weather={"preset": "rain", "rain_mm_h": 4},
         soil_water={"wetness": 0.6, "fields": [48]})
{"message":"clock 2026-07-04 15.50 h; weather rain rain
 4.0 mm/h; soil wetness 0.60 on 360 cells",
 "id":30,"ok":true,"result":1}

set_vehicle_shift#

Sets the hardware shift of one agent: offsets on the actuators and on the sensor mounts. A shift models a vehicle that is different from its calibration. The new shift replaces the old shift of the agent. The actuator offsets apply from the next physics step. The mount offsets apply from the next sensor sample.

Name Type Unit Default Description
shift.agent string agent 0 The name of the agent.
shift.label string empty A text for the datasets.
shift.steering_offset_deg number deg 0 Polaris: an offset on the steering wheel angle. Maxxum: an offset on the road wheel angle.
shift.ulc_speed_bias_mps number m/s 0 An offset on the speed that the ULC measures.
shift.throttle_bias_pct number % 0 An offset on the output of the throttle actuator.
shift.brake_bias_bar number bar 0 An offset on the output of the brake actuator.
shift.camera_offset_m number list m [0, 0, 0] A translation of the camera mount in base_footprint.
shift.camera_offset_rpy_deg number list deg [0, 0, 0] A rotation of the camera mount: roll, pitch, yaw.
shift.lidar_offset_m number list m [0, 0, 0] A translation of the LiDAR mount.
shift.lidar_offset_rpy_deg number list deg [0, 0, 0] A rotation of the LiDAR mount: roll, pitch, yaw.

The result is 2 when no agent has the name.

simctl '{"id":32,"op":"set_vehicle_shift","shift":
 {"agent":"polaris","label":"test shift",
 "steering_offset_deg":2.0,"ulc_speed_bias_mps":0.1,
 "lidar_offset_rpy_deg":[0,0.5,0]}}'
ctl.call("set_vehicle_shift", shift={
    "agent": "polaris", "label": "test shift",
    "steering_offset_deg": 2.0, "ulc_speed_bias_mps": 0.1,
    "lidar_offset_rpy_deg": [0, 0.5, 0]})
{"id":32,"ok":true,"result":1}
ACRES_VEHICLE_SHIFT agent=polaris label="test shift"
    steer=2.000 deg ulc=0.100 m/s throttle=0.00 % brake=0.00 bar

get_vehicle_shifts#

Lists the hardware shifts that clients set in this session. The reply contains shifts: a list of objects with the fields of set_vehicle_shift.

simctl '{"id":33,"op":"get_vehicle_shifts"}'
ctl.call("get_vehicle_shifts")["shifts"]
{"shifts":[{"agent":"polaris","label":"test shift",
  "steering_offset_deg":2,"ulc_speed_bias_mps":0.1,
  "throttle_bias_pct":0,"brake_bias_bar":0,
  "camera_offset_m":[0,0,0],
  "camera_offset_rpy_deg":[0,0,0],
  "lidar_offset_m":[0,0,0],
  "lidar_offset_rpy_deg":[0,0.5,0]}],
 "id":33,"ok":true,"result":1}

record#

Starts or stops the Episode Log.

Name Type Unit Default Description
action string start or stop.
path path session folder The MCAP file of a start. The default name is episode-NNNN.mcap.

Reply

Name Type Unit Default Description
path path The file.
messages integer The number of messages in the file.
duration_s number s Only for stop: the simulation time between the first and the last message.

The result is 3 for a start during a recording and for a stop without a recording.

simctl '{"id":35,"op":"record","action":"start",
 "path":"/data/episode.mcap"}'
simctl '{"id":38,"op":"record","action":"stop"}'
ctl.call("record", action="start", path="/data/episode.mcap")
ctl.call("step", steps=24)
ctl.call("record", action="stop")
{"path":"/data/episode.mcap","messages":4,
 "id":35,"ok":true,"result":1}
{"messages":40,"duration_s":0.19999997800000002,
 "path":"/data/episode.mcap","id":38,"ok":true,"result":1}

farm_state#

Gives the ground truth of the fields: the crop, the damage, the field work, the soil water and the ruts. Each field object has the fields of the message acres_interfaces/msg/FieldState: refer to Messages.

Name Type Unit Default Description
fields integer list fields with crop or marks The field numbers.
edge_band_m number m 0 The width of the band along the field edge for crushed_in_band_m2.

Reply

Name Type Unit Default Description
crushed_m2 number m² The crop area that the vehicles crushed in the session.
harvested_kg number kg The crop mass that the session harvested.
time_s number s The simulation time.
fields object list One object for each field, with the keys below.
field, name integer, string The field number and its name, for example 48 and F48.
crop string corn, soybean, potato or empty.
crop_area_m2 number m² The area of the crop.
crushed_m2 number m² The crushed crop area of the field.
crushed_in_band_m2 number m² The part of crushed_m2 in the edge band.
crushed_out_band_m2 number m² The part of crushed_m2 out of the edge band.
harvested_m2, tilled_m2, seeded_m2, sprayed_m2 number m² The areas of the field work.
theta_mean number m³/m³ The mean water content of the soil surface.
pond_mean_m number m The mean depth of the water on the surface.
rut_area_m2 number m² The area of the cells with a rut deeper than 1 cm.
simctl '{"id":40,"op":"farm_state","fields":[48],
 "edge_band_m":3}'
ctl.call("farm_state", fields=[48], edge_band_m=3)
{"crushed_m2":0,"harvested_kg":0,
 "fields":[{"field":48,"name":"F48","crop":"soybean",
  "crop_area_m2":5098.080000000243,"crushed_m2":0,
  "crushed_in_band_m2":0,"crushed_out_band_m2":0,
  "harvested_m2":0,"tilled_m2":0,"seeded_m2":0,
  "sprayed_m2":0,"theta_mean":0.3231109465020577,
  "pond_mean_m":0,"rut_area_m2":0}],
 "time_s":0.608333365060389,"id":40,"ok":true,"result":1}

field_query#

Gives the ground truth at one point of the tile.

Name Type Unit Default Description
x, y number m 0 The point. In the frame utm: easting and northing.
frame string world world or utm.

Reply

Name Type Unit Default Description
field integer The field number. 0 when the point is not in a field.
surface string soil or hard.
ground_z number m The height of the ground.
theta number m³/m³ The water content of the soil surface. -1 on a hard surface.
pond_m number m The depth of the water on the surface.
rut_m number m The depth of the rut.
worked integer A sum of flags: 1 tilled, 2 seeded, 4 sprayed.
crop string The crop of the nearest patch in 1 m. Empty when there is no crop.
crop_crushed number The crushed or harvested fraction of this patch, from 0 to 1.
edge_distance_m number m The distance to the edge of the field. 0 when the point is not in a field.

Note

The game classifies the point with the vector surface map, then samples the live soil-water state. Hard surfaces give theta=-1. Field soil, grass and dirt give the sampled water content.

simctl '{"id":42,"op":"field_query","x":-38.71,"y":-81.67}'
ctl.call("field_query", x=-38.71, y=-81.67)
{"field":53,"surface":"soil","ground_z":0.1400557905435562,
 "theta":0.22414907813072205,"pond_m":0,"rut_m":0,"worked":0,
 "crop":"soybean","crop_crushed":0,
 "edge_distance_m":28.918342892677302,
 "id":42,"ok":true,"result":1}

screenshot#

Writes the main view of the game to a PNG file after the next frame. The reply comes before the file is complete. In the paused state the game continues to show frames of the same world state.

Name Type Unit Default Description
path path An absolute path that ends with .png.

The reply contains path. The result is 4 when the path is not absolute or has a different extension.

simctl '{"id":43,"op":"screenshot","path":"/data/view.png"}'
ctl.call("screenshot", path="/data/view.png")
{"path":"/data/view.png","id":43,"ok":true,"result":1}

Differences in ACRES Core#

The Core server core_sim of the package acres_core_sim gives the same protocol for ACRES Core. Its agents are Polaris vehicles. It has no renderer, no weather model and no Maxxum.

ros2 run acres_core_sim core_sim --control-port 5600 --sensor-ports 5601 --rl-ports 5556 --lockstep
Item Game Core Server
Option of the channel -SimControl=<port>, no default. --control-port <port>, default 5600. The value 0 closes the channel.
hello headless is false when the game has a renderer. headless is true. The reply has the added key source with the value core.
step One frame for each physics step. The game waits for the sensors of each step. The steps run without frames. The LiDAR scans of the steps go out before the barrier.
Requests during a step The game refuses step and set_state with the result 3. The Core server also refuses reset with the result 3.
time_s A value with single precision, for example 0.10000000521540642. The exact value step / 120.
set_entity_state for a prop The z coordinate of the pose is the height of the prop. With z equal to 0, the prop is on the ground.
spawn_entity A mesh with collision. person is a worker of the traffic model. A box that the LiDAR sees. person is a box of 0.5 m by 0.4 m by 1.75 m.
delete_entity for an agent Result 4. Result 2.
set_conditions: clock, weather The game applies them to the sky and the weather model. The Core server records them for the episode log. They have no effect.
set_conditions: soil water Sets the water content of each soil cell of the fields. Sets one value for each field that gives the same mean water content.
set_vehicle_shift Applies all offsets. Applies the actuator offsets and the LiDAR offsets. It has no camera.
record: default path episode-NNNN.mcap in the session folder. Acres/Saved/work/core_sim/episode-<time>-<NNNN>.mcap in the repository.
farm_state All quantities. The crop, the crushed areas, the water content and the ruts. The other quantities are 0.
field_query All quantities. worked is 0.
screenshot Writes the file. Result 0.