ev3dev / ev3dev-lang-python

Pure python bindings for ev3dev
MIT License
431 stars 145 forks source link

Can't use screen AND motors ? #276

Closed romainschmid closed 7 years ago

romainschmid commented 7 years ago

Hi,

I can't figure what the problem is with my code : I want to calibrate my light sensor, in order to follow a black line. I want the robot to oscillate 4 times, so that the sensor can update the max_white and max_black values. I have succeeded using the lcd screen and the motors, but separately. If I put all my functions in the same file, it will not work, but if I use the functions in different files, the code works. In fact, the motors will run, but the display isn't even changed from its original state, and the values are not updated.

I am not exactly sure if it's a bug somewhere, or if it's due to my very beginner level in programming.

Here is my code :

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from ev3dev.ev3 import *
from  PIL import ImageFont
from threading import Thread
from time import sleep
lcd = Screen()
globalstop = 0
ts = TouchSensor()
color_sensor = ColorSensor()
color_sensor.mode='COL-REFLECT'
motor_left = LargeMotor('outB')
motor_right = LargeMotor('outC')
f = ImageFont.truetype('/usr/share/fonts/truetype/msttcorefonts/Arial.ttf', 15)
max_white = 0
max_black = 100

def motors():
    global globalstop
    sleep(0.5)
    for i in range(4):
        if i % 2 == 0:
            motor_right.run_to_rel_pos(position_sp=180, speed_sp=200, stop_action='brake')
            motor_left.run_to_rel_pos(position_sp=-180, speed_sp=200, stop_action='brake')
            motor_right.wait_while('running')
            motor_left.wait_while('running')

        else:
            motor_right.run_to_rel_pos(position_sp=-180, speed_sp=200, stop_action='brake')
            motor_left.run_to_rel_pos(position_sp=180, speed_sp=200, stop_action='brake')
            motor_right.wait_while('running')
            motor_left.wait_while('running')
        print (i+1)
    globalstop = 1

motors = Thread(target=motors())

def display_and_update():
    global max_white, max_black, globalstop
    while globalstop == 0:
        lcd.clear()
        lcd.draw.text((10, 10), "max_white : %s" % max_white,font=f)
        lcd.draw.text((10, 40), "max_black : %s" % max_black,font=f)
        lcd.update()
        if max_white < color_sensor.value():
            max_white = color_sensor.value()
        if max_black > color_sensor.value():
            max_black = color_sensor.value()

disp_and_update = Thread(target=display_and_update())

#start the threads
motors.start()
disp_and_update.start()

#finally, output the values
print ("max_white : ", max_white, "max_black : ", max_black)

Any help appreciated !

ddemidov commented 7 years ago

How do you run the program?

If you do this from an ssh session, then brickman (the default interface) is still in control of the screen and is probably overwriting your changes.

If you launch the script from brickman, the script should get a separate virtual screen and will have full control of it.

It is also possible to get the separate screen from the command line with sudo chvt <n> where <n> is replaced by the virtual terminal you want to switch to. You can use this to switch between Brickman on tty1 and a text login on tty2. (see https://github.com/rhempel/ev3dev-lang-python/issues/222#issuecomment-250642791)

EDIT: another link: https://github.com/rhempel/ev3dev-lang-python/issues/194#issuecomment-244631589

romainschmid commented 7 years ago

Aha, I remember reading about that. I must confess I didn't understand neither the post you mentioned nor your answer ! I am on a Mac, and not really familiar with non GUI apps, like terminal, although I'm getting better. Could you tell me where exactly I should sudo something ? On the bot, when ssh-logged, or on my computer ?

I never launch my programs on the brick directly, because I am using a nice pyCharm feature, the git repository. Like that, I don't have to chmod my files in order to launch them directly on the brick. Plus, I can let my bot on my playground, and remain on my seat !

romainschmid commented 7 years ago

Thank you for your help. I didn't get exactly what I wanted with the trick you showed me. It displays some kind of terminal on the bot, but what I wanted to have was to be able to display live values from the light sensor (I can do it, but in a separate program). Although it's not exactly an issue, could you help me with this : I really don't understand something about threads : aren't they supposed to work independently, at the SAME time ?? This code might help you understand my problem :

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from ev3dev.ev3 import *
from threading import Thread

gauche = LargeMotor('outB')
droite = LargeMotor('outC')

def m_droite():
    for i in range(4):
        droite.run_to_rel_pos(position_sp=90, speed_sp=100, stop_action='brake')
        droite.wait_while('running')
        print ("gauche :", i)

def m_gauche():
    for j in range(4):
        gauche.run_to_rel_pos(position_sp=90, speed_sp=100, stop_action='brake')
        gauche.wait_while('running')
        print ("droite : ", j)

right = Thread(target=m_droite())
left = Thread(target=m_gauche())

right.start()
left.start()

Instead of making both motors turn at the same time, they do it one after the other ! Is it ok, doc ?

ddemidov commented 7 years ago

There is a demo project that uses threads here: https://github.com/rhempel/ev3dev-lang-python/blob/develop/demo/R3PTAR/r3ptar.py.

romainschmid commented 7 years ago

Thanks for the link. I will check this, definitely. I've read something about threads, and it's a bit complicated for me now. I think I will have to study that .join thing... The http://stackoverflow.com/questions/15085348/what-is-the-use-of-join-in-python-threading forum article pointed me on the way, and I've seen that in the code you mentioned. Nice week-end, I will have to dig something nice !

ddemidov commented 7 years ago

Could you tell me where exactly I should sudo something ? On the bot, when ssh-logged, or on my computer ?

You need to enter this into the command prompt of the ssh session:

robot@ev3dev:~$ sudo chvt 2
[sudo] password for robot:

when you enter your password, you should see that the robot screen switched from the brickman interface to a new console with a login prompt. Ignore that prompt.

Now, when you update the screen from your python script, brickman won't be able to mess with the results. The following script works for me:

from time import sleep
import ev3dev.ev3   as ev3
import ev3dev.fonts as fonts

ir = ev3.InfraredSensor()
screen = ev3.Screen()
f = fonts.load('luBS18')

while True:
    screen.clear()
    screen.draw.text((10,10), 'Dist: {}'.format(ir.proximity), font=f)
    screen.update()
    sleep(0.25)

When you are done and want to bring the brickman interface back, enter in the ssh session:

robot@ev3dev:~$  sudo chvt 1
WasabiFan commented 7 years ago

@ndward has a guide on doing this at the bottom of this page: https://sites.google.com/site/ev3python/learn_ev3_python/screen.

robot@ev3dev:~$ sudo chvt 2

@ddemidov I think (?) that virtual terminal 2 is reserved for Brickman child apps or something of that nature... I can't remember for sure though. Either way it shouldn't make any real difference.

dlech commented 7 years ago

I think (?) that virtual terminal 2 is reserved for Brickman child apps

OpenRoberta, specifically. But if openroberta.service is not running, it is free.