Skip to content

Services and Actions#

This page gives each service and the action of the ROS 2 bridge: the name, the type, the fields and the result codes. The services control the session: the simulation state, lockstep steps, resets, entities, conditions, the episode log and the farm ground truth.

The node sim_bridge offers the interfaces when its parameter control_port is not 0. Each request goes to one operation of the Simulator Control Channel. The game and ACRES Core answer the same operations. All names are in the root namespace. They apply to the session, not to one agent.

The standard interfaces are those of the package simulation_interfaces (release 1.4.0 in the conda environment). The package acres_interfaces adds five services. Messages gives all fields of its types.

Item Type Function
Python Client Example The function call() that the Python examples use.
Result Codes Table The values of result.result.
Supported Features Table The features of simulation_interfaces that ACRES has and does not have.
/get_simulator_features Service Gives the list of the features.
/get_simulation_state Service Gives the simulation state.
/set_simulation_state Service Plays, pauses, stops or quits the simulation.
/step_simulation Service Advances the paused simulation by N physics steps.
/simulate_steps Action The same, with feedback and a cancel function.
/reset_simulation Service Starts a new episode.
/get_entities Service Lists the agents and the spawned entities.
/get_entity_state Service Gives the pose and the twist of an entity.
/set_entity_state Service Moves an entity to a pose.
/spawn_entity Service Adds a prop or a worker.
/delete_entity Service Removes a spawned entity.
/get_spawnables Service Lists the entity types that a client can spawn.
/get_named_poses Service Lists the named places of the tile.
/acres/set_conditions Service Sets the clock, the weather and the soil water. Type SetConditions.
/acres/set_vehicle_shift Service Sets the hardware shift of one agent. Type SetVehicleShift.
/acres/record Service Starts or stops the episode log. Type Record.
/acres/farm_state Service Gives the ground truth of the fields. Type FarmState.
/acres/field_query Service Gives the ground truth at one point. Type FieldQuery.

Conventions#

Python Client#

The Python examples of this page use this node and this function.

import rclpy

rclpy.init()
node = rclpy.create_node("acres_client")


def call(srv_type, name, request):
    client = node.create_client(srv_type, name)
    client.wait_for_service(timeout_sec=10.0)
    future = client.call_async(request)
    rclpy.spin_until_future_complete(node, future)
    return future.result()

Result Codes#

Each response has a field result of the type simulation_interfaces/msg/Result: the code result.result and the text result.error_message. The bridge copies the code of the simulator into the response.

Code Name Meaning in ACRES
0 RESULT_FEATURE_UNSUPPORTED The simulator does not have the operation, or the request uses a filter that ACRES does not have.
1 RESULT_OK The operation was successful.
2 RESULT_NOT_FOUND No entity or agent has the given name.
3 RESULT_INCORRECT_STATE The simulation is not in the necessary state, for example a step request while the simulation plays.
4 RESULT_OPERATION_FAILED The operation failed. The text gives the cause.
101 and above Service codes A code of one service. The row of the service gives it.

The bridge returns the code 4 when it has no connection to the simulator or when no reply arrives in time.

error_message Cause
the simulator control channel is not connected (game started with -SimControl=5600?) The simulator has no control channel on the port, or it is not in operation.
no reply from the simulator within 30 s The simulator did not answer in control_timeout_s.
the simulator control channel closed The connection closed during the request.

Supported Features#

The service /get_simulator_features returns these feature numbers.

Number Name Interface
0 SPAWNING /spawn_entity
1 DELETING /delete_entity
2 NAMED_POSES /get_named_poses
10 ENTITY_STATE_GETTING /get_entity_state
11 ENTITY_STATE_SETTING /set_entity_state
14 SPAWNABLES /get_spawnables
20 SIMULATION_RESET /reset_simulation
22 SIMULATION_RESET_STATE The scope SCOPE_STATE.
23 SIMULATION_RESET_SPAWNED The scope SCOPE_SPAWNED.
24 SIMULATION_STATE_GETTING /get_simulation_state
25 SIMULATION_STATE_SETTING /set_simulation_state
26 SIMULATION_STATE_PAUSE The state STATE_PAUSED.
31 STEP_SIMULATION_SINGLE /step_simulation with one step.
32 STEP_SIMULATION_MULTIPLE /step_simulation with more than one step.
33 STEP_SIMULATION_ACTION /simulate_steps

ACRES does not have these parts of simulation_interfaces:

