micropython-IMU / micropython-mpu9x50

Drivers for InvenSense inertial measurement units MPU9250, MPU9150, MPU6050
MIT License
260 stars 82 forks source link

Porting to ESP32 anyone? #7

Closed DaneGrinder closed 6 years ago

DaneGrinder commented 7 years ago

Hi I was about to write my own implementation for the mpu6050 and 9250, when I came across this nice library :)

As the title says, anyone working on a port to the esp32. Maybe a port to esp8266? Guess that would work on the esp32 too.

Side note. Anyone tested this on the mpu6050 for the accelerometer / gyro ?

peterhinch commented 7 years ago

I'm currently modifying the MPU9x50 libraries to use the machine and utime libraries rather than pyb. The aim is to make the drivers work on most MicroPython targets. Also the sensor fusion library. I plan to have code ready in a few days.

I think someone in the forum has used an MPU6050 successfully.

DaneGrinder commented 7 years ago

Sounds great :-) I'll wait to see that before I do anything else. Thanks

peterhinch commented 7 years ago

I've submitted PR's for the MPU9x50 and sensor fusion libraries so if you want to try code which hasn't yet been peer-reviewed you're welcome to give it a try. I've tested synchronous and asynchronous sensor fusion with an MPU9250 on the ESP8266 and also with an MPU9150 on the Pyboard.

From a quick look at the chip data I believe the MPU6050 will work with the MPU9150 driver but I lack the means of testing this. I'd appreciate your feedback whenever you decide to try this so I can fix any issues and document the outcome.

DaneGrinder commented 7 years ago

Amazing, Thank you mate

I'll try to do some testing tonight or tomorrow. Not promising anything though...

If not before, I'll do it in the weekend for sure. I have both the mpu9250 and 6050, and maybe a 9150 or 9255.

turbinenreiter commented 7 years ago

The code is now merged. As soon as @DaneGrinder reports successfully using this on the esp32 we can close this issue.

DaneGrinder commented 7 years ago

Good and bad news

I forgot about a family thing this weekend, so haven't had much time to test. Really sorry guys.

BUT, I just did some quick tests using the mpu6050 (the only one I have available here now), and it works with very few changes to the code.

Tested on esp32 with mpu6050: imu.accel imu.gyro imu.sensors and it all works :-)

To make it work, I just commented out: imu.py lines 157 - 158 to stop the check for the chip id. Can't find the chip id of the 6050 in the docs, but also haven't spend much time looking. Sorry

mpu9250.py line 49, 52 - 54, 63 (only commented out the reference to self._mag) Everything relating to the mag sensors, because the mpu6050 does not have a mag sensor!

Sorry I can't do more today, but I really think it all looks good. I will be back at the office tomorrow, and will try to make this my main focus.

peterhinch commented 7 years ago

I wouldn't expect it to work with mpu9250.py, only with mpu9150.py. I was hoping it would work without modification. Did you try mpu9150.py as-is?

I read the datasheet: the chip ID is the same as the mpu9150. This led me to believe that the gyro/accel chip itself was probably the same - the magnetometer on the mpu9150 is implemented as a separate chip, connected via passthrough. Hence my belief that mpu9150.py would work with an mpu6050 unchanged.

I'd be grateful if you could try this.

DaneGrinder commented 7 years ago

mpu9150.py seems to work with mpu6050, if you just remove the call to magsetup() in init() line 64. The magsetup() raises an error in line 204, when you try to write to the not existing mag sensors.

Again, I don't have time to do a lot of testing here today, but have it running and giving me the values from access, gyro and sensors, so it seems good to go, unless there's a call to something mag related.

peterhinch commented 7 years ago

OK, thanks for that. Perhaps when you get time you could try adapting your test code on these lines:

from imu import MPUException
try:
    imu = MPU9150(your_args)
except MPUException:
    pass

If this works with the standard driver I'll amend the docs for users of the MPU6050. I appreciate your help - I've wanted to settle this issue for some time.

DaneGrinder commented 7 years ago

So, Python is not my first language, so please excuse me if I'm completely of here :-)

Putting the initialization of "imu" in a try/catch block, doesn't work. But wouldn't that just catch the MPUException thrown in line 204 in mpu9150.py? The exception will still stop the mpu9150.py from finishing its initialization no?

If we put the try/catch in the mpu9150.py instead, the exception will be thrown and handled only when there's no mag sensors available, but the initialization will still run in full?

The last approach works, so I'll try to do a fuller implementation of the mpu6050 using this, and you just get back to me if I have misunderstood your suggestion, or the try/catch implementation in Python, and you want me to try something else.

