Announcing - .Net/C# wrapper for ROS2

596 views
Skip to first unread message

Lennart Na

unread,
Sep 2, 2016, 12:38:49 PM9/2/16
to ROS SIG NG ROS
Hi,
I wrote a C# wrapper for ROS2 which should by now support all major features that are provided by the ros client library of ROS2.
The code still needs some polishing and testing but should be usable on linux (Tested on debian testing).

The wrapper should have quite a good performance because it should avoid copying data where ever possible and doesn't use any glue code between C# and the ros client lib.
The api is based on the rclcpp but due to usability I decided to break the naming and using pattern in some cases.

It currently supports:
  • Generation of messages with:
    • Simple types
    • Unbounded arrays
    • Nested types
  • Publishing and recieving these messages
  • Creating services and corresponding clients

Everything else like QoS, Intraprocress communication and domains isn't implemented at the moment.


For more information see: https://github.com/firesurfer/rclcs

I'm going to create a proper example and some tests the next days. ( My testing example is at: https://github.com/firesurfer/rclcs_testing_ws )


Also have a look at the discussion about a .Net client lib on ros discourse : http://discourse.ros.org/t/net-bindings-for-ros2/460/4


Esteve Fernandez also wrote a .Net wrapper for ROS 2 which targets windows and .Net core (my wrapper currently lacks support for windows). I hope that in the future both wrapper will be merged into one that targets all plattforms.




William Woodall

unread,
Sep 2, 2016, 2:03:49 PM9/2/16
to ROS SIG NG ROS
Awesome! Maybe I'll have some time to try it out over the long weekend.

I'd be interested in any feedback you could give us on the process you went through. I'm aware of several places that must not have been easy to work with, like the typesupport system. It's one of those things we'd like to make simpler and easier, but we're still trying to figure out how to do that. If you decide to chronicle your journey, as a blog post or mailing list post or something, I'd be interested to read it too.

Cheers,

--
You received this message because you are subscribed to the Google Groups "ROS SIG NG ROS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ros-sig-ng-ros+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
William Woodall
ROS Development Team

Lennart Na

unread,
Sep 2, 2016, 4:01:40 PM9/2/16
to ROS SIG NG ROS
Okey I try to outline what steps I performed in the development.
At first it is to say that C# makes it very easy to call native code. In contrast to the java wrapper I didn't need to write any glue code. I started by reading the rcl headers to get a rough overview which methods I need to call. For testing purposes I implemented the node first because the node doesn't need any typesupport. It took me quite a long time in order create to correct managed wrappers for some rcl types and I'm still not sure on how to represent the QoS options in C#. I found it a bit confusing that there are some rmw - types in the rcl, but also a lot of rcl types which are typedefed to rmw types - It would be great to have a bit more consistency at this point. In the end I realised that a structure like in the C# Node  is the easiest for access to the native code. First I got a Node class which does the user interaction, the Node class accesses a rcl_node class which does the native interop and marshalling. The rcl_node_t and rcl_node_options_t have simply the same definition like in C code.

The next step I took was writing the publisher code so I would have some testing code for the generated messages. The message generation is a point where I had a lot of trouble at first and what took me a long time to figure out. As you might recognize I didn't use the template engine like the other rosidl_generators but hacked my own tool, (I want to change this in the future) simply because it looked to difficult to use. It also was kind of hard to figure out how to register your own idl_generator. At this point some documentation about the architecture would be nice. Also, as far a I can remember the cmake files don't have a lot of comments that explain what is happening.
In order to figure out how the typesupport system works, I created a package with one message that contains all possible data types and arrays and had a look at the resulting code and binaries (the nm tool is great for that). The way I get the correct typesupport struct is a bit fancy. I generate a dll import statement in the generated c# code and use later on reflection in order to call the get_type_support function in the c# message code. In languages without reflection there might not be such a swift solution (which doesn't force the user to write any code for getting the typesupport). But overall this step was fairly easy because in C# I could create the messages with the same memory layout as in C.

At first I just wrote an implementation for simple types and later on arrays. The arrays almost drove me crazy because I had problems to create the correct memory layout at first and wondered a whole week why I couldn't receive a message. In the end there simply was no code that would serialize C arrays in fastRTPS (https://github.com/eProsima/ROS-RMW-Fast-RTPS-cpp/pull/45 ). The code for the standard type arrays is hardcoded in the rclcs at the moment, because I need to do the marshalling by hand. At the same time with the code generation for the simple types I wrote the code for the subscription, mapped the rcl return types to an C# enum and created exceptions for several error cases. With mono it is hard to debug the step from managed to native code. I'm also not sure how to safely free the memory allocated by hand in C# for messages that contain arrays at the moment.

Afterwards I started an implementation for the services which were easy to implement because they mostly work the same way as the publishers and subscribers.  At this point I was a bit confused that the rosidl_generator_c generates typesupport functions even for requests and responses even though you just need the typesupport for the whole service in order to initialize it. Overall I would love an asynchronous interface for services/clients and subscriptions. The wrapper wouldn't have to take care  about actively polling the functions (like in the rclcpp).

