Skip to content

MicroROS on Espressif #9955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft

Conversation

hierophect
Copy link
Collaborator

@hierophect hierophect commented Jan 12, 2025

This draft PR is an attempt to add MicroROS as an optional addition to the Espressif port. The Robot Operating System (ROS and successor ROS2) is kind of a misnomer: it is not an operating system, but it is a very commonly used collection of middleware and type standards for different robot parts to talk to each other. MicroROS is a tiny implementation of the ROS API that lets embedded systems talk to ROS system on a linux machine, such as a big computer running perception, kinematics or machine learning tasks.

The goal of this project is to allow Espressif Circuitpython boards to recieve and transmit data over WIFI to a computer running ROS, to publish sensor data, take in motor commands, and other tasks. I'm hoping it'll be useful to experimenters who want to learn about ROS without having to dive too deep into embedded systems.

Implementation notes (edited 4/5/25):

  • Module is rclcpy, after the standard ROS2 python API rclpy (RCL: Ros Client Library)
  • I'm attempting to follow rclpy where possible, excluding features that aren't supported by MicroROS.
  • ROS is a big project with lots of message definitions, build tools, and build output. MicroROS uses many of these tools when building the embedded version, which results in extra python requirements and lots of excess build output. To spare the Circuitpython CI this build complexity/load this module uses pre-built libraries in a separate repository.
  • Currently hung up on a MicroROS initialization failure, working on getting more informative debug output.

Will resolve #8139

@hierophect hierophect added the espressif applies to multiple Espressif chips label Jan 12, 2025
@hierophect hierophect added this to the Long term milestone Jan 12, 2025
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these microros-ext files come from? Can you submodule them instead?

@hierophect
Copy link
Collaborator Author

hierophect commented Jan 13, 2025

I agree we should submodule somehow - microros-ext is sourced from https://github.com/micro-ROS/micro_ros_espidf_component, which I avoided including directly due to the additional requirements in the idf virtual environment, pip3 install catkin_pkg lark-parser colcon-common-extensions empy==3.3.4, and the extended build process caused by colcon fetching all the message type packages. I didn't want them clogging up Circuitpython's CI process, but @dhalbert suggested it might not be a big deal to include those directly, as long as the component isn't enabled willy-nilly. I'm happy to either include it directly and refactor the build system changes, or make a submodule of the precompiled version to use instead.

@tannewt
Copy link
Member

tannewt commented Jan 14, 2025

I don't understand the build stuff you are referring to. A README.md in microros-ext would be helpful in explaining where it came from and how to update it.

@hierophect
Copy link
Collaborator Author

I don't understand the build stuff you are referring to.

Sorry, let me say that in a less confusing way. Compiling the MicroROS component installs a bunch of python package managers, is slow, and has a lot of verbose build output. To avoid adding that to Circuitpython's CI tests, I made a pre-compiled version, which is what I've included. I've recieved feedback already that that might be less easy to maintain, so I could revert back to including the original component as a submodule if you feel this feature being more futureproofed would be worth a small performance hit to the CI. Which would you prefer?

@tannewt
Copy link
Member

tannewt commented Jan 16, 2025

How about splitting the difference and making a separate repo for the precompiled files? That way it isn't in CP but it doesn't require building every time.

@furbrain
Copy link

I'd just like to comment that this looks awesome, having ROS on CircuitPython could open up lots of opportunities. I'm currently thinking of making a CircuitPython sensor -> ROS factory utility class - so you could just set up your sensor, pass it into the class and it starts publishing on an appropriate topic - so an accelerometer+gyro would start publishing IMU messages for example.

@hierophect
Copy link
Collaborator Author

hierophect commented Jan 19, 2025

a separate repo for the precompiled files?

Ok, this sounds good to me. I can maintain the repo and we can revisit if another solution becomes better. I'm out for 1 week starting tomorrow but I'll implement this and the shared-bindings as soon as I get back.

Adds minimal implementations for the rclcpy module, including Node and
Publisher, with a placeholder int-only publish function for testing.
@hierophect
Copy link
Collaborator Author

hierophect commented Apr 3, 2025

I'm currently thinking of making a CircuitPython sensor -> ROS factory utility class - so you could just set up your sensor, pass it into the class and it starts publishing on an appropriate topic

I've just implemented the first bit of the shared-bindings layer so I'd love to hear more about what you meant by this. I'm currently basing the module API off of rclpy, but we could make a python wrapper library for other tasks.

@furbrain
Copy link

