Best practice for turning the balboa? Should I use the encoder or drive_ticks?

Hello

The following code runs fine…

@app.route("/drive_test")
def drive_test():
    # The following is a VL6180X sensor attached via the I2C bus
    tof_distance = tof_sensor.get_distance()
    # while no object is detected 
    while (tof_distance > 254):
        # drive forwards
        drive(-10,-10)
        # and take another reading
        tof_distance = tof_sensor.get_distance()
    #if you detect an object, stop
    drive(0,0)
    #then turn slowly, for 0.5 seconds
    drive(10,-10)
    # pirouettes forever! Never executes the following sleep function
    time.sleep(0.5)
    drive(-10,-10)
    tof_distance = tof_sensor.get_distance()

Until the time.sleep(0.5). I’m trying to make the Balboa turn through 90 degrees to the left or right but there’s no time argument available to pass to the balancer.drive(int, int) call to tell the motors to spin for a fixed length of time before stopping.

Is there a better way to do this? Should I use short for loops to adjust the speed of the motor to get it to turn in an orderly and measurable way?

Many thanks

Could you try adding some debugging output by printing some short messages before and after time.sleep(0.5)? That should help narrow down where the program is getting stuck.

Kevin

Hi again Kevin,

Many thanks for the suggestion. I added the following basic logging:

import logging

@app.route("/drive_test")
def drive_test():
    tof_distance = tof_sensor.get_distance()
    while (tof_distance > 254):
        drive(-10,-10)
        tof_distance = tof_sensor.get_distance()
    logging.debug("Detected object")
    drive(0,0)
    logging.debug("Just stopped")
    drive(10,-10)
    logging.debug("Turning round through 10,-10")
    time.sleep(0.2)
    logging.debug("Just slept for 0.2 seconds")
    drive(-10,-10)
    logging.debug("Driving forwards on -10,-10")
    tof_distance = tof_sensor.get_distance()
    logging.debug("Taking VL6180X sensor reading")

and the output looks like this:

(snipped)
2019-02-09 09:14:51,343 192.168.1.137 - - [09/Feb/2019 09:14:51] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:51,614 192.168.1.137 - - [09/Feb/2019 09:14:51] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:55,415 Detected object
2019-02-09 09:14:55,416 Just stopped
2019-02-09 09:14:55,419 Turning round through 10,-10
2019-02-09 09:14:55,504 192.168.1.137 - - [09/Feb/2019 09:14:55] "GET /drive_test HTTP/1.1" 500 -
2019-02-09 09:14:55,629 192.168.1.137 - - [09/Feb/2019 09:14:55] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:55,634 127.0.0.1 - - [09/Feb/2019 09:14:55] "GET /heartbeat/1 HTTP/1.1" 200 -
2019-02-09 09:14:55,652 127.0.0.1 - - [09/Feb/2019 09:14:55] "GET /heartbeat/0 HTTP/1.1" 200 -
2019-02-09 09:14:55,885 192.168.1.137 - - [09/Feb/2019 09:14:55] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:56,147 192.168.1.137 - - [09/Feb/2019 09:14:56] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:56,417 192.168.1.137 - - [09/Feb/2019 09:14:56] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:56,686 192.168.1.137 - - [09/Feb/2019 09:14:56] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:56,696 127.0.0.1 - - [09/Feb/2019 09:14:56] "GET /heartbeat/1 HTTP/1.1" 200 -
2019-02-09 09:14:56,719 127.0.0.1 - - [09/Feb/2019 09:14:56] "GET /heartbeat/0 HTTP/1.1" 200 -
2019-02-09 09:14:56,956 192.168.1.137 - - [09/Feb/2019 09:14:56] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:57,226 192.168.1.137 - - [09/Feb/2019 09:14:57] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:57,504 192.168.1.137 - - [09/Feb/2019 09:14:57] "GET /status.json HTTP/1.1" 200 -
2019-02-09 09:14:57,771 192.168.1.137 - - [09/Feb/2019 09:14:57] "GET /status.json HTTP/1.1" 200 -
(snipped)

So it doesn’t really tell us much I think.

The only way I can see of breaking out of the loop is if Balboa32U4.motors in BalboaRPiSlaveDemo.ino has a milliseconds argument to run the motors for a fixed length of time. It doesn’t look like that code is available to modify though.

I’m not really sure what’s going on to cause your program to get stuck on the time.sleep(); I’ll try to look into it later today or sometime this week.

There isn’t a way to pass a duration to the slave program because all the commands are designed to immediately take effect (e.g. set the motor speed), and there’s no mechanism for either delaying an action or scheduling an action for a future time (with the exception of some buzzer functionality). Doing the wait on the Python side seems like the better way to do it to me, as long as we can figure out how to make it work correctly.

Kevin

