Its been nearly a month since my last update, my full-time job took over most of my time as we neared an important phase in our project. But since that is resolved now, I dove back into where I left off last time. Last time, I had completed the micro-ROS firmware part (except a few details I will come to at a later stage), and was preparing to setup the ROS2 packages required to run Nav2. This update is mostly about the progress I made on my plans, but also a bit of a rant.
First, I had to start off with one of the issues I was having, with launch files. I had converted my *.launch
files from ROS1 into *_launch.xml
files in ROS2. Unfortunately, I was unable to use conditional statements in the XML launch files and had posted a question on ROS Answers. I eventually found out that this is currently not possible to do this, but a solution was in the works as there was already an issue created on the ROS2 Github by another user. Looks like after my comment there, things are moving ahead and there might be a solution in the near future. However, for now, I decided to dive head first into the mysterious world of ROS2 Python launch files.
I had a bit of an idea of what I was getting into here. Python launch files are essentially python scripts that can programatically launch different nodes or other launch files. However, it is not as straightforward as it seems, as can be seen in multiple posts complaining about it.
My first big problem with Python launch files - the documentation is all over the place. There is no single place to access information about Python launch files and the different libraries/APIs that need to be imported. For some specific issues, simple Google searches aren’t enough to find what you need. The tutorials are quite useful but really basic. I noticed that the best way to go about it is by looking at the launch files other people have made. The Linorobot2 bringup launch file and the Nav2 bringup launch files are a good starting point.
The next big problem is that you need to build the package every time a change is made in a python file. This is applicable for normal scripts as well as launch files. This is really annoying. This wasn’t an issue with ROS1, where changes made in launch files and Python files did not need the entire package to be rebuilt.
The third issue is lack of standardization. Now that there are multiple ways of implementing launch files, everyone has their own preference. Doing it your own way is normally okay, sometimes encouraged, but in this situation most robots are made up of multiple packages (from different sources) with their own launch files. So, to make these different launch files work together is a hassle if they’re all implemented in different formats and coding styles.
Unfortunately, I don’t see a way out of this. If I was a proper software engineer, I would take some time and contribute, but I definitely lack the skills for this. So, for now I have to live with it. After some more time reading and learning about python launch files, I started off with akros2_drive_launch.py. This launch file from the akros2_drive package launches a few things:
- The PS4 controller nodes (setup and publisher)
- The Twist related nodes (publisher, mixer, multiplexer)
- The micro-ROS Agent for the micro-ROS client implemented on the Teensy board.
Next, I created the akros2_bringup package. This package is used to bringup different components of the AKROS2 robot - the sensors (Tracking Camera, LiDAR), the drive system (akros2_drive), the navigation stack (Nav2), and the visualization packages (ROSBoard, ROSBridge for Foxglove Studio). I created the akros2_bringup_launch.py file in this package to launch all the elements except Nav2 since its not implemented yet. This launch file launches the following components:
- The LD06 LiDAR launch file
- The Intel Realsense launch file with T265 configuration
- The akros2_drive launch file
- The rosbridge_server launch file, if the
viz_foxglove
launch argument is set. rosbridge_server is used to connect with Foxglove Studio - The rosboard package executable, if the
viz_rosboard
launch argument is set. I’m using a personal fork of the rosboard package, with the only change being that it uses port number 8000 instead of 8888. This is because 8888 is used by JupyterLab in my environment.
Finally, it was time to test. First, the akros2_drive package. Running this, I did not observe any immediate issues. Everything worked fine, all the relevant nodes were being launched and the launch arguments worked. The only issue I observed was when implementing the launch file. If the launch file fails (lets say due to a syntax error) at any point, it terminates the process. However, if any nodes have already been launched before the error occured, they are not terminated and exist as ‘zombie’ nodes. I need to physically kill these processes before I try the launch file again. However, this is only an issue that was observed when there was an issue in the syntax of the launch file. If the syntax is correct, there should be no issues in initializing and terminating the launch file. ‘Ctrl+C’ should terminate everything, and no zombie nodes are left.
Once the akros2_drive launch file was working fine, I moved on to the akros2_bringup launch file. Things were different here because I am launching other launch files, instead of launching nodes like in akros2_drive. The first issue I found was while launching the ROSBridge launch file. The rosbridge_server package provides an XML launch file, which I tried including in the bringup launch file in the same way I was including Python launch files. This of course did not work. I thought it was not possible to do it, so I spent some time re-writing the XML launch file as a Python launch file. Meanwhile, as a last ditch attempt, I also posted on ROS Answers. Turns out, it IS possible, as explained in this detailed response. Once again, all of this could have been prevented if there was documentation that was easily accessible.
With this, I was good to go. Or so I thought. The next issue I observed was while launching the T265 launch file, I realized that there was no pose/odom info being published. A bit of context here: Intel Realsense has some really problematic software. For starters, it provides the package realsense-ros which is a single driver to run all of Intel Realsense’s products - including depth cameras, tracking cameras and also a LiDAR, I believe. This makes the entire code base very complicated, and a change for one product might mean that another one stops working.
So, like I do normally, I went to the realsense-ros repo and opened the default branch. The README directed me to the ros2-beta
branch, which I tested and where I observed the issue I mentioned earlier. I found out that there was another stale ROS2 branch called ros2
, which on testing ended up working just fine. I initially thought they stopped software support for discontinued hardware, but their documentation on the latest branch still mentions T265. Or maybe they made changes and never tested it out on real hardware, especially the discontinued ones. So, rolling back to the older ros2
branch worked… but for only about a day.
Updated realsense-ros to ROS2 and now the T265 visual SLAM doesn't work anymore. It publishes camera and IMU data just fine, but no pose or odom info.. Does anyone know how to fix this? #ros2 #t265 #realsense pic.twitter.com/PFuJpzOmYV
— Aditya Kamath (@kamathsblog) August 28, 2022
The next issue was that the T265 randomly started crashing my Raspberry Pi. It seemed like the nodes were still running in the background, but I wasn’t able to access the Pi via JupyterLab or SSH. Even the ROSBridge and ROSBoard connections stopped working. This was extremely weird, and something I hadn’t seen before. I did a few things to solve this: first, I replaced the USB cable. The old one was probably worn out because of all the unplugging/replugging I had to do. This is because of an issue that has persisted since ROS1 - the Realsense drivers aren’t able to access the T265 for the first time unless it is physically disconnected and reconnected to the Raspberry Pi. This is an issue that Intel has stated in a different thread that it won’t be fixing, so there’s nothing I can do here. The second change I did was disabling the camera streams. I am not using them for anything, and even when disabled, they’re still being used internally in the T265 for visual SLAM. With these changes, the T265 started working once again.
Relieved that the T265 is working again. Had to roll back to an older ROS2 branch, the default one is no good. And it only works reliably if the camera streams are disabled. #realsense #ros2 https://t.co/AKNTOvcetT pic.twitter.com/qzsIv8auJO
— Aditya Kamath (@kamathsblog) August 31, 2022
But this only fixed the issue for a while. Eventually, after a few days, the issue started happening again. The T265 randomly started freezing the RPi again, this time around, the issue happened immediately when the node was launched, and not after some time. Other than trying (unsuccessfully) with different cables, I did not have the patience to investigate further, so I just created a launch argument in the bringup file to stop this node from intializing. If I do not fix this issue, I will eventually remove the T265. It should not be a big issue, since I’ve now also added an extra IMU to the robot. Without the T265, everything functions as expected and also really well.
Next Steps
For my next steps, there are a few things I want to finish with AKROS2 before I take a break from the robot. I want to leave it in a state where I can pick it up and implement Nav2 easily. For this, I need to first fix the URDF of the robot, and the resulting transform tree. I also need to filter the IMU measurements on the Teensy so that I can eliminate some of the noise and offset. Next - sensor fusion, in two ways: first, a simple check in micro-ROS to see if the robot is actually moving before updating the wheel odometry. This is to prevent the odometry from being updated when the robot is stuck, but the wheels are still spinning. The second on ROS2 using an Extended Kalman Filter, where I will fuse the filtered IMU measurements and the filtered odometry data to provide a more accurate odometry measurement. This will also provide the odom
to base_link
transform for Nav2. Finally, I want to end with finishing the micro-ROS Tracing tutorial I have been meaning to get to. If I still have time, I can figure out the T265 issue as well.
The reason I will take a break is because I’m first going to Japan to attend ROSCon in October, then in a few weeks I will go on a three week trip to India. For this, I am currently arranging everything needed for the Japan trip and I am really excited. I will also be moving around this time, so it is going to get really busy for me. This also means that I will have a lot of spare time in flights, airports, hotels, so I’m putting together a project idea that I can design and prototype on my trip, and can build during the winter holidays later in the year. I’m currently thinking of a 3-omni-wheeled holonomic robot using the three Feetech STS3215 servo motors I had purchased a few months ago. I also recently received some boards from Pimoroni, and I intend to build some kind of micro-ROS display using the e-ink screen, which I have connected to a RPi Pico W at the back, so it is micro-ROS compatible already. The other board on the top right, the [Motor2040] is an amazing motor driver that can drive 4 motors in closed loop with encoder feedback. It has a RP2040 onboard, so it should also be micro-ROS compatible. I’ve been designing a miniature AKROS2 robot with N20 metal gear motors with encoders, so this board will be used there. However, since its a bit repetitive to what I’m working on with AKROS2, I have put that project on hold.
Some shiny pirate treasure arrived in the mail today. @pimoroni pic.twitter.com/qFWmjMCg5y
— Aditya Kamath (@kamathsblog) September 3, 2022
More updates next week. Apologies if there have been any typos and for the lack of photos/videos, I’ve definitely rushed this post, as I have an appointment with at the Japanese Embassy tomorrow. I’m also experimenting with a different style of embedding content. Since I tweet about my projects, I figured I could just embed my tweets here instead of uploading the actual image itself. Uploading images is probably the most frustrating part about writing this blog, so embedding my tweets definitely makes my life easier…