Support for Analog Devices IMUs in Python

Are there currently any libraries to interface with Analog Devices IMUs from Python (pyfrc)? (Our team currently uses the ADIS16448 with a roboRIO MXP connector.) If not, is there any interest in writing some? (For the ADIS16448, a port of https://github.com/juchong/ADIS16448-RoboRIO-Driver might work.)

While we unfortunately don’t officially support Python for the ADI IMUs, I know there are some people out there who do library ports or wrappers often for Python. If someone did want to do the port, I’d be happy to link to it in our user guides!

Thanks for the info. After taking a more careful look at the WPILib classes (WPILib was ported as part of pyfrc), several ADI device interfaces seem to be present (e.g. ADXL345, ADXL362), but not one for the ADIS16448; I’ll have to look more closely at ways of beginning the port process later.

There is currently no RobotPy support for the ADIS16448. Creating a port would be very similar to what we did for the SparkMAX controllers.

If you’re interested in making a port for this, let us know and we can provide advice on the easiest way to go about this – I don’t personally have the time or hardware to do it at this time. robotpy issue #605 might affect this sort of thing in the future.

Having had a bit of time and a few days to test things, I took a stab at writing a port. Essentially, I wrapped the entire ADIS16448_IMU class from ADIS16448_IMU.h using pybind11 and compiled it into a .so shared library Python module for roboRIO.

This seems to work on its own (i.e. in a Python shell over SSH), but it seems to interfere with WPILib in Python: It hangs the main loop (the loop that receives Driver Station packets and calls robot code functions). The loop appears to wait indefinitely for a DS packet. By commenting out parts of ADIS16448_IMU.h/ADIS16448_IMU.cpp, I was able to deduce that this occurs when WPILib objects like frc::SPI are initialized in the C++ constructor. I can post more details and source code if necessary.

Am I taking the right approach to making a wrapper/port, and is this a problem that has been encountered and/or resolved before? (Also, please let me know if there is a better place to post this problem.)

That’s the right approach. Most of our other libraries have been HAL-only, and avoided using wpilibc objects, so haven’t encountered that specific problem yet.

OK, thanks for the info. Next time I can test code, I’ll start replacing frc::SPI calls with calls to SPI HAL functions (and similar for DIO functions) and see if they work better.

Update: Replacing wpilibc (frc::*) calls with HAL function calls does not seem to resolve the loop-hanging problem; for instance, HAL_InitializeDIOPort triggers the same problem. Also, I can’t seem to find examples of other hardware driver ports for Python that use compiled C++ modules with WPILib I/O (wpilibc or HAL). The closest I could find are the REV Robotics device bindings, but those use header files that link with vendor-provided static libraries with no visible implementation details. Any examples that you could mention would be appreciated.

My guess, based on looking at the C++ HAL initialization routines, is that the Python and C++ HALs are not sharing control: They both attempt to gain exclusive ownership of the FRC Network Communication interface (perhaps causing the loop-hanging detailed above) and FPGA components.

Assuming that HAL duplication is the problem, is there some way to share Python WPILib’s HAL with the C++ module given that, at least on the roboRIO, the Python module hal_impl appears to download and use C++ shared libraries anyway? (Should I try linking against these libraries instead of using the HAL implementation that C++ team robot code uses in FRC VS Code?) Otherwise, is there something else I’m missing?

You shouldn’t try to initialize HAL (eg, HAL_Initialize). RobotPy will have already done that for you.

You also should link to the shared HAL, but not provide the .so since it already comes with the RobotPy HAL … if the RobotPy HAL is loaded first, then the linker will use that one.

How did this end up turning out?

I was able to link with the shared HAL, and the code now runs and detects the IMU. Unfortunately, the HAL functions for reading the auto-accumulated SPI, according to the documentation, have slightly different behavior than their wpilibc counterparts (e.g. HAL_ReadSPIAutoReceivedData returns the number of words read instead of the number remaining, among other differences). As a result, I had modified the data acquisition code in the IMU source, and I’ve been trying to get it working. As I look more closely now, though, the wpilibc function just wraps the HAL function, so the inconsistency may be with the documentation; I may try just using the logic from the original driver (with changed function calls).

Before I continue with that, though, is there any way to have wpilibc classes use the same HAL shared library? This would be ideal, as the original source could then be left intact.

The real question is why are you using python!

Before I continue with that, though, is there any way to have wpilibc classes use the same HAL shared library? This would be ideal, as the original source could then be left intact.

Well, if we went the way of robotpy-wpilib#605, this would become quite a bit easier I suspect.

However, even without that, you should be able to compile all of the wpilibc classes that you need and link them to a HAL – as long as it’s the same one that the RobotPy HAL is using, it should work. If you have your source code in a repo somewhere, I’d be happy to glance at it and make suggestions for what you might be doing wrong.

Using the logic from the original driver, I was able to get the code working (at least to the extent that I can use GetAngle). As for a source code repo, I cleaned my code up and posted it on GitHub; more testing and tweaks are probably needed.

However, even without that, you should be able to compile all of the wpilibc classes that you need and link them to a HAL – as long as it’s the same one that the RobotPy HAL is using, it should work.

I could probably modify build.py to compile and link the wpilibc header and source files (from the allwpilib submodule) that the code depends on. This would also make it easier for others to make these kind of libraries, as they could substitute any wpilibc-using source code and pybind wrappers. If I have time, I might work on this in the future. Of course, if robotpy-wpilib#605 is implemented, it would likely just be a matter of linking against wpilibc; how likely is this?

I would really like to see robotpy-wpilib#605 happen. I’ve started binding other pieces, and I think it could be doable. However, it’s a lot of work up front, so would be good if others could collaborate on that. :slight_smile:

I put together the first major step towards automatic wrappers for robotpy projects. Check it out at https://github.com/robotpy/robotpy-build

This looks neat; I’ll be sure to keep an eye on it as it develops. Also, thank you very much for your help and expertise in getting my initial port running.