furbrain commented Apr 3, 2025

@hierophect I've actually implemented the adaptor using circuitpython on a raspberry pi. It should work on ROS1 and ROS2 - but I've only tested on ROS1.

The code is here and a usage example is here

It basically allows you to create any sensor that complies with the adafruit sensor conventions - simply create a normal sensor and then call ROSAdapter.create_from_sensor with that class as an argument.

That wraps the sensor in a class and allows you to publish the sensor data. I should probably get around to publishing this properly, not buried in a repo with a bunch of other stuff.

Fixes missing lines for adding source to the makefile, and fixes subsequent
overlooked errors in shared-bindings. Aligns the module compliation flag with
the module name.
@hierophect
Copy link
Collaborator Author

Getting a hardfault on init, which probably means there's an issue with the microros allocator overrides I provided using the micropython allocator tools (m_alloc, m_free etc). If anyone's willing to lend an eyeball to see if there's something grossly wrong there it'd be very helpful.

@dhalbert
Copy link
Collaborator

dhalbert commented Apr 8, 2025

What is the reason for calling it rclcpy vs microros or similar?

@tannewt
Copy link
Member

tannewt commented Apr 8, 2025

rclcpy

That's what the ROS2 CPython version is called: https://github.com/ros2/rclpy

@hierophect
Copy link
Collaborator Author

I'd be ok with either. Originally I was naming the whole thing microros but after deciding to follow the rclpy API in terms of implementation I felt rclcpy was more in line with Circuitpython conventions for adapting existing Cpython modules.

@dhalbert
Copy link
Collaborator

dhalbert commented Apr 8, 2025

Got it. I missed the rclpy reference above, and thought cpy was for CircuitPython, not CPython. I ahve to say, though, that rcl standing for ROS client library is quite obscure.

Removes use of the m5stack_cardputer base definition and adds a ros-specific
m5stack_cardputer_ros version. Also repairs some lost changes intended for the
prior commit that were dropped in an accidental reset. Compiles, but has a
memory access error.
@hierophect
Copy link
Collaborator Author

hierophect commented Apr 9, 2025

Fixed some lost changes that should have been in the last commit, I think I missed adding changes and reset them by accident. Hence the mismatched macros etc.

I'm having no luck even calling rclc_support_init(&_my_default_support, 0, NULL, &_my_default_allocator); with the default allocation settings, as an isolated call with no followup. Is there any kind of memory protection I need to be taking into account here? It seems like other modules use m_malloc, etc, without much issue.

@tannewt
Copy link
Member

tannewt commented Apr 10, 2025

I'd suggest getting a backtrace via a DEBUG=1 build. That should explain further why it is crashing.

m_malloc will crash if the VM isn't active but if you are doing an import rclcpy then it should be.

@hierophect
Copy link
Collaborator Author

Will DEBUG=1 override the Circuitpython USB with the esp monitor? I was looking around for docs on that but didn't come up with anything initially. Currently trying a new build with the Espressif Devkit C.

@hierophect
Copy link
Collaborator Author

hierophect commented Apr 10, 2025

Whoops, looks like this could actually be a wifi coordination issue:

assert failed: tcpip_send_msg_wait_sem /IDF/components/lwip/lwip/src/api/tcpip.c:449 (Invalid mbox)

This is actually the kind of problem I expected, just not for it to manifest as a hardfault. I'm glad it's not a problem the allocator.

Edit: can confirm hardfault goes away when wifi.radio is initialized properly first.

@tannewt
Copy link
Member

tannewt commented Apr 14, 2025

Will DEBUG=1 override the Circuitpython USB with the esp monitor? I was looking around for docs on that but didn't come up with anything initially. Currently trying a new build with the Espressif Devkit C.

No, it puts the IDF logging out the default UART.

@hierophect
Copy link
Collaborator Author

Might be stuck on some ROS issues for a little bit:

  • microros initialization is failing, with a generic error code (error 1). Could be for any number of reasons, but it's hard to tell because microros logging is not printing when set up with rcutils_logging_set_output_handler(my_custom_log_handler);. Might need to double-check if I'm missing a build setting on the pre-built library and recompile.
  • ESP JTAG gives limited visibility because I don't have debug symbols in the library, also a reason to recompile.
  • Still have to make sure that I'm bringing in all the appropriate SDKConfig settings microros expects, but it doesn't make sense to focus on this until the above two bullets are done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
espressif applies to multiple Espressif chips
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ROS2 networking via MicroROS
4 participants