Feature Not Available
Reset of the time (SIMULATION_RESET_TIME) The physics clock does not go back. A reset starts the episode time again.
Entity information, tags, categories and bounds The services GetEntityInfo, SetEntityInfo, GetEntityBounds and GetEntitiesStates. The filters of /get_entities for categories, tags and bounds.
Bounds of named poses The service GetNamedPoseBounds.
Spawn from a resource string The field resource_string of /spawn_entity. ACRES has no spawn formats (spawn_formats is empty).
Spawn of more than one entity The service SpawnEntities.
Worlds The services LoadWorld, UnloadWorld, GetCurrentWorld and GetAvailableWorlds.

The control channel has two operations without a ROS 2 service: get_vehicle_shifts and screenshot. Refer to Simulator Control Channel.

Simulation State#

/get_simulator_features#

Type simulation_interfaces/srv/GetSimulatorFeatures. Gives the features of the simulator and data about the session. The request has no fields.

Name Type Unit Default Description
features.features integer list The numbers of the table Supported Features.
features.spawn_formats string list empty ACRES has no spawn formats.
features.custom_info string The text ACRES and a JSON object.

The JSON object of custom_info has the keys protocol, map, physics_hz, lockstep, headless, seed and episode_id. Without a connection to the simulator, custom_info is ACRES: and the cause, and the feature list is empty.

ros2 service call /get_simulator_features \
    simulation_interfaces/srv/GetSimulatorFeatures
from simulation_interfaces.srv import GetSimulatorFeatures

reply = call(GetSimulatorFeatures, "/get_simulator_features",
             GetSimulatorFeatures.Request())
print(list(reply.features.features))
print(reply.features.custom_info)
features=[0, 1, 2, 10, 11, 14, 20, 22, 23, 24, 25, 26, 31, 32, 33]
spawn_formats=[]
custom_info='ACRES {"protocol": 1, "map": "V03ACRE", "physics_hz": 120, "lockstep": false, "headless": false, "seed": 42, "episode_id": "20261002T072309-0000"}'

/get_simulation_state#

Type simulation_interfaces/srv/GetSimulationState. Gives the state of the simulation. The request has no fields.

Name Type Unit Default Description
state.state integer 0 = stopped, 1 = playing, 2 = paused.
result Result Code 1.
ros2 service call /get_simulation_state \
    simulation_interfaces/srv/GetSimulationState
from simulation_interfaces.srv import GetSimulationState

reply = call(GetSimulationState, "/get_simulation_state",
             GetSimulationState.Request())
print(reply.state.state)
state=simulation_interfaces.msg.SimulationState(state=1),
result=simulation_interfaces.msg.Result(result=1, error_message='')

/set_simulation_state#

Type simulation_interfaces/srv/SetSimulationState. Changes the state of the simulation.

Name Type Unit Default Description
state.state integer The new state. Refer to the table below.
State Name Effect
0 STATE_STOPPED A full reset. Then the world stops.
1 STATE_PLAYING The world advances in real time.
2 STATE_PAUSED The world stops. It advances only on step requests (lockstep).
3 STATE_QUITTING The simulator closes.
Code Meaning
1 The state changed.
3 A step request is in progress.
4 The state is not one of 0, 1, 2 and 3.
101 ALREADY_IN_TARGET_STATE: the simulation was in this state.

Each change of the state gives a new message on /sim/episode.

ros2 service call /set_simulation_state \
    simulation_interfaces/srv/SetSimulationState \
    "{state: {state: 2}}"
from simulation_interfaces.srv import SetSimulationState

request = SetSimulationState.Request()
request.state.state = request.state.STATE_PAUSED
reply = call(SetSimulationState, "/set_simulation_state", request)
print(reply.result.result)
result=simulation_interfaces.msg.Result(result=1, error_message='')

The same request a second time:

result=simulation_interfaces.msg.Result(result=101, error_message='already in that state')

Lockstep#

/step_simulation#

Type simulation_interfaces/srv/StepSimulation. Advances the paused simulation by steps physics steps. One step is 1/120 s.

Name Type Unit Default Description
steps integer steps 1 The number of physics steps. The minimum is 1.

The service does these operations in this sequence:

  1. It sends the commands that the bridge received on the command topics to the simulator.
  2. It sends the step request and waits for the reply of the simulator.
  3. It waits until the bridge published all data of the steps. A barrier on each channel shows the end of the data.

