Repository: RobotecAI/ros2-for-unity
Branch: develop
Commit: 096e46ccd16a
Files: 59
Total size: 122.3 KB
Directory structure:
gitextract_58lbld25/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.AL2
├── README-UBUNTU.md
├── README-WINDOWS.md
├── README.md
├── build.ps1
├── build.sh
├── create_unity_package.ps1
├── create_unity_package.sh
├── deploy_unity_plugins.ps1
├── deploy_unity_plugins.sh
├── docker/
│ ├── Dockerfile
│ ├── README.md
│ ├── build_image.sh
│ ├── custom_messages/
│ │ └── INSERT_CUSTOM_MESSAGES_HERE
│ ├── entrypoint.sh
│ └── run_container.sh
├── pull_repositories.ps1
├── pull_repositories.sh
├── ros2_for_unity_custom_messages.repos
├── ros2cs.repos
└── src/
├── Ros2ForUnity/
│ ├── COLCON_IGNORE
│ ├── Plugins.meta
│ ├── Scripts/
│ │ ├── PostInstall.cs
│ │ ├── ROS2ClientExample.cs
│ │ ├── ROS2ForUnity.cs
│ │ ├── ROS2ForUnity.cs.meta
│ │ ├── ROS2ListenerExample.cs
│ │ ├── ROS2ListenerExample.cs.meta
│ │ ├── ROS2Node.cs
│ │ ├── ROS2Node.cs.meta
│ │ ├── ROS2PerformanceTest.cs
│ │ ├── ROS2PerformanceTest.cs.meta
│ │ ├── ROS2ServiceExample.cs
│ │ ├── ROS2TalkerExample.cs
│ │ ├── ROS2TalkerExample.cs.meta
│ │ ├── ROS2UnityComponent.cs
│ │ ├── ROS2UnityComponent.cs.meta
│ │ ├── ROS2UnityCore.cs
│ │ ├── ROS2UnityCore.cs.meta
│ │ ├── Sensor.cs
│ │ ├── Sensor.cs.meta
│ │ ├── Time/
│ │ │ ├── DotnetTimeSource.cs
│ │ │ ├── ITimeSource.cs
│ │ │ ├── ROS2Clock.cs
│ │ │ ├── ROS2Clock.cs.meta
│ │ │ ├── ROS2ScalableTimeSource.cs
│ │ │ ├── ROS2TimeSource.cs
│ │ │ ├── TimeUtils.cs
│ │ │ └── UnityTimeSource.cs
│ │ ├── Transformations.cs
│ │ └── Transformations.cs.meta
│ └── Scripts.meta
└── scripts/
└── metadata_generator.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Ubuntu20.04, Windows 10]
- ros2 distro [e.g. foxy, galactic]
- ros2-for-unity version [e.g. 1.1.0, git-sha 6dc898352c9d45996fb0d43e2c8225707e239f4a]
- ros2cs version [e.g. git-sha 2a45785b080fffb3ae5e2f645e976a69698810f0]
- ros2 dds middleware [e.g. cyclonedds, fastdds]
- ros2 environment setup [e.g. single pc, local network]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .gitignore
================================================
install
log
build
.idea
src/ros2cs
**/metadata*.xml
src/Ros2ForUnity/Plugins
!src/Ros2ForUnity/Plugins/.gitkeep
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
office@robotec.ai.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
Any contribution that you make to this repository will
be under the Apache 2 License, as dictated by that
[license](http://www.apache.org/licenses/LICENSE-2.0.html):
> 5. Submission of Contributions. Unless You explicitly state otherwise,
> any Contribution intentionally submitted for inclusion in the Work
> by You to the Licensor shall be under the terms and conditions of
> this License, without any additional terms or conditions.
> Notwithstanding the above, nothing herein shall supersede or modify
> the terms of any separate license agreement you may have executed
> with Licensor regarding such Contributions.
================================================
FILE: LICENSE.AL2
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.
================================================
FILE: README-UBUNTU.md
================================================
# ROS2 For Unity - Ubuntu 20.04 and 22.04
This readme contains information specific to Ubuntu 20.04/22.04. For general information, please see [README.md](README.md)
## Building
We assume that working directory is `~/ros2-for-unity` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).
### Prerequisites
Start with installation of dependencies. Make sure to complete each step of `ros2cs` [Prerequisites section](https://github.com/RobotecAI/ros2cs/blob/master/README-UBUNTU.md#prerequisites).
### Steps
* Clone this project.
```bash
git clone git@github.com:RobotecAI/ros2-for-unity.git ~/ros2-for-unity
```
* You need to source your ROS2 installation before you proceed, for each new open terminal. It is convenient to include this command in your `~/.profile` file.
```bash
# galactic
. /opt/ros/galactic/setup.bash
```
* Enter `Ros2ForUnity` working directory.
```bash
cd ~/ros2-for-unity
```
* Set up you custom messages in `ros2_for_unity_custom_messages.repos`
* Import necessary and custom messages repositories.
```bash
./pull_repositories.sh
```
> *NOTE* `pull_repositories.sh` script doesn't update already existing repositories, you have to remove `src/ros2cs` folder to re-import new versions.
* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.
```bash
# standalone mode
./build.sh --standalone
# overlay mode
./build.sh
```
* You can add `--clean-install` flag to make sure your installation directory is cleaned before deploying.
* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.
* (optionally) To create `.unitypackage` in `install/unity_package`
```bash
create_unity_package.sh -u <your-path-to-unity-editor-executable>
```
> *NOTE* Unity license is required.
## OS-Specific usage remarks
You can run Unity Editor or App executable from GUI (clicking) or from terminal as long as ROS2 is sourced in your environment.
The best way to ensure that system-wide is to add `source /opt/ros/foxy/setup.bash` to your `~/.profile` file.
Note that you need to re-log for changes in `~/.profile` to take place.
Running Unity Editor through Unity Hub is also supported.
## Usage troubleshooting
**No ROS environment sourced. You need to source your ROS2 (..)**
* If you see `"No ROS environment sourced. You need to source your ROS2 (..)"` message in Unity3D Editor, it means your environment was not sourced properly. This could happen if you run Unity but it redirects to Hub and ignores your console environment variables (this behavior can depend on Unity3D version). In such case, run project directly with `-projectPath` or add ros2 sourcing to your `~/.profile` file (you need to re-log for it to take effect).
* Keep in mind that `UnityHub` stays in the background after its first launch and Unity Editor launch without `-projectPath` will redirect to it and the Hub will start Unity Editor. Since environment variables for the process are set on launch and inherited by child processes, your sourced ros2 environment in the console launching the Editor this way won't be applied. To make sure it applies (and to change between different ros2 distributions), make sure to terminate existing UnityHub process and run it with the correct ros2 distribution sourced.
**There are no errors but I can't see topics published by Ros2ForUnity**
* Make sure your dds config is correct.
* Sometimes ROS2 daemon brakes up when changing network interfaces or ROS2 version. Try to stop it forcefully (`pkill -9 _ros2_daemon`) and restart (`ros2 daemon start`).
================================================
FILE: README-WINDOWS.md
================================================
# ROS2 For Unity - Windows 10
This readme contains information specific to Window 10. For general information, please see [README.md](README.md).
## Building
We assume that working directory is `C:\dev` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).
### Prerequisites
It is necessary to complete all the steps for `ros2cs` [Prerequisites](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#prerequisites) and consider [Important notices](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices) sections.
### Steps
* Make sure [long paths on Windows are enabled](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)
* Make sure you open [`Developer PowerShell for VS` with administrator privileges](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)
* For `ros2 galactic` distribution, it is best to [create a `C:\ci\ws\install\include` directory](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)
* Clone this project.
```powershell
git clone git@github.com:RobotecAI/ros2-for-unity.git C:\dev\ros2-for-unity
```
* Source your ROS2 installation (`C:\dev\ros2_foxy\local_setup.ps1`) in the terminal before you proceed.
```
C:\dev\ros2_foxy\local_setup.ps1
```
* Enter `Ros2ForUnity` working directory.
```powershell
cd C:\dev\ros2-for-unity
```
* Set up you custom messages in `ros2_for_unity_custom_messages.repos`
* Import necessary and custom messages repositories.
```powershell
.\pull_repositories.ps1
```
> *NOTE* `pull_repositories.ps1` script doesn't update already existing repositories, you have to remove `src\ros2cs` folder to re-import new versions.
* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.
```powershell
# standalone mode
./build.ps1 -standalone
# overlay mode
./build.ps1
```
* You can build with `-clean_install` to make sure your installation directory is cleaned before deploying.
* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.
* (optionally) To create `.unitypackage` in `install/unity_package`
```powershell
create_unity_package.ps1
```
> *NOTE* Please provide path to your Unity executable when prompted. Unity license is required. In case your Unity license has expired, the `create_unity_package.ps1` won't throw any errors but `Ros2ForUnity.unitypackage` won't be generated too.
## Build troubleshooting
- If you see one of the following errors:
><script_name> is not digitally signed
><script_name> cannot be loaded because running scripts is disabled on this system
Please execute `Set-ExecutionPolicy Bypass -Scope Process` in PS shell session to enable third party scripts execution only for this session. Otherwise please refer to official [Execution Policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.1).
- If you see the following error:
> [4.437s] Traceback (most recent call last):
> [4.437s] File "<string>", line 1, in <module>
> [4.437s] File "C:\Python38\lib\site-packages\numpy\__init__.py", line 148, in <module>
> [4.437s] from . import _distributor_init
> [4.437s] File "C:\Python38\lib\site-packages\numpy\_distributor_init.py", line 26, in <module>
> [4.437s] WinDLL(os.path.abspath(filename))
> [4.437s] File "C:\Python38\lib\ctypes\__init__.py", line 373, in __init__
> [4.453s] self._handle = _dlopen(self._name, mode)
> [4.453s] OSError: [WinError 193] %1 is not a valid Win32 application
> [4.469s] CMake Error at C:/dev/ros2_foxy/share/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake:213 (message)
> [4.469s] execute_process(C:/Python38/python.exe -c 'import
> [4.469s] numpy;print(numpy.get_include())') returned error code 1
> [4.469s] Call Stack (most recent call first):
> [4.469s] C:/dev/ros2_foxy/share/ament_cmake_core/cmake/core/ament_execute_extensions.cmake:48 (include)
> [4.469s] C:/dev/ros2_foxy/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:286 (ament_execute_extensions)
> [4.484s] CMakeLists.txt:16 (rosidl_generate_interfaces)
Please reinstall `numpy` package from python by typing:
```powershell
pip uninstall numpy
pip install numpy
```
**If no solution of your problem is present in the section above, please make sure to check out `ros2cs` [Troubleshooting section](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#troubleshooting)**
## OS-Specific usage remarks
> If the Asset is built with `-standalone` flag (the default), then nothing extra needs to be done.
Otherwise, you have to source your ros distribution before launching either Unity3D Editor or Application.
> Note that after you build the Asset, you can use it on a machine that has no ros2 installation (if built with `-standalone`).
> You can simply copy over the `Ros2ForUnity` subdirectory to update your Asset.
================================================
FILE: README.md
================================================
Ros2 For Unity
===============
> [!NOTE]
> This project is officially supported for [AWSIM](https://github.com/tier4/AWSIM) users of Autoware. However, the Robotec team is unable to provide support and maintain the project for the general
> community. If you are looking for an alternative to Unity3D, [Open 3D Engine (O3DE)](https://o3de.org/) is a great, open-source and free simulation engine with excellent [ROS 2 integration](https://development--o3deorg.netlify.app/docs/user-guide/interactivity/), which Robotec is actively supporting and developing.
ROS2 For Unity is a high-performance communication solution to connect Unity3D and ROS2 ecosystem in a ROS2 "native" way. Communication is not bridged as in several other solutions, but instead it uses ROS2 middleware stack (rcl layer and below), which means you can have ROS2 nodes in your simulation.
Advantages of this module include:
- High performance - higher throughput and considerably lower latencies comparing to bridging solutions.
- Your simulation entities are real ROS2 nodes / publishers / subscribers. They will behave correctly with e.g. command line tools such as `ros2 topic`. They will respect QoS settings and can use ROS2 native time.
- The module supplies abstractions and tools to use in your Unity project, including transformations, sensor interface, a clock, spinning loop wrapped in a MonoBehavior, handling initialization and shutdown.
- Supports all standard ROS2 messages.
- Custom messages are generated automatically with build, using standard ROS2 way. It is straightforward to generate and use them without having to define `.cs` equivalents by hand.
- The module is wrapped as a Unity asset.
## Platforms
Supported OSes:
- Ubuntu 22.04 (bash)
- Ubuntu 20.04 (bash)
- Windows 10 (powershell)
- Windows 11* (powershel)
> \* ROS2 Galactic and Humble support only Windows 10 ([ROS 2 Windows system requirements](https://docs.ros.org/en/humble/Installation/Windows-Install-Binary.html#system-requirements)), but it is proven that it also works fine on Windows 11.
Supported ROS2 distributions:
- Galactic
- Humble
Supported Unity3d:
- 2020+
Older versions of Unity3d may work, but the editor executable most probably won't be detected properly by deployment script. This would require user confirmation for using unsupported version.
This asset can be prepared in two flavours:
- standalone mode, where no ROS2 installation is required on target machine, e.g., your Unity3D simulation server. All required dependencies are installed and can be used e.g. as a complete set of Unity3D plugins.
- overlay mode, where the ROS2 installation is required on target machine. Only asset libraries and generated messages are installed therefore ROS2 instance must be sourced.
## Releases
The best way to start quickly is to use our releases.
You can download pre-built [releases](https://github.com/RobotecAI/ros2-for-unity/releases) of the Asset that support both platforms and specific ros2 and Unity3D versions.
## Building
> **Note:** The project will pull `ros2cs` into the workspace, which also functions independently as it is a more general project aimed at any `C# / .Net` environment.
It has its own README and scripting, but for building the Unity Asset, please use instructions and scripting in this document instead, unless you also wish to run tests or examples for `ros2cs`.
Please see OS-specific instructions:
- [Instructions for Ubuntu](README-UBUNTU.md)
- [Instructions for Windows](README-WINDOWS.md)
## Custom messages
Custom messages can be included in the build by either:
* listing them in `ros2_for_unity_custom_messages.repos` file, or
* manually inserting them in `src/ros2cs` directory. If the folder doesn't exist, you must pull repositories first (see building steps for each OS).
## Installation
1. Perform building steps described in the OS-specific readme or download pre-built Unity package. Do not source `ros2-for-unity` nor `ros2cs` project into ROS2 workspace.
1. Open or create Unity project.
1. Import asset into project:
1. copy `install/asset/Ros2ForUnity` into your project `Assets` folder, or
1. if you have deployed an `.unitypackage` - import it in Unity Editor by selecting `Import Package` → `Custom Package`
## Usage
**Prerequisites**
* If your build was prepared with `--standalone` flag then you are fine, and all you have to do is run the editor
otherwise
* source ROS2 which matches the `Ros2ForUnity` version, then run the editor from within the very same terminal/console.
**Initializing Ros2ForUnity**
1. Initialize `Ros2ForUnity` by creating a "hook" object which will be your wrapper around ROS2. You have two options:
1. `ROS2UnityComponent` based on `MonoBehaviour` which must be attached to a `GameObject` somewhere in the scene, then:
```c#
using ROS2;
...
// Example method of getting component, if ROS2UnityComponent lives in different GameObject, just use different get component methods.
ROS2UnityComponent ros2Unity = GetComponent<ROS2UnityComponent>();
```
1. or `ROS2UnityCore` which is a standard class that can be created anywhere
```c#
using ROS2;
...
ROS2UnityCore ros2Unity = new ROS2UnityCore();
```
1. Create a node. You must first check if `Ros2ForUnity` is initialized correctly:
```c#
private ROS2Node ros2Node;
...
if (ros2Unity.Ok()) {
ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
}
```
**Publishing messages:**
1. Create publisher
```c#
private IPublisher<std_msgs.msg.String> chatter_pub;
...
if (ros2Unity.Ok()){
chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>("chatter");
}
```
1. Send messages
```c#
std_msgs.msg.String msg = new std_msgs.msg.String();
msg.Data = "Hello Ros2ForUnity!";
chatter_pub.Publish(msg);
```
**Subscribing to a topic**
1. Create subscriber:
```c#
private ISubscription<std_msgs.msg.String> chatter_sub;
...
if (ros2Unity.Ok()) {
chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(
"chatter", msg => Debug.Log("Unity listener heard: [" + msg.Data + "]"));
}
```
**Creating a service**
1. Create service body:
```c#
public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)
{
example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();
response.Sum = msg.A + msg.B;
return response;
}
```
1. Create a service with a service name and callback:
```c#
IService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> service =
ros2Node.CreateService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(
"add_two_ints", addTwoInts);
```
**Calling a service**
1. Create a client:
```c#
private IClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> addTwoIntsClient;
...
addTwoIntsClient = ros2Node.CreateClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(
"add_two_ints");
```
1. Create a request and call a service:
```c#
example_interfaces.srv.AddTwoInts_Request request = new example_interfaces.srv.AddTwoInts_Request();
request.A = 1;
request.B = 2;
var response = addTwoIntsClient.Call(request);
```
1. You can also make an async call:
```c#
Task<example_interfaces.srv.AddTwoInts_Response> asyncTask = addTwoIntsClient.CallAsync(request);
...
asyncTask.ContinueWith((task) => { Debug.Log("Got answer " + task.Result.Sum); });
```
### Examples
1. Create a top-level object containing `ROS2UnityComponent.cs`. This is the central `Monobehavior` for `Ros2ForUnity` that manages all the nodes. Refer to class documentation for details.
> **Note:** Each example script looks for `ROS2UnityComponent` in its own game object. However, this is not a requirement, just example implementation.
**Topics**
1. Add `ROS2TalkerExample.cs` script to the very same game object.
1. Add `ROS2ListenerExample.cs` script to the very same game object.
Once you start the project in Unity, you should be able to see two nodes talking with each other in Unity Editor's console or use `ros2 node list` and `ros2 topic echo /chatter` to verify ros2 communication.
**Services**
1. Add `ROS2ServiceExample.cs` script to the very same game object.
1. Add `ROS2ClientExample.cs` script to the very same game object.
Once you start the project in Unity, you should be able to see client node calling an example service.
## Acknowledgements
Open-source release of ROS2 For Unity was made possible through cooperation with [TIER IV](https://tier4.jp). Thanks to encouragement, support and requirements driven by TIER IV the project was significantly improved in terms of portability, stability, core structure and user-friendliness.
================================================
FILE: build.ps1
================================================
<#
.SYNOPSIS
Builds Ros2ForUnity asset
.DESCRIPTION
This script builds Ros2DorUnity asset
.PARAMETER with_tests
Build tests
.PARAMETER standalone
Add ros2 binaries. Currently standalone flag is fixed to true, so there is no way to build without standalone libs. Parameter kept for future releases
.PARAMETER clean_install
Makes a clean installation. Removes install dir before deploying
#>
Param (
[Parameter(Mandatory=$false)][switch]$with_tests=$false,
[Parameter(Mandatory=$false)][switch]$standalone=$false,
[Parameter(Mandatory=$false)][switch]$clean_install=$false
)
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
if(-Not (Test-Path -Path "$scriptPath\src\ros2cs")) {
Write-Host "Pull repositories with 'pull_repositories.ps1' first." -ForegroundColor Red
exit 1
}
Write-Host $msg -ForegroundColor Green
$options = @{
with_tests = $with_tests
standalone = $standalone
}
if($clean_install) {
Write-Host "Cleaning install directory..." -ForegroundColor White
Remove-Item -Path "$scriptPath\install" -Force -Recurse -ErrorAction Ignore
}
if($standalone) {
& "python" $SCRIPTPATH\src\scripts\metadata_generator.py --standalone
} else {
& "python" $SCRIPTPATH\src\scripts\metadata_generator.py
}
& "$scriptPath\src\ros2cs\build.ps1" @options
if($?) {
md -Force $scriptPath\install\asset | Out-Null
Copy-Item -Path $scriptPath\src\Ros2ForUnity -Destination $scriptPath\install\asset\ -Recurse -Force
$plugin_path=Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity\Plugins\"
Write-Host "Deploying build to $plugin_path" -ForegroundColor Green
& "$scriptPath\deploy_unity_plugins.ps1" $plugin_path
Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\Windows\x86_64\
Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\
} else {
Write-Host "Ros2cs build failed!" -ForegroundColor Red
exit 1
}
================================================
FILE: build.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`
display_usage() {
echo "Usage: "
echo ""
echo "build.sh [--with-tests] [--standalone] [--clean-install]"
echo ""
echo "Options:"
echo "--with-tests - build with tests"
echo "--standalone - standalone version"
echo "--clean-install - makes a clean installation, removes install directory before deploying"
}
if [ ! -d "$SCRIPTPATH/src/ros2cs" ]; then
echo "Pull repositories with 'pull_repositories.sh' first."
exit 1
fi
OPTIONS=""
STANDALONE=0
TESTS=0
CLEAN_INSTALL=0
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-t|--with-tests)
OPTIONS="$OPTIONS --with-tests"
TESTS=1
shift # past argument
;;
-s|--standalone)
if ! hash patchelf 2>/dev/null ; then
echo "Patchelf missing. Standalone build requires patchelf. Install it via apt 'sudo apt install patchelf'."
exit 1
fi
OPTIONS="$OPTIONS --standalone"
STANDALONE=1
shift # past argument
;;
-c|--clean-install)
CLEAN_INSTALL=1
shift # past argument
;;
-h|--help)
display_usage
exit 0
shift # past argument
;;
*) # unknown option
shift # past argument
;;
esac
done
if [ $CLEAN_INSTALL == 1 ]; then
echo "Cleaning install directory..."
rm -rf $SCRIPTPATH/install/*
fi
if [ $STANDALONE == 1 ]; then
python3 $SCRIPTPATH/src/scripts/metadata_generator.py --standalone
else
python3 $SCRIPTPATH/src/scripts/metadata_generator.py
fi
if $SCRIPTPATH/src/ros2cs/build.sh $OPTIONS; then
mkdir -p $SCRIPTPATH/install/asset && cp -R $SCRIPTPATH/src/Ros2ForUnity $SCRIPTPATH/install/asset/
$SCRIPTPATH/deploy_unity_plugins.sh $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/Linux/x86_64/metadata_ros2cs.xml
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/metadata_ros2cs.xml
else
echo "Ros2cs build failed!"
exit 1
fi
================================================
FILE: create_unity_package.ps1
================================================
<#
.SYNOPSIS
Creates a 'unitypackage' from an input asset.
.DESCRIPTION
This script screates a temporary Unity project in "%USERPROFILE%\AppData\Local\Temp" directory, copy input asset and makes an unity package out of it. Valid Unity license is required.
.PARAMETER unity_path
Unity editor executable path
.PARAMETER input_asset
input asset to pack into unity package
.PARAMETER package_name
Unity package name
.PARAMETER output_dir
output file directory
#>
Param (
[Parameter(Mandatory=$true)][string]$unity_path,
[Parameter(Mandatory=$false)][string]$input_asset,
[Parameter(Mandatory=$false)][string]$package_name="Ros2ForUnity",
[Parameter(Mandatory=$false)][string]$output_dir
)
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$temp_dir = $Env:TEMP
if(-Not $PSBoundParameters.ContainsKey('input_asset')) {
$input_asset= Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity"
}
if(-Not $PSBoundParameters.ContainsKey('output_dir')) {
$output_dir= Join-Path -Path $scriptPath -ChildPath "\install\unity_package"
}
if(-Not (Test-Path -Path "$input_asset")) {
Write-Host "Input asset '$input_asset' doesn't exist! Use 'build.ps1' to build project first." -ForegroundColor Red
exit 1
}
if(-Not (Test-Path -Path "$output_dir")) {
mkdir ${output_dir} | Out-Null
}
& "$unity_path" -version | Tee-Object -Variable unity_version | Out-Null
if ($unity_version -match '^[0-9]{4}\.[0-9]*\.[0-9]*[f]?[0-9]*$') {
Write-Host "Unity editor confirmed."
} else {
while (1) {
$confirmation = Read-Host "Can't confirm Unity editor. Do you want to force $unity_path as an Unity editor executable? [y]es or [n]o"
if ($confirmation -eq 'y' -or $confirmation -eq 'Y') {
break;
} elseif ( $confirmation -eq 'n' -or $confirmation -eq 'N' ) {
exit 1;
} else {
Write-Host "Please answer [y]es or [n]o.";
}
}
}
Write-Host "Using ${unity_path} editor."
$tmp_project_path = Join-Path -Path "$temp_dir" -ChildPath "\ros2cs_unity_project\$unity_version"
# Create temp project
if(Test-Path -Path "$tmp_project_path") {
Write-Host "Found existing temporary project for Unity $unity_version."
Remove-Item -Path "$tmp_project_path\Assets\*" -Force -Recurse -ErrorAction Ignore
} else {
Write-Host "Creating Unity temporary project for Unity $unity_version..."
& "$unity_path" -createProject "$tmp_project_path" -batchmode -quit | Out-Null
}
# Copy asset
Write-Host "Copying asset '$input_asset' to export..."
Copy-Item -Path "$input_asset" -Destination "$tmp_project_path\Assets\$package_name" -Recurse
# Creating asset
Write-Host "Saving unitypackage '$output_dir\$package_name.unitypackage'..."
& "$unity_path" -projectPath "$tmp_project_path" -exportPackage "Assets\$package_name" "$output_dir\$package_name.unitypackage" -batchmode -quit | Out-Null
# Cleaning up
Write-Host "Cleaning up temporary project..."
Remove-Item -Path "$tmp_project_path\Assets\*" -Force -Recurse -ErrorAction Ignore
Write-Host "Done!" -ForegroundColor Green
================================================
FILE: create_unity_package.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`
display_usage() {
echo "This script creates a temporary Unity project in '/tmp' directory, copy input asset and makes an unity package out of it. Valid Unity license is required."
echo ""
echo "Usage:"
echo "create_unity_package.sh -u <UNITY_PATH> -i [INPUT_ASSET] -p [PACKAGE_NAME] -o [OUTPUT_DIR]"
echo ""
echo "UNITY_PATH - Unity editor executable path"
echo "INPUT_ASSET - input asset to pack into unity package, default = 'install/asset/Ros2ForUnity'"
echo "PACKAGE_NAME - unity package name, default = 'Ros2ForUnity'"
echo "OUTPUT_DIR - output file directory, default = 'install/unity_package'"
}
UNITY_PATH=""
INPUT_ASSET="install/asset/Ros2ForUnity"
PACKAGE_NAME="Ros2ForUnity"
OUTPUT_DIR="$SCRIPTPATH/install/unity_package"
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-u|--unity-path)
UNITY_PATH="$2"
shift # past argument
shift # past value
;;
-p|--package_name)
PACKAGE_NAME="$2"
shift # past argument
shift # past value
;;
-i|--input-directory)
INPUT_ASSET="$2"
shift # past argument
shift # past value
;;
-o|--output-directory)
OUTPUT_DIR="$2"
shift # past argument
shift # past value
;;
-h|--help)
display_usage
exit 0
shift # past argument
;;
*) # unknown option
shift # past argument
;;
esac
done
if [ -z "$UNITY_PATH" ] || [ -z "$PACKAGE_NAME" ] || [ -z "$INPUT_ASSET" ] || [ -z "$OUTPUT_DIR" ]; then
echo -e "\nMissing arguments!"
echo ""
display_usage
exit 1
fi
if [ ! -d "$INPUT_ASSET" ]; then
echo "Input asset '$INPUT_ASSET' doesn't exist! Use 'build.sh' to build project first."
exit 1
fi
UNITY_VERSION=`$UNITY_PATH -version`
# Test if unity editor is valid
if [[ $UNITY_VERSION =~ ^[0-9]{4}\.[0-9]*\.[0-9]*[f]?[0-9]*$ ]]; then
echo "Unity editor confirmed."
else
while true; do
read -p "Can't confirm Unity editor. Do you want to force \"$UNITY_PATH\" as an Unity editor executable? [y]es or [N]o: " yn
yn=${yn:-"n"}
case $yn in
[Yy]* ) break;;
[Nn]* ) exit 1;;
* ) echo "Please answer [y]es or [n]o.";;
esac
done
fi
echo "Using \"${UNITY_PATH}\" editor."
TMP_PROJECT_PATH=/tmp/ros2cs_unity_project/$UNITY_VERSION
# Create temp project
if [ -d "$TMP_PROJECT_PATH" ]; then
echo "Found existing temporary project for Unity $UNITY_VERSION."
rm -rf $TMP_PROJECT_PATH/Assets/*
else
rm -rf $TMP_PROJECT_PATH
echo "Creating Unity temporary project for Unity $UNITY_VERSION..."
$UNITY_PATH -createProject $TMP_PROJECT_PATH -batchmode -quit
fi
# Copy asset
echo "Copying asset to export..."
cp -r "$INPUT_ASSET" "$TMP_PROJECT_PATH/Assets/$PACKAGE_NAME"
# Creating asset
echo "Saving unitypackage '$OUTPUT_DIR/$PACKAGE_NAME.unitypackage'..."
mkdir -p $OUTPUT_DIR
$UNITY_PATH -projectPath "$TMP_PROJECT_PATH" -exportPackage "Assets/$PACKAGE_NAME" "$OUTPUT_DIR/$PACKAGE_NAME.unitypackage" -batchmode -quit
# Cleaning up
echo "Cleaning up temporary project..."
rm -rf $TMP_PROJECT_PATH/Assets/*
echo "Done!"
================================================
FILE: deploy_unity_plugins.ps1
================================================
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$pluginDir=$args[0]
function Print-Help {
"
Usage:
deploy_unity_plugins.ps1 <PLUGINS_DIR>
PLUGINS_DIR - Ros2ForUnity/Plugins.
"
}
if (([string]::IsNullOrEmpty($pluginDir)) -Or $args[0] -eq "--help" -Or $args[0] -eq "-h")
{
Print-Help
exit
}
if (Test-Path -Path $pluginDir) {
Write-Host "Copying plugins to to: '$pluginDir' ..."
Get-ChildItem $scriptPath\install\lib\dotnet\ -Recurse -Exclude @('*.pdb') | Copy-Item -Destination ${pluginDir}
Write-Host "Plugins copied to: '$pluginDir'" -ForegroundColor Green
if(-not (Test-Path -Path $pluginDir\Windows\x86_64\)) {
mkdir ${pluginDir}\Windows\x86_64\
}
Write-Host "Copying libraries to: '$pluginDir\Windows\x86_64\' ..."
Get-ChildItem $scriptPath\install\bin\ -Recurse -Exclude @('*_py.dll', '*_python.dll') | Copy-Item -Destination ${pluginDir}\Windows\x86_64\
if(-not (Test-Path -Path $scriptPath\install\standalone\)) {
mkdir $scriptPath\install\standalone
}
(Copy-Item -Path $scriptPath\install\standalone\*.dll -Destination ${pluginDir}\Windows\x86_64\ 4>&1).Message
if(-not (Test-Path -Path $scriptPath\install\resources\)) {
mkdir $scriptPath\install\resources
}
(Copy-Item -Path $scriptPath\install\resources\*.dll -Destination ${pluginDir}\Windows\x86_64\ 4>&1).Message
Write-Host "Libraries copied to '${pluginDir}\Windows\x86_64\'" -ForegroundColor Green
} else {
Write-Host "Plugins directory: '$pluginDir' doesn't exist. Please create it first manually." -ForegroundColor Red
}
================================================
FILE: deploy_unity_plugins.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`
if [ $# -eq 0 ] || [ $1 = "-h" ] || [ $1 = "--help" ]; then
echo "Usage:"
echo "deploy_unity_plugins.sh <PLUGINS_DIR>"
echo ""
echo "PLUGINS_DIR - Ros2ForUnity/Plugins folder."
exit 1
fi
pluginDir=$1
mkdir -p ${pluginDir}/Linux/x86_64/
find install/lib/dotnet/ -maxdepth 1 -not -name "*.pdb" -type f -exec cp {} ${pluginDir} \;
cp $SCRIPTPATH/install/standalone/* ${pluginDir}/Linux/x86_64/ 2>/dev/null
find install/lib/ -maxdepth 1 -not -name "*_python.so" -type f -exec cp {} ${pluginDir}/Linux/x86_64/ \;
cp $SCRIPTPATH/install/resources/*.so ${pluginDir}/Linux/x86_64/ 2>/dev/null
================================================
FILE: docker/Dockerfile
================================================
ARG ROS2_DISTRO=humble
FROM ros:${ROS2_DISTRO}-ros-base
RUN apt update && apt install -y ros-${ROS_DISTRO}-test-msgs ros-${ROS_DISTRO}-fastrtps ros-${ROS_DISTRO}-rmw-fastrtps-cpp ros-${ROS_DISTRO}-cyclonedds ros-${ROS_DISTRO}-rmw-cyclonedds-cpp
RUN apt update && apt install -y curl wget git
RUN curl -s https://packagecloud.io/install/repositories/dirk-thomas/vcstool/script.deb.sh | sudo bash
RUN apt update && apt install -y python3-vcstool
RUN apt update && apt install -y apt-transport-https patchelf dotnet-sdk-6.0
RUN apt update && apt install -y ffmpeg libsm6 libxext6 libgtk-3-0
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN mkdir -p /workdir/ros2-for-unity
RUN chmod -R 777 /workdir
RUN chmod -R 777 /home
ENTRYPOINT [ "/entrypoint.sh" ]
================================================
FILE: docker/README.md
================================================
Ros2 For Unity Docker
===============
Currently only building asset on Ubuntu is supported. Build windows version is not supported.
## Build docker image
1. Source ROS2 (foxy or galactic):
```bash
. /opt/ros/<ROS_DISTRO>/setup.bash
```
2. Build image - image will be based on sourced ROS2 version:
```bash
./build_image.sh
```
## Using docker container
1. Run docker container. Container will fetch `master` version of `ros2-for-unity`:
```bash
./run_container.sh
```
2. Build asset. `./run_container.sh` script mounts `install` host directory inside docker, so you can find install results on host machine:
```bash
./build.sh --with-tests
```
## Adding custom messages
You can add custom messages by putting them inside `docker/custom_messages` folder or just simply `git clone` them inside docker containers `/workdir/ros2-for-unity/src/ros2cs/src/custom_messages`
================================================
FILE: docker/build_image.sh
================================================
#!/bin/bash
if [ -z "$ROS_DISTRO" ]; then
echo "Source your ros2 distro first."
exit 1
fi
docker build . --build-arg ROS2_DISTRO=$ROS_DISTRO --tag ros2-for-unity
================================================
FILE: docker/custom_messages/INSERT_CUSTOM_MESSAGES_HERE
================================================
================================================
FILE: docker/entrypoint.sh
================================================
#!/bin/bash
source "/opt/ros/$ROS_DISTRO/setup.bash"
echo "######################################################################"
echo ""
echo "Cloning recent version of 'ros2-for-unity'"
echo ""
echo "######################################################################"
echo ""
git clone https://github.com/RobotecAI/ros2-for-unity.git /workdir/.ros2-for-unity
shopt -s dotglob
mkdir -p /workdir/ros2-for-unity
mv /workdir/.ros2-for-unity/* /workdir/ros2-for-unity
cd /workdir/ros2-for-unity/ && ./pull_repositories.sh
mkdir -p /home/$(whoami)
git config --global --add safe.directory /workdir/ros2-for-unity
shopt -u dotglob
ln -s /workdir/custom_messages /workdir/ros2-for-unity/src/ros2cs/src/custom_messages
echo ""
echo "######################################################################"
echo ""
echo "Welcome to 'ros2-for-unity' docker container. Your ROS2 distro is $ROS_DISTRO."
echo ""
echo "Type './build.sh' to build 'ros2-for-unity'. You will find installed libs on your host machine inside 'install' directory"
echo ""
echo "######################################################################"
echo ""
exec bash
================================================
FILE: docker/run_container.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`
mkdir -p $SCRIPTPATH/../install
docker run \
--rm \
-it \
--name ros2-for-unity \
--user $(id -u):$(id -g) \
-v /etc/passwd:/etc/passwd:ro \
-v /etc/group:/etc/group:ro \
-v /etc/shadow:/etc/shadow:ro \
-v $(pwd)/../install:/workdir/ros2-for-unity/install:rw \
-v $(pwd)/custom_messages:/workdir/custom_messages \
ros2-for-unity \
bash
================================================
FILE: pull_repositories.ps1
================================================
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
if (([string]::IsNullOrEmpty($Env:ROS_DISTRO)))
{
Write-Host "Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported." -ForegroundColor Red
exit
}
$ros2cs_repos = Join-Path -Path $scriptPath -ChildPath "\ros2cs.repos"
$custom_repos = Join-Path -Path $scriptPath -ChildPath "\ros2_for_unity_custom_messages.repos"
Write-Host "========================================="
Write-Host "* Pulling ros2cs repository:"
vcs import --input $ros2cs_repos
Write-Host ""
Write-Host "========================================="
Write-Host "Pulling custom repositories:"
vcs import --input $custom_repos
Write-Host ""
Write-Host "========================================="
Write-Host "Pulling ros2cs dependencies:"
& "$scriptPath/src/ros2cs/get_repos.ps1"
================================================
FILE: pull_repositories.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`
if [ -z "${ROS_DISTRO}" ]; then
echo "Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported"
exit 1
fi
echo "========================================="
echo "* Pulling ros2cs repository:"
vcs import < "ros2cs.repos"
echo ""
echo "========================================="
echo "Pulling custom repositories:"
vcs import < "ros2_for_unity_custom_messages.repos"
echo ""
echo "========================================="
echo "Pulling ros2cs dependencies:"
cd "$SCRIPTPATH/src/ros2cs"
./get_repos.sh
cd -
================================================
FILE: ros2_for_unity_custom_messages.repos
================================================
# NOTE: Use this file if you want to build with custom messages that reside in a separate remote repo.
# NOTE: use the following format
repositories:
# src/ros2cs/custom_messages/<package_name>:
# type: git
# url: <repo_url>
# version: <repo_branch>
# custom_messages/<package2_name>:
# ...
# ...
================================================
FILE: ros2cs.repos
================================================
repositories:
src/ros2cs/:
type: git
url: https://github.com/RobotecAI/ros2cs.git
version: 1.3.0
================================================
FILE: src/Ros2ForUnity/COLCON_IGNORE
================================================
================================================
FILE: src/Ros2ForUnity/Plugins.meta
================================================
fileFormatVersion: 2
guid: 79b46a636d96b67468a29e597cb7b06a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/PostInstall.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// 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.
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
namespace ROS2
{
/// <summary>
/// An internal class responsible for installing ros2-for-unity metadata files
/// </summary>
internal class PostInstall : IPostprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPostprocessBuild(BuildReport report)
{
var r2fuMetadataName = "metadata_ros2_for_unity.xml";
var r2csMetadataName = "metadata_ros2cs.xml";
// FileUtil.CopyFileOrDirectory: All file separators should be forward ones "/".
var r2fuMeta = ROS2ForUnity.GetRos2ForUnityPath() + "/" + r2fuMetadataName;
var r2csMeta = ROS2ForUnity.GetPluginPath() + "/" + r2csMetadataName;
var outputDir = Directory.GetParent(report.summary.outputPath);
var execFilename = Path.GetFileNameWithoutExtension(report.summary.outputPath);
FileUtil.CopyFileOrDirectory(
r2fuMeta, outputDir + "/" + execFilename + "_Data/" + r2fuMetadataName);
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinux64) {
FileUtil.CopyFileOrDirectory(
r2csMeta, outputDir + "/" + execFilename + "_Data/Plugins/" + r2csMetadataName);
// Copy versioned libraries (Unity skips them)
Regex soWithVersionReg = new Regex(@".*\.so(\.[0-9])+$");
var versionedLibs = new List<String>(Directory.GetFiles(ROS2ForUnity.GetPluginPath()))
.Where(path => soWithVersionReg.IsMatch(path))
.ToList();
foreach (var libPath in versionedLibs) {
FileUtil.CopyFileOrDirectory(
libPath, outputDir + "/" + execFilename + "_Data/Plugins/" + Path.GetFileName(libPath));
}
} else {
FileUtil.CopyFileOrDirectory(
r2csMeta, outputDir + "/" + execFilename + "_Data/Plugins/x86_64/" + r2csMetadataName);
}
}
}
}
#endif
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ClientExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using ROS2;
using addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;
using addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;
/// <summary>
/// An example class provided for testing of basic ROS2 client
/// </summary>
public class ROS2ClientExample : MonoBehaviour
{
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IClient<addTwoIntsReq, addTwoIntsResp> addTwoIntsClient;
private bool isRunning = false;
private Task<addTwoIntsResp> asyncTask;
IEnumerator periodicAsyncCall()
{
while (ros2Unity.Ok())
{
while (!addTwoIntsClient.IsServiceAvailable())
{
yield return new WaitForSecondsRealtime(1);
}
addTwoIntsReq request = new addTwoIntsReq();
request.A = Random.Range(0, 100);
request.B = Random.Range(0, 100);
asyncTask = addTwoIntsClient.CallAsync(request);
asyncTask.ContinueWith((task) => { Debug.Log("Got async answer " + task.Result.Sum); });
yield return new WaitForSecondsRealtime(1);
}
}
IEnumerator periodicCall()
{
while (ros2Unity.Ok())
{
while (!addTwoIntsClient.IsServiceAvailable())
{
yield return new WaitForSecondsRealtime(1);
}
addTwoIntsReq request = new addTwoIntsReq();
request.A = Random.Range(0, 100);
request.B = Random.Range(0, 100);
var response = addTwoIntsClient.Call(request);
Debug.Log("Got sync answer " + response.Sum);
yield return new WaitForSecondsRealtime(1);
}
}
void Start()
{
ros2Unity = GetComponent<ROS2UnityComponent>();
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
ros2Node = ros2Unity.CreateNode("ROS2UnityClient");
addTwoIntsClient = ros2Node.CreateClient<addTwoIntsReq, addTwoIntsResp>(
"add_two_ints");
}
}
}
void Update()
{
if (!isRunning)
{
isRunning = true;
// Async calls
StartCoroutine(periodicAsyncCall());
// Sync calls
StartCoroutine(periodicCall());
}
}
}
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Xml;
namespace ROS2
{
/// <summary>
/// An internal class responsible for handling checking, proper initialization and shutdown of ROS2cs,
/// </summary>
internal class ROS2ForUnity
{
private static bool isInitialized = false;
private static string ros2ForUnityAssetFolderName = "Ros2ForUnity";
private XmlDocument ros2csMetadata = new XmlDocument();
private XmlDocument ros2ForUnityMetadata = new XmlDocument();
public enum Platform
{
Windows,
Linux
}
public static Platform GetOS()
{
if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer)
{
return Platform.Linux;
}
else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
{
return Platform.Windows;
}
throw new System.NotSupportedException("Only Linux and Windows are supported");
}
private static bool InEditor() {
return Application.isEditor;
}
private static string GetOSName()
{
switch (GetOS())
{
case Platform.Linux:
return "Linux";
case Platform.Windows:
return "Windows";
default:
throw new System.NotSupportedException("Only Linux and Windows are supported");
}
}
private string GetEnvPathVariableName()
{
string envVariable = "LD_LIBRARY_PATH";
if (GetOS() == Platform.Windows)
{
envVariable = "PATH";
}
return envVariable;
}
private string GetEnvPathVariableValue()
{
return Environment.GetEnvironmentVariable(GetEnvPathVariableName());
}
public static string GetRos2ForUnityPath()
{
char separator = Path.DirectorySeparatorChar;
string appDataPath = Application.dataPath;
string pluginPath = appDataPath;
if (InEditor()) {
pluginPath += separator + ros2ForUnityAssetFolderName;
}
return pluginPath;
}
public static string GetPluginPath()
{
char separator = Path.DirectorySeparatorChar;
string ros2ForUnityPath = GetRos2ForUnityPath();
string pluginPath = ros2ForUnityPath;
pluginPath += separator + "Plugins";
if (InEditor()) {
pluginPath += separator + GetOSName();
}
if (InEditor() || GetOS() == Platform.Windows)
{
pluginPath += separator + "x86_64";
}
if (GetOS() == Platform.Windows)
{
pluginPath = pluginPath.Replace("/", "\\");
}
return pluginPath;
}
/// <summary>
/// Function responsible for setting up of environment paths for standalone builds
/// </summary>
/// <description>
/// Note that on Linux, LD_LIBRARY_PATH as used for dlopen() is determined on process start and this change won't
/// affect it. Ros2 looks for rmw implementation based on this variable (independently) and the change
/// is effective for this process, however rmw implementation's dependencies itself are loaded by dynamic linker
/// anyway so setting it for Linux is pointless.
/// </description>
private void SetEnvPathVariable()
{
string currentPath = GetEnvPathVariableValue();
string pluginPath = GetPluginPath();
char envPathSep = ':';
if (GetOS() == Platform.Windows)
{
envPathSep = ';';
}
Environment.SetEnvironmentVariable(GetEnvPathVariableName(), pluginPath + envPathSep + currentPath);
}
public bool IsStandalone() {
return Convert.ToBoolean(Convert.ToInt16(GetMetadataValue(ros2csMetadata, "/ros2cs/standalone")));
}
public string GetROSVersion()
{
string ros2SourcedCodename = GetROSVersionSourced();
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");
// Sourced ROS2 libs takes priority
if (string.IsNullOrEmpty(ros2SourcedCodename)) {
return ros2FromRos4UMetadata;
}
return ros2SourcedCodename;
}
/// <summary>
/// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as
/// the current sourced ros version matches ros2cs binaries.
/// </summary>
public void CheckIntegrity()
{
string ros2SourcedCodename = GetROSVersionSourced();
string ros2FromRos2csMetadata = GetMetadataValue(ros2csMetadata, "/ros2cs/ros2");
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");
if (ros2FromRos4UMetadata != ros2FromRos2csMetadata) {
Debug.LogError(
"ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. " +
"This is caused by mixing versions/builds. Plugin might not work correctly."
);
}
if(!IsStandalone() && ros2SourcedCodename != ros2FromRos2csMetadata) {
Debug.LogError(
"ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. " +
"This is caused by mixing versions/builds. Plugin might not work correctly."
);
}
if (IsStandalone() && !string.IsNullOrEmpty(ros2SourcedCodename)) {
Debug.LogError(
"You should not source ROS2 in 'ros2-for-unity' standalone build. " +
"Plugin might not work correctly."
);
}
}
public string GetROSVersionSourced()
{
return Environment.GetEnvironmentVariable("ROS_DISTRO");
}
/// <summary>
/// Check if the ros version is supported, only applicable to non-standalone plugin versions
/// (i. e. without ros2 libraries included in the plugin).
/// </summary>
private void CheckROSSupport(string ros2Codename)
{
List<string> supportedVersions = new List<string>() { "foxy", "galactic", "humble", "rolling" };
var supportedVersionsString = String.Join(", ", supportedVersions);
if (string.IsNullOrEmpty(ros2Codename))
{
string errMessage = "No ROS environment sourced. You need to source your ROS2 " + supportedVersionsString
+ " environment before launching Unity (ROS_DISTRO env variable not found)";
Debug.LogError(errMessage);
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
throw new System.InvalidOperationException(errMessage);
#else
const int ROS_NOT_SOURCED_ERROR_CODE = 33;
Application.Quit(ROS_NOT_SOURCED_ERROR_CODE);
#endif
}
if (!supportedVersions.Contains(ros2Codename))
{
string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + ros2Codename
+ ", supported: " + supportedVersionsString + ".";
Debug.LogError(errMessage);
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
throw new System.NotSupportedException(errMessage);
#else
const int ROS_BAD_VERSION_CODE = 34;
Application.Quit(ROS_BAD_VERSION_CODE);
#endif
} else if (ros2Codename.Equals("rolling") ) {
Debug.LogWarning("You are using ROS2 rolling version. Bleeding edge version might not work correctly.");
}
}
private void RegisterCtrlCHandler()
{
#if ENABLE_MONO
// Il2CPP build does not support Console.CancelKeyPress currently
Console.CancelKeyPress += (sender, eventArgs) => {
eventArgs.Cancel = true;
DestroyROS2ForUnity();
};
#endif
}
private void ConnectLoggers()
{
Ros2csLogger.setCallback(LogLevel.ERROR, Debug.LogError);
Ros2csLogger.setCallback(LogLevel.WARNING, Debug.LogWarning);
Ros2csLogger.setCallback(LogLevel.INFO, Debug.Log);
Ros2csLogger.setCallback(LogLevel.DEBUG, Debug.Log);
Ros2csLogger.LogLevel = LogLevel.WARNING;
}
private string GetMetadataValue(XmlDocument doc, string valuePath)
{
return doc.DocumentElement.SelectSingleNode(valuePath).InnerText;
}
private void LoadMetadata()
{
char separator = Path.DirectorySeparatorChar;
try
{
ros2csMetadata.Load(GetPluginPath() + separator + "metadata_ros2cs.xml");
ros2ForUnityMetadata.Load(GetRos2ForUnityPath() + separator + "metadata_ros2_for_unity.xml");
}
catch (System.IO.FileNotFoundException)
{
#if UNITY_EDITOR
var errMessage = "Could not find metadata files.";
EditorApplication.isPlaying = false;
throw new System.IO.FileNotFoundException(errMessage);
#else
const int NO_METADATA = 1;
Application.Quit(NO_METADATA);
#endif
}
}
internal ROS2ForUnity()
{
// Load metadata
LoadMetadata();
string currentRos2Version = GetROSVersion();
string standalone = IsStandalone() ? "standalone" : "non-standalone";
// Self checks
CheckROSSupport(currentRos2Version);
CheckIntegrity();
// Library loading
if (GetOS() == Platform.Windows) {
// Windows version can run standalone, modifies PATH to ensure all plugins visibility
SetEnvPathVariable();
} else {
// For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.
ROS2.GlobalVariables.absolutePath = GetPluginPath() + "/";
if (currentRos2Version == "foxy") {
ROS2.GlobalVariables.preloadLibrary = true;
ROS2.GlobalVariables.preloadLibraryName = "librcpputils.so";
}
}
// Initialize
ConnectLoggers();
Ros2cs.Init();
RegisterCtrlCHandler();
string rmwImpl = Ros2cs.GetRMWImplementation();
Debug.Log("ROS2 version: " + currentRos2Version + ". Build type: " + standalone + ". RMW: " + rmwImpl);
#if UNITY_EDITOR
EditorApplication.playModeStateChanged += this.EditorPlayStateChanged;
EditorApplication.quitting += this.DestroyROS2ForUnity;
#endif
isInitialized = true;
}
private static void ThrowIfUninitialized(string callContext)
{
if (!isInitialized)
{
throw new InvalidOperationException("Ros2 For Unity is not initialized, can't " + callContext);
}
}
/// <summary>
/// Check if ROS2 module is properly initialized and no shutdown was called yet
/// </summary>
/// <returns>The state of ROS2 module. Should be checked before attempting to create or use pubs/subs</returns>
public bool Ok()
{
if (!isInitialized)
{
return false;
}
return Ros2cs.Ok();
}
internal void DestroyROS2ForUnity()
{
if (isInitialized)
{
Debug.Log("Shutting down Ros2 For Unity");
Ros2cs.Shutdown();
isInitialized = false;
}
}
~ROS2ForUnity()
{
DestroyROS2ForUnity();
}
#if UNITY_EDITOR
void EditorPlayStateChanged(PlayModeStateChange change)
{
if (change == PlayModeStateChange.ExitingPlayMode)
{
DestroyROS2ForUnity();
}
}
#endif
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs.meta
================================================
fileFormatVersion: 2
guid: 4cdb4e72fb0aa46c09e52778257ed142
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using System;
using UnityEngine;
namespace ROS2
{
/// <summary>
/// An example class provided for testing of basic ROS2 communication
/// </summary>
public class ROS2ListenerExample : MonoBehaviour
{
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private ISubscription<std_msgs.msg.String> chatter_sub;
void Start()
{
ros2Unity = GetComponent<ROS2UnityComponent>();
}
void Update()
{
if (ros2Node == null && ros2Unity.Ok())
{
ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(
"chatter", msg => Debug.Log("Unity listener heard: [" + msg.Data + "]"));
}
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs.meta
================================================
fileFormatVersion: 2
guid: 75a1bd43b302c4c578a744060319517e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace ROS2
{
/// <summary>
/// A class representing a ros2 node. Multiple nodes can be used. Node can be removed by GC when not used anymore,
/// but will also be removed properly with Ros2cs Shutdown, which ROS2 for Unity performs on application quit
/// The node should be constructed through ROS2UnityComponent class, which also handles spinning
/// </summary>
public class ROS2Node
{
internal INode node;
public ROS2Clock clock;
public string name;
// Use ROS2UnityComponent to create a node
internal ROS2Node(string unityROS2NodeName = "unity_ros2_node")
{
name = unityROS2NodeName;
node = Ros2cs.CreateNode(name);
clock = new ROS2Clock();
}
~ROS2Node()
{
Ros2cs.RemoveNode(node);
}
private static void ThrowIfUninitialized(string callContext)
{
if (!Ros2cs.Ok())
{
throw new InvalidOperationException("Ros2 For Unity is not initialized, can't " + callContext);
}
}
/// <summary>
/// Create a publisher with QoS suitable for sensor data
/// </summary>
/// <returns>The publisher</returns>
/// <param name="topicName">topic that will be used for publishing</param>
public Publisher<T> CreateSensorPublisher<T>(string topicName) where T : Message, new()
{
QualityOfServiceProfile sensorProfile = new QualityOfServiceProfile(QosPresetProfile.SENSOR_DATA);
return CreatePublisher<T>(topicName, sensorProfile);
}
/// <summary>
/// Create a publisher with indicated QoS.
/// </summary>
/// <returns>The publisher</returns>
/// <param name="topicName">topic that will be used for publishing</param>
/// <param name="qos">QoS for publishing. If no QoS is selected, it will default to reliable, keep 10 last</param>
public Publisher<T> CreatePublisher<T>(string topicName, QualityOfServiceProfile qos = null) where T : Message, new()
{
ThrowIfUninitialized("create publisher");
return node.CreatePublisher<T>(topicName, qos);
}
/// <summary>
/// Create a subscription
/// </summary>
/// <returns>The subscription</returns>
/// <param name="topicName">topic to subscribe to</param>
/// <param name="qos">QoS for subscription. If no QoS is selected, it will default to reliable, keep 10 last</param>
public Subscription<T> CreateSubscription<T>(string topicName, Action<T> callback,
QualityOfServiceProfile qos = null) where T : Message, new()
{
if (qos == null)
{
qos = new QualityOfServiceProfile(QosPresetProfile.DEFAULT);
}
ThrowIfUninitialized("create subscription");
return node.CreateSubscription<T>(topicName, callback, qos);
}
/// <summary>
/// Remove existing subscription (returned earlier with CreateSubscription)
/// </summary>
/// <returns>The whether subscription was found (e. g. false if removed earlier elsewhere) </returns>
/// <param name="subscription">subscrition to remove, returned from CreateSubscription</param>
public bool RemoveSubscription<T>(ISubscriptionBase subscription)
{
ThrowIfUninitialized("remove subscription");
return node.RemoveSubscription(subscription);
}
/// <summary>
/// Remove existing publisher
/// </summary>
/// <returns>The whether publisher was found (e. g. false if removed earlier elsewhere) </returns>
/// <param name="publisher">publisher to remove, returned from CreatePublisher or CreateSensorPublisher</param>
public bool RemovePublisher<T>(IPublisherBase publisher)
{
ThrowIfUninitialized("remove publisher");
return node.RemovePublisher(publisher);
}
/// <inheritdoc cref="INode.CreateService"/>
public Service<I, O> CreateService<I, O>(string topic, Func<I, O> callback, QualityOfServiceProfile qos = null)
where I : Message, new()
where O : Message, new()
{
ThrowIfUninitialized("create service");
return node.CreateService<I, O>(topic, callback, qos);
}
/// <inheritdoc cref="INode.RemoveService"/>
public bool RemoveService(IServiceBase service)
{
ThrowIfUninitialized("remove service");
return node.RemoveService(service);
}
/// <inheritdoc cref="INode.CreateClient"/>
public Client<I, O> CreateClient<I, O>(string topic, QualityOfServiceProfile qos = null)
where I : Message, new()
where O : Message, new()
{
ThrowIfUninitialized(callContext: "create client");
return node.CreateClient<I, O>(topic, qos);
}
/// <inheritdoc cref="INode.RemoveClient"/>
public bool RemoveClient(IClientBase client)
{
ThrowIfUninitialized(callContext: "remove client");
return node.RemoveClient(client);
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs.meta
================================================
fileFormatVersion: 2
guid: 3e21db77b82bbeb8693eabe308d76f45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using UnityEngine;
using System.Threading;
namespace ROS2
{
/// <summary>
/// An example class provided for performance testing of ROS2 communication
/// </summary>
public class ROS2PerformanceTest : MonoBehaviour
{
public int messageSize = 10000;
public int rate = 10;
private int interval_ms = 100;
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IPublisher<sensor_msgs.msg.PointCloud2> perf_pub;
sensor_msgs.msg.PointCloud2 msg;
private bool initialized = false;
void Start()
{
ros2Unity = GetComponent<ROS2UnityComponent>();
PrepMessage();
}
void OnValidate()
{
if (rate < 1)
{
interval_ms = 0;
}
else
{
interval_ms = 1000 / rate;
}
PrepMessage();
}
private void Publish()
{
while(true)
{
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
ros2Node = ros2Unity.CreateNode("ros2_unity_performance_test_node");
perf_pub = ros2Node.CreateSensorPublisher<sensor_msgs.msg.PointCloud2>("perf_chatter");
}
var msgWithHeader = msg as MessageWithHeader;
ros2Node.clock.UpdateROSTimestamp(ref msgWithHeader);
perf_pub.Publish(msg);
if (interval_ms > 0)
{
Thread.Sleep(interval_ms);
}
}
}
}
void FixedUpdate()
{
if (!initialized)
{
Thread publishThread = new Thread(() => Publish());
publishThread.Start();
initialized = true;
}
}
private void AssignField(ref sensor_msgs.msg.PointField pf, string n, uint off, byte dt, uint count)
{
pf.Name = n;
pf.Offset = off;
pf.Datatype = dt;
pf.Count = count;
}
private void PrepMessage()
{
uint count = (uint)messageSize; //point per message
uint fieldsSize = 16;
uint rowSize = count * fieldsSize;
msg = new sensor_msgs.msg.PointCloud2()
{
Height = 1,
Width = count,
Is_bigendian = false,
Is_dense = true,
Point_step = fieldsSize,
Row_step = rowSize,
Data = new byte[rowSize * 1]
};
uint pointFieldCount = 4;
msg.Fields = new sensor_msgs.msg.PointField[pointFieldCount];
for (int i = 0; i < pointFieldCount; ++i)
{
msg.Fields[i] = new sensor_msgs.msg.PointField();
}
AssignField(ref msg.Fields[0], "x", 0, 7, 1);
AssignField(ref msg.Fields[1], "y", 4, 7, 1);
AssignField(ref msg.Fields[2], "z", 8, 7, 1);
AssignField(ref msg.Fields[3], "intensity", 12, 7, 1);
float[] pointsArray = new float[count * msg.Fields.Length];
var floatIndex = 0;
for (int i = 0; i < count; ++i)
{
float intensity = 100;
pointsArray[floatIndex++] = 1;
pointsArray[floatIndex++] = 2;
pointsArray[floatIndex++] = 3;
pointsArray[floatIndex++] = intensity;
}
System.Buffer.BlockCopy(pointsArray, 0, msg.Data, 0, msg.Data.Length);
msg.SetHeaderFrame("pc");
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs.meta
================================================
fileFormatVersion: 2
guid: 387d300b788c9bd29b6e38808a481155
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ROS2;
using addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;
using addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;
/// <summary>
/// An example class provided for testing of basic ROS2 service
/// </summary>
public class ROS2ServiceExample : MonoBehaviour
{
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IService<addTwoIntsReq, addTwoIntsResp> addTwoIntsService;
void Start()
{
ros2Unity = GetComponent<ROS2UnityComponent>();
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
ros2Node = ros2Unity.CreateNode("ROS2UnityService");
addTwoIntsService = ros2Node.CreateService<addTwoIntsReq, addTwoIntsResp>(
"add_two_ints", addTwoInts);
}
}
}
public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)
{
Debug.Log("Incoming Service Request A=" + msg.A + " B=" + msg.B);
example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();
response.Sum = msg.A + msg.B;
return response;
}
}
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using UnityEngine;
namespace ROS2
{
/// <summary>
/// An example class provided for testing of basic ROS2 communication
/// </summary>
public class ROS2TalkerExample : MonoBehaviour
{
// Start is called before the first frame update
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IPublisher<std_msgs.msg.String> chatter_pub;
private int i;
void Start()
{
ros2Unity = GetComponent<ROS2UnityComponent>();
}
void Update()
{
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
ros2Node = ros2Unity.CreateNode("ROS2UnityTalkerNode");
chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>("chatter");
}
i++;
std_msgs.msg.String msg = new std_msgs.msg.String();
msg.Data = "Unity ROS2 sending: hello " + i;
chatter_pub.Publish(msg);
}
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs.meta
================================================
fileFormatVersion: 2
guid: 72620fb0a9290863f8643557405c48e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Threading;
using ROS2;
namespace ROS2
{
/// <summary>
/// The principal MonoBehaviour class for handling ros2 nodes and executables.
/// Use this to create ros2 node, check ros2 status.
/// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread
/// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work
/// anyway with more than one since the underlying library can handle multiple init and shutdown calls,
/// and does node name uniqueness check independently.
/// </summary>
public class ROS2UnityComponent : MonoBehaviour
{
private ROS2ForUnity ros2forUnity;
private List<ROS2Node> nodes;
private List<INode> ros2csNodes; // For performance in spinning
private List<Action> executableActions;
private bool initialized = false;
private bool quitting = false;
private int interval = 2; // Spinning / executor interval in ms
private object mutex = new object();
private double spinTimeout = 0.0001;
public bool Ok()
{
lock (mutex)
{
if (ros2forUnity == null)
LazyConstruct();
return (nodes != null && ros2forUnity.Ok());
}
}
private void LazyConstruct()
{
lock (mutex)
{
if (ros2forUnity != null)
return;
ros2forUnity = new ROS2ForUnity();
nodes = new List<ROS2Node>();
ros2csNodes = new List<INode>();
executableActions = new List<Action>();
}
}
void Start()
{
LazyConstruct();
}
public ROS2Node CreateNode(string name)
{
LazyConstruct();
lock (mutex)
{
foreach (ROS2Node n in nodes)
{ // Assumed to be a rare operation on rather small (<1k) list
if (n.name == name)
{
throw new InvalidOperationException("Cannot create node " + name + ". A node with this name already exists!");
}
}
ROS2Node node = new ROS2Node(name);
nodes.Add(node);
ros2csNodes.Add(node.node);
return node;
}
}
public void RemoveNode(ROS2Node node)
{
lock (mutex)
{
ros2csNodes.Remove(node.node);
nodes.Remove(node); //Node will be later deleted if unused, by GC
}
}
/// <summary>
/// Works as a simple executor registration analogue. These functions will be called with each Tick()
/// Actions need to take care of correct call resolution by checking in their body (TODO)
/// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)
/// </summary>
public void RegisterExecutable(Action executable)
{
LazyConstruct();
lock (mutex)
{
executableActions.Add(executable);
}
}
public void UnregisterExecutable(Action executable)
{
lock (mutex)
{
executableActions.Remove(executable);
}
}
/// <summary>
/// "Executor" thread will tick all clocks and spin the node
/// </summary>
private void Tick()
{
while (!quitting)
{
if (Ok())
{
lock (mutex)
{
foreach (Action action in executableActions)
{
action();
}
Ros2cs.SpinOnce(ros2csNodes, spinTimeout);
}
}
Thread.Sleep(interval);
}
}
void FixedUpdate()
{
if (!initialized)
{
Thread publishThread = new Thread(() => Tick());
publishThread.Start();
initialized = true;
}
}
void OnApplicationQuit()
{
quitting = true;
ros2forUnity.DestroyROS2ForUnity();
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs.meta
================================================
fileFormatVersion: 2
guid: feab04ad06492965492b3edc6423aa53
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// 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.
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Threading;
using ROS2;
namespace ROS2
{
/// <summary>
/// The principal class for handling ros2 nodes and executables.
/// Use this to create ros2 node, check ros2 status.
/// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread
/// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work
/// anyway with more than one since the underlying library can handle multiple init and shutdown calls,
/// and does node name uniqueness check independently.
/// </summary>
public class ROS2UnityCore
{
private ROS2ForUnity ros2forUnity;
private List<ROS2Node> nodes;
private List<INode> ros2csNodes; // For performance in spinning
private List<Action> executableActions;
private bool quitting = false;
private int interval = 2; // Spinning / executor interval in ms
private object mutex = new object();
private double spinTimeout = 0.0001;
public bool Ok()
{
lock (mutex)
{
return (nodes != null && ros2forUnity.Ok());
}
}
public ROS2UnityCore()
{
lock (mutex)
{
ros2forUnity = new ROS2ForUnity();
nodes = new List<ROS2Node>();
ros2csNodes = new List<INode>();
executableActions = new List<Action>();
Thread publishThread = new Thread(() => Tick());
publishThread.Start();
}
}
public ROS2Node CreateNode(string name)
{
lock (mutex)
{
foreach (ROS2Node n in nodes)
{ // Assumed to be a rare operation on rather small (<1k) list
if (n.name == name)
{
throw new InvalidOperationException("Cannot create node " + name + ". A node with this name already exists!");
}
}
ROS2Node node = new ROS2Node(name);
nodes.Add(node);
ros2csNodes.Add(node.node);
return node;
}
}
public void RemoveNode(ROS2Node node)
{
lock (mutex)
{
ros2csNodes.Remove(node.node);
nodes.Remove(node); //Node will be later deleted if unused, by GC
}
}
/// <summary>
/// Works as a simple executor registration analogue. These functions will be called with each Tick()
/// Actions need to take care of correct call resolution by checking in their body (TODO)
/// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)
/// </summary>
public void RegisterExecutable(Action executable)
{
lock (mutex)
{
executableActions.Add(executable);
}
}
public void UnregisterExecutable(Action executable)
{
lock (mutex)
{
executableActions.Remove(executable);
}
}
/// <summary>
/// "Executor" thread will tick all clocks and spin the node
/// </summary>
private void Tick()
{
while (!quitting)
{
if (Ok())
{
lock (mutex)
{
foreach (Action action in executableActions)
{
action();
}
Ros2cs.SpinOnce(ros2csNodes, spinTimeout);
}
}
Thread.Sleep(interval);
}
}
public void DestroyNow()
{
quitting = true;
ros2forUnity.DestroyROS2ForUnity();
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs.meta
================================================
fileFormatVersion: 2
guid: d80a8dd00d331ce458b98a7707d03bb3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/Sensor.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using UnityEngine;
using UnityEngine.Profiling;
using System;
namespace ROS2
{
/// <summary>
/// An abstract base class for ROS2-enabled sensor.
/// </summary>
public abstract class ISensor : MonoBehaviour
{
/// <summary>
/// The desired update frequency for the sensor. The maximum can be the rate with which FixedUpdate is called,
/// which depends on the physics step (usually 50 or 100 times per second).
/// </summary>
public double desiredUpdateFreq = 25.0;
/// <summary>
/// The frameID corresponds to the ROS frame_id element of the header and is important
/// for transformations
/// </summary>
public string frameID = "sensor";
/// <summary>
/// A topic to which the sensor publishes. Only one per sensor. Don't add the namespace of
/// the agent name, it is handled externally (i.e. sensor does not know to what object it belongs).
/// </summary>
public string topicName = "";
/// <summary>
/// Controls whether sensor is publishing messages
/// </summary>
public bool publishing = false;
/// <summary>
/// Creates sensor publishers and registers it in the executor so that it publishes when new data is available
/// </summary>
/// <param name="ros2Unity"> Central ros2 monobehavior for Unity </param>
/// <param name="node"> ros2 node that will publish sensor data </param>
/// <param name="agentName"> name of the agent (vehicle) to be added to the sensor publish namespace </param>
public abstract void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName);
/// <summary>
/// Returns the constructed frame name, taking in account the agent name(space)
/// </summary>
public abstract string frameName();
}
/// <summary>
/// A base template class for the sensor. The type is the message type of sensor data.
/// </summary>
public abstract class Sensor<T> : ISensor where T : MessageWithHeader, new()
{
/// <summary>
/// Acquires the value by performing sensor type characteristic computations (e.g. raycasts).
/// Implemented in subclasses.
/// </summary>
/// <returns>The message which contains the sensor data.
/// Mind that the header for message is handled in a generic way by this class.</returns>
protected abstract T AcquireValue();
/// <summary>
/// Returns true when there is a new data available from sensor.
/// </summary>
protected abstract bool HasNewData();
protected double desiredFrameTime = 0.0;
private const double minimumFrequency = 0.001;
private Publisher<T> publisher;
private Subscription<rosgraph_msgs.msg.Clock> clockSubscriber;
private ROS2UnityComponent ros2UnityComponent;
private ROS2Node ros2Node;
private string ownerAgentName;
private double lastTimestamp;
private double timeSinceLastFixedUpdate;
private T readings;
private bool newReadings;
public override string frameName()
{
return ownerAgentName + "/" + frameID;
}
/// <summary>
/// Visualises the effects of the sensor. It doesn't make sense for some sensor and the
/// default implementation is empty.
/// </summary>
protected virtual void VisualiseEffects()
{
}
/// <summary>
/// When parameters in editor change (i.e. frequency),
/// this function is called to calculate new frame time.
/// </summary>
protected virtual void OnValidate()
{
CalculateFrameTime();
}
/// <summary>
/// An entry point for the per-frame processing done in subclass
/// </summary>
protected virtual void OnUpdate() {}
/// <summary>
/// See superclass definition
/// </summary>
public override void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName)
{
if (!ros2Unity.Ok())
{
throw new System.InvalidOperationException("Publisher for sensor can't be created when node is not OK");
}
if (String.IsNullOrEmpty(topicName))
{
throw new System.InvalidOperationException("Topic name not set for the sensor " + this);
}
ownerAgentName = agentName;
ros2UnityComponent = ros2Unity;
ros2Node = node;
string nsName = agentName.Replace(" ", "_");
publisher = node.CreateSensorPublisher<T>(nsName + "/" + topicName);
ros2UnityComponent.RegisterExecutable(ExecutorThreadSensorPublishAction);
publishing = true;
}
/// <summary>
/// This is executed in an executor thread (through RegisterExecutable)
/// Sensor fequency is indirectly handed through newReadings, which are acquired at a requested
/// frequency if possible (e. g. due to simulation resource constraints)
/// </summary>
internal void ExecutorThreadSensorPublishAction()
{
if (!HasNewData())
return;
if (publisher != null & publishing)
{
if (ros2UnityComponent.Ok())
{
readings = AcquireValue();
readings.SetHeaderFrame(frameName());
if (readings != null)
{
MessageWithHeader readingsHeader = readings as MessageWithHeader;
ros2Node.clock.UpdateROSTimestamp(ref readingsHeader);
publisher.Publish(readings);
}
}
}
}
/// <summary>
/// Once each frame, visualise effects of the sensor (if any). Visualisation
/// rate is independent of publishing/acquisition rate, which happen at the sensor
/// frequency instead of the app frame rate.
/// </summary>
void Update()
{
VisualiseEffects();
OnUpdate();
}
/// <summary>
/// Initialize header and calculate frame time
/// </summary>
void Awake()
{
// turn on publishing on start
publishing = true;
CalculateFrameTime();
lastTimestamp = DateTime.UtcNow.Ticks / 1E7;
}
/// <summary>
/// Sensor frequency is used to calculate frame time, based on desired frequency and the bounds.
/// </summary>
void CalculateFrameTime()
{
double maxFrameFreq = 1.0 / Time.fixedDeltaTime;
if (desiredUpdateFreq > maxFrameFreq)
{
Debug.LogWarning("Desired frame rate of " + desiredUpdateFreq + " can't be met, "
+ "physics frequency is " + maxFrameFreq);
desiredUpdateFreq = maxFrameFreq; //Can't go faster than physics
}
if (desiredUpdateFreq < minimumFrequency)
{
Debug.LogWarning("Minimum frequency of " + minimumFrequency
+ " applied instead of " + desiredUpdateFreq);
desiredUpdateFreq = minimumFrequency;
}
desiredFrameTime = 1.0 / desiredUpdateFreq;
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Sensor.cs.meta
================================================
fileFormatVersion: 2
guid: d3ebd1e57dae48cca9c88c51f18cf510
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
using System;
using System.Diagnostics;
namespace ROS2
{
/// <summary>
/// DateTime based clock that has resolution increased using Stopwatch.
/// DateTime is used to synchronize since Stopwatch tends to drift.
/// </summary>
public class DotnetTimeSource : ITimeSource
{
private readonly double maxUnsyncedSeconds = 10;
private Stopwatch stopwatch = new Stopwatch();
private readonly object mutex = new object();
private double systemTimeIntervalStart = 0;
private double stopwatchStartTimeStamp;
private double TotalSystemTimeSeconds()
{
return TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds;
}
private void UpdateSystemTime()
{
systemTimeIntervalStart = TotalSystemTimeSeconds();
stopwatchStartTimeStamp = Stopwatch.GetTimestamp();
}
public DotnetTimeSource()
{
UpdateSystemTime();
}
public void GetTime(out int seconds, out uint nanoseconds)
{
lock(mutex) // Threading
{
double endTimestamp = Stopwatch.GetTimestamp();
var durationInSeconds = endTimestamp - stopwatchStartTimeStamp;
double timeOffset = 0;
if (durationInSeconds >= maxUnsyncedSeconds)
{ // acquire DateTime to sync
UpdateSystemTime();
}
else
{ // use Stopwatch offset
timeOffset = durationInSeconds;
}
TimeUtils.TimeFromTotalSeconds(systemTimeIntervalStart + timeOffset, out seconds, out nanoseconds);
}
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/ITimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
namespace ROS2
{
/// <summary>
/// Interace for acquiring time
/// </summary>
public interface ITimeSource
{
public void GetTime(out int seconds, out uint nanoseconds);
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// 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.
using System;
using UnityEngine;
namespace ROS2
{
/// <summary>
/// A ros2 clock class that for interfacing between a time source (unity or ros2 system time) and ros2cs messages, structs.
/// </summary>
public class ROS2Clock
{
private ITimeSource _timeSource;
public ROS2Clock() : this(new ROS2TimeSource())
{ // By default, use ROS2TimeSource
}
public ROS2Clock(ITimeSource ts)
{
_timeSource = ts;
}
public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)
{
int seconds;
uint nanoseconds;
_timeSource.GetTime(out seconds, out nanoseconds);
clockMessage.Clock_.Sec = seconds;
clockMessage.Clock_.Nanosec = nanoseconds;
}
public void UpdateROSClockTime(builtin_interfaces.msg.Time time)
{
int seconds;
uint nanoseconds;
_timeSource.GetTime(out seconds, out nanoseconds);
time.Sec = seconds;
time.Nanosec = nanoseconds;
}
public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)
{
int seconds;
uint nanoseconds;
_timeSource.GetTime(out seconds, out nanoseconds);
message.UpdateHeaderTime(seconds, nanoseconds);
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs.meta
================================================
fileFormatVersion: 2
guid: 69e097a4a027d5a55b991c0b0b1bdf88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
using System.Threading;
using UnityEngine;
namespace ROS2
{
/// <summary>
/// ros2 time source (system time by default).
/// </summary>
public class ROS2ScalableTimeSource : ITimeSource
{
private Thread mainThread;
private double lastReadingSecs;
private ROS2.Clock clock;
private double initialTime = 0;
private double initialTimeScale = 0;
private bool initialTimeAcquired = false;
private bool initialTimeScaleAcquired = false;
private bool timeScaleChanged = false;
public ROS2ScalableTimeSource()
{
mainThread = Thread.CurrentThread;
}
public void GetTime(out int seconds, out uint nanoseconds)
{
if (!ROS2.Ros2cs.Ok())
{
seconds = 0;
nanoseconds = 0;
Debug.LogWarning("Cannot acquire valid ros time, ros either not initialized or shut down already");
return;
}
if (clock == null)
{ // Create clock which uses system time by default (unless use_sim_time is set in ros2)
clock = new ROS2.Clock();
}
if (!initialTimeScaleAcquired)
{
initialTimeScaleAcquired = true;
initialTimeScale = Time.timeScale;
}
if (initialTimeScale != Time.timeScale)
{
timeScaleChanged = true;
}
lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;
if (initialTimeScale == 1.0 && !timeScaleChanged)
{
TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);
}
else
{
if (!initialTimeAcquired)
{
initialTimeAcquired = true;
initialTime = clock.Now.Seconds - Time.timeAsDouble;
}
TimeUtils.TimeFromTotalSeconds(lastReadingSecs + initialTime, out seconds, out nanoseconds);
}
}
~ROS2ScalableTimeSource()
{
if (clock != null)
{
clock.Dispose();
}
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
using UnityEngine;
namespace ROS2
{
/// <summary>
/// ros2 time source (system time by default).
/// </summary>
public class ROS2TimeSource : ITimeSource
{
private ROS2.Clock clock;
public void GetTime(out int seconds, out uint nanoseconds)
{
if (!ROS2.Ros2cs.Ok())
{
seconds = 0;
nanoseconds = 0;
Debug.LogWarning("Cannot acquire valid ros time, ros either not initialized or shut down already");
return;
}
if (clock == null)
{ // Create clock which uses system time by default (unless use_sim_time is set in ros2)
clock = new ROS2.Clock();
}
TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);
}
~ROS2TimeSource()
{
if (clock != null)
{
clock.Dispose();
}
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/TimeUtils.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
namespace ROS2
{
/// <summary>
/// Interace for acquiring time
/// </summary>
internal static class TimeUtils
{
public static void TimeFromTotalSeconds(in double secondsIn, out int seconds, out uint nanoseconds)
{
long nanosec = (long)(secondsIn * 1e9);
seconds = (int)(nanosec / 1000000000);
nanoseconds = (uint)(nanosec % 1000000000);
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// 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.
using System;
using System.Threading;
using UnityEngine;
namespace ROS2
{
/// <summary>
/// Acquires Unity time. Note that Time API only allows main thread access,
/// but this class object also stores last acquired value for other threads.
/// This is done without a warning, so the class will not behave as expected
/// when not used by main thread.
/// </summary>
public class UnityTimeSource : ITimeSource
{
private Thread mainThread;
private double lastReadingSecs;
public UnityTimeSource()
{
mainThread = Thread.CurrentThread;
}
public void GetTime(out int seconds, out uint nanoseconds)
{
lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;
TimeUtils.TimeFromTotalSeconds(lastReadingSecs, out seconds, out nanoseconds);
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Transformations.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// 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.
using UnityEngine;
namespace ROS2
{
/// <summary>
/// A set of transformation functions between coordinate systems of Unity and ROS
/// </summary>
public static class Transformations
{
public static Vector3 Ros2Unity(this Vector3 vector3)
{
return new Vector3(-vector3.y, vector3.z, vector3.x);
}
public static Vector3 Unity2Ros(this Vector3 vector3)
{
return new Vector3(vector3.z, -vector3.x, vector3.y);
}
public static Vector3 Ros2UnityScale(this Vector3 vector3)
{
return new Vector3(vector3.y, vector3.z, vector3.x);
}
public static Vector3 Unity2RosScale(this Vector3 vector3)
{
return new Vector3(vector3.z, vector3.x, vector3.y);
}
public static Quaternion Ros2Unity(this Quaternion quaternion)
{
return new Quaternion(quaternion.y, -quaternion.z, -quaternion.x, quaternion.w);
}
public static Quaternion Unity2Ros(this Quaternion quaternion)
{
return new Quaternion(-quaternion.z, quaternion.x, -quaternion.y, quaternion.w);
}
public static void Unity2Ros(ref Quaternion quaternion)
{
var z = quaternion.z;
var x = quaternion.x;
var y = quaternion.y;
quaternion.x = -z;
quaternion.y = x;
quaternion.z = -y;
}
public static void Unity2Ros(ref Vector3 vector)
{
var z = vector.z;
var x = vector.x;
var y = vector.y;
vector.x = z;
vector.y = -x;
vector.z = y;
}
public static Matrix4x4 Unity2RosMatrix4x4()
{
// Note: The matrix here is written as-if on paper,
// but Unity's Matrix4x4 is constructed from column-vectors, hence the transpose.
return new Matrix4x4(
new Vector4( 0.0f, 0.0f, 1.0f, 0.0f),
new Vector4(-1.0f, 0.0f, 0.0f, 0.0f),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f)
).transpose;
}
}
} // namespace ROS2
================================================
FILE: src/Ros2ForUnity/Scripts/Transformations.cs.meta
================================================
fileFormatVersion: 2
guid: 5ab7c5f5cc85e9e3aa6134862d9c1cba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/Ros2ForUnity/Scripts.meta
================================================
fileFormatVersion: 2
guid: f750980d49c8bcf39830e89365689d16
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: src/scripts/metadata_generator.py
================================================
# Copyright 2019-2022 Robotec.ai.
#
# 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.
import argparse
import xml.etree.ElementTree as ET
from xml.dom import minidom
import subprocess
import pathlib
import os
parser = argparse.ArgumentParser(description='Generate metadata file for ros2-for-unity.')
parser.add_argument('--standalone', action='store_true', help='is a standalone build')
args = parser.parse_args()
def get_git_commit(working_directory) -> str:
return subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=working_directory).decode('ascii').strip()
def get_git_description(working_directory) -> str:
return subprocess.check_output(['git', 'describe', '--tags', '--always'], cwd=working_directory).decode('ascii').strip()
def get_commit_date(working_directory) -> str:
return subprocess.check_output(['git', 'show', '-s', '--format=%ci'], cwd=working_directory).decode('ascii').strip()
def get_git_abbrev(working_directory) -> str:
return subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=working_directory).decode('ascii').strip()
def get_ros2_for_unity_root_path() -> pathlib.Path:
return pathlib.Path(__file__).parents[2]
def get_ros2_for_unity_path() -> pathlib.Path:
return pathlib.Path(__file__).parents[1].joinpath("Ros2ForUnity")
def get_ros2cs_path() -> pathlib.Path:
return pathlib.Path(__file__).parents[1].joinpath("ros2cs")
def get_ros2_path() -> pathlib.Path:
return get_ros2cs_path().joinpath("src").joinpath("ros2").joinpath("rcl_interfaces")
def get_ros2_version() -> str:
return os.environ.get("ROS_DISTRO", "unknown")
ros2_for_unity = ET.Element("ros2_for_unity")
ET.SubElement(ros2_for_unity, "ros2").text = get_ros2_version()
ros2_for_unity_version = ET.SubElement(ros2_for_unity, "version")
ET.SubElement(ros2_for_unity_version, "sha").text = get_git_commit(get_ros2_for_unity_root_path())
ET.SubElement(ros2_for_unity_version, "desc").text = get_git_description(get_ros2_for_unity_root_path())
ET.SubElement(ros2_for_unity_version, "date").text = get_commit_date(get_ros2_for_unity_root_path())
ros2_cs = ET.Element("ros2cs")
ET.SubElement(ros2_cs, "ros2").text = get_ros2_version()
ros2_cs_version = ET.SubElement(ros2_cs, "version")
ET.SubElement(ros2_cs_version, "sha").text = get_git_commit(get_ros2cs_path())
ET.SubElement(ros2_cs_version, "desc").text = get_git_description(get_ros2cs_path())
ET.SubElement(ros2_cs_version, "date").text = get_commit_date(get_ros2cs_path())
ET.SubElement(ros2_cs, "standalone").text = str(int(args.standalone))
rf2u_xmlstr = minidom.parseString(ET.tostring(ros2_for_unity)).toprettyxml(indent=" ")
metadata_rf2u_file = get_ros2_for_unity_path().joinpath("metadata_ros2_for_unity.xml")
with open(str(metadata_rf2u_file), "w") as f:
f.write(rf2u_xmlstr)
r2cs_xmlstr = minidom.parseString(ET.tostring(ros2_cs)).toprettyxml(indent=" ")
metadata_r2cs_file = get_ros2_for_unity_path().joinpath("metadata_ros2cs.xml")
with open(str(metadata_r2cs_file), "w") as f:
f.write(r2cs_xmlstr)
gitextract_58lbld25/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.AL2
├── README-UBUNTU.md
├── README-WINDOWS.md
├── README.md
├── build.ps1
├── build.sh
├── create_unity_package.ps1
├── create_unity_package.sh
├── deploy_unity_plugins.ps1
├── deploy_unity_plugins.sh
├── docker/
│ ├── Dockerfile
│ ├── README.md
│ ├── build_image.sh
│ ├── custom_messages/
│ │ └── INSERT_CUSTOM_MESSAGES_HERE
│ ├── entrypoint.sh
│ └── run_container.sh
├── pull_repositories.ps1
├── pull_repositories.sh
├── ros2_for_unity_custom_messages.repos
├── ros2cs.repos
└── src/
├── Ros2ForUnity/
│ ├── COLCON_IGNORE
│ ├── Plugins.meta
│ ├── Scripts/
│ │ ├── PostInstall.cs
│ │ ├── ROS2ClientExample.cs
│ │ ├── ROS2ForUnity.cs
│ │ ├── ROS2ForUnity.cs.meta
│ │ ├── ROS2ListenerExample.cs
│ │ ├── ROS2ListenerExample.cs.meta
│ │ ├── ROS2Node.cs
│ │ ├── ROS2Node.cs.meta
│ │ ├── ROS2PerformanceTest.cs
│ │ ├── ROS2PerformanceTest.cs.meta
│ │ ├── ROS2ServiceExample.cs
│ │ ├── ROS2TalkerExample.cs
│ │ ├── ROS2TalkerExample.cs.meta
│ │ ├── ROS2UnityComponent.cs
│ │ ├── ROS2UnityComponent.cs.meta
│ │ ├── ROS2UnityCore.cs
│ │ ├── ROS2UnityCore.cs.meta
│ │ ├── Sensor.cs
│ │ ├── Sensor.cs.meta
│ │ ├── Time/
│ │ │ ├── DotnetTimeSource.cs
│ │ │ ├── ITimeSource.cs
│ │ │ ├── ROS2Clock.cs
│ │ │ ├── ROS2Clock.cs.meta
│ │ │ ├── ROS2ScalableTimeSource.cs
│ │ │ ├── ROS2TimeSource.cs
│ │ │ ├── TimeUtils.cs
│ │ │ └── UnityTimeSource.cs
│ │ ├── Transformations.cs
│ │ └── Transformations.cs.meta
│ └── Scripts.meta
└── scripts/
└── metadata_generator.py
SYMBOL INDEX (136 symbols across 20 files)
FILE: src/Ros2ForUnity/Scripts/PostInstall.cs
class PostInstall (line 32) | internal class PostInstall : IPostprocessBuildWithReport
method OnPostprocessBuild (line 35) | public void OnPostprocessBuild(BuildReport report)
FILE: src/Ros2ForUnity/Scripts/ROS2ClientExample.cs
class ROS2ClientExample (line 27) | public class ROS2ClientExample : MonoBehaviour
method periodicAsyncCall (line 35) | IEnumerator periodicAsyncCall()
method periodicCall (line 56) | IEnumerator periodicCall()
method Start (line 77) | void Start()
method Update (line 91) | void Update()
FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs
class ROS2ForUnity (line 28) | internal class ROS2ForUnity
type Platform (line 35) | public enum Platform
method GetOS (line 41) | public static Platform GetOS()
method InEditor (line 54) | private static bool InEditor() {
method GetOSName (line 58) | private static string GetOSName()
method GetEnvPathVariableName (line 71) | private string GetEnvPathVariableName()
method GetEnvPathVariableValue (line 81) | private string GetEnvPathVariableValue()
method GetRos2ForUnityPath (line 86) | public static string GetRos2ForUnityPath()
method GetPluginPath (line 98) | public static string GetPluginPath()
method SetEnvPathVariable (line 132) | private void SetEnvPathVariable()
method IsStandalone (line 146) | public bool IsStandalone() {
method GetROSVersion (line 150) | public string GetROSVersion()
method CheckIntegrity (line 167) | public void CheckIntegrity()
method GetROSVersionSourced (line 195) | public string GetROSVersionSourced()
method CheckROSSupport (line 204) | private void CheckROSSupport(string ros2Codename)
method RegisterCtrlCHandler (line 239) | private void RegisterCtrlCHandler()
method ConnectLoggers (line 250) | private void ConnectLoggers()
method GetMetadataValue (line 259) | private string GetMetadataValue(XmlDocument doc, string valuePath)
method LoadMetadata (line 264) | private void LoadMetadata()
method ROS2ForUnity (line 285) | internal ROS2ForUnity()
method ThrowIfUninitialized (line 325) | private static void ThrowIfUninitialized(string callContext)
method Ok (line 337) | public bool Ok()
method DestroyROS2ForUnity (line 346) | internal void DestroyROS2ForUnity()
method EditorPlayStateChanged (line 362) | void EditorPlayStateChanged(PlayModeStateChange change)
FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs
class ROS2ListenerExample (line 24) | public class ROS2ListenerExample : MonoBehaviour
method Start (line 30) | void Start()
method Update (line 35) | void Update()
FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs
class ROS2Node (line 28) | public class ROS2Node
method ROS2Node (line 35) | internal ROS2Node(string unityROS2NodeName = "unity_ros2_node")
method ThrowIfUninitialized (line 47) | private static void ThrowIfUninitialized(string callContext)
method CreateSensorPublisher (line 60) | public Publisher<T> CreateSensorPublisher<T>(string topicName) where T...
method CreatePublisher (line 72) | public Publisher<T> CreatePublisher<T>(string topicName, QualityOfServ...
method CreateSubscription (line 84) | public Subscription<T> CreateSubscription<T>(string topicName, Action<...
method RemoveSubscription (line 101) | public bool RemoveSubscription<T>(ISubscriptionBase subscription)
method RemovePublisher (line 112) | public bool RemovePublisher<T>(IPublisherBase publisher)
method CreateService (line 119) | public Service<I, O> CreateService<I, O>(string topic, Func<I, O> call...
method RemoveService (line 128) | public bool RemoveService(IServiceBase service)
method CreateClient (line 135) | public Client<I, O> CreateClient<I, O>(string topic, QualityOfServiceP...
method RemoveClient (line 144) | public bool RemoveClient(IClientBase client)
FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs
class ROS2PerformanceTest (line 24) | public class ROS2PerformanceTest : MonoBehaviour
method Start (line 35) | void Start()
method OnValidate (line 41) | void OnValidate()
method Publish (line 54) | private void Publish()
method FixedUpdate (line 77) | void FixedUpdate()
method AssignField (line 87) | private void AssignField(ref sensor_msgs.msg.PointField pf, string n, ...
method PrepMessage (line 95) | private void PrepMessage()
FILE: src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs
class ROS2ServiceExample (line 26) | public class ROS2ServiceExample : MonoBehaviour
method Start (line 32) | void Start()
method addTwoInts (line 46) | public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_...
FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs
class ROS2TalkerExample (line 23) | public class ROS2TalkerExample : MonoBehaviour
method Start (line 31) | void Start()
method Update (line 36) | void Update()
FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs
class ROS2UnityComponent (line 32) | public class ROS2UnityComponent : MonoBehaviour
method Ok (line 44) | public bool Ok()
method LazyConstruct (line 54) | private void LazyConstruct()
method Start (line 68) | void Start()
method CreateNode (line 73) | public ROS2Node CreateNode(string name)
method RemoveNode (line 93) | public void RemoveNode(ROS2Node node)
method RegisterExecutable (line 107) | public void RegisterExecutable(Action executable)
method UnregisterExecutable (line 117) | public void UnregisterExecutable(Action executable)
method Tick (line 128) | private void Tick()
method FixedUpdate (line 147) | void FixedUpdate()
method OnApplicationQuit (line 157) | void OnApplicationQuit()
FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs
class ROS2UnityCore (line 32) | public class ROS2UnityCore
method Ok (line 43) | public bool Ok()
method ROS2UnityCore (line 51) | public ROS2UnityCore()
method CreateNode (line 65) | public ROS2Node CreateNode(string name)
method RemoveNode (line 83) | public void RemoveNode(ROS2Node node)
method RegisterExecutable (line 97) | public void RegisterExecutable(Action executable)
method UnregisterExecutable (line 105) | public void UnregisterExecutable(Action executable)
method Tick (line 116) | private void Tick()
method DestroyNow (line 135) | public void DestroyNow()
FILE: src/Ros2ForUnity/Scripts/Sensor.cs
class ISensor (line 25) | public abstract class ISensor : MonoBehaviour
method CreateROSParticipants (line 56) | public abstract void CreateROSParticipants(ROS2UnityComponent ros2Unit...
method frameName (line 61) | public abstract string frameName();
class Sensor (line 67) | public abstract class Sensor<T> : ISensor where T : MessageWithHeader, n...
method AcquireValue (line 75) | protected abstract T AcquireValue();
method HasNewData (line 80) | protected abstract bool HasNewData();
method frameName (line 95) | public override string frameName()
method VisualiseEffects (line 104) | protected virtual void VisualiseEffects()
method OnValidate (line 112) | protected virtual void OnValidate()
method OnUpdate (line 120) | protected virtual void OnUpdate() {}
method CreateROSParticipants (line 125) | public override void CreateROSParticipants(ROS2UnityComponent ros2Unit...
method ExecutorThreadSensorPublishAction (line 151) | internal void ExecutorThreadSensorPublishAction()
method Update (line 177) | void Update()
method Awake (line 186) | void Awake()
method CalculateFrameTime (line 197) | void CalculateFrameTime()
FILE: src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs
class DotnetTimeSource (line 25) | public class DotnetTimeSource : ITimeSource
method TotalSystemTimeSeconds (line 37) | private double TotalSystemTimeSeconds()
method UpdateSystemTime (line 42) | private void UpdateSystemTime()
method DotnetTimeSource (line 48) | public DotnetTimeSource()
method GetTime (line 53) | public void GetTime(out int seconds, out uint nanoseconds)
FILE: src/Ros2ForUnity/Scripts/Time/ITimeSource.cs
type ITimeSource (line 21) | public interface ITimeSource
method GetTime (line 23) | public void GetTime(out int seconds, out uint nanoseconds);
FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs
class ROS2Clock (line 24) | public class ROS2Clock
method ROS2Clock (line 28) | public ROS2Clock() : this(new ROS2TimeSource())
method ROS2Clock (line 32) | public ROS2Clock(ITimeSource ts)
method UpdateClockMessage (line 37) | public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)
method UpdateROSClockTime (line 46) | public void UpdateROSClockTime(builtin_interfaces.msg.Time time)
method UpdateROSTimestamp (line 55) | public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)
FILE: src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs
class ROS2ScalableTimeSource (line 24) | public class ROS2ScalableTimeSource : ITimeSource
method ROS2ScalableTimeSource (line 35) | public ROS2ScalableTimeSource()
method GetTime (line 40) | public void GetTime(out int seconds, out uint nanoseconds)
FILE: src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs
class ROS2TimeSource (line 23) | public class ROS2TimeSource : ITimeSource
method GetTime (line 27) | public void GetTime(out int seconds, out uint nanoseconds)
FILE: src/Ros2ForUnity/Scripts/Time/TimeUtils.cs
class TimeUtils (line 21) | internal static class TimeUtils
method TimeFromTotalSeconds (line 23) | public static void TimeFromTotalSeconds(in double secondsIn, out int s...
FILE: src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs
class UnityTimeSource (line 28) | public class UnityTimeSource : ITimeSource
method UnityTimeSource (line 33) | public UnityTimeSource()
method GetTime (line 38) | public void GetTime(out int seconds, out uint nanoseconds)
FILE: src/Ros2ForUnity/Scripts/Transformations.cs
class Transformations (line 22) | public static class Transformations
method Ros2Unity (line 24) | public static Vector3 Ros2Unity(this Vector3 vector3)
method Unity2Ros (line 29) | public static Vector3 Unity2Ros(this Vector3 vector3)
method Ros2UnityScale (line 34) | public static Vector3 Ros2UnityScale(this Vector3 vector3)
method Unity2RosScale (line 39) | public static Vector3 Unity2RosScale(this Vector3 vector3)
method Ros2Unity (line 44) | public static Quaternion Ros2Unity(this Quaternion quaternion)
method Unity2Ros (line 49) | public static Quaternion Unity2Ros(this Quaternion quaternion)
method Unity2Ros (line 54) | public static void Unity2Ros(ref Quaternion quaternion)
method Unity2Ros (line 64) | public static void Unity2Ros(ref Vector3 vector)
method Unity2RosMatrix4x4 (line 74) | public static Matrix4x4 Unity2RosMatrix4x4()
FILE: src/scripts/metadata_generator.py
function get_git_commit (line 26) | def get_git_commit(working_directory) -> str:
function get_git_description (line 29) | def get_git_description(working_directory) -> str:
function get_commit_date (line 32) | def get_commit_date(working_directory) -> str:
function get_git_abbrev (line 35) | def get_git_abbrev(working_directory) -> str:
function get_ros2_for_unity_root_path (line 38) | def get_ros2_for_unity_root_path() -> pathlib.Path:
function get_ros2_for_unity_path (line 41) | def get_ros2_for_unity_path() -> pathlib.Path:
function get_ros2cs_path (line 44) | def get_ros2cs_path() -> pathlib.Path:
function get_ros2_path (line 47) | def get_ros2_path() -> pathlib.Path:
function get_ros2_version (line 50) | def get_ros2_version() -> str:
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (134K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 939,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/custom.md",
"chars": 126,
"preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".gitignore",
"chars": 112,
"preview": "install\nlog\nbuild\n.idea\nsrc/ros2cs\n**/metadata*.xml\nsrc/Ros2ForUnity/Plugins\n!src/Ros2ForUnity/Plugins/.gitkeep\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5219,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 639,
"preview": "Any contribution that you make to this repository will\nbe under the Apache 2 License, as dictated by that\n[license](http"
},
{
"path": "LICENSE.AL2",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README-UBUNTU.md",
"chars": 3688,
"preview": "# ROS2 For Unity - Ubuntu 20.04 and 22.04\n\nThis readme contains information specific to Ubuntu 20.04/22.04. For general "
},
{
"path": "README-WINDOWS.md",
"chars": 5147,
"preview": "# ROS2 For Unity - Windows 10\n\nThis readme contains information specific to Window 10. For general information, please s"
},
{
"path": "README.md",
"chars": 9110,
"preview": "\nRos2 For Unity\n===============\n\n> [!NOTE] \n> This project is officially supported for [AWSIM](https://github.com/tier4"
},
{
"path": "build.ps1",
"chars": 2085,
"preview": "\n<#\n.SYNOPSIS\n Builds Ros2ForUnity asset\n.DESCRIPTION\n This script builds Ros2DorUnity asset\n.PARAMETER with_tests"
},
{
"path": "build.sh",
"chars": 2111,
"preview": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n echo \"Usage: \"\n echo \"\"\n "
},
{
"path": "create_unity_package.ps1",
"chars": 3119,
"preview": "\n<#\n.SYNOPSIS\n Creates a 'unitypackage' from an input asset.\n.DESCRIPTION\n This script screates a temporary Unity "
},
{
"path": "create_unity_package.sh",
"chars": 3193,
"preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n echo \"This script creates a temp"
},
{
"path": "deploy_unity_plugins.ps1",
"chars": 1605,
"preview": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n$pluginDir=$args[0]\n\nfunction Print-Help {\n\"\nUsage: "
},
{
"path": "deploy_unity_plugins.sh",
"chars": 667,
"preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ $# -eq 0 ] || [ $1 = \"-h\" ] || [ $1 = \"--help\" "
},
{
"path": "docker/Dockerfile",
"chars": 774,
"preview": "ARG ROS2_DISTRO=humble\nFROM ros:${ROS2_DISTRO}-ros-base\n\nRUN apt update && apt install -y ros-${ROS_DISTRO}-test-msgs ro"
},
{
"path": "docker/README.md",
"chars": 879,
"preview": "Ros2 For Unity Docker\n===============\n\nCurrently only building asset on Ubuntu is supported. Build windows version is no"
},
{
"path": "docker/build_image.sh",
"chars": 172,
"preview": "#!/bin/bash\n\nif [ -z \"$ROS_DISTRO\" ]; then\n echo \"Source your ros2 distro first.\"\n exit 1\nfi\n\ndocker build . --bui"
},
{
"path": "docker/custom_messages/INSERT_CUSTOM_MESSAGES_HERE",
"chars": 0,
"preview": ""
},
{
"path": "docker/entrypoint.sh",
"chars": 1145,
"preview": "#!/bin/bash\n\nsource \"/opt/ros/$ROS_DISTRO/setup.bash\"\n\necho \"###########################################################"
},
{
"path": "docker/run_container.sh",
"chars": 403,
"preview": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nmkdir -p $SCRIPTPATH/../install\ndocker run \\\n--rm \\\n-"
},
{
"path": "pull_repositories.ps1",
"chars": 855,
"preview": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n\nif (([string]::IsNullOrEmpty($Env:ROS_DISTRO)))\n{\n "
},
{
"path": "pull_repositories.sh",
"chars": 617,
"preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ -z \"${ROS_DISTRO}\" ]; then\n echo \"Can't dete"
},
{
"path": "ros2_for_unity_custom_messages.repos",
"chars": 316,
"preview": "# NOTE: Use this file if you want to build with custom messages that reside in a separate remote repo.\n# NOTE: use the f"
},
{
"path": "ros2cs.repos",
"chars": 115,
"preview": "repositories:\n src/ros2cs/:\n type: git\n url: https://github.com/RobotecAI/ros2cs.git\n version: 1.3.0\n"
},
{
"path": "src/Ros2ForUnity/COLCON_IGNORE",
"chars": 0,
"preview": ""
},
{
"path": "src/Ros2ForUnity/Plugins.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: 79b46a636d96b67468a29e597cb7b06a\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "src/Ros2ForUnity/Scripts/PostInstall.cs",
"chars": 2784,
"preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ClientExample.cs",
"chars": 3067,
"preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs",
"chars": 12629,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 4cdb4e72fb0aa46c09e52778257ed142\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs",
"chars": 1418,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 75a1bd43b302c4c578a744060319517e\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2Node.cs",
"chars": 5693,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2Node.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 3e21db77b82bbeb8693eabe308d76f45\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs",
"chars": 4139,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 387d300b788c9bd29b6e38808a481155\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs",
"chars": 1888,
"preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs",
"chars": 1625,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 72620fb0a9290863f8643557405c48e3\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs",
"chars": 4837,
"preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: feab04ad06492965492b3edc6423aa53\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs",
"chars": 4664,
"preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: d80a8dd00d331ce458b98a7707d03bb3\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/Sensor.cs",
"chars": 7528,
"preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/Sensor.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: d3ebd1e57dae48cca9c88c51f18cf510\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs",
"chars": 2201,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Time/ITimeSource.cs",
"chars": 785,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs",
"chars": 1851,
"preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 69e097a4a027d5a55b991c0b0b1bdf88\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs",
"chars": 2441,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs",
"chars": 1400,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Time/TimeUtils.cs",
"chars": 971,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs",
"chars": 1419,
"preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
},
{
"path": "src/Ros2ForUnity/Scripts/Transformations.cs",
"chars": 2597,
"preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "src/Ros2ForUnity/Scripts/Transformations.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 5ab7c5f5cc85e9e3aa6134862d9c1cba\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "src/Ros2ForUnity/Scripts.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: f750980d49c8bcf39830e89365689d16\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "src/scripts/metadata_generator.py",
"chars": 3542,
"preview": "# Copyright 2019-2022 Robotec.ai.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use "
}
]
About this extraction
This page contains the full source code of the RobotecAI/ros2-for-unity GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (122.3 KB), approximately 30.8k tokens, and a symbol index with 136 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.