Skip to content

Learn with ACRES#

Use the map data of acres_learn as input for a model. Make new maps for training. Train a learned skill that drives through the vehicle software.

Before you start

  • Set the environment of Use the Python API.
  • Install PyTorch for the training example. CPU only is sufficient.

What Each Package Gives#

Package Content Use It For
acres_learn The map products and their reader, the map-variant generator, the configuration files, the readers of vehicle recordings Features of the map, new maps, settings
acres_simulator.Training Many vehicles in one world, each with a task. Your model proposes path chunks. The checker accepts or refuses each chunk. Training a driving model
acres_simulator.evaluate, plot A fixed set of test cases for a skill, and the plots of the result Evaluation
Skills/ The schema of a skill and the example skill slow_pass A start for your skill

acres_learn has no driving code. The run-time library drives the vehicles.

Read the Map Products#

The map products describe what a planner and a model can know of the farm. They give the fields and their scout loops, the route graph, the speed limits and a 0.5 m raster of ground classes.

map_products.py
"""Read the map products of the default map: a field loop, its path from an entry, and the ground classes."""
import numpy as np
from acres_learn.tasks.scouting.map_products import ScoutingMap

m = ScoutingMap.load()
print(len(m.task_fields), "task fields")
f = m.field("F56")
print("F56 loop:", len(f.loop_xy), "points,", round(f.loop_length_m), "m")
path = m.path("F56", entry=0, clockwise=True)
print("path keys:", sorted(path)[:6])
classes = m.ground_class(f.loop_xy[:5, 0], f.loop_xy[:5, 1])
print("ground classes of the first loop points:", classes.tolist())

Expected result

129 task fields
F56 loop: 247 points, 247 m
path keys: ['class', 'curvature', 'speed_limit', 'xy']
ground classes of the first loop points: [3, 3, 3, 3, 3]

The ground classes are 0 crop, 1 lane, 2 verge, 3 edge band, 4 obstacle and 5 other. ScoutingMap.load(folder) reads a map variant. The Map Products reference gives each product.

Make and Describe a Map#

A map variant changes the fields, the lanes, the crops and the wet patches of the farm from a seed. Train on several maps to make a model that does not learn one map by heart.

python -m acres_learn.maps.describe Acres/Content/SimulationMaps/acre-mixed

Expected result

The numbers of the map structure, as JSON:

{
 "fields": 136,
 "tile_fields": 59,
 "task_fields": 129,
 "graph_nodes": 822,
 "graph_edges": 826,
 ...

python -m acres_learn.maps.variants --config Learning/acres_learn/configs/map_variants_v1.json --only <name> makes a variant from its seed and recipe. Run it from the folder Learning. The Map Data reference gives the recipe keys and the cost tables of a new map.

The Learned Skill#

A learned skill is a folder. It describes a new skill for a planner, the rules of its run, and the code or the model that proposes its path chunks.

Skills/slow_pass/
    skill.json               the description
    proposer.py              the proposer as a Python function, and the same rule as a PyTorch module
    cases.json               the cases of the evaluation
    train.py                 the example training loop
    make_model.py            exports the model
    model.pt                 the exported model (TorchScript)
Key of skill.json Function
name, text, use_when The name of the skill and its tool, and the text that a planner reads
runs_on place (to a field entry or H), field (a field and its scout loop), home (to H) or pose (no path)
arguments place or field, speed_cap_mps, seconds, and at most 4 more numbers for the proposer
results, end The result codes, and the end conditions: the path end, a hold of the shield, a time or distance limit, a signal
limits A lower speed cap, and the places where the skill can run
chunk The number of samples (3 to 64) and their spacing
proposer builtin, python (<module>:<function>) or model (an exported file)

The library checks the description and names the line that is wrong:

python -m acres_simulator.evaluate --check Skills/slow_pass

Expected result

Skills/slow_pass: the skill slow_pass is valid

The schema is Skills/skill.schema.json. The acres_simulator reference gives the full description.

Train on Many Vehicles#

Training holds one world with many vehicles. In each control step, your model reads one array of observations and gives one array of path chunks. The trajectory checker accepts or refuses each chunk. A refused chunk does not drive: the default driver drives that vehicle in that step.

train_loop.py
"""The training loop of a learned skill on 8 vehicles: the default chunks, slowed to 1 m/s."""
import numpy as np
import acres_simulator as acres
from acres_simulator.learned import vector_layout

train = acres.Training(acres.Core(num_envs=8, threads=4), "Skills/slow_pass")
train.reset(range(8), tasks=[{"place": "F56"}] * 8, seeds=range(8))
layout = vector_layout()
n = train.skill.handle.chunk_samples
offsets = slice(layout["chunk_offset_m"].start, layout["chunk_offset_m"].start + n)
speeds = slice(layout["chunk_speed_mps"].start, layout["chunk_speed_mps"].start + n)
accepted = 0
for step in range(200):
    vectors = train.observe()                                  # [8, PROPOSER_VECTOR]: the input of a model
    rows = np.concatenate([vectors[:, offsets], np.minimum(vectors[:, speeds], 1.0)], axis=1)  # your model here
    verdicts = train.propose(rows)                             # the trajectory checker checks each chunk
    accepted += int(verdicts["accept"].sum())
    train.step()                                               # one control step of each vehicle
    for slot in train.ended():
        print("episode end:", slot, train.result(slot)["code"])
print(f"{accepted} of {8 * 200} chunks accepted")

Expected result

1600 of 1600 chunks accepted
Call Function
reset(slots, tasks, seeds) Starts a new episode on some vehicles: a task (a place and a speed cap) and a seed of the other actors
observe() The observation vector of each vehicle: its pose, its speed, the route ahead and the default chunk
propose(rows) Your chunks: the side offsets, then the speeds. Returns accept and reason for each vehicle.
step() One control step of each vehicle
ended(), result(slot) The vehicles whose episode ended, and the result code, the cause and the counters

The reset of one vehicle changes only that vehicle. A run with 1 thread and a run with 8 threads give the same numbers.

The Full Example#

Skills/slow_pass/train.py trains one parameter with PyTorch: the pace of the skill. It draws tasks from the training fields of Learning/acres_learn/configs/splits_v1.json and puts actors on the path.

  1. Train on 8 vehicles for 300 steps.

    CUDA_VISIBLE_DEVICES= python Skills/slow_pass/train.py --slots 8 --steps 300 --threads 4 --out runs/slow_pass_train
    

    Expected result

    8 agents, 300 control steps of each: 106 control steps for each second (22.6 s); 0 episodes {}; contacts {}; 96.6 % of 2400 chunks accepted; the pace went from 0.51 to 1.500 m/s
    
  2. Evaluate the trained model on the cases of the folder, and plot the result.

    python -m acres_simulator.evaluate --skill runs/slow_pass_train/slow_pass --proposer model --out runs/slow_pass_eval
    python -m acres_simulator.plot runs/slow_pass_eval
    

    Expected result

    f56_truck_beside       done               185.0 s  path error 0.33 m  contact 0  proposer 100 %  refused 0
    f56_truck_on_path      blocked            116.8 s  path error 0.22 m  contact 0  proposer 84 %  refused 190
    f04_person_on_path     blocked             73.6 s  path error 0.19 m  contact 0  proposer 66 %  refused 252
    

    The plot command writes share.png, refusals.png, path_error.png and results.png.

A skill folder also works in the harness as a local planner. Refer to Use the Harness.

Next Steps#