The response thus arrives after the sensor data and the reports of the steps are on the topics. Lockstep Stepping gives the procedure for a control loop.

Code Meaning
1 The steps are complete and the data is on the topics.
3 The simulation is not in the paused state, or a different step request is in progress.
4 steps is less than 1, or a barrier did not arrive in barrier_timeout_s. The text names the channel.

The time limit of the request is control_timeout_s plus 0.2 s for each step.

ros2 service call /step_simulation \
    simulation_interfaces/srv/StepSimulation "{steps: 120}"
from simulation_interfaces.srv import StepSimulation

reply = call(StepSimulation, "/step_simulation",
             StepSimulation.Request(steps=120))
print(reply.result.result, reply.result.error_message)
result=simulation_interfaces.msg.Result(result=1, error_message='')

A request while the simulation plays:

result=simulation_interfaces.msg.Result(result=3, error_message='the simulation is not paused (set_state 2 first)')

/simulate_steps#

Type simulation_interfaces/action/SimulateSteps. Advances the paused simulation, with feedback. The bridge sends the steps to the simulator in groups of 12 steps. A client can cancel the goal between two groups.

Name Type Unit Default Description
Goal steps integer steps The number of physics steps.
Feedback completed_steps integer steps The steps that are complete. One message for each 12 steps.
Feedback remaining_steps integer steps The steps that remain.
Result result Result The code and the text.

The bridge accepts one goal at a time. It rejects a second goal while the first is in progress.

Status Code Meaning
SUCCEEDED 1 All steps are complete and the data is on the topics.
CANCELED 1 The client cancelled the goal. The text gives the number of complete steps.
ABORTED 3 or 4 The simulator refused a group of steps, or a barrier did not arrive.
ros2 action send_goal --feedback /simulate_steps \
    simulation_interfaces/action/SimulateSteps "{steps: 36}"
from rclpy.action import ActionClient
from simulation_interfaces.action import SimulateSteps

client = ActionClient(node, SimulateSteps, "/simulate_steps")
client.wait_for_server(timeout_sec=10.0)
goal = client.send_goal_async(
    SimulateSteps.Goal(steps=36),
    feedback_callback=lambda m: print(m.feedback.completed_steps))
rclpy.spin_until_future_complete(node, goal)
result = goal.result().get_result_async()
rclpy.spin_until_future_complete(node, result)
print(result.result().result.result.result)
Goal accepted with ID: 222aad20cb7f4c6db0fac8fb77562d20
Feedback:
    completed_steps: 12
remaining_steps: 24
Feedback:
    completed_steps: 24
remaining_steps: 12
Feedback:
    completed_steps: 36
remaining_steps: 0
Result:
    result:
  result: 1
  error_message: ''
Goal finished with status: SUCCEEDED

Reset#

/reset_simulation#

Type simulation_interfaces/srv/ResetSimulation. Starts a new episode. The episode number increases by 1. The simulator sends a new message on /sim/episode.

Name Type Unit Default Description
scope integer 0 A bit field. Refer to the table below.
Scope Name Effect
0 SCOPE_DEFAULT The same as SCOPE_ALL.
1 SCOPE_TIME No effect on the physics clock. The response has the code 1 and a text about this.
2 SCOPE_STATE Each agent goes back to its spawn pose.
4 SCOPE_SPAWNED The simulator removes the spawned entities.
255 SCOPE_ALL All the above. The simulator also removes the marks and the crop damage from all fields.

To move one vehicle and keep the episode, use the topic /sim/reset. Refer to Topics.

ros2 service call /reset_simulation \
    simulation_interfaces/srv/ResetSimulation "{scope: 2}"
from simulation_interfaces.srv import ResetSimulation

request = ResetSimulation.Request(
    scope=ResetSimulation.Request.SCOPE_STATE)
reply = call(ResetSimulation, "/reset_simulation", request)
print(reply.result.result)
result=simulation_interfaces.msg.Result(result=1, error_message='')

With scope: 0:

result=simulation_interfaces.msg.Result(result=1, error_message='the physics clock is not reset (SCOPE_TIME): the episode clock restarts instead')

Entities#

An entity is an agent or an object that a client added with /spawn_entity. The name of an agent is its entity name. The poses use the frame world: east, north and up in metres, with the origin at the centre of the tile. A request can also use the frame utm (UTM zone 16N).

/get_entities#

Type simulation_interfaces/srv/GetEntities. Lists the names of the agents and of the spawned entities.