peterhinch commented 7 years ago

Sorry, brain not woken up when I posted that ;-) The same realisation just struck me and I came back to amend my post: you are of course right. I'll post some amended code later today.

DaneGrinder commented 7 years ago

Haha. It's all good.

The smallest change to mpu9150.py would look something like this (line 65 - 68)

try:
     self.mag_correction = self._magsetup()  # Returns correction factors.
except MPUException:
     pass

Like this, you get the call to magsetup() inclosed in a try block, so from there it would be a matter of checking why it failed, so you can differentiate between exceptions thrown because of a missing mag sensors and exceptions thrown because of another error?

But maybe it would be better to actually do a specific call to the mpu in the beginning of the init, to see if it had a mag sensor or not?

peterhinch commented 7 years ago

Hopefully my brain is now awake :) I've posted some code - branch "supports_mpu6050".

MPU9150 has a class variable has_mag defaulting True. So if you issue

from mpu9150 import MPU9150
MPU9150.has_mag = False

you should be able to instantiate an MPU6050 without an exception being thrown. This has the benefit (as you suggest) of retaining the exception for the case of a broken MPU9150 device. It retains compatibility with existing code: I've verified this with an MPU9150.

I'd be grateful for comments and testing.

DaneGrinder commented 7 years ago

Sounds like a straightforward solution :)

Busy for the next couple of hours, but will test later and get back to you

DaneGrinder commented 7 years ago

Tested the mpu6050 with the

from mpu9150 import MPU9150
MPU9150.has_mag = False

Now it initializes fine, and the accel and gyro seems to work perfectly. The sensors (imu.sensors) works great too, but it returns a tuple with 3 Vector3d objects, and trying to acces the last object will throw an MPUException

>>> sensors = mpu9150.imu.sensors
>>> sensors[0].xyz
(-0.1940918, -0.006347656, 0.9775391)
>>> sensors[1].xyz
(-1.832061, 0.648855, -0.9694656)
>>> sensors[2].xyz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "vector3d.py", line 101, in xyz
  File "app/mpu/mpu9150.py", line 185, in _mag_callback
MPUException: I2C failure when communicating with IMU

Again, I might be using it wrong Not a big problem, but worth looking into an easy solution for.

peterhinch commented 7 years ago

Well sensors[2] returns the mag, but crashing the bus is not good, and it's legitimate to run sensors on the MPU6050. So I've posted an update so that sensors[2] is now None.

Accessing magnetometer methods will lead to trouble and one would hope users would not do this. The correct way to implement MPU6050 support would be a new class with these properties and methods eliminated. However few people are using the device so I was looking for a quick fix. I've asked my co-developer @turbinenreiter for his views on whether to stick with the quick fix or write a new class.

DaneGrinder commented 7 years ago

Yes, I was aware that the last Vector was the mag, the problem was crashing the i2c, if you were to iterated through the vector :-)

I think just returning None would be absolutely acceptable. That is easy to check for. Not returning the 3. vector would of course be the best, but not at all necessary to spend time on now. If I end up using both the 6050 and 9250 in this project, I might rewrite it for the 6050, just to cut down on the code.

Yes, calling any mag methods will most likely have bad results, but then again, if we have them separated from any methods implementing the access and gyro, I don't see why this would ever be an issue. I would hope, the developer knows before hand, if they have a device with or without a magnetometer :-) Again, the main reason to get ride of all code relating to the mag, would be simply to shrink the library. I will do that, if I end up using the 6050 in production.

DaneGrinder commented 7 years ago

Everything seems to work fine, and I see you added a pull request for the mpu6050 @peterhinch , so I'll just close this issue here.

Thank you for all your good work and help. Really appreciated

peterhinch commented 7 years ago

Thanks for your help in testing.

peterhinch commented 7 years ago

Sebastian (@turbinenreiter) has convinced me to make the base class correspond to the MPU6050 (branch mpu6050_baseclass). This means that you no longer need to import MPU9150.py reducing the code you're running. You should be able to issue:

from imu import MPU6050
imu = MPU6050('X')
print(imu.accel.xyz)
print(imu.gyro.xyz)
print(imu.temperature)
print(imu.accel.z)

I'd be grateful if you could give this version a try.

DaneGrinder commented 7 years ago

Again, sorry for the late reply.

This seems to work perfectly, and is a great reduction in complexity and code for projects that uses the MPU6050.

Great work

peterhinch commented 7 years ago

Thanks for testing. I'll raise a PR so @turbinenreiter can review the code.