Problem: Unity Won't Connect to Your ROS2 Node
You're building a robot simulation in Unity and need to send sensor data to ROS2, but the TCP Connector keeps timing out with Connection refused or messages never arrive.
You'll learn:
- How to configure TCP Connector for ROS2 Humble/Iron
- Fix the "10s timeout" connection error
- Verify bidirectional message flow
Time: 12 min | Level: Intermediate
Why This Happens
The ROS TCP Connector uses a client-server model where Unity acts as the TCP client. If the ROS TCP Endpoint server isn't running first, or if IP/port config mismatches, Unity fails silently after timeout.
Common symptoms:
- Unity console shows
[ROS] Waiting for ROS connection...indefinitely - Server starts but no messages appear in
ros2 topic echo - Works locally but fails on network/Docker setups
Solution
Step 1: Install ROS TCP Endpoint Package
# In your ROS2 workspace
cd ~/ros2_ws/src
git clone https://github.com/Unity-Technologies/ROS-TCP-Endpoint.git
cd ~/ros2_ws
colcon build --packages-select ros_tcp_endpoint
source install/setup.bash
Expected: Build succeeds with no errors. If colcon fails, check ROS2 is sourced.
If it fails:
- Error: "package 'ros_tcp_endpoint' not found": Run
rosdep install --from-paths src --ignore-src -yfirst - Python errors: Endpoint requires Python 3.8+, verify with
python3 --version
Step 2: Start the TCP Endpoint Server
# Default: localhost:10000
ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:=0.0.0.0
# For specific port
ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:=0.0.0.0 -p ROS_TCP_PORT:=10000
Why 0.0.0.0: Binds to all network interfaces. Use 127.0.0.1 only for same-machine testing.
You should see:
Starting ROS TCP Server...
Listening on 0.0.0.0:10000
Keep this Terminal open - server must run before Unity connects.
Step 3: Configure Unity TCP Connector
In Unity Editor:
Install via Package Manager:
- Window → Package Manager → Add package from git URL
- Enter:
https://github.com/Unity-Technologies/ROS-TCP-Connector.git?path=/com.unity.robotics.ros-tcp-connector
Configure ROSConnectionPref:
// Create in any GameObject using Unity.Robotics.ROSTCPConnector; void Start() { // Before ANY ROS operations ROSConnection.GetOrCreateInstance().Connect( "192.168.1.100", // ROS machine IP 10000 // Port matching server ); }Or use Robotics → ROS Settings UI:
- ROS IP Address:
192.168.1.100(your ROS machine) - ROS Port:
10000 - Protocol:
ROS2 - Tick "Connect on Startup"
- ROS IP Address:
Expected: Unity console shows [ROS] Connection established within 2 seconds.
Step 4: Test Message Publishing
using Unity.Robotics.ROSTCPConnector;
using RosMessageTypes.Std;
public class TestPublisher : MonoBehaviour
{
private ROSConnection ros;
private float publishTimer = 0f;
void Start()
{
ros = ROSConnection.GetOrCreateInstance();
// Register publisher - topic auto-created in ROS
ros.RegisterPublisher<StringMsg>("unity_test");
}
void Update()
{
publishTimer += Time.deltaTime;
if (publishTimer > 1f) // Publish every 1s
{
StringMsg msg = new StringMsg
{
data = $"Unity time: {Time.time:F2}"
};
ros.Publish("unity_test", msg);
publishTimer = 0f;
}
}
}
Why this works: RegisterPublisher creates the topic in ROS2. Messages serialize to ROS format automatically.
If it fails:
- No error but messages don't appear: Check server terminal for connection logs
- "Topic not registered": Call
RegisterPublisherbeforePublish - "Type mismatch": Verify
RosMessageTypes.Stdpackage is imported
Step 5: Subscribe to ROS Topics
using RosMessageTypes.Geometry;
void Start()
{
ros = ROSConnection.GetOrCreateInstance();
// Subscribe to existing ROS topic
ros.Subscribe<TwistMsg>("cmd_vel", ReceiveVelocity);
}
void ReceiveVelocity(TwistMsg msg)
{
// This runs on ROS message thread - be careful with Unity objects
float linearX = (float)msg.linear.x;
Debug.Log($"Received velocity: {linearX}");
// For Unity operations, queue to main thread:
UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
transform.Translate(Vector3.forward * linearX * Time.deltaTime);
});
}
Critical: ROS callbacks run on background threads. Access Unity API only via main thread dispatcher or you'll get crashes.
Verification
Test publishing:
# In ROS terminal
ros2 topic echo /unity_test
You should see:
data: 'Unity time: 1.23'
---
data: 'Unity time: 2.24'
Test subscribing:
# Publish to topic Unity subscribes to
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.5}}" -1
Unity console should log the velocity value immediately.
What You Learned
- TCP Endpoint server must start before Unity connects
0.0.0.0binding allows network connections beyond localhost- ROS callbacks run on background threads - use dispatcher for Unity API
- Topics auto-register on first publish in ROS2 mode
Limitations:
- 100 Hz message rate limit (TCP overhead). Use UDP for higher rates.
- Large messages (>1MB) can timeout - consider compression.
- Doesn't support ROS2 QoS policies yet (reliability/durability).
Common Network Troubleshooting
Docker Setup
If ROS runs in Docker:
# Start endpoint with host network
docker run --network host ros:humble ros2 run ros_tcp_endpoint default_server_endpoint
In Unity, use host.docker.internal (macOS/Windows) or bridge IP (Linux).
Firewall Issues
# Ubuntu - allow port 10000
sudo ufw allow 10000/tcp
# Check if port is listening
ss -tuln | grep 10000
Multiple Unity Instances
Each needs a unique port:
// Unity Instance 1
ROSConnection.GetOrCreateInstance().Connect("192.168.1.100", 10000);
// Unity Instance 2
ROSConnection.GetOrCreateInstance().Connect("192.168.1.100", 10001);
Start separate endpoints:
ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_TCP_PORT:=10001
Performance Tips
Reduce Latency:
// Set update frequency (default: 60 Hz)
ROSConnection.GetOrCreateInstance().SetMessageBatchingInterval(16); // ~60 Hz
Message Pooling:
// Reuse message objects instead of creating new ones
private TwistMsg reusableMsg = new TwistMsg();
void PublishVelocity(float x)
{
reusableMsg.linear.x = x;
ros.Publish("cmd_vel", reusableMsg);
// Don't create new TwistMsg() every frame
}
Conditional Publishing:
// Only publish when values change significantly
private float lastPublishedValue = 0f;
void Update()
{
float currentValue = GetSensorReading();
if (Mathf.Abs(currentValue - lastPublishedValue) > 0.01f)
{
PublishSensorData(currentValue);
lastPublishedValue = currentValue;
}
}
Tested on Unity 2023.2 LTS, ROS2 Humble, Ubuntu 22.04 & Windows 11