Name Type Unit Default Description
filters.filter string empty A regular expression. The service returns the names that contain a match.
filters.categories, filters.tags, filters.bounds empty Not available. A request with one of them gets the code 0.
Code Meaning
1 The list entities is valid.
0 The request has a filter for categories, tags or bounds.
4 The regular expression is not valid.
ros2 service call /get_entities \
    simulation_interfaces/srv/GetEntities \
    "{filters: {filter: '^pol'}}"
from simulation_interfaces.srv import GetEntities

request = GetEntities.Request()
request.filters.filter = "^pol"
reply = call(GetEntities, "/get_entities", request)
print(reply.entities)
result=simulation_interfaces.msg.Result(result=1, error_message=''), entities=['polaris']

/get_entity_state#

Type simulation_interfaces/srv/GetEntityState. Gives the exact pose and twist of one entity.

Name Type Unit Default Description
entity string The name of the entity.
Name Type Unit Default Description
state.header.frame_id string world The frame of the pose and the twist.
state.header.stamp time The simulation time of the state.
state.pose pose m For a vehicle: the pose of base_footprint.
state.twist.linear vector m/s The velocity in the world frame.
state.twist.angular vector rad/s The angular rate in the world frame.
state.acceleration accel zero ACRES does not fill this field.

The code 2 shows that no entity has the name.

ros2 service call /get_entity_state \
    simulation_interfaces/srv/GetEntityState "{entity: polaris}"
from simulation_interfaces.srv import GetEntityState

reply = call(GetEntityState, "/get_entity_state",
             GetEntityState.Request(entity="polaris"))
position = reply.state.pose.position
print(reply.state.header.frame_id, position.x, position.y)
result=simulation_interfaces.msg.Result(result=1, error_message=''),
state=EntityState(
  header=Header(stamp=Time(sec=154, nanosec=791674740), frame_id='world'),
  pose=Pose(position=Point(x=-38.745806072951815, y=-121.47239796968378, z=0.24291916509505385),
            orientation=Quaternion(x=-0.004382870625704527, y=0.0021762894466519356,
                                   z=0.70587158203125, w=0.7083228826522827)),
  twist=Twist(linear=Vector3(x=-5.597616545855999e-05, y=3.333876375108957e-06, z=-2.062867133645341e-07),
              angular=Vector3(x=1.3538922871703107e-07, y=1.5225332390400581e-06, z=-2.5343013476231135e-05)),
  acceleration=Accel(linear=Vector3(x=0.0, y=0.0, z=0.0), angular=Vector3(x=0.0, y=0.0, z=0.0)))

An unknown name:

result=simulation_interfaces.msg.Result(result=2, error_message='no entity "nothing"')

/set_entity_state#

Type simulation_interfaces/srv/SetEntityState. Moves an entity to a pose. A vehicle goes to the pose on the ground at the next physics step. The pose is that of base_footprint.

Name Type Unit Default Description
entity string The name of the entity.
state.header.frame_id string world utm or world. All other values mean world.
state.pose.position point m The position. For a vehicle, the simulator ignores z. For an object, the game uses the given z.
state.pose.orientation quaternion The simulator uses only the yaw.
state.twist.linear vector m/s zero The simulator uses the size of x and y as the forward speed of a vehicle.

The code 2 shows that no entity has the name.

ros2 service call /set_entity_state \
    simulation_interfaces/srv/SetEntityState \
    "{entity: polaris, state: {header: {frame_id: world}, pose: {position: {x: -38.7, y: -130.0}, orientation: {z: 0.7071, w: 0.7071}}}}"
from simulation_interfaces.srv import SetEntityState

request = SetEntityState.Request(entity="polaris")
request.state.header.frame_id = "world"
request.state.pose.position.x = -38.7
request.state.pose.position.y = -130.0
request.state.pose.orientation.z = 0.7071
request.state.pose.orientation.w = 0.7071
reply = call(SetEntityState, "/set_entity_state", request)
print(reply.result.result)
result=simulation_interfaces.msg.Result(result=1, error_message='')

/spawn_entity#

Type simulation_interfaces/srv/SpawnEntity. Adds one object to the world. The field uri selects the object type from the list of /get_spawnables.

Name Type Unit Default Description
name string The name of the new entity.
allow_renaming boolean false When the name is empty or in use, the simulator makes a name: <name>_<number>.
uri string The object type, for example prop:cone.
resource_string string Not available.
entity_namespace string ACRES ignores this field.
initial_pose pose with header m origin The pose. The frame is utm or world. With z = 0, the object goes on the ground.

