diff --git a/tutorials/pick_and_place/PickAndPlaceProject/Assets/DemoScripts/Demo.cs b/tutorials/pick_and_place/PickAndPlaceProject/Assets/DemoScripts/Demo.cs index c30ea524..7508cbb5 100644 --- a/tutorials/pick_and_place/PickAndPlaceProject/Assets/DemoScripts/Demo.cs +++ b/tutorials/pick_and_place/PickAndPlaceProject/Assets/DemoScripts/Demo.cs @@ -60,9 +60,6 @@ public class Demo : MonoBehaviour string rosConnectName = "ROSConnect"; string publisherName = "Publisher"; int hostPort = 10000; - int unityPort = 5005; - int awaitDataMaxRetries = 10; - int awaitDataSleepSeconds = 1; string trajectoryPlannerType = "TrajectoryPlanner"; string rosServiceName = "niryo_moveit"; diff --git a/tutorials/pick_and_place/ROS/src/ros_tcp_endpoint b/tutorials/pick_and_place/ROS/src/ros_tcp_endpoint index 18612dcb..a5e0fd1c 160000 --- a/tutorials/pick_and_place/ROS/src/ros_tcp_endpoint +++ b/tutorials/pick_and_place/ROS/src/ros_tcp_endpoint @@ -1 +1 @@ -Subproject commit 18612dcb5f8ce662e691926930aa6469714d82aa +Subproject commit a5e0fd1ce1a711f557fe67e73657c74f92482fbe diff --git a/tutorials/pick_and_place/Scripts/SourceDestinationPublisher.cs b/tutorials/pick_and_place/Scripts/SourceDestinationPublisher.cs index 7fc2fab5..a2d649a5 100644 --- a/tutorials/pick_and_place/Scripts/SourceDestinationPublisher.cs +++ b/tutorials/pick_and_place/Scripts/SourceDestinationPublisher.cs @@ -52,7 +52,7 @@ void Start() public void Publish() { - MNiryoMoveitJoints sourceDestinationMessage = new MNiryoMoveitJoints(); + NiryoMoveitJointsMsg sourceDestinationMessage = new NiryoMoveitJointsMsg(); sourceDestinationMessage.joint_00 = jointArticulationBodies[0].xDrive.target; sourceDestinationMessage.joint_01 = jointArticulationBodies[1].xDrive.target; @@ -62,14 +62,14 @@ public void Publish() sourceDestinationMessage.joint_05 = jointArticulationBodies[5].xDrive.target; // Pick Pose - sourceDestinationMessage.pick_pose = new MPose + sourceDestinationMessage.pick_pose = new PoseMsg { position = target.transform.position.To(), orientation = Quaternion.Euler(90, target.transform.eulerAngles.y, 0).To() }; // Place Pose - sourceDestinationMessage.place_pose = new MPose + sourceDestinationMessage.place_pose = new PoseMsg { position = targetPlacement.transform.position.To(), orientation = pickOrientation.To() diff --git a/tutorials/pick_and_place/Scripts/TrajectoryPlanner.cs b/tutorials/pick_and_place/Scripts/TrajectoryPlanner.cs index a9b57d2e..cd8251d6 100644 --- a/tutorials/pick_and_place/Scripts/TrajectoryPlanner.cs +++ b/tutorials/pick_and_place/Scripts/TrajectoryPlanner.cs @@ -78,9 +78,9 @@ private void OpenGripper() /// Get the current values of the robot's joint angles. /// /// NiryoMoveitJoints - MNiryoMoveitJoints CurrentJointConfig() + NiryoMoveitJointsMsg CurrentJointConfig() { - MNiryoMoveitJoints joints = new MNiryoMoveitJoints(); + NiryoMoveitJointsMsg joints = new NiryoMoveitJointsMsg(); joints.joint_00 = jointArticulationBodies[0].xDrive.target; joints.joint_01 = jointArticulationBodies[1].xDrive.target; @@ -101,11 +101,11 @@ MNiryoMoveitJoints CurrentJointConfig() /// public void PublishJoints() { - MMoverServiceRequest request = new MMoverServiceRequest(); + MoverServiceRequest request = new MoverServiceRequest(); request.joints_input = CurrentJointConfig(); // Pick Pose - request.pick_pose = new MPose + request.pick_pose = new PoseMsg { position = (target.transform.position + pickPoseOffset).To(), // The hardcoded x/z angles assure that the gripper is always positioned above the target cube before grasping. @@ -113,16 +113,16 @@ public void PublishJoints() }; // Place Pose - request.place_pose = new MPose + request.place_pose = new PoseMsg { position = (targetPlacement.transform.position + pickPoseOffset).To(), orientation = pickOrientation.To() }; - ros.SendServiceMessage(rosServiceName, request, TrajectoryResponse); + ros.SendServiceMessage(rosServiceName, request, TrajectoryResponse); } - void TrajectoryResponse(MMoverServiceResponse response) + void TrajectoryResponse(MoverServiceResponse response) { if (response.trajectories.Length > 0) { @@ -149,7 +149,7 @@ void TrajectoryResponse(MMoverServiceResponse response) /// /// MoverServiceResponse received from niryo_moveit mover service running in ROS /// - private IEnumerator ExecuteTrajectories(MMoverServiceResponse response) + private IEnumerator ExecuteTrajectories(MoverServiceResponse response) { if (response.trajectories != null) { diff --git a/tutorials/pick_and_place/docker/set-up-workspace b/tutorials/pick_and_place/docker/set-up-workspace index 3669ecbb..9eee40eb 100644 --- a/tutorials/pick_and_place/docker/set-up-workspace +++ b/tutorials/pick_and_place/docker/set-up-workspace @@ -1,6 +1,6 @@ #!/bin/bash source /opt/ros/melodic/setup.bash -echo "ROS_IP: $(hostname -i)" > $ROS_WORKSPACE/src/niryo_moveit/config/params.yaml +echo "ROS_IP: $(hostname -i)" > $ROS_WORKSPACE/src/ros-tcp-endpoint/config/params.yaml cd $ROS_WORKSPACE -catkin_make \ No newline at end of file +catkin_make diff --git a/tutorials/ros_packages/robotics_demo/scripts/color_publisher.py b/tutorials/ros_packages/robotics_demo/scripts/color_publisher.py deleted file mode 100755 index ac38b11f..00000000 --- a/tutorials/ros_packages/robotics_demo/scripts/color_publisher.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -import random -import rospy -from robotics_demo.msg import UnityColor - - -TOPIC_NAME = 'color' -NODE_NAME = 'color_publisher' - - -def post_color(): - pub = rospy.Publisher(TOPIC_NAME, UnityColor, queue_size=10) - rospy.init_node(NODE_NAME, anonymous=True) - rate = rospy.Rate(10) # 10hz - - while not rospy.is_shutdown(): - - r = random.randint(0, 255) - g = random.randint(0, 255) - b = random.randint(0, 255) - color = UnityColor(r, g, b, 1) - pub.publish(color) - rate.sleep() - break - - -if __name__ == '__main__': - try: - post_color() - except rospy.ROSInterruptException: - pass diff --git a/tutorials/ros_packages/robotics_demo/scripts/object_pose_client.py b/tutorials/ros_packages/robotics_demo/scripts/object_pose_client.py deleted file mode 100755 index 3ebe8b07..00000000 --- a/tutorials/ros_packages/robotics_demo/scripts/object_pose_client.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -import random -import rospy -import sys - -from robotics_demo.srv import ObjectPoseService, ObjectPoseServiceResponse - - -def get_object_pose_client(name): - rospy.wait_for_service('obj_pose_srv') - try: - get_obj_pose = rospy.ServiceProxy('obj_pose_srv', ObjectPoseService) - obj_pose_resp = get_obj_pose(name) - return obj_pose_resp.object_pose - except rospy.ServiceException as e: - print("Service call failed: %s"%e) - - -def usage(): - return "%s [object_name]"%sys.argv[0] - -if __name__ == "__main__": - if len(sys.argv) == 2: - name = str(sys.argv[1]) - else: - print(usage()) - sys.exit(1) - print("Requesting pose for %s"%(name)) - print("Pose for %s: %s"%(name, get_object_pose_client(name))) diff --git a/tutorials/ros_packages/robotics_demo/scripts/server_endpoint.py b/tutorials/ros_packages/robotics_demo/scripts/server_endpoint.py deleted file mode 100644 index ababf68c..00000000 --- a/tutorials/ros_packages/robotics_demo/scripts/server_endpoint.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -import rospy - -from ros_tcp_endpoint import TcpServer, RosPublisher, RosSubscriber, RosService, UnityService -from robotics_demo.msg import PosRot, UnityColor -from robotics_demo.srv import PositionService, ObjectPoseService - -def main(): - ros_node_name = rospy.get_param("/TCP_NODE_NAME", 'TCPServer') - buffer_size = rospy.get_param("/TCP_BUFFER_SIZE", 1024) - connections = rospy.get_param("/TCP_CONNECTIONS", 10) - tcp_server = TcpServer(ros_node_name, buffer_size, connections) - rospy.init_node(ros_node_name, anonymous=True) - - tcp_server.start({ - 'pos_rot': RosPublisher('pos_rot', PosRot, queue_size=10), - 'color': RosSubscriber('color', UnityColor, tcp_server), - 'pos_srv': RosService('pos_srv', PositionService), - 'obj_pose_srv': UnityService('obj_pose_srv', ObjectPoseService, tcp_server), - }) - - rospy.spin() - - -if __name__ == "__main__": - main() diff --git a/tutorials/ros_unity_integration/README.md b/tutorials/ros_unity_integration/README.md index 45997364..f96b7202 100644 --- a/tutorials/ros_unity_integration/README.md +++ b/tutorials/ros_unity_integration/README.md @@ -14,13 +14,12 @@ The `ROSConnection` plugin (also from [ROS TCP Connector](https://github.com/Uni ## Tutorials -- [ROS–Unity Integration: Initial Setup](setup.md) - ROS-Unity Initial Setup +- [ROS–Unity Integration: Initial Setup](setup.md) - ROS–Unity Initial Setup - [ROS–Unity Integration: Network Description](network.md) - Description of network settings and troubleshooting -- [ROS–Unity Integration: Publisher](publisher.md) - Adding a Publisher to a Unity Scene -- [ROS–Unity Integration: Subscriber](subscriber.md) - Adding a Subscriber to a Unity Scene -- [ROS–Unity Integration: Service](service.md) - Adding a Service call to a Unity Scene -- [ROS–Unity Integration: UnityService](unity_service.md) - Adding a Service that runs in a Unity Scene -- [ROS–Unity Integration: Server Endpoint](server_endpoint.md) - How to write a Server Endpoint +- [ROS–Unity Integration: Publisher](publisher.md) - Publish messages from a Unity Scene +- [ROS–Unity Integration: Subscriber](subscriber.md) - Subscribe to receive messages in a Unity Scene +- [ROS–Unity Integration: Unity Service](unity_service.md) - Implement a service inside a Unity Scene +- [ROS–Unity Integration: Service Call](service_call.md) - Call an external service from a Unity Scene ## Example Unity Scripts @@ -32,9 +31,9 @@ Example scripts implemented in tutorials: - `unity_scripts/RosSubscriberExample.cs` - Subscribes to a topic that accepts color messages and uses them to change the color of a GameObject in the Unity scene. -- `unity_scripts/RosServiceExample.cs` - - Returns a destination position for a GameObject to move towards each time the service is called. - - `unity_scripts/RosUnityServiceExample.cs` - Runs a service in the Unity scene that takes a GameObject's name and responds with the Pose of that object. +- `unity_scripts/RosServiceExample.cs` + - Returns a destination position for a GameObject to move towards each time the service is called. + diff --git a/tutorials/ros_unity_integration/images/create_cube.png b/tutorials/ros_unity_integration/images/create_cube.png new file mode 100644 index 00000000..a91fec1a Binary files /dev/null and b/tutorials/ros_unity_integration/images/create_cube.png differ diff --git a/tutorials/ros_unity_integration/images/docker_cli.png b/tutorials/ros_unity_integration/images/docker_cli.png new file mode 100644 index 00000000..98eceac3 Binary files /dev/null and b/tutorials/ros_unity_integration/images/docker_cli.png differ diff --git a/tutorials/ros_unity_integration/images/generate_messages_3.png b/tutorials/ros_unity_integration/images/generate_messages_3.png new file mode 100644 index 00000000..14058159 Binary files /dev/null and b/tutorials/ros_unity_integration/images/generate_messages_3.png differ diff --git a/tutorials/ros_unity_integration/images/move_tool.png b/tutorials/ros_unity_integration/images/move_tool.png new file mode 100644 index 00000000..b5d97840 Binary files /dev/null and b/tutorials/ros_unity_integration/images/move_tool.png differ diff --git a/tutorials/ros_unity_integration/images/ros1_icon.png b/tutorials/ros_unity_integration/images/ros1_icon.png new file mode 100644 index 00000000..34f2784f Binary files /dev/null and b/tutorials/ros_unity_integration/images/ros1_icon.png differ diff --git a/tutorials/ros_unity_integration/images/ros2_icon.png b/tutorials/ros_unity_integration/images/ros2_icon.png new file mode 100644 index 00000000..dcb7d71d Binary files /dev/null and b/tutorials/ros_unity_integration/images/ros2_icon.png differ diff --git a/tutorials/ros_unity_integration/images/ros2_protocol.png b/tutorials/ros_unity_integration/images/ros2_protocol.png new file mode 100644 index 00000000..d7116862 Binary files /dev/null and b/tutorials/ros_unity_integration/images/ros2_protocol.png differ diff --git a/tutorials/ros_unity_integration/images/settings_ros_ip.png b/tutorials/ros_unity_integration/images/settings_ros_ip.png new file mode 100644 index 00000000..dd33dce4 Binary files /dev/null and b/tutorials/ros_unity_integration/images/settings_ros_ip.png differ diff --git a/tutorials/ros_unity_integration/images/unity-tab-square-white.png b/tutorials/ros_unity_integration/images/unity-tab-square-white.png new file mode 100644 index 00000000..154551f5 Binary files /dev/null and b/tutorials/ros_unity_integration/images/unity-tab-square-white.png differ diff --git a/tutorials/ros_unity_integration/network.md b/tutorials/ros_unity_integration/network.md index cefb66ed..35b5d822 100644 --- a/tutorials/ros_unity_integration/network.md +++ b/tutorials/ros_unity_integration/network.md @@ -15,8 +15,6 @@ `ROS_IP` : The IP address of the machine, VM, or container running ROS. -`UNITY_IP` : The IP address of the machine running Unity. - > It is possible to set both of these variables on the machines running Unity and ROS. The specifics of where and why each of these settings will be described below. On the ROS machine these settings are set as a rosparam and will typically be set in a launch file like [this](https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/ros_packages/robotics_demo/launch/robo_demo.launch) or in a [param file](https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/pick_and_place/ROS/src/niryo_moveit/config/params.yaml) loaded by a launch file like [this](https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/pick_and_place/ROS/src/niryo_moveit/launch/part_3.launch#L2). The param file can also be loaded manually by running the `rosparam load params.yaml` command. @@ -37,19 +35,10 @@ The container will need to be started with the following arguments to forward th - On the ROS side, set `ROS_IP` to `0.0.0.0`. -- On the Unity side, set `ROS_IP` to `127.0.0.1` and the `Override Unity IP Address` to your local machine's IP address. +- On the Unity side, set `ROS_IP` to `127.0.0.1`. ![](images/troubleshoot-docker-unity.png) -## Explicitly setting `UNITY_IP` - -The `UNITY_IP` can be set in two different places. - -1. If set on the ROS side as a rosparam, the `server_endpoint` will only use this IP to send messages to Unity. -1. If set on the Unity side as the `Override Unity IP Address`, the `UNITY_IP` on the ROS side will be set to this value during the initial handshake between ROS and Unity once play is pressed in the Editor. - -> If the `UNITY_IP` is not set in either of these places, then the IP that makes the first connection to ROS during the initial handshake will be used. - # Troubleshooting ## Where Does Communication Break Down diff --git a/tutorials/ros_unity_integration/publisher.md b/tutorials/ros_unity_integration/publisher.md index 502d1ab2..9c3421b3 100644 --- a/tutorials/ros_unity_integration/publisher.md +++ b/tutorials/ros_unity_integration/publisher.md @@ -2,44 +2,20 @@ Create a simple Unity scene which publishes a GameObject's position and rotation to a [ROS topic](http://wiki.ros.org/ROS/Tutorials/UnderstandingTopics#ROS_Topics). -## Setting Up ROS +These instructions cover the setup for both ROS1 and ROS2. Instructions for ROS2 users are marked with this icon: ros2. -- Copy the `tutorials/ros_packages/robotics_demo` folder of this repo into the `src` folder in your Catkin workspace. +## Setting Up -- Follow the [ROS–Unity Initial Setup](setup.md) guide. +- Follow the [ROS–Unity Demo Setup](setup.md#ros2-environment) guide. -- Open a new terminal window and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo server_endpoint.py - ``` - -Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. - -- Open another new terminal window, navigate to your ROS workspace, and run the following commands: - ```bash - source devel/setup.bash - rostopic echo pos_rot - ``` - -## Setting Up Unity Scene -- In the menu bar, find and select `Robotics` -> `Generate ROS Messages...` -- Set the ROS message path to `PATH/TO/Unity-Robotics-Hub/tutorials/ros_packages/robotics_demo`. - - Expand the robotics_demo subfolder and click "Build 2 msgs" to generate new C# scripts from the ROS .msg files. - -![](images/generate_messages_1.png) - - - The generated files will be saved in the default directory `Assets/RosMessages/RoboticsDemo/msg`. -- Create a new directory in `Assets` and name it `Scripts` -- Create a new script in the `Scripts` directory and name it `RosPublisherExample.cs` -- Open `RosPublisherExample.cs` and paste the following code: - - **Note** Script can be found at `tutorials/ros_unity_integration/unity_scripts` +## Create Unity Publisher +- In your Project tab in Unity, create a new C# script and name it `RosPublisherExample`. Paste the following code into the new script file. + - (Alternatively, you can drag the script file into Unity from `tutorials/ros_unity_integration/unity_scripts/RosPublisherExample.cs` in this repo.) ```csharp -using RosMessageTypes.RoboticsDemo; using UnityEngine; using Unity.Robotics.ROSTCPConnector; +using RosMessageTypes.UnityRoboticsDemo; /// /// @@ -61,6 +37,7 @@ public class RosPublisherExample : MonoBehaviour { // start the ROS connection ros = ROSConnection.instance; + ros.RegisterPublisher(topicName); } private void Update() @@ -71,7 +48,7 @@ public class RosPublisherExample : MonoBehaviour { cube.transform.rotation = Random.rotation; - MPosRot cubePos = new MPosRot( + PosRotMsg cubePos = new PosRotMsg( cube.transform.position.x, cube.transform.position.y, cube.transform.position.z, @@ -90,14 +67,48 @@ public class RosPublisherExample : MonoBehaviour } ``` -- Add a plane and a cube to the empty Unity scene -- Move the cube a little ways up so it is hovering above the plane -- In the main menu bar, open `Robotics/ROS Settings`. - - Set the ROS IP address and port to match the ROS IP and port variables defined when you set up ROS. +- Add a plane and a cube to your Unity scene. You can create simple geometric shapes in Unity by going to the Hierarchy window, clicking the + button, and navigating to the shape you want to create. + +![](images/create_cube.png) + +- Move the cube a little ways up so it is hovering above the plane. To do this, select the cube in the hierarchy window, and click on the move tool in the toolbar at the top left of the Unity window. + +![](images/move_tool.png) + +- Draggable arrows will appear around the cube in the Scene view; to move the cube up, drag the vertical (green) arrow upwards. + - Create another empty GameObject, name it `RosPublisher` and attach the `RosPublisherExample` script. - Drag the cube GameObject onto the `Cube` parameter. -- Pressing play in the Editor should publish a message to the terminal running the `rostopic echo pos_rot` command every 0.5 seconds +- Press play in the Editor. You should see the connection lights at the top left corner of the Game window turn blue, and something like `[INFO] [1622242057.562860400] [TCPServer]: Connection from 172.17.0.1` appear in the terminal running your server_endpoint. + +## Common Errors + +If you see the error `Failed to resolve message name: No module named unity_robotics_demo_msgs.msg` followed by `Topic 'pos_rot' is not registered` in the ROS-TCP-Endpoint log, you may have missed the step about installing the unity_robotics_demo_msgs package, or perhaps you forgot to build and/or source it afterwards. Try following the "Install Unity Robotics Demo" instructions [here](setup.md#install-unity-robotics-demo). + +## Start the Echo monitor + +- To prove that messages are actually being received by ROS, let's run the rostopic echo command. + + a) ros1 In ROS1, open a new terminal window, navigate to your ROS workspace, and run the following commands: + + ```bash + source devel/setup.bash + rostopic echo pos_rot + ``` + + b) ros2 In ROS2, the commands to run are + + ```bash + source install/setup.bash + ros2 topic echo pos_rot + ``` + +- If you're using Docker, you can use the command `docker ps` to get a list of all running containers; `docker exec -ti bash bash` starts a new terminal for the specified container. Alternatively, click the "CLI" button in the Docker UI to open a new terminal ("command line interface"). + +![](images/docker_cli.png) + +- If it's working correctly, you should see the contents of the message Unity is sending appearing every 0.5 seconds. > Please reference [networking troubleshooting](network.md) doc if any errors are thrown. diff --git a/tutorials/ros_unity_integration/ros2_docker/Dockerfile b/tutorials/ros_unity_integration/ros2_docker/Dockerfile new file mode 100644 index 00000000..0a6c39fb --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_docker/Dockerfile @@ -0,0 +1,22 @@ +FROM ros:foxy-ros-base + +# Make ROS2 Workspace Dirss +RUN mkdir -p /home/dev_ws/src + +# Copy ROS2 packages into workspace +COPY ./ros2_packages/ /home/dev_ws/src + +#Check out ROS-TCP-Endpoint, ROS2 version +RUN git clone https://github.com/Unity-Technologies/ROS-TCP-Endpoint /home/dev_ws/src/ros_tcp_endpoint -b ROS2 + +# Reference script with commands to source workspace +COPY ./ros2_docker/source_ros.sh /home/dev_ws/source_ros.sh + +# Change to workspace on sign in +RUN echo "cd home/dev_ws" >> ~/.bashrc + +# Build the workspace +RUN cd home/dev_ws && . /opt/ros/foxy/setup.sh && colcon build + +# Source the workspace on sign in +RUN echo ". install/local_setup.bash" >> ~/.bashrc diff --git a/tutorials/ros_unity_integration/ros2_docker/source_ros.sh b/tutorials/ros_unity_integration/ros2_docker/source_ros.sh new file mode 100644 index 00000000..c8b4e141 --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_docker/source_ros.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source /opt/ros/foxy/setup.bash +. install/local_setup.bash +ros2 launch ros2_test test_launcher.py diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/package.xml b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/package.xml new file mode 100644 index 00000000..bf4f2f0c --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/package.xml @@ -0,0 +1,25 @@ + + + + unity_robotics_demo + 0.0.1 + Package for use in ROS-Unity Integration tutorials (ROS2 version) + Unity Robotics + TODO: License declaration + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + rclpy + std_msgs + + rosidl_default_generators + rosidl_default_runtime + rosidl_interface_packages + + + ament_python + + diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/resource/unity_robotics_demo b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/resource/unity_robotics_demo new file mode 100644 index 00000000..e69de29b diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.cfg b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.cfg new file mode 100644 index 00000000..183d7717 --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script-dir=$base/lib/unity_robotics_demo +[install] +install-scripts=$base/lib/unity_robotics_demo diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.py new file mode 100644 index 00000000..a612e7eb --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/setup.py @@ -0,0 +1,29 @@ +import glob +import os + +from setuptools import setup + +package_name = 'unity_robotics_demo' + +setup( + name=package_name, + version='0.0.1', + packages=[package_name], + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Unity Robotics', + maintainer_email='unity-robotics@unity3d.com', + description='ROS2 Unity Integration Testing', + license='TODO: License declaration', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'color_publisher = unity_robotics_demo.color_publisher:main', + 'position_service = unity_robotics_demo.position_service:main', + ], + }, +) diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_copyright.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_copyright.py new file mode 100644 index 00000000..cc8ff03f --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_copyright.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_flake8.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_flake8.py new file mode 100644 index 00000000..27ee1078 --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_pep257.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_pep257.py new file mode 100644 index 00000000..b234a384 --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/__init__.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/color_publisher.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/color_publisher.py new file mode 100644 index 00000000..a6427f1c --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/color_publisher.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +import random +import rclpy + +from rclpy.node import Node + +from unity_robotics_demo_msgs.msg import UnityColor + + +class ColorPublisher(Node): + + def __init__(self): + super().__init__('color_publisher') + self.publisher_ = self.create_publisher(UnityColor, 'color', 10) + timer_period = 0.5 # seconds + self.timer = self.create_timer(timer_period, self.timer_callback) + self.i = 0 + self.do_publish() + + def do_publish(self): + if self.i == 0: + color = UnityColor() + color.r = random.randint(0, 255) + color.g = random.randint(0, 255) + color.b = random.randint(0, 255) + color.a = 1 + self.get_logger().info(f'Publishing: {color}') + self.publisher_.publish(color) + + self.i += 1 + + def timer_callback(self): + quit() + + +def main(args=None): + rclpy.init(args=args) + + color_pub = ColorPublisher() + + while rclpy.ok(): + rclpy.spin_once(color_pub) + + #color_pub.destroy_node() + #rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/position_service.py b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/position_service.py new file mode 100644 index 00000000..72a4d63d --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo/unity_robotics_demo/position_service.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import random +import rclpy + +from unity_robotics_demo_msgs.srv import PositionService + +from rclpy.node import Node + +class PositionServiceNode(Node): + + def __init__(self): + super().__init__('position_service_node') + self.srv = self.create_service(PositionService, 'pos_srv', self.new_position_callback) + + def new_position_callback(self, request, response): + response.output.pos_x = random.uniform(-4.0, 4.0) + response.output.pos_z = random.uniform(-4.0, 4.0) + + return response + + +def main(args=None): + rclpy.init(args=args) + + pos_service = PositionServiceNode() + + rclpy.spin(pos_service) + + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/CMakeLists.txt b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/CMakeLists.txt new file mode 100644 index 00000000..b3348a74 --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.5) +project(unity_robotics_demo_msgs) + +# find dependencies +find_package(rosidl_default_generators REQUIRED) +find_package(builtin_interfaces REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(std_msgs REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + "msg/PosRot.msg" + "msg/UnityColor.msg" + "srv/PositionService.srv" + "srv/ObjectPoseService.srv" + DEPENDENCIES builtin_interfaces geometry_msgs std_msgs + ) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # uncomment the line when a copyright and license is not present in all source files + #set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # uncomment the line when this package is not in a git repo + #set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() + + + + + diff --git a/tutorials/ros_packages/robotics_demo/msg/PosRot.msg b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/msg/PosRot.msg similarity index 100% rename from tutorials/ros_packages/robotics_demo/msg/PosRot.msg rename to tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/msg/PosRot.msg diff --git a/tutorials/ros_packages/robotics_demo/msg/UnityColor.msg b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/msg/UnityColor.msg similarity index 100% rename from tutorials/ros_packages/robotics_demo/msg/UnityColor.msg rename to tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/msg/UnityColor.msg diff --git a/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/package.xml b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/package.xml new file mode 100644 index 00000000..74488d6b --- /dev/null +++ b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/package.xml @@ -0,0 +1,25 @@ + + + + unity_robotics_demo_msgs + 0.0.1 + Messages used by ROS-Unity Integration tutorial (ROS2 version) + Unity Robotics + TODO: License declaration + + ament_cmake + + ament_lint_auto + ament_lint_common + + rosidl_default_generators + + rosidl_default_runtime + + rosidl_interface_packages + + + + ament_cmake + + diff --git a/tutorials/ros_packages/robotics_demo/srv/ObjectPoseService.srv b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/srv/ObjectPoseService.srv similarity index 100% rename from tutorials/ros_packages/robotics_demo/srv/ObjectPoseService.srv rename to tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/srv/ObjectPoseService.srv diff --git a/tutorials/ros_packages/robotics_demo/srv/PositionService.srv b/tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/srv/PositionService.srv similarity index 100% rename from tutorials/ros_packages/robotics_demo/srv/PositionService.srv rename to tutorials/ros_unity_integration/ros2_packages/unity_robotics_demo_msgs/srv/PositionService.srv diff --git a/tutorials/ros_unity_integration/ros_docker/Dockerfile b/tutorials/ros_unity_integration/ros_docker/Dockerfile new file mode 100644 index 00000000..f6032c53 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_docker/Dockerfile @@ -0,0 +1,24 @@ +FROM ros:melodic-ros-base + +ENV ROS_WORKSPACE=/catkin_ws + +# Copy packages +COPY ./ros_packages/ $ROS_WORKSPACE/src/ + +RUN git clone https://github.com/Unity-Technologies/ROS-TCP-Endpoint $ROS_WORKSPACE/src/ros_tcp_endpoint -b laurie/Ros2SinglePackage + +COPY ./ros_docker/set-up-workspace /setup.sh +#COPY docker/tutorial / + +RUN chmod +x /setup.sh && /setup.sh && rm /setup.sh + +WORKDIR $ROS_WORKSPACE + +# Source the workspace on sign in +RUN echo ". devel/setup.bash" >> ~/.bashrc + +# making sure the file modes are executable +RUN chmod +x src/ros_tcp_endpoint/src/ros_tcp_endpoint/*.py + +#ENTRYPOINT ["/tutorial"] + diff --git a/tutorials/ros_unity_integration/ros_docker/set-up-workspace b/tutorials/ros_unity_integration/ros_docker/set-up-workspace new file mode 100644 index 00000000..634d1a13 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_docker/set-up-workspace @@ -0,0 +1,6 @@ +#!/bin/bash + +source /opt/ros/melodic/setup.bash +echo "ROS_IP: 0.0.0.0" > $ROS_WORKSPACE/src/ros_tcp_endpoint/config/params.yaml +cd $ROS_WORKSPACE +catkin_make \ No newline at end of file diff --git a/tutorials/ros_packages/robotics_demo/CMakeLists.txt b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/CMakeLists.txt similarity index 79% rename from tutorials/ros_packages/robotics_demo/CMakeLists.txt rename to tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/CMakeLists.txt index bd843c37..993703c9 100644 --- a/tutorials/ros_packages/robotics_demo/CMakeLists.txt +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.8.3) -project(robotics_demo) +project(unity_robotics_demo) find_package(catkin REQUIRED COMPONENTS rospy @@ -9,22 +9,11 @@ find_package(catkin REQUIRED COMPONENTS message_generation ) -add_message_files(DIRECTORY msg) - -add_service_files(DIRECTORY srv) - -generate_messages( - DEPENDENCIES - geometry_msgs - std_msgs -) - catkin_package(CATKIN_DEPENDS ros_tcp_endpoint message_runtime) catkin_install_python(PROGRAMS - scripts/server_endpoint.py scripts/position_service.py scripts/color_publisher.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} diff --git a/tutorials/ros_packages/robotics_demo/launch/robo_demo.launch b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/launch/robo_demo.launch similarity index 100% rename from tutorials/ros_packages/robotics_demo/launch/robo_demo.launch rename to tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/launch/robo_demo.launch diff --git a/tutorials/ros_packages/robotics_demo/package.xml b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/package.xml similarity index 75% rename from tutorials/ros_packages/robotics_demo/package.xml rename to tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/package.xml index f7ee200f..a967ae0d 100644 --- a/tutorials/ros_packages/robotics_demo/package.xml +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/package.xml @@ -1,8 +1,8 @@ - robotics_demo + unity_robotics_demo 0.0.0 - The robotics_demo package + The unity_robotics_demo package (ROS1 version) Unity Robotics @@ -14,13 +14,16 @@ message_generation std_msgs ros_tcp_endpoint + unity_robotics_demo_msgs rospy std_msgs ros_tcp_endpoint + unity_robotics_demo_msgs message_runtime rospy std_msgs ros_tcp_endpoint + unity_robotics_demo_msgs diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/color_publisher.py b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/color_publisher.py new file mode 100644 index 00000000..28dc4e29 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/color_publisher.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import random +import rospy +import rosgraph +import time +from unity_robotics_demo_msgs.msg import UnityColor + + +TOPIC_NAME = 'color' +NODE_NAME = 'color_publisher' + + +def post_color(): + pub = rospy.Publisher(TOPIC_NAME, UnityColor, queue_size=10) + rospy.init_node(NODE_NAME, anonymous=True) + + r = random.randint(0, 255) + g = random.randint(0, 255) + b = random.randint(0, 255) + color = UnityColor(r, g, b, 1) + + wait_for_connections(pub, TOPIC_NAME) + pub.publish(color) + + time.sleep(0.1) + + +def wait_for_connections(pub, topic): + ros_master = rosgraph.Master('/rostopic') + topic = rosgraph.names.script_resolve_name('rostopic', topic) + num_subs = 0 + for sub in ros_master.getSystemState()[1]: + if sub[0] == topic: + num_subs+=1 + + for i in range(10): + if pub.get_num_connections() == num_subs: + return + time.sleep(0.1) + raise RuntimeError("failed to get publisher") + + +if __name__ == '__main__': + try: + post_color() + except rospy.ROSInterruptException: + pass diff --git a/tutorials/ros_packages/robotics_demo/scripts/position_service.py b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/position_service.py old mode 100755 new mode 100644 similarity index 87% rename from tutorials/ros_packages/robotics_demo/scripts/position_service.py rename to tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/position_service.py index 1a0e11a5..d2e5ef2a --- a/tutorials/ros_packages/robotics_demo/scripts/position_service.py +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo/scripts/position_service.py @@ -5,7 +5,7 @@ import random import rospy -from robotics_demo.srv import PositionService, PositionServiceResponse +from unity_robotics_demo_msgs.srv import PositionService, PositionServiceResponse def new_position(req): diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/CMakeLists.txt b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/CMakeLists.txt new file mode 100644 index 00000000..5ab09ce3 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 2.8.3) +project(unity_robotics_demo_msgs) + +find_package(catkin REQUIRED COMPONENTS + rospy + std_msgs + geometry_msgs + message_generation +) + +add_message_files(FILES + PosRot.msg + UnityColor.msg +) +add_service_files(FILES + ObjectPoseService.srv + PositionService.srv +) + +generate_messages( + DEPENDENCIES + geometry_msgs + std_msgs +) + +catkin_package(CATKIN_DEPENDS + message_runtime) + +############# +## Testing ## +############# + +## Add gtest based cpp test target and link libraries +# catkin_add_gtest(${PROJECT_NAME}-test test/test_robotics_demo.cpp) +# if(TARGET ${PROJECT_NAME}-test) +# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) +# endif() + +## Add folders to be run by python nosetests +# catkin_add_nosetests(test) diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/PosRot.msg b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/PosRot.msg new file mode 100644 index 00000000..43abb1f5 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/PosRot.msg @@ -0,0 +1,7 @@ +float32 pos_x +float32 pos_y +float32 pos_z +float32 rot_x +float32 rot_y +float32 rot_z +float32 rot_w \ No newline at end of file diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/UnityColor.msg b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/UnityColor.msg new file mode 100644 index 00000000..a77ff60b --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/msg/UnityColor.msg @@ -0,0 +1,4 @@ +int32 r +int32 g +int32 b +int32 a diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/package.xml b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/package.xml new file mode 100644 index 00000000..39ab862f --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/package.xml @@ -0,0 +1,28 @@ + + + unity_robotics_demo_msgs + 0.0.1 + Messages for the unity_robotics_demo package (ROS1 version) + + Unity Robotics + + + Apache 2.0 + + catkin + message_generation + std_msgs + rospy + std_msgs + message_runtime + rospy + std_msgs + ros_tcp_endpoint + + + + + + + + diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/ObjectPoseService.srv b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/ObjectPoseService.srv new file mode 100644 index 00000000..83d3c5b9 --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/ObjectPoseService.srv @@ -0,0 +1,3 @@ +string object_name +--- +geometry_msgs/Pose object_pose diff --git a/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/PositionService.srv b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/PositionService.srv new file mode 100644 index 00000000..e780c56e --- /dev/null +++ b/tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs/srv/PositionService.srv @@ -0,0 +1,3 @@ +PosRot input +--- +PosRot output \ No newline at end of file diff --git a/tutorials/ros_unity_integration/server_endpoint.md b/tutorials/ros_unity_integration/server_endpoint.md deleted file mode 100644 index a27fc7e6..00000000 --- a/tutorials/ros_unity_integration/server_endpoint.md +++ /dev/null @@ -1,154 +0,0 @@ -# ROS–Unity Integration: Server Endpoint - -A walkthrough of the important components of a ROS TCP endpoint script using the `robotics_demo` package as a example. - -The following is an example of a server endpoint Python script that: - -- Gets parameters from `rosparam` -- Creates corresponding ROS Publisher, Subscriber, and Service objects to interact with topics and services running in ROS network -- Starts TCP Server process to handle incoming and outgoing connections - - -```python -#!/usr/bin/env python - -import rospy - -from ros_tcp_endpoint import TcpServer, RosPublisher, RosSubscriber, RosService, UnityService -from robotics_demo.msg import PosRot, UnityColor -from robotics_demo.srv import PositionService, ObjectPoseService - -def main(): - ros_node_name = rospy.get_param("/TCP_NODE_NAME", 'TCPServer') - buffer_size = rospy.get_param("/TCP_BUFFER_SIZE", 1024) - connections = rospy.get_param("/TCP_CONNECTIONS", 10) - tcp_server = TcpServer(ros_node_name, buffer_size, connections) - rospy.init_node(ros_node_name, anonymous=True) - - tcp_server.start({ - 'pos_rot': RosPublisher('pos_rot', PosRot, queue_size=10), - 'color': RosSubscriber('color', UnityColor, tcp_server), - 'pos_srv': RosService('pos_srv', PositionService), - 'obj_pose_srv': UnityService('obj_pose_srv', ObjectPoseService, tcp_server), - }) - - rospy.spin() - - -if __name__ == "__main__": - main() -``` - - -## Import Statements for Services and Messages -```python -from ros_tcp_endpoint import TcpServer, RosPublisher, RosSubscriber, RosService, UnityService -from robotics_demo.msg import PosRot, UnityColor -from robotics_demo.srv import PositionService, ObjectPoseService -``` - -## Creating the Server - -Requires: - -- The ROS node name - -```python - tcp_server = TcpServer(ros_node_name, buffer_size, connections) -``` - -The `ros_node_name` argument is required and the `buffer_size` and `connections` are optional. They are set to `1024` and `10` by default if not provided in the constructor arguments. - -## Instantiate the ROS Node - -```python - rospy.init_node(ros_node_name, anonymous=True) -``` - -## Starting the Server - -```python - tcp_server.start({ - 'pos_rot': RosPublisher('pos_rot', PosRot, queue_size=10), - 'color': RosSubscriber('color', UnityColor, tcp_server), - 'pos_srv': RosService('pos_srv', PositionService), - 'obj_pose_srv': UnityService('obj_pose_srv', ObjectPoseService, tcp_server), - }) - - rospy.spin() -``` - -## Source Destination Dictionary - -The argument to start() is a dictionary keyed by topic or service with the corresponding ROS communication class as the value. The dictionary is used by the TCP server to direct messages to and from the ROS network. - -## ROS Publisher -A ROS Publisher allows a Unity component to send messages on a given topic to other ROS nodes. It requires three components: - -- Topic name -- ROS message class generated from running `catkin_make` command -- Queue size (optional) - -`RosPublisher('pos_rot', PosRot, queue_size=10)` - -## ROS Subscriber -A ROS Subscriber allows a Unity component to receive messages from other ROS nodes on a given topic. It requires three components: - -- Topic name -- ROS message class generated from running `catkin_make` command -- The tcp server that will connect to Unity - -`RosSubscriber('color', UnityColor, tcp_server)` - -## ROS Service -A ROS Service is similar to a RosPublisher, in that a Unity component sends a Request message to another ROS node. Unlike a Publisher, the Unity component then waits for a Response back. It requires two components: - -- Service name -- ROS Service class generated from running `catkin_make` command - -`RosService('pos_srv', PositionService)` - -## Unity Service - -A Unity Service is similar to a RosSubscriber, in that a Unity component receives a Request message from another ROS node. It then sends a Response back. It requires three components: - -- Service name -- ROS Service class generated from running `catkin_make` command -- The tcp server that will connect to Unity - -`UnityService('obj_pose_srv', ObjectPoseService, tcp_server)` - - -## Parameters - -The following parameters can be hardcoded, but for the sake of portability, we recommend setting the parameters using the `rosparam set` command, or a `rosparam` YAML file. - -```python - ros_node_name = rospy.get_param("/TCP_NODE_NAME", 'TCPServer') - buffer_size = rospy.get_param("/TCP_BUFFER_SIZE", 1024) - connections = rospy.get_param("/TCP_CONNECTIONS", 10) -``` - -In addition, the TCPServer class uses the ROS parameters ROS_IP and ROS_TCP_PORT to determine what ip & port to listen on. - -> Note: Read more about the ROS Parameter Server [here](http://wiki.ros.org/Parameter%20Server). - -## Launch File -An example launch file that will set the appropriate ROSPARAM values required for a parameterized TCP Endpoint script. - -``` - - - - - - - - - - - - - - -``` \ No newline at end of file diff --git a/tutorials/ros_unity_integration/service.md b/tutorials/ros_unity_integration/service.md deleted file mode 100644 index 6cb57f69..00000000 --- a/tutorials/ros_unity_integration/service.md +++ /dev/null @@ -1,112 +0,0 @@ -# ROS–Unity Integration: Service - -Create a simple Unity scene which calls a [ROS service](http://wiki.ros.org/Services) with a GameObject's position and rotation to receive a new position to move the GameObject towards. - -## Setting Up ROS - -(Skip to [Start the Position service](service.md#start-the-position-service) if you already did the [ROS–Unity Integration Publisher](publisher.md) or [Subscriber](subscriber.md) tutorials.) - -- Copy the `tutorials/ros_packages/robotics_demo` folder of this repo into the `src` folder in your Catkin workspace. - -- Follow the [ROS–Unity Initial Setup](setup.md) guide. - -- Open a new terminal window, navigate to your ROS workspace, and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo server_endpoint.py - ``` - -Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. - -## Start the Position service -- Open a new terminal window, navigate to your ROS workspace, and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo position_service.py - ``` - -## Setting Up Unity Scene -- Generate the C# code for `PositionService`'s messages by going to `Robotics` -> `Generate ROS Messages...` -- Set the input file path to `PATH/TO/Unity-Robotics-Hub/tutorials/ros_packages/robotics_demo`, expand the robotics_demo folder and click `Build 2 srvs`. - -![](images/generate_messages_2.png) - - - The generated files will be saved in the default directory `Assets/RosMessages/RoboticsDemo/srv`. -- Create a script and name it `RosServiceExample.cs` -- Paste the following code into `RosServiceExample.cs` - - **Note:** This script can be found at `tutorials/ros_unity_integration/unity_scripts`. - -```csharp -using RosMessageTypes.RoboticsDemo; -using UnityEngine; -using Unity.Robotics.ROSTCPConnector; - -public class RosServiceExample : MonoBehaviour -{ - ROSConnection ros; - - public string serviceName = "pos_srv"; - - public GameObject cube; - - // Cube movement conditions - public float delta = 1.0f; - public float speed = 2.0f; - private Vector3 destination; - - float awaitingResponseUntilTimestamp = -1; - - void Start() - { - ros = ROSConnection.instance; - destination = cube.transform.position; - } - - private void Update() - { - // Move our position a step closer to the target. - float step = speed * Time.deltaTime; // calculate distance to move - cube.transform.position = Vector3.MoveTowards(cube.transform.position, destination, step); - - if (Vector3.Distance(cube.transform.position, destination) < delta && Time.time > awaitingResponseUntilTimestamp) - { - Debug.Log("Destination reached."); - - MPosRot cubePos = new MPosRot( - cube.transform.position.x, - cube.transform.position.y, - cube.transform.position.z, - cube.transform.rotation.x, - cube.transform.rotation.y, - cube.transform.rotation.z, - cube.transform.rotation.w - ); - - MPositionServiceRequest positionServiceRequest = new MPositionServiceRequest(cubePos); - - // Send message to ROS and return the response - ros.SendServiceMessage(serviceName, positionServiceRequest, Callback_Destination); - awaitingResponseUntilTimestamp = Time.time+1.0f; // don't send again for 1 second, or until we receive a response - } - } - - void Callback_Destination(MPositionServiceResponse response) - { - awaitingResponseUntilTimestamp = -1; - destination = new Vector3(response.output.pos_x, response.output.pos_y, response.output.pos_z); - Debug.Log("New Destination: " + destination); - } -} -``` - -- From the main menu bar, open `Robotics/ROS Settings`, and change the `ROS IP Address` variable to the ROS IP. -- Create an empty GameObject and name it `RosService`. -- Attach the `RosServiceExample` script to the `RosService` GameObject. Drag the cube GameObject onto its `cube` parameter. -- Pressing play in the Editor should start communication with the `position_service` script, running as a ROS node, causing the cube to move to random positions in the scene. - - -> Please reference [networking troubleshooting](network.md) doc if any errors are thrown. - -![](images/tcp_3.gif) diff --git a/tutorials/ros_unity_integration/service_call.md b/tutorials/ros_unity_integration/service_call.md new file mode 100644 index 00000000..299737b1 --- /dev/null +++ b/tutorials/ros_unity_integration/service_call.md @@ -0,0 +1,103 @@ +# ROS–Unity Integration: Service Call + +Create a simple Unity scene which calls an external [ROS service](http://wiki.ros.org/Services) with a GameObject's position and rotation to receive a new position to move the GameObject towards. + +## Setting Up + +- Follow the [ROS–Unity Demo Setup](setup.md) guide if you haven't already done so. + +## Start the Position service +- For this tutorial we will need a ros service for Unity to call. In a new terminal window, navigate to your ROS workspace. + + a) ros1 In ROS1, run the following commands: + + ```bash + source devel/setup.bash + rosrun unity_robotics_demo position_service.py + ``` + + b) ros2 In ROS2, instead run: + + ```bash + source install/setup.bash + ros2 run unity_robotics_demo position_service + ``` + + +## Create Unity Service Caller +- Create a script and name it `RosServiceCallExample.cs` +- Paste the following code into `RosServiceCallExample.cs` + - (Alternatively, you can drag the script file into Unity from `tutorials/ros_unity_integration/unity_scripts`). + +```csharp +using UnityEngine; +using Unity.Robotics.ROSTCPConnector; +using RosMessageTypes.UnityRoboticsDemo; + +public class RosServiceCallExample : MonoBehaviour +{ + ROSConnection ros; + + public string serviceName = "pos_srv"; + + public GameObject cube; + + // Cube movement conditions + public float delta = 1.0f; + public float speed = 2.0f; + private Vector3 destination; + + float awaitingResponseUntilTimestamp = -1; + + void Start() + { + ros = ROSConnection.instance; + ros.RegisterRosService(serviceName); + destination = cube.transform.position; + } + + private void Update() + { + // Move our position a step closer to the target. + float step = speed * Time.deltaTime; // calculate distance to move + cube.transform.position = Vector3.MoveTowards(cube.transform.position, destination, step); + + if (Vector3.Distance(cube.transform.position, destination) < delta && Time.time > awaitingResponseUntilTimestamp) + { + Debug.Log("Destination reached."); + + PosRotMsg cubePos = new PosRotMsg( + cube.transform.position.x, + cube.transform.position.y, + cube.transform.position.z, + cube.transform.rotation.x, + cube.transform.rotation.y, + cube.transform.rotation.z, + cube.transform.rotation.w + ); + + PositionServiceRequest positionServiceRequest = new PositionServiceRequest(cubePos); + + // Send message to ROS and return the response + ros.SendServiceMessage(serviceName, positionServiceRequest, Callback_Destination); + awaitingResponseUntilTimestamp = Time.time + 1.0f; // don't send again for 1 second, or until we receive a response + } + } + + void Callback_Destination(PositionServiceResponse response) + { + awaitingResponseUntilTimestamp = -1; + destination = new Vector3(response.output.pos_x, response.output.pos_y, response.output.pos_z); + Debug.Log("New Destination: " + destination); + } +} +``` + +- Create an empty GameObject and name it `RosService`. +- Attach the `RosServiceExample` script to the `RosService` GameObject. Drag the cube GameObject onto its `cube` parameter. +- Pressing play in the Editor should start communication with the `position_service` script, running as a ROS node, causing the cube to move to random positions in the scene. + + +> Please reference [networking troubleshooting](network.md) doc if any errors are thrown. + +![](images/tcp_3.gif) diff --git a/tutorials/ros_unity_integration/setup.md b/tutorials/ros_unity_integration/setup.md index bae5a7eb..ec06d393 100644 --- a/tutorials/ros_unity_integration/setup.md +++ b/tutorials/ros_unity_integration/setup.md @@ -1,65 +1,141 @@ -# ROS–Unity Initial Setup +# ROS–Unity Demo Setup -The minimum requirements for a ROS–Unity integration. +This document is in two parts - part 1 covers the minimum requirements for a ROS–Unity integration. Part 2 sets up the Unity Robotics Demo package, which you will need if you're following the ROS–Unity Integration tutorials. -## ROS Environment +These instructions cover both ROS1 and ROS2. The symbols ros1 and ros2 indicate instructions for ROS1 and ROS2 users, respectively. If using ROS2, start with [ROS2 Environment](setup.md#-ros2-environment). -1. Download and copy the [TCP Endpoint](https://github.com/Unity-Technologies/ROS-TCP-Endpoint) package to the `src` folder in your Catkin workspace. +## ros1 ROS Environment -1. Navigate to your Catkin workspace and run `catkin_make && source devel/setup.bash`. Ensure there are no errors. +Follow these steps to use ROS (melodic or noetic): -1. Open a new terminal, navigate to your Catkin workspace, and run: +1. + a) If you don't already have a ROS environment set up, we recommend using Docker. Navigate to `tutorials/ros_unity_integration` in your copy of this repo and run the following commands: ```bash - source devel/setup.bash - roscore & + docker build -t melodic -f ros_docker/Dockerfile . + docker run -it --rm -p 10000:10000 melodic /bin/bash ``` -Once ROS Core has started, it will print `started core service [/rosout]` to the terminal window. + This should build a docker image and start it. -1. Note that in the `server_endpoint`, the script fetches parameters for the TCP connection. You will need to know the IP address of your ROS machine as well as the IP address of the machine running Unity. - - The ROS machine IP, i.e. `ROS_IP` should be the same value as the one set as `Host Name` on the RosConnect component in Unity. -1. The ROS parameter values can be set using a YAML file. Create a `params.yaml` file in your package, e.g. `./config/params.yaml`. Open the file for editing. + b) (Alternative) If you're using your own ROS environment, download and copy the [ROS-TCP-Endpoint](https://github.com/Unity-Technologies/ROS-TCP-Endpoint) package into the `src` folder in your Catkin workspace. Then navigate to your Catkin workspace and run `catkin_make`, then `source devel/setup.bash`. Ensure there are no errors. -1. Update the `ROS_IP` below with the appropriate address and copy the contents into the `params.yaml` file. +2. Open a new terminal, navigate to your Catkin workspace, and run: - ```yaml - ROS_IP: - ROS_TCP_PORT: 10000 - ``` + ```bash + source devel/setup.bash + roscore + ``` + + Once ROS Core has started, it will print `started core service [/rosout]` to the terminal window. - e.g. +3. In your previous terminal, run the following command, replacing the `` with your ROS machine's IP or hostname. - ```yaml - ROS_IP: 127.0.0.1 - ROS_TCP_PORT: 10000 + ```bash + rosparam set ROS_IP ``` - Ensure that the `ROS_TCP_PORT` is set to 10000. + - If you're running ROS in a Docker container, you can just use `rosparam set ROS_IP 0.0.0.0` + - On Linux you can find out your IP address with the command `hostname -I` + - On MacOS you can find out your IP address with `ipconfig getifaddr en0` -1. Set these newly defined parameters by running `rosparam load`, e.g.: +6. (Optional) By default, the server_endpoint will listen on port 10000, but this is also controlled by a parameter. If you need to change it, you can run the command `rosparam set ROS_TCP_PORT 10000`, replacing 10000 with the desired port number. - ```bash - rosparam load PATH/TO/config/params.yaml - ``` - Alternatively, this YAML can be loaded from a launch file, e.g.: +7. Start the server endpoint with the following command: - ```xml - - - - ``` + ```bash + rosrun ros_tcp_endpoint default_server_endpoint.py + ``` + + Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. + +> Note, for this tutorial we have illustrated how to do everything manually, but for day-to-day use, we recommend starting the endpoint with a launch file. Replace all the above (including the roscore step) with the following command: `roslaunch ros_tcp_endpoint endpoint.launch`. +> While using this launch file, your ROS_IP and ROS_TCP_PORT parameters are read from the file src/ros_tcp_endpoint/config/params.yaml. You can edit this file to adjust your settings - for example, this command will set the appropriate IP address for your machine: +> `echo "ROS_IP: $(hostname -i)" > src/ros-tcp-endpoint/config/params.yaml` > Read more about rosparam YAML options [here](http://wiki.ros.org/rosparam). > > Read more about the ROS Parameter Server [here](http://wiki.ros.org/Parameter%20Server). -## Unity Scene -1. Launch Unity and create a new scene. -2. Open Package Manager and click the + button at the top left corner. Select "add package from git URL" and enter "https://github.com/Unity-Technologies/ROS-TCP-Connector.git?path=/com.unity.robotics.ros-tcp-connector" to install the [ROS TCP Connector](https://github.com/Unity-Technologies/ROS-TCP-Connector) package. +## ros2 ROS2 Environment + +Follow these steps if using ROS2: + +1. + a) If you don't already have a ROS2 environment set up, we recommend using Docker. Navigate to `tutorials/ros_unity_integration` in your copy of this repo and run the following commands: + + ```bash + docker build -t foxy -f ros2_docker/Dockerfile . + docker run -it --rm -p 10000:10000 foxy /bin/bash + ``` + + This should build a docker image and start it. + + b) Alternatively, if you're not going to use the Docker image, download the [ROS2 branch of the ROS-TCP-Endpoint](https://github.com/Unity-Technologies/ROS-TCP-Endpoint/tree/ROS2) repository and copy it into the `src` folder in your Colcon workspace. Then navigate to your Colcon workspace and run the following commands: + + ```bash + source install/setup.bash + colcon build + source install/setup.bash + ``` + + Note: yes, you need to run the source command twice. The first sets up the environment for the build to use, the second time adds the newly built packages to the environent. + +2. In your Colcon workspace, run the following command, replacing `` with your ROS machine's IP or hostname. + + ```bash + ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:= + ``` + + - If you're running ROS in a Docker container, 0.0.0.0 is a valid incoming address, so you can write `ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:=0.0.0.0` + - On Linux you can find out your IP address with the command `hostname -I` + - On MacOS you can find out your IP address with `ipconfig getifaddr en0` + + Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. + +3. (Alternative) If you need the server to listen on a port that's different from the default 10000, here's the command line to also set the ROS_TCP_PORT parameter: + + ```bash + ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:=127.0.0.1 -p ROS_TCP_PORT:=10000 + ``` + +## ros2 Unity Setup +1. Launch Unity and create a new project. The Robotics package works best with a version of Unity no older than 2020. +2. Open Package Manager and click the + button at the top left corner. Select "add package from git URL" and enter "https://github.com/Unity-Technologies/ROS-TCP-Connector.git?path=/com.unity.robotics.ros-tcp-connector#dev" to install the [ROS-TCP-Connector](https://github.com/Unity-Technologies/ROS-TCP-Connector) package. + + ![](images/add_package.png) + + ![](images/add_package_2.png) + +3. If you're not using a Docker container, open `Robotics/ROS Settings` from the Unity menu bar, and set the `ROS IP Address` variable to the IP you set earlier. (If you're using Docker, leave it as the default 127.0.0.1.) + + ![](images/settings_ros_ip.png) + +4. ros2 Also in the ROS Settings window, ROS2 users should switch the protocol to ROS2 now. + ![](images/ros2_protocol.png) + +## Install Unity Robotics Demo + +The instructions so far have set up the ROS-TCP-Connector package for general use. If you are specifically following one of the [ROS–Unity Integration tutorials](README.md), you'll need to do the following additional steps: + +1. Copy the `unity_robotics_demo` and `unity_robotics_demo_msgs` packages into the `src` folder in your Catkin workspace. (Skip this step if you're using one of the Dockerfiles from this repo: they have the demo packages pre-installed.) + + - ros1 If using ROS1, copy them from from `tutorials/ros_unity_integration/ros_packages` in this repo. + + - ros2 If using ROS2, copy them from `tutorials/ros_unity_integration/ros2_packages` in this repo. + +1. Build the new packages. + + - ros1 In ROS1: Run `catkin_make`, and then `source devel/setup.bash` (again) so that ROS can find the newly built messages. + + - ros2 In ROS2: run `colcon build`, then `source install/setup.bash` (again) so that ROS can find the newly built messages. + +2. In the Unity menu bar, go to `Robotics` -> `Generate ROS Messages...`. In the Message Browser window, click the Browse button at the top right to set the ROS message path to `tutorials/ros_unity_integration/ros_packages/unity_robotics_demo_msgs` in this repo. + + (Note: The version in the ros2_packages folder is equivalent; ROS2 users can feel free to use it, or not.) -![](images/add_package.png) +3. In the message browser, expand the unity_robotics_demo_msgs subfolder and click "Build 2 msgs" and "Build 2 srvs" to generate C# scripts from the ROS .msg and .srv files. -![](images/add_package_2.png) + ![](images/generate_messages_3.png) -Messages being passed between Unity and ROS need to be serialized exactly as ROS serializes them internally. This is achieved with the RosMessageGeneration utility, which generates C# classes, including serialization and deserialization functions, based on ROS message files. Adding the ROS TCP Connector package should have created a new Unity menu option, “Robotics/Generate ROS Messages”, which we will use to generate these messages later. \ No newline at end of file + The generated files will be saved in the default directories `Assets/RosMessages/UnityRoboticsDemo/msg` and `Assets/RosMessages/UnityRoboticsDemo/srv`. Note, there is no difference between the message scripts generated in ROS1 and ROS2 mode. You don't need to regenerate messages when you switch between them. diff --git a/tutorials/ros_unity_integration/subscriber.md b/tutorials/ros_unity_integration/subscriber.md index 5ae578d6..5efea233 100644 --- a/tutorials/ros_unity_integration/subscriber.md +++ b/tutorials/ros_unity_integration/subscriber.md @@ -2,39 +2,19 @@ Create a simple Unity scene which subscribes to a [ROS topic](http://wiki.ros.org/ROS/Tutorials/UnderstandingTopics#ROS_Topics) to change the colour of a GameObject. -## Setting Up ROS +## Setting Up -(Skip to [Setting Up Unity Scene](subscriber.md#setting-up-unity-scene) if you already did the [Publisher](publisher.md) tutorial.) +- Follow the [ROS–Unity Demo Setup](setup.md) guide if you haven't already done so. -- Copy the `tutorials/ros_packages/robotics_demo` folder of this repo into the `src` folder in your Catkin workspace. +## Create Unity Subscriber -- Follow the [ROS–Unity Initial Setup](setup.md) guide. - -- Open a new terminal window, navigate to your Catkin workspace, and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo server_endpoint.py - ``` - -Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. - -- In Unity, we need to generate the C# code for the `UnityColor` message. Open `Robotics` -> `Generate ROS Messages...`. - - Set the ROS message path to `PATH/TO/Unity-Robotics-Hub/tutorials/ros_packages/robotics_demo/`, expand the robotics_demo subfolder and click `Build 2 msgs`. - -![](images/generate_messages_1.png) - - - The generated files will be saved in the default directory `Assets/RosMessages/RoboticsDemo/msg`. - -## Setting Up Unity Scene -- Create a script and name it `RosSubscriberExample.cs` -- Paste the following code into `RosSubscriberExample.cs` - - **Note** Script can be found at `tutorials/ros_unity_integration/unity_scripts` +- In Unity, create a new C# script and name it `RosSubscriberExample`. Paste the following code into the new script file. + (Alternatively, you can drag the script file into Unity from `tutorials/ros_unity_integration/unity_scripts/RosSubscriberExample.cs`.) ```csharp using UnityEngine; using Unity.Robotics.ROSTCPConnector; -using RosColor = RosMessageTypes.RoboticsDemo.MUnityColor; +using RosColor = RosMessageTypes.UnityRoboticsDemo.UnityColorMsg; public class RosSubscriberExample : MonoBehaviour { @@ -55,14 +35,17 @@ public class RosSubscriberExample : MonoBehaviour - Create an empty GameObject and name it `RosSubscriber` - Attach the `RosSubscriberExample` script to the `RosSubscriber` GameObject and drag the cube GameObject onto the `cube` parameter in the Inspector window. -- From the Unity menu bar, open `Robotics/ROS Settings`, and set the `ROS IP Address` variable to your ROS IP. - Press play in the editor ### In ROS Terminal Window -- After the scene has entered Play mode, run the following command: `rosrun robotics_demo color_publisher.py` to change the color of the cube GameObject in Unity to a random color +Let's send a color message to change the color of the cube GameObject in Unity to a random color. + + a) ros1 In ROS1, run: `rosrun unity_robotics_demo color_publisher.py` + + b) ros2 In ROS2, instead run: `ros2 run unity_robotics_demo color_publisher` > Please reference [networking troubleshooting](network.md) doc if any errors are thrown. ![](images/tcp_2.gif) -Continue to the [ROS–Unity Integration Service](service.md). \ No newline at end of file +Continue to the [ROS–Unity Integration Unity Service](unity_service.md). \ No newline at end of file diff --git a/tutorials/ros_unity_integration/unity_scripts/RosPublisherExample.cs b/tutorials/ros_unity_integration/unity_scripts/RosPublisherExample.cs index 64eeba74..c65452be 100644 --- a/tutorials/ros_unity_integration/unity_scripts/RosPublisherExample.cs +++ b/tutorials/ros_unity_integration/unity_scripts/RosPublisherExample.cs @@ -1,6 +1,6 @@ -using RosMessageTypes.RoboticsDemo; using UnityEngine; using Unity.Robotics.ROSTCPConnector; +using RosMessageTypes.UnityRoboticsDemo; /// /// @@ -22,6 +22,7 @@ void Start() { // start the ROS connection ros = ROSConnection.instance; + ros.RegisterPublisher(topicName); } private void Update() @@ -32,7 +33,7 @@ private void Update() { cube.transform.rotation = Random.rotation; - MPosRot cubePos = new MPosRot( + PosRotMsg cubePos = new PosRotMsg( cube.transform.position.x, cube.transform.position.y, cube.transform.position.z, diff --git a/tutorials/ros_unity_integration/unity_scripts/RosServiceExample.cs b/tutorials/ros_unity_integration/unity_scripts/RosServiceCallExample.cs similarity index 79% rename from tutorials/ros_unity_integration/unity_scripts/RosServiceExample.cs rename to tutorials/ros_unity_integration/unity_scripts/RosServiceCallExample.cs index d2e419b8..ea15ed96 100644 --- a/tutorials/ros_unity_integration/unity_scripts/RosServiceExample.cs +++ b/tutorials/ros_unity_integration/unity_scripts/RosServiceCallExample.cs @@ -1,4 +1,4 @@ -using RosMessageTypes.RoboticsDemo; +using RosMessageTypes.UnityRoboticsDemo; using UnityEngine; using Unity.Robotics.ROSTCPConnector; @@ -20,6 +20,7 @@ public class RosServiceExample : MonoBehaviour void Start() { ros = ROSConnection.instance; + ros.RegisterRosService(serviceName); destination = cube.transform.position; } @@ -33,7 +34,7 @@ private void Update() { Debug.Log("Destination reached."); - MPosRot cubePos = new MPosRot( + PosRotMsg cubePos = new PosRotMsg( cube.transform.position.x, cube.transform.position.y, cube.transform.position.z, @@ -43,15 +44,15 @@ private void Update() cube.transform.rotation.w ); - MPositionServiceRequest positionServiceRequest = new MPositionServiceRequest(cubePos); + PositionServiceRequest positionServiceRequest = new PositionServiceRequest(cubePos); // Send message to ROS and return the response - ros.SendServiceMessage(serviceName, positionServiceRequest, Callback_Destination); + ros.SendServiceMessage(serviceName, positionServiceRequest, Callback_Destination); awaitingResponseUntilTimestamp = Time.time + 1.0f; // don't send again for 1 second, or until we receive a response } } - void Callback_Destination(MPositionServiceResponse response) + void Callback_Destination(PositionServiceResponse response) { awaitingResponseUntilTimestamp = -1; destination = new Vector3(response.output.pos_x, response.output.pos_y, response.output.pos_z); diff --git a/tutorials/ros_unity_integration/unity_scripts/RosSubscriberExample.cs b/tutorials/ros_unity_integration/unity_scripts/RosSubscriberExample.cs index c36a3da3..4e1addd9 100644 --- a/tutorials/ros_unity_integration/unity_scripts/RosSubscriberExample.cs +++ b/tutorials/ros_unity_integration/unity_scripts/RosSubscriberExample.cs @@ -1,6 +1,6 @@ using UnityEngine; using Unity.Robotics.ROSTCPConnector; -using RosColor = RosMessageTypes.RoboticsDemo.MUnityColor; +using RosColor = RosMessageTypes.UnityRoboticsDemo.UnityColorMsg; public class RosSubscriberExample : MonoBehaviour { diff --git a/tutorials/ros_unity_integration/unity_scripts/RosUnityServiceExample.cs b/tutorials/ros_unity_integration/unity_scripts/RosUnityServiceExample.cs index 83efb5b4..76ef4296 100644 --- a/tutorials/ros_unity_integration/unity_scripts/RosUnityServiceExample.cs +++ b/tutorials/ros_unity_integration/unity_scripts/RosUnityServiceExample.cs @@ -1,4 +1,4 @@ -using RosMessageTypes.RoboticsDemo; +using RosMessageTypes.UnityRoboticsDemo; using UnityEngine; using Unity.Robotics.ROSTCPConnector; using Unity.Robotics.ROSTCPConnector.ROSGeometry; @@ -14,7 +14,7 @@ public class RosUnityServiceExample : MonoBehaviour void Start() { // register the service with ROS - ROSConnection.instance.ImplementService(m_ServiceName, GetObjectPose); + ROSConnection.instance.ImplementService(m_ServiceName, GetObjectPose); } /// @@ -22,13 +22,13 @@ void Start() /// /// service request containing the object name /// service response containing the object pose (or 0 if object not found) - private MObjectPoseServiceResponse GetObjectPose(MObjectPoseServiceRequest request) + private ObjectPoseServiceResponse GetObjectPose(ObjectPoseServiceRequest request) { // process the service request Debug.Log("Received request for object: " + request.object_name); // prepare a response - MObjectPoseServiceResponse objectPoseResponse = new MObjectPoseServiceResponse(); + ObjectPoseServiceResponse objectPoseResponse = new ObjectPoseServiceResponse(); // Find a game object with the requested name GameObject gameObject = GameObject.Find(request.object_name); if (gameObject) diff --git a/tutorials/ros_unity_integration/unity_service.md b/tutorials/ros_unity_integration/unity_service.md index 15235896..68821934 100644 --- a/tutorials/ros_unity_integration/unity_service.md +++ b/tutorials/ros_unity_integration/unity_service.md @@ -1,38 +1,18 @@ # ROS–Unity Integration: UnityService -Create a simple Unity scene which create a [Service](http://wiki.ros.org/Services) in Unity that takes a request with a GameObject's name and responds with the GameObject's pose (position and orientation) in the ROS coordinate system. +Create a simple Unity scene which runs a [Service](http://wiki.ros.org/Services) in Unity that takes a request with a GameObject's name and responds with the GameObject's pose (position and orientation) in the ROS coordinate system. -## Setting Up ROS +## Setting Up -(Skip to [Setting Up the Unity Scene](unity_service.md#setting-up-the-unity-scene) if you already did the [ROS–Unity Integration Publisher](publisher.md) or [Subscriber](subscriber.md) tutorials.) - -- Copy the `tutorials/ros_packages/robotics_demo` folder of this repo into the `src` folder in your Catkin workspace. - -- Follow the [ROS–Unity Initial Setup](setup.md) guide. - -- Open a new terminal window, navigate to your ROS workspace, and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo server_endpoint.py - ``` - -Once the server_endpoint has started, it will print something similar to `[INFO] [1603488341.950794]: Starting server on 192.168.50.149:10000`. - -## Setting Up the Unity Scene -- Generate the C# code for `ObjectPoseService`'s messages by going to `Robotics` -> `Generate ROS Messages...` - - Set the input file path to `PATH/TO/Unity-Robotics-Hub/tutorials/ros_packages/robotics_demo`, expand the robotics_demo folder and click `Build 2 srvs` (Note that you may skip this step if you have already done it in the previous tutorial). - - ![](images/generate_messages_2.png) - - - The generated files will be saved in the default directory `Assets/RosMessages/RoboticsDemo/srv`. +- Follow the [ROS–Unity Demo Setup](setup.md) guide if you haven't already done so. +## Create Unity Service - Create a new C# script and name it `RosUnityServiceExample.cs` - Paste the following code into `RosUnityServiceExample.cs` - - **Note:** This script can be found at `tutorials/ros_unity_integration/unity_scripts`. + - (Alternatively, you can drag the script file into Unity from `tutorials/ros_unity_integration/unity_scripts`). ```csharp -using RosMessageTypes.RoboticsDemo; +using RosMessageTypes.UnityRoboticsDemo; using UnityEngine; using Unity.Robotics.ROSTCPConnector; using Unity.Robotics.ROSTCPConnector.ROSGeometry; @@ -48,7 +28,7 @@ public class RosUnityServiceExample : MonoBehaviour void Start() { // register the service with ROS - ROSConnection.instance.ImplementService(m_ServiceName, GetObjectPose); + ROSConnection.instance.ImplementService(m_ServiceName, GetObjectPose); } /// @@ -56,13 +36,13 @@ public class RosUnityServiceExample : MonoBehaviour /// /// service request containing the object name /// service response containing the object pose (or 0 if object not found) - private MObjectPoseServiceResponse GetObjectPose(MObjectPoseServiceRequest request) + private ObjectPoseServiceResponse GetObjectPose(ObjectPoseServiceRequest request) { // process the service request Debug.Log("Received request for object: " + request.object_name); // prepare a response - MObjectPoseServiceResponse objectPoseResponse = new MObjectPoseServiceResponse(); + ObjectPoseServiceResponse objectPoseResponse = new ObjectPoseServiceResponse(); // Find a game object with the requested name GameObject gameObject = GameObject.Find(request.object_name); if (gameObject) @@ -77,41 +57,23 @@ public class RosUnityServiceExample : MonoBehaviour } ``` -- From the main menu bar, open `Robotics/ROS Settings`, and change the `ROS IP Address` variable to the ROS IP. - Create an empty GameObject and name it `UnityService`. - Attach the `RosUnityServiceExample` script to the `UnityService` GameObject. - Pressing play in the Editor should start running as a ROS node, waiting to accept ObjectPose requests. Once a connection to ROS has been established, a message will be printed on the ROS terminal similar to `Connection from 172.17.0.1`. ## Start the Client -- On your ROS system, open a new terminal window, navigate to your ROS workspace, and run the following commands: - - ```bash - source devel/setup.bash - rosrun robotics_demo object_pose_client.py Cube - ``` -- This wil print an output similar to the following with the current pose information of the game object (note that the coordinates are converted to the ROS coordinate system in our Unity Service): - ```bash - Requesting pose for Cube - Pose for Cube: - position: - x: 0.0 - y: -1.0 - z: 0.20000000298023224 - orientation: - x: 0.0 - y: -0.0 - z: 0.0 - w: -1.0 - ``` -You may replace `Cube` with the name of any other GameObject currently present in the Unity hierarchy. +- To test our new service is working, let's call it using the built-in ROS service command. -- Alternatively you may also call the ROS service using `rosservice call`: + a) ros1 In ROS1, run the following command in your ROS terminal: ```bash rosservice call /obj_pose_srv Cube ``` + + In your Unity console you should see the log message `Received request for object: Cube`, and in your terminal it will report the object's position, like this: + ```bash object_pose: position: @@ -123,4 +85,19 @@ You may replace `Cube` with the name of any other GameObject currently present i y: -0.0 z: 0.0 w: -1.0 - ``` \ No newline at end of file + ``` + + b) ros2 If you're using ROS2, the command is: + ```bash + ros2 service call obj_pose_srv unity_robotics_demo_msgs/ObjectPoseService "{object_name: Cube}" + ``` + + And the output will look like this: + + ```bash + requester: making request: unity_robotics_demo_msgs.srv.ObjectPoseService_Request(object_name='Cube') + response: + unity_robotics_demo_msgs.srv.ObjectPoseService_Response(object_pose=geometry_msgs.msg.Pose(position=geometry_msgs.msg.Point(x=0.0, y=-0.0, z=0.0), orientation=geometry_msgs.msg.Quaternion(x=-0.558996319770813, y=-0.3232670724391937, z=-0.6114855408668518, w=-0.4572822153568268))) + ``` + +Continue to the [ROS–Unity Integration Service Call](service_call.md). \ No newline at end of file