Warning
Warning: Unity Simulation is deprecated as of December 2023, and is no longer available.
Communicating with external processes
In the previous section you installed Simulation-Foundation and Simulation-ROS-Integrations packages. This section helps you set up the communication between Unity and ROS using those packages.
Simulation-Foundation is the interfaces needed to communicate with an external process or service. (Herein, we will use "service" to mean any external process, service, or similar.) These interfaces are agnostic and can be implemented for ROS, GRPC etc.
Simulation-ROS-Integrations is an implementation of those interfaces needed to communicate with ROS.
In this section we discuss the components/interface we use to communicate with an external service.
| Interface/BaseClass | Function |
|---|---|
| BaseConnectionComponent | BaseConnectionComponent is a MonoBehaviour class to configure and instantiate communication with the external service. It exposes the IConnector settings to the user via Inspector settings. It will then instantiate an IConnector instance at runtime. |
| IConnector | The IConnector interface is used to establish communication with the external service and then handle all communication with that service. An IConnector class implements methods to: 1. Create or get an IPublisher used to publish messages to a topic to be read by an external service. 2. Subscribe a callback to read messages from a topic sent by an external service. 3. Implement a listener to handle service requests. 4. Create a service client to send requests to a service. |
| IPublisher | The IPublisher interface is used to publish messages to a topic to be read by an external service. |
| IServiceClient | The IServiceClient interface is used to send service requests to an external application and receive a corresponding response. |
Adding a BaseConnectionComponent
First we add a BaseConnectionComponent to the Unity scene.
RosEndpointConnectorComponent is a derived class of BaseConnectionComponent. Add this component to the scene to communicate with ROS. RosEndpointConnectorComponent is contained in Simulation-ROS-Integrations.
- Create an empty GameObject in the scene. Rename it
RosEndpointConnector. - Click on
Add Componentin the inspector window and type the nameROS Endpoint Connector Component. - Add the
RosEndpointConnectorComponentfrom the list. - Set the
Endpoint IP addressandEndpoint Portto the IP address and port of the machine running ROS. - Make sure
Connect On Startis checked.