The response has the field entity_name: the name that the simulator gave to the entity.

Code Name Meaning
1 The entity is in the world.
4 The spawn failed. A worker needs a game session with NPC workers or NPC vehicles.
101 NAME_NOT_UNIQUE The name is in use and allow_renaming is false.
102 NAME_INVALID The name is empty and allow_renaming is false.
103 UNSUPPORTED_FORMAT The uri is not in the list, or the request uses resource_string.
104 NO_RESOURCE uri and resource_string are empty.
107 MISSING_ASSETS The mesh of the object is not in this build of the game.
ros2 service call /spawn_entity \
    simulation_interfaces/srv/SpawnEntity \
    "{name: cone1, uri: 'prop:cone', initial_pose: {header: {frame_id: world}, pose: {position: {x: -38.7, y: -120.0, z: 0.0}}}}"
from simulation_interfaces.srv import SpawnEntity

request = SpawnEntity.Request(name="cone1", uri="prop:cone")
request.initial_pose.header.frame_id = "world"
request.initial_pose.pose.position.x = -38.7
request.initial_pose.pose.position.y = -120.0
reply = call(SpawnEntity, "/spawn_entity", request)
print(reply.result.result, reply.entity_name)
result=simulation_interfaces.msg.Result(result=1, error_message=''), entity_name='cone1'

The same request a second time, and then with allow_renaming: true:

result=simulation_interfaces.msg.Result(result=101, error_message='"cone1" exists'), entity_name=''
result=simulation_interfaces.msg.Result(result=1, error_message=''), entity_name='cone1_1'

/delete_entity#

Type simulation_interfaces/srv/DeleteEntity. Removes an entity that a client spawned. A client cannot remove an agent.

Name Type Unit Default Description
entity string The name of the entity.
Code Meaning
1 The simulator removed the entity.
2 No spawned entity has the name.
4 The name is that of an agent (game). ACRES Core returns the code 2 for an agent.
ros2 service call /delete_entity \
    simulation_interfaces/srv/DeleteEntity "{entity: cone1}"
from simulation_interfaces.srv import DeleteEntity

reply = call(DeleteEntity, "/delete_entity",
             DeleteEntity.Request(entity="cone1"))
print(reply.result.result)
result=simulation_interfaces.msg.Result(result=1, error_message='')

/get_spawnables#

Type simulation_interfaces/srv/GetSpawnables. Lists the object types for /spawn_entity. ACRES ignores the request field sources.

uri Object
prop:cone A traffic cone, 0.7 m high.
prop:box A box 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 who stands. In the game, the session must have NPC workers or NPC vehicles.

In ACRES Core, each object is a box that the LiDAR detects. The vehicle does not collide with it.

ros2 service call /get_spawnables \
    simulation_interfaces/srv/GetSpawnables
from simulation_interfaces.srv import GetSpawnables

reply = call(GetSpawnables, "/get_spawnables", GetSpawnables.Request())
print([s.uri for s in reply.spawnables])
['prop:cone', 'prop:box', 'prop:bale_round', 'prop:bale_square', 'person']

/get_named_poses#

Type simulation_interfaces/srv/GetNamedPoses. Lists the named places of the tile that are points, in the frame world.

Name Type Unit Default Description
tags.tags string list empty The tags to find. Empty = all places.
tags.filter_mode integer 0 0 = a place with one of the tags. 1 = a place with all tags.

Each pose of the response has these fields:

Name Type Unit Default Description
name string The identifier of the place, for example spawn-icsc-garage.
description string The name of the place for a person.
tags string list The category of the place, and heading when the place has a direction.
pose pose m The pose in the frame world. A place without a direction points north.
ros2 service call /get_named_poses \
    simulation_interfaces/srv/GetNamedPoses \
    "{tags: {tags: [spawn]}}"
from simulation_interfaces.srv import GetNamedPoses

request = GetNamedPoses.Request()
request.tags.tags = ["spawn"]
reply = call(GetNamedPoses, "/get_named_poses", request)
print([p.name for p in reply.poses])
['spawn-beck-lot', 'spawn-icsc-garage']

One pose of the response:

