Add a ROS 2 Interface#
This page gives the procedure to add a message, a service or a topic to the ROS 2 side of ACRES. One interface has parts in five locations: the definition, the game, ACRES Core, the bridge and the tests.
Parts of an Interface#
A service request goes through these parts. A new interface needs a change in each part that it uses.
| Part | Location | Function |
|---|---|---|
| Definition | ROS/acres_interfaces/msg, ROS/acres_interfaces/srv |
The .msg or .srv file. |
| Episode-log schema | Acres/Source/Acres/AcresEpisodeSchemas.h |
The definition text that the simulator writes into an episode log. A tool generates this file. |
| Operation of the game | Acres/Source/Acres/AcresSimControl.cpp |
The operation of the simulator control channel that does the work. |
| Operation of ACRES Core | ROS/acres_core_sim/src/CoreSimServer.cpp |
The same operation for the Core server. |
| Bridge | ROS/acres_sim/src/sim_control.cpp, ROS/acres_sim/src/vehicle_bridge.cpp |
The ROS 2 service or topic. It changes the ROS 2 message into JSON and back. |
| Tests | ROS/acres_sim/test, ROS/acres_core_sim/test |
A substitute for the game and the integration tests. |
The bridge contains no simulation logic. It only changes the format of the data. Put the logic into the game and into ACRES Core, behind one operation of the control channel.
Use the service /acres/field_query as a model. The steps below show its code in each part.
WARNING
Do all ROS 2 commands in a shell that has the DDS loopback fence (source ROS/Env/setup_env.sh).
Before You Start#
- The ROS 2 workspace builds and its tests pass. Refer to Build the ROS 2 Workspace.
- For a new operation of the game, you can build the game. Refer to Build the Game.
- You know the protocol of the socket that the interface uses: Simulator Control Channel, Vehicle Bridge or Sensor Stream.
Use an interface of a standard package when one exists.
The bridge uses simulation_interfaces for the simulation state, the steps, the resets and the entities.
Add a type to acres_interfaces only for data that the standard packages do not have.
Add the Definition#
-
Write the
.msgor.srvfile inROS/acres_interfaces/msgorROS/acres_interfaces/srv. -
Obey these rules in the file.
Rule Reason Start with a comment that describes the type. The page Messages shows this comment. Put a comment after each field. The same page shows it as the description of the field. Give the unit in the field name: _m,_mps,_deg,_rad,_s,_n,_kg,_m2.The generator of the page reads the unit from the name. Make simulation_interfaces/Result resultthe first field of a service response.All services of the bridge report errors in this field. Use the frame worldfor poses, and acceptutmwhen a client has UTM coordinates.The control channel uses these two frames. -
Add the file to the list in
ROS/acres_interfaces/CMakeLists.txt. -
Build the workspace and show the new type.
source ROS/Env/setup_env.sh cd ROS colcon build cd .. source ROS/Env/setup_env.sh ros2 interface show acres_interfaces/srv/FieldQueryExpected Result
The command prints the definition, with the fields of
simulation_interfaces/Resultbelow the fieldresult.
Add a Message to the Episode Log#
Do this section only when the simulator writes the new message into the Episode Log. The game and ACRES Core write the log without ROS 2. They thus contain the definition text and an encoder for each message.
-
Add the name of the message to the list
TOPinTools/EpisodeLog/gen_schemas.py. -
Generate the schema header again.
Expected Result
The tool writes
Acres/Source/Acres/AcresEpisodeSchemas.h. Do not edit this file. -
Add the message to the episode writer in
Acres/Source/Acres/AcresEpisodeLog.handAcresEpisodeLog.cpp.- A structure for the message, in the sequence of the fields of the
.msgfile. - An
Encodefunction that writes the fields in the CDR format. - A channel name in
FEpisodeWriter(for example/sim/shifts) and aWritefunction.
ACRES Core uses the same writer through
Core/Source/AcresCoreEpisodeLog.cpp. - A structure for the message, in the sequence of the fields of the
-
Run the tests of the episode log.
Expected Result
schemas up to date ... 19 passed, 0 failed ROS 2 read 3029 messages: {'/sim/episode': 1, '/sim/agents': 1, '/sim/agent_states': 3000, '/sim/conditions': 25, '/sim/farm_stamps': 1, '/sim/farm_events': 1} ROS 2 check: PASSThe first line is the result of
gen_schemas.py --check. The last two lines come only in a shell with the ROS 2 environment.CAUTION
A change of the fields of a logged message changes the file format. Old episode logs then need the old definition. Add fields at the end of a message when this is possible.
Add the Operation to the Simulators#
A service of the bridge calls one operation of the simulator control channel. The operation has a name, JSON fields and a result code.
-
In the game, add the operation to
FAcresSimControl::HandleLineinAcresSimControl.cppand declare the function inAcresSimControl.h.The function fills the reply fields in
Outand returns a result code ofsimulation_interfaces/Result: 1 = OK, 2 = not found, 3 = incorrect state, 4 = failed. Put the cause intoError. -
In the Core server, add the same operation to
FCoreSimServer::HandleOpinROS/acres_core_sim/src/CoreSimServer.cpp.When ACRES Core cannot do the operation, return the code 0 and a text, as the operation
screenshotdoes. -
Use the same field names and units in the two simulators. A client must not see a difference.
All operations of the game run on the game thread. Do not block in an operation.
Add the Interface to the Bridge#
-
For a service, add a server to
SimControl::create_interfacesinROS/acres_sim/src/sim_control.cpp. Include the header of the type insim_control.hpp.add(n.create_service<ai::FieldQuery>( "/acres/field_query", [this, timeout](const std::shared_ptr<ai::FieldQuery::Request> req, std::shared_ptr<ai::FieldQuery::Response> res) { const std::string frame = req->frame_id.empty() ? "world" : req->frame_id; std::string error; const auto reply = request( Json{{"op", "field_query"}, {"x", req->x}, {"y", req->y}, {"frame", frame}}, timeout, &error); res->result = result_from_reply(reply, error); if (!reply) return; res->field = static_cast<uint16_t>(get_int(*reply, "field")); res->ground_z = get_number(*reply, "ground_z"); }, qos, control_group_));Function Use request()Sends one request and waits for the reply. It returns no value when the bridge has no connection to the simulator or gets no answer. result_from_reply()Makes the Resultfield from the reply, or the code 4 with the cause when there is no reply.get_int(),get_number(),get_truthy(),text()Read a JSON field. A field that is absent gives zero or an empty text. control_group_The callback group for all services that do not step the simulation. -
For a topic of a vehicle, add the publisher or the subscription to
VehicleBridge::setupinROS/acres_sim/src/vehicle_bridge.cpp.- Use
vehicle_qos()and a relative topic name, so that the namespace of the vehicle applies. - For a command, use the function
subscribe<T>()of the class. In lockstep, the bridge then sends the command before the next step. - Put the conversion into a function without side effects in
conversions.cpp, and add a unit test intest/test_conversions.cpp. - Use frame names with
frames_.prefix, so that a session with more than one vehicle has different frames.
- Use
-
Build the workspace again.
Add Tests#
The tests of acres_sim do not start the game. The file ROS/acres_sim/test/fake_game.py is a substitute that uses the same three sockets.
-
Add the operation to
FakeGame.handleinfake_game.py. Return constant values. -
Add the absolute name of the new service or topic to the list
ROOT_NAMESinROS/acres_sim/test/harness.py. Each test bridge runs in a namespace of its own. The list tells the test which absolute names to move into this namespace. -
Add assertions to
test_control_services_and_actioninROS/acres_sim/test/test_integration.py.fq = FieldQuery.Request() fq.frame_id, fq.x, fq.y = "utm", 500100.0, 4480200.0 q = ros.call(FieldQuery, p + "/acres/field_query", fq) assert (q.field, q.surface) == (12, "soil") assert game.requests[-1]["frame"] == "utm"Examine the two directions: the fields of the response, and the JSON request that the substitute received.
-
Add the same call to
test_services_and_actioninROS/acres_core_sim/test/test_integration.py. This test uses ACRES Core as the simulator. -
Run the tests.
Expected Result
The summary shows 0 errors and 0 failures.
-
For a change of the Polaris topics, compare a bag of the simulator with a bag of the real vehicle.
python ROS/Tools/check_topic_parity.py --sim /data/sim_bag --vehicle /data/bags/dbw_direct_test_01 python ROS/Tools/check_msg_defs.py /data/sim_bag --msgs ROS/vendor/ds_dbw_msgs --limit 200Expected Result
The first tool shows no
DIFFline for the topic. The second tool showsMATCH.Use
check_msg_defs.pyalso when you change the release of the packageds_dbw_msgsinROS/vendor.
Update the Documentation#
-
Build the site. The build generates the page Messages from the
.msgand.srvfiles. -
Add the new name to the correct reference page.
Interface Page A topic Topics A service or an action Services and Actions An operation of the control channel Simulator Control Channel A channel of the episode log Episode Log A node, a parameter or a launch argument Nodes and Launch Files -
Run the coverage check. It fails when a
.msgor.srvname ofacres_interfacesis not on the pages Topics or Services and Actions.Expected Result
The tool prints
coverage: doneand no error line.
Write Documentation gives the other checks of the site.
Checklist#
| Item | Check |
|---|---|
The .msg or .srv file has a comment for the type and for each field. |
ros2 interface show |
| The schema header is up to date. | python Tools/EpisodeLog/gen_schemas.py --check |
| The game and the Core server answer the operation with the same fields. | The two integration tests. |
| The substitute for the game answers the operation. | ROS/acres_sim/test/fake_game.py |
The new absolute name is in ROOT_NAMES. |
ROS/acres_sim/test/harness.py |
| The reference pages contain the new name. | python Documentation/Tools/check_docs.py --coverage |