Getting a Reference to the IConnector Interface in Your Script
Now you have a RosEndpointConnectorComponent in your scene. We can use it to get a reference to the IConnector interface to send and receive messages to ROS. There are two methods to get a reference to IConnector interface.
Using RosEndpointConnector
The RosEndpointConnectorComponent contains the reference to the IConnector interface. If you know which GameObject contains the RosEndpointConnectorComponent (RosEndpointConnector, from above), you can follow these steps to get the IConnector instance.
- Use the GetComponent API to get the
RosEndpointConnectorComponent. - Use
GetConnectorAPI to get theIConnectorinterface.
The following code block illustrates this method.
public class NewScript : MonoBehaviour
{
GameObject rosGameObject;
RosEndpointConnectorComponent rosComponent;
IConnector rosConnector;
void Start()
{
rosComponent = rosGameObject.GetComponent<RosEndpointConnectorComponent>();
var rosConnector = rosComponent.GetConnector();
}
}
Note that, while this works fine, it is not the recommended way to fetch a reference to the Connector as it tightly couples your script to that specific Connector instance. Instead, consider using the ConnectorInjector interface.
Using ConnectorInjector
FindConnector
The ConnectorInjector class in Simulation-Foundation makes it easier to get the IConnector interface from any script in the project. Use the FindConnector API in ConnectorInjector to get the IConnector. You pass a GameObject or MonoBehaviour to the FindConnector API. It searches down, and then up the GameObject tree hierarchy, looking for a BaseConnectionComponent component and returns its IConnector interface.
If a BaseConnectionComponent cannot be found in the GameObject tree hierarchy, it returns the default IConnector from the default BaseConnectionComponent.
Here is a code block on how to use ConnectorInjector from any MonoBehaviour.
var myConnector = ConnectorInjector.FindConnector(this);
Multiple Connectors
FindConnector will respond dynamically to the composition of your scene. If you want to change the Connector a specific GameObject in the scene is using, simply add a new Connector to that GameObject's hierarchy. You may use a ProxyConnector to point your Object at a specific Connector not in the Object's hierarchy, or create a Connector per Object. The only restriction is that no two Connectors should contain the same connection information (e.g. you cannot create two RosEndpointConnectors that both connect to the exact same IP:Port). If you need that functionality, use the ProxyConnector to bind both objects to the shared connection.
Using ConnectorInjector.GetDefaultConnector
The ConnectorInjector class provides the GetDefaultConnector API to return the default IConnector. It will search the scene for the all BaseConnectionComponent components and return the first one found.
The default IConnector can also be set with SetDefaultConnector:
SetDefaultConnector(IConnector connection)
SetDefaultConnector(BaseConnectionComponent connection)
var rosConnector = ConnectorInjector.GetDefaultConnector();
Sending and Receiving Messages Using the IPublisher Interface and Subscribe Method
Now that you have an IConnector interface, you can send and receive messages. IConnector provides APIs to register a publisher or subscriber to a specific topic.
Subscribing to Messages from an External Service
IConnector provides the Subscribe API to subscribe to a topic.
In this example, we subscribe to a topic called RobotPose with the message type Pose. Whenever the IConnector receives a message, it passes the message to the PoseCallback callback function.
void PoseCallback(PoseMsg message){}
void Update()
{
// Option 1
rosConnector.Subscribe<PoseMsg>("RobotPose", PoseCallback);
// Option 2
rosConnector.Subscribe("RobotPose", PoseMsg.MessageTypeName, PoseCallback);
}
Publishing Messages to an External Service
IConnector provides the RegisterPublisher API to create or get an IPublisher interface to publish to a topic. The IPublisher interface provides the Publish API to publish a message.
In this example, we publish a PoseMsg to the RobotPose topic.
// Registering the IPublisher
// Option 1
IPublisher posePublisher = rosConnector.RegisterPublisher<PoseMsg>("RobotPose")
// Option 2
IPublisher posePublisher = rosConnector.RegisterPublisher("RobotPose",PoseMsg.MessageTypeName)
// Publish a Message
PoseMsg sampleMsg = new PoseMsg();
posePublisher.Publish(sampleMsg);
Sending and Receiving Service Requests and Response Using the ImplementService Method and IServiceClient Interface
Hosting a Service in Unity
IConnector provides the ImplementService API to host a service in Unity. It receives an IMessage type request and responds with another IMessage type.
In this example, we implement a service named "exampleService" that receives a TransformMsg request and responds with a PoseMsg.
// Creating a service in Unity
// Option 1
IConnector exampleConnector = ConnectorInjector.FindConnector(gameObject);
exampleConnector.ImplementService<TransformMsg,PoseMsg>("exampleServicde",exampleFunction);
// Option 2
exampleConnector.ImplementService("exampleService", TransformMsg.k_MessageTypeId, PoseMsg.k_MessageTypeId, exampleFunction);
PoseMsg exampleFunction(TransformMsg arg1)
{
return new PoseMsg();
}
Creating a Service Client in Unity
IConnector provides the RegisterServiceClient API to create a service client in Unity. It returns an IServiceClient interface. You can use its SendRequest API to send an IMessage request to a service and receive the IMessage response.
In this example, we create a client to a service named "exampleService". We send a TransformMsg request to this service and receive a PoseMsg response.
IConnector exampleConnector = ConnectorInjector.FindConnector(gameObject);
IServiceClient serviceClient = exampleConnector.RegisterServiceClient<TransformMsg,PoseMsg>("exampleService");
exampleConnector.RegisterServiceClient("exampleService", TransformMsg.k_MessageTypeId, PoseMsg.k_MessageTypeId);
// Option 1
serviceClient.SendRequest<TransformMsg, PoseMsg>(new TransformMsg(), exampleCallback);
// Option 2
serviceClient.SendRequest(new TransformMsg(), exampleCallbackIMessage);
// Option 3
Task<PoseMsg> result = serviceClient.SendRequest<TransformMsg, PoseMsg>(new TransformMsg());
PoseMsg response = result.Result;
// Option 4
Task<IMessage> result1 = serviceClient.SendRequest(new TransformMsg());
PoseMsg response1 = (PoseMsg)result1.Result;
void exampleCallback(PoseMsg msg)
{
}
void exampleCallbackIMessage(IMessage msg)
{
}
IPublisher/Subscribe vs IServiceClient/ImplementService
Both IPublisher and IServiceClient will send a message to an external service.
IPublisher will publish the message but not wait for a response.
IServiceClient will send the request and wait for a response.
Updated 2022-08-31T23:11:26.000Z