NamedPose(name='spawn-icsc-garage', description='Spawn: ICSC garage', tags=['spawn', 'heading'],
  pose=Pose(position=Point(x=-38.71, y=-140.513, z=0.6391577191766892),
            orientation=Quaternion(x=0.0, y=0.0, z=0.7071067811865475, w=0.7071067811865476)))

ACRES Services#

/acres/set_conditions#

Type acres_interfaces/srv/SetConditions. Sets the clock, the weather and the soil water, and removes the marks from fields. The request has four parts. The simulator applies a part only when its flag is true.

Name Type Unit Default Description
set_clock boolean false Applies date and local_hour.
date string The local date, YYYY-MM-DD.
local_hour number h The local time of day.
set_weather boolean false Applies weather and rain_mm_h.
weather string clear, fair, overcast, rain, storm or fog. Empty = no change of the sky.
rain_mm_h number mm/h The rain rate. A negative value = the rate of the preset.
set_soil_water boolean false Applies soil_wetness to soil_fields.
soil_wetness number 0 = the wilting point, 1 = saturated. For the top layer of the soil.
soil_fields integer list all fields The field numbers.
restore_fields boolean false Removes the marks and the crop damage from fields.
fields integer list all fields The field numbers.

The response has the field message: a text with the parts that the simulator applied. The code is 4 when one part failed. The other parts stay applied. ACRES Core applies the soil water and the restore function. It records the clock and the weather, but they have no effect.

ros2 service call /acres/set_conditions \
    acres_interfaces/srv/SetConditions \
    "{set_weather: true, weather: rain, rain_mm_h: 4.0, set_soil_water: true, soil_wetness: 0.8, soil_fields: [48]}"
from acres_interfaces.srv import SetConditions

request = SetConditions.Request(
    set_weather=True, weather="rain", rain_mm_h=4.0)
request.set_soil_water = True
request.soil_wetness = 0.8
request.soil_fields = [48]
reply = call(SetConditions, "/acres/set_conditions", request)
print(reply.result.result, reply.message)
result=simulation_interfaces.msg.Result(result=1, error_message=''),
message='weather rain rain 4.0 mm/h; soil wetness 0.80 on 360 cells'

/acres/set_vehicle_shift#

Type acres_interfaces/srv/SetVehicleShift. Sets the hardware shift of one agent. A hardware shift is a set of offsets on the actuators and the sensor mounts. It makes a vehicle that differs from its calibration. The request replaces the shift of the agent. A shift with all values at zero removes it.

The request has one field shift of the type acres_interfaces/msg/VehicleShift:

Name Type Unit Default Description
agent string agent 0 The name of the agent.
label string A text for datasets, for example steer+3deg.
steering_offset_deg number deg 0 Polaris: an offset of the steering wheel angle. Maxxum: an offset of the road wheel angle.
ulc_speed_bias_mps number m/s 0 An offset of the speed that the ULC measures.
throttle_bias_pct number % 0 An offset of the throttle output.
brake_bias_bar number bar 0 An offset of the brake output.
camera_offset_m vector m zero An offset of the camera position, in base_footprint.
camera_offset_rpy_deg vector deg zero An offset of the camera roll, pitch and yaw.
lidar_offset_m vector m zero An offset of the LiDAR position.
lidar_offset_rpy_deg vector deg zero An offset of the LiDAR roll, pitch and yaw.

The code 2 shows that no agent has the name. The episode log records each change on the channel /sim/shifts.

ros2 service call /acres/set_vehicle_shift \
    acres_interfaces/srv/SetVehicleShift \
    "{shift: {agent: polaris, label: steer+3deg, steering_offset_deg: 3.0}}"
from acres_interfaces.srv import SetVehicleShift

request = SetVehicleShift.Request()
request.shift.agent = "polaris"
request.shift.label = "steer+3deg"
request.shift.steering_offset_deg = 3.0
reply = call(SetVehicleShift, "/acres/set_vehicle_shift", request)
print(reply.result.result)
result=simulation_interfaces.msg.Result(result=1, error_message='')

An unknown agent:

result=simulation_interfaces.msg.Result(result=2, error_message='no agent "nobody"')

/acres/record#

Type acres_interfaces/srv/Record. Starts or stops the Episode Log.

Name Type Unit Default Description
action integer START = 1, STOP = 2.
path path session folder START: the MCAP file, as an absolute path. Empty = a file episode-<number>.mcap in the session folder.
Name Type Unit Default Description
path path The file.
messages integer The number of messages in the file at this time.
duration_s number s STOP: the simulation time of the recording.
Code Meaning
1 The recording started or stopped.
3 START while a recording is in progress, or STOP without a recording.
4 action is not 1 or 2, or the simulator cannot write the file.
ros2 service call /acres/record acres_interfaces/srv/Record \
    "{action: 1, path: /data/episode.mcap}"
