Skip to content

buildhat.exc.DeviceNotFound: No device found #122

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

Closed
OliverFaust opened this issue Feb 25, 2022 · 15 comments
Closed

buildhat.exc.DeviceNotFound: No device found #122

OliverFaust opened this issue Feb 25, 2022 · 15 comments

Comments

@OliverFaust
Copy link

Hi,

Writing to the device in a hard loop causes a `No device found' after some time. Here is the complete call trace:

Traceback (most recent call last):
File "/home/pi/Python/xBoxController/minimalError.py", line 5, in
pair.start(0,0)
File "/usr/local/lib/python3.9/dist-packages/buildhat/motors.py", line 390, in start
self._rightmotor.start(speedr)
File "/usr/local/lib/python3.9/dist-packages/buildhat/motors.py", line 229, in start
self._write(cmd)
File "/usr/local/lib/python3.9/dist-packages/buildhat/devices.py", line 155, in _write
self.isconnected()
File "/usr/local/lib/python3.9/dist-packages/buildhat/devices.py", line 91, in isconnected
raise DeviceNotFound("No device found")
buildhat.exc.DeviceNotFound: No device found

The time delay, after which that error occurs, seems to be random. I have observed that error anywhere from 1 min to 5 min. That might indicate a runtime problem with the framework, such as a buffer overrun.
Here is a minimal example to reproduce the error:

import buildhat

pair = buildhat.MotorPair('A', 'B')
while True:
    pair.start(0,0)

Debugging revealed that BuildHAT.DEVTIMEOUT followed by BuildHAT.DISCONNECTED occurred in serinterface.py

@mutesplash
Copy link
Contributor

Firmware on the RP2040 basically can't handle what you're asking it to do.

@OliverFaust
Copy link
Author

Dear mutesplash,

Thank you for your response.
Are you going to fix this issue?
From my experiments, I know that the error is correlated with the call frequency to your interface, i.e. if a lower call frequency prolongs the time without error. For example:

import buildhat
from time import sleep
pair = buildhat.MotorPair('A', 'B')
while True:
    pair.start(0,0)
    sleep(0.2)

However, there are at least three issues with this workaround:

  1. The error still occurs - the program just runs for longer. It is not clear how long to sleep.
  2. The program is less reactive - bad for real time
  3. Sleeping is the wrong solution. Blocking is the correct solution: the call, in this case pair.start(0,0), should only return when the framework is ready to take the next command (API call).

@mutesplash
Copy link
Contributor

Unfortunately there is no way to block in the Python library because the firmware does not respond in the affirmative when you issue a command.

@OliverFaust
Copy link
Author

Dear mutesplash,

I understand that the UART protocol might be a bit slow to respond in the affirmative when a command is issued. My suggestion would be: Use a GPIO pin.

Without the blocking functionality the buildhat cannot be used for reactive robots.
For example, I tried to build a simple game pad controlled car. The sensor information from the controller axis comes arbitrarily fast. Standard procedure is that the slowest component, in the control structure, determines the message rate. In this case, the slowest component is the motor and hence the motor API should block.

@mutesplash
Copy link
Contributor

The python library in this repo doesn't seem cover the BuildHAT firmware source change that would create an out-of-band feedback loop that you suggest. It would be nice if it did, though! I see where you're coming from and would suggest coming up with a more realistic test case to argue for this change. (I am not a committer)

@OliverFaust
Copy link
Author

OliverFaust commented Feb 26, 2022

Dear mutesplash,

Here is a minimal use case for controlling two motors with a game pad:

from xbox360controller import Xbox360Controller
import buildhat
controller = Xbox360Controller(0, axis_threshold=0.2)
pair = buildhat.MotorPair('A', 'B')
while True:
    pair.start(controller.axis_r.x*100, controller.axis_r.y*100)

The importance of this test case becomes clearer when we start to use parallel processing. Here is an example with two communicating sequential processes that form a process network with the same functionality:

from pycsp.parallel import *
from xbox360controller import Xbox360Controller
import buildhat

class xboxBuildHat:
    def __init__(self):
        ch = Channel()
        Parallel(
            self.sampleRightStick(-ch),
            self.MotorProcess(+ch)
            )
    
    @process
    def MotorProcess(self, cin):
        pair = buildhat.MotorPair('A', 'B')
        while True:
            speed = cin() 
            pair.start(speed[0]*100,speed[1]*100)
                                  
    @process
    def sampleRightStick(self, cout):
        controller = Xbox360Controller(0, axis_threshold=0.2)
        while True:
            cout([controller.axis_r.x, controller.axis_r.y])
    
if __name__ == "__main__":
    while True:
        xboxBuildHat()

By now pycsp is a bit dated and the library code must be patched for python3, but the example shows the principle of building reactive process networks with blocking channels.

@mutesplash
Copy link
Contributor

Hm, I see. Without a patch to the closed-source firmware, the BuildHAT library would have to guess at a max input rate and throttle all write()s in the BuildHAT class. But, it seems you haven't been able to determine what that rate is.

@OliverFaust
Copy link
Author

Dear mutesplash,

It seems that the problem is nonlinear and a linear solution would reduce the system performance significantly. When the code, from my previous post, is running, the motors are reactive. That means, with my senses I feel that the motors react adequately to my input. Introducing a long delay, say sleep(1) will break the user experience required for a simple game pad controlled car.
In the robotics area, it is undisputed that reaction time is one of the most critical parameters that will determine whether a specific robotic problem solution is possible. To put it plainly, with the current capability of the buildhat, we can only build rather slow industry style robots. Anything that requires real time control will fail. This is likely to disappoint many people.

@tenderandshy
Copy link

When I tried to download python 3 library for build hat it didn't work
raise Hat not found()
build hat.exc. Hat Not Found
I am trying to use the build hat with Lego Spike Prime and i am not getting anywhere Could someone help me, please?

@mutesplash
Copy link
Contributor

You should use the RPi forums for this kind of support, not github issues, @tenderandshy

https://forums.raspberrypi.com/viewforum.php?f=45

@chrisruk
Copy link
Contributor

chrisruk commented Mar 3, 2022

@OliverFaust I think I see what you're trying to achieve with regards to using a gamepad and controlling the motor speed.

Mutesplash mentioned some good points re. throttling the messages. I'll have a think about this.

@OliverFaust
Copy link
Author

@chrisruk from a user perspective, throttling the messages is inevitable. The task is to find a solution which does not kill the performance.
The game pad controlled car might be a good use case to determine the level of performance needed for a large class of real time applications. My experiments show that a rate of 5 messages a second is still acceptable - anything less will significantly diminish the user experience, i.e. the game pad controlled car is undrivable. That result was established by inserting a sleep(0.2) into the while loop.
You might also want to look into the sequence which precedes the error. What I observed was BuildHAT.DEVTIMEOUT followed by BuildHAT.DISCONNECTED. It might be possible to avoid BuildHAT.DISCONNECTED by handling BuildHAT.DEVTIMEOUT properly.

@mutesplash
Copy link
Contributor

The devtimeout and disconnected BuildHAT errors actually are text that is sent from the firmware on the RP2040 that refer to a specific motor port, indicating that the motors themselves literally cannot handle the data rate you're sending.

@OliverFaust
Copy link
Author

Unfortunately, there is no official documentation available. However, from what I understand, the H-bridge, which determines the motor speed, sits on the BuildHAT PCB. If this is the case, it might be possible to set the motor speed without communicating over the motor data interface.

@chrisruk
Copy link
Contributor

Closing now, thanks to @mutesplash for the pull request!

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

No branches or pull requests

4 participants