In the end I implemented the support for nested types. This support is a bit hacked at the moment because it simply references all generated assemblies in the ros2_ws/install/lib folder. But this is mainly an issue because I use my own generator tool.

So in summary:

  • code generation was the most difficult part
    • template engine is complicated and not documented at the moment (at least I couldn't find the documentation)
    • typesupport functions names aren't documented -> needed reverse engineering
  • the c-typesupport needs some more tests
    • Nevertheless the way messages are defined in the C code is great for using them in other languages.
  • debugging native code from managed code isn't easy. Perhaps some more debug messages which can be switched on by a flag would be useful
  • QoS options are going to be hard to implement I think

Also I got some more points that could be improved so it is easier to write a language wrapper:

  • Provide an asynchronous interface in the rcl. So wrappers won't need to implement the executables (or something like that) like the rclcpp
  • Move the intraprocess support into the rcl. (I was quite surprised that the rclcpp distinguishes between inter and intra process communication)
  • Have an api definition a wrapper/ros client lib should implement (with language specific adaptions - for example method and variable spelling in c#
 I hope this feedback is useful for you.
Lennart
To unsubscribe from this group and stop receiving emails from it, send an email to ros-sig-ng-ro...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Lennart Na

unread,
Sep 8, 2016, 7:14:25 PM9/8/16
to ROS SIG NG ROS
Hi,
I fixed a bunch of stuff the last days.

Regards,
Lennart

Lennart Na

unread,
Sep 13, 2016, 2:33:49 PM9/13/16
to ROS SIG NG ROS
I got another update,


I implemented the support for

* fixed size arrays
* string arrays
* QOS Settings

Furthermore I'm refactoring my rosidl_generator_cs. I decided not to use the templating engine (at least not at the moment) in favour of the C# CodeDom (or in future the C# Roselyn codegeneration Api) ( https://msdn.microsoft.com/de-de/library/y2k85ax6(v=vs.110).aspx ). By doing so it might be possible in the future, if ros2 will support this feature, to create messages at runtime by invoking the codegenerator from your own application.

Lennart

Am Freitag, 2. September 2016 18:38:49 UTC+2 schrieb Lennart Na:

Brad Baillio

unread,
Sep 13, 2016, 6:50:41 PM9/13/16
to ROS SIG NG ROS
Awesome work! Wondering if you've tried this in Windows with real .NET?
Any hesitations on why it wouldn't just work?

Lennart Na

unread,
Sep 14, 2016, 3:43:56 AM9/14/16
to ROS SIG NG ROS
I haven't had to chance to try it on windows. But I think you would have to adjust at least two cmake files:

https://github.com/firesurfer/rclcs/blob/master/CMakeLists.txt#L12
https://github.com/firesurfer/rosidl_generator_cs/blob/master/CMakeLists.txt#L12

to run msbuild (.Net) instead of xbuild (Mono).

Furthermore you might want to use the Visual Studio Isolated Shell ( https://msdn.microsoft.com/en-US/en-en/library/bb685691.aspx ) in order to compile ros2 with .Net support, because otherwise you might have add msbuild and the C# compiler manually to the path variable. Perhaps I'll find some time the next days to install a windows on my computer and test the compilation.

If you want to give it a try:
In order to build against the rclcs and the generated messages simply create ros2 workspace, add a package with a message and build it. Afterwards you will find a <package name>.dll in the install/lib directory. Simply add this file and the rclcs.dll which can be found in the <ros2 ws>/install/lib directory as a reference to a new visual studio solution. Remember to tell visualstudio not to copy the files, but tell .Net to search in the  <package_name>install/lib and <ros2_ws>/install/lib directory.

Lennart Na

unread,
Sep 20, 2016, 5:11:15 PM9/20/16
to ROS SIG NG ROS
Another update!

I started to port my wrapper to windows but I'm still having some troubles, that mono and .Net behave differently with native interop.

A list of things I've done so far
  • Added a lot of comments to some classes of the rclcs
  • Generating an xml documentation file via the /doc option. This xml file can be used by visualstudio or monodevolop in order to have comments for functions/classes for intellisense.
  • Implemented the functions defined in the graph.h
  • Started the rewrite of the rosidl_generator_cs to use the CodeCom Api
The fixes for windows are still sitting in the windows branches of the rclcs, rosidl_generator_cs and ros2sharp_testing_ws
They contain a lot of stuff which does platform specific operations. A major problem I had on windows, that in contrast to mono you can't simply have an environment variable that tells mono where to search for assemblies. So I came up with a rather unpleasant solution. I wrote a WindowsAssemblyLoader class (https://github.com/firesurfer/rclcs_testing_ws/blob/windows/src/test_cs/test_cs/Examples/WindowsAssemblyLoader.cs) which wraps your own program code. The class overrides the AssemblyResolve event and searches for the given assemblies in the AMENT_PREFIX_PATH/bin. Afterwards it calls the main program.
Perhaps somebody has a better solution for this problem ?

Appart from that the code crashes at the moment with a System.AccessViolationException probably due to a interop error. I hope I can fix this the next days.

Am Freitag, 2. September 2016 18:38:49 UTC+2 schrieb Lennart Na:
Reply all
Reply to author
Forward
0 new messages