ros2 service call /acres/record acres_interfaces/srv/Record \
    "{action: 2}"
from acres_interfaces.srv import Record

request = Record.Request(action=Record.Request.START,
                         path="/data/episode.mcap")
reply = call(Record, "/acres/record", request)
print(reply.result.result, reply.path)

reply = call(Record, "/acres/record",
             Record.Request(action=Record.Request.STOP))
print(reply.messages, reply.duration_s)
result=simulation_interfaces.msg.Result(result=1, error_message=''),
path='/data/episode.mcap', messages=4, duration_s=0.0

result=simulation_interfaces.msg.Result(result=1, error_message=''),
path='/data/episode.mcap', messages=2292, duration_s=9.483324214000021

/acres/farm_state#

Type acres_interfaces/srv/FarmState. Gives the ground truth of the fields for the score of a task: the crop damage, the field work and the soil water.

Name Type Unit Default Description
edge_band_m number m 0 The width of the permitted band along the edge of each field. 0 = no band.
fields integer list fields with crop or marks The field numbers, 1 to 59.

The response has result, a header (frame world, simulation time), the totals crushed_m2 and harvested_kg of the farm, and the list fields. Each element of fields has the type acres_interfaces/msg/FieldState:

Name Type Unit Default Description
field integer The field number.
name string F01 to F59.
crop string corn, soybean, potato or empty.
crop_area_m2 number m² The area of the crop of the field.
crushed_m2 number m² The crop that vehicles crushed.
crushed_in_band_m2 number m² The part of it in the permitted band.
crushed_out_band_m2 number m² The part of it farther in the field.
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. -1 = no soil cells.
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.

The code 4 shows that the session has no farm.

ros2 service call /acres/farm_state \
    acres_interfaces/srv/FarmState \
    "{edge_band_m: 4.0, fields: [48]}"
from acres_interfaces.srv import FarmState

request = FarmState.Request(edge_band_m=4.0, fields=[48])
reply = call(FarmState, "/acres/farm_state", request)
for field in reply.fields:
    print(field.name, field.crop, field.crushed_out_band_m2)
result=simulation_interfaces.msg.Result(result=1, error_message=''),
header=Header(stamp=Time(sec=176, nanosec=825009222), frame_id='world'),
fields=[FieldState(field=48, name='F48', crop='soybean', crop_area_m2=5098.080000000243,
  crushed_m2=0.0, crushed_in_band_m2=0.0, crushed_out_band_m2=0.0, harvested_m2=0.0,
  tilled_m2=0.0, seeded_m2=0.0, sprayed_m2=0.0, theta_mean=0.37230732510288195,
  pond_mean_m=0.0, rut_area_m2=0.0)],
crushed_m2=0.0, harvested_kg=0.0

/acres/field_query#

Type acres_interfaces/srv/FieldQuery. Gives the ground truth at one point of the tile.

Name Type Unit Default Description
frame_id string world world or utm.
x, y number m East and north in world, or easting and northing in utm.
Name Type Unit Default Description
field integer The field number. 0 = not in a field.
surface string soil or hard.
ground_z number m The height of the ground in the frame world.
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 Bit flags: 1 = tilled, 2 = seeded, 4 = sprayed.
crop string The crop of the nearest patch in 1 m. Empty = no crop.
crop_crushed number The crushed part of that patch, 0 to 1.
edge_distance_m number m The distance to the edge of the field. 0 = not in a field.
ros2 service call /acres/field_query \
    acres_interfaces/srv/FieldQuery \
    "{frame_id: world, x: -38.7, y: -60.0}"
from acres_interfaces.srv import FieldQuery

request = FieldQuery.Request(frame_id="world", x=-38.7, y=-60.0)
reply = call(FieldQuery, "/acres/field_query", request)
print(reply.field, reply.surface, reply.theta)
result=simulation_interfaces.msg.Result(result=1, error_message=''),
field=53, surface='soil', ground_z=0.2081112414598465, theta=0.22407099604606628, pond_m=0.0, rut_m=0.0,
worked=0, crop='soybean', crop_crushed=0.0, edge_distance_m=11.250190500575918