I tried running some test programs in a virtual machine yesterday and was not able to reproduce your problem; the drive_test() function executed fine, other than causing Flask to raise an error afterward because it doesn’t return a valid response. I can try it on an actual Raspberry Pi, but in the meantime, could you also post all of your source files so I can look at the complete code you are using?

Here it is:

I added the return "" to make sure that the flask error doesn’t come up but that’s what I’ve got and it still hangs when it turns at the final debugging message:

@app.route("/drive_test")
def drive_test():
    tof_distance = tof_sensor.get_distance()
    while (tof_distance > 254):
        drive(-10,-10)
        tof_distance = tof_sensor.get_distance()
    logging.debug("Detected object")
    drive(0,0)
    logging.debug("Just stopped")
    drive(10,-10)
    logging.debug("Turning round through 10,-10")
    time.sleep(0.2)

and these are the debugging messages:

(snipped)

2019-02-20 21:07:07,747 192.168.1.137 - - [20/Feb/2019 21:07:07] "GET /status.json HTTP/1.1" 200 -
2019-02-20 21:07:08,805 Detected object
2019-02-20 21:07:08,806 Just stopped
2019-02-20 21:07:08,807 Turning round through 10,-10
2019-02-20 21:07:08,877 192.168.1.137 - - [20/Feb/2019 21:07:08] "GET /drive_test HTTP/1.1" 500 -

(snipped)

Thinking about it Kevin, would it be quicker if you post your working code because the above won’t work without the VL6180X ToF sensor? Thank you kindly for your help!

Here is my code:

test.zip (4.8 KB)

balance.py is unchanged from what’s in the RPi slave library repo, but the rest contains dummy implementations of the A-Star and sensors so that I could test the Flask part in a VM. I haven’t been able to look at your code yet, but I’ll try to see if I can get it running on some actual hardware today and reproduce your problem.

I’m testing your code on an actual Raspberry Pi and Balboa now, but with the VL6180X still dummied out, and I noticed that loading the drive_test page results in the error NameError: name 'time' is not defined because you don’t have import time in your server_balboa.py.

Could you check if that is the problem? I’m thinking you might have missed that error message since it doesn’t appear in the log file, only on the server console and on the drive_test page (if viewed in a browser). The behavior I saw is otherwise consistent with what you reported: the program seems to stop exactly when time.sleep() should run.

Kevin

Hi Kevin, yes that works - many thanks. Such a small thing and a goofy oversight. It wasn’t apparent to me because I run the script as a service and not all of the console messages appear in the service status.

For the purpose of this topic question, I think this can be closed. There is another issue below which has emerged, but I’m happy raising another thread if that’s preferable. This issue might be arising because I am now connecting to the RPI as a closed network wireless client, with hostapd and dhcpd configured to broadcast an SSID without Internet connectivity.

I observed your suggested fix (and a very basic one) working once but then something went awry and I can’t seem to right it. Now when I run the server_balboa.py using the vanilla github code, I get the following error, even after rebooting and resetting the 32U4!

root@rpi3-32U4-pololu:/home/pi/pololu-rpi-slave-arduino-library-balboa/pi# python3 server_balboa.py 
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python3/dist-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python3/dist-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/lib/python3/dist-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/pi/pololu-rpi-slave-arduino-library-balboa/pi/server_balboa.py", line 124, in hearbeat
    a_star.leds(not led0_state, not led1_state, not led2_state)
  File "/home/pi/pololu-rpi-slave-arduino-library-balboa/pi/a_star.py", line 42, in leds
    self.write_pack(0, 'BBB', red, yellow, green)
  File "/home/pi/pololu-rpi-slave-arduino-library-balboa/pi/a_star.py", line 37, in write_pack
    self.bus.write_i2c_block_data(SLAVE_ADDRESS, address, data_array)
  File "/usr/local/lib/python3.5/dist-packages/smbus/util.py", line 59, in validator
    return fn(*args, **kwdefaults)
  File "/usr/local/lib/python3.5/dist-packages/smbus/smbus.py", line 275, in write_i2c_block_data
    raise IOError(ffi.errno)
OSError: 121

The screen looks like this and the calibrate button doesn’t work. I am mystified.

wheres%20it%20gone

I’m glad to hear you solved the original problem. For the new error, since the error trace points to a problem in write_i2c_block_data, I would suspect some problem with the I2C communications. In particular, check that the second template argument for the PololuRPiSlave object in the Arduino code here is set to an appropriate value (which depends on your RPi model and the I2C speed you have configured). There are some notes on good values in PololuRPiSlave.h here.

You can run the benchmark.py script in the pi directory to test I2C. Usually, if the delay argument is wrong, you’ll see an error when running the benchmark; if you can run it a few times in a row with no errors, it’s probably fine.