Toon posts:

[Python] Voer command uit en controleer uitkomst op string

Pagina: 1
Acties:

Vraag


Anoniem: 1277286

Topicstarter
Mijn vraag
Ik heb een Python script dat een command uitvoert m.b.v. subprocess.run(). Deze command heeft een uitkomst, maar die wordt niet meteen "gemaakt". Er is een onbekende tijd tussen waneer er een reactie/uitkomt is. Wat ik nu wil, is dat er na het uitvoeren van het commando, telkens gecheckt wordt of er al een reactie is. Als dit het geval is, dan wil ik deze reactie controleren om te kijken of er een specifieke string in zit. Als deze string aanwezig is, dan wil ik het subprocess sluiten en een andere normale Python functie uitvoeren.

Relevante software en hardware die ik gebruik
Python3 op Ubuntu 16.04 met eric6 Python IDE

Wat ik al gevonden of geprobeerd heb
Ik heb al gekeken naar subprocess.check_output(), of de uitkomst van het command decoden om vervolgens de string eruit te halen. Echter loop ik (volgens mij) telkens vast op het feit dat er geen onmiddelijke reactie is op het command.

Aanvullende informatie
Dit is het script wat ik nu heb. Hiermee krijg ik al wel de gewenste output op mijn scherm. Echter lukt het me niet om de gewenste vervolgstappen te halen.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import subprocess
import sys

def next_step():
  print("Navigation goal reached!")

def check_status():
  command = "rostopic echo move_base/result/status/text"
  task = subprocess.run(command, shell=True, stderr=subprocess.PIPE)
  while(True):
    out = task.stderr.read(1)
    if out == '' and task.poll() != None:
      break
    elif out != '':
      sys.stdout.write(out)
      sys.stdout.flush()

if __name__ == "__main__":
  check_status()


De output van het command is na X seconden (nogmaals, deze periode is onbekend en kan variëren van 3 seconden tot enkele minuten):
code:
1
2
"Goal reached."
---

Nu wil ik in mijn code dus controleren of de string "Goal reached" aanwezig is in de output. Zo ja, dan wil ik uit de hele next_step() functie stappen (en alles wat hierin staat sluiten) en doorgaan naar de next_step() functie.

Hopelijk heb ik genoeg informatie gegeven en is er iemand die mij kan helpen :)

Beste antwoord (via Anoniem: 1277286 op 03-06-2020 19:21)


  • CCJJVV
  • Registratie: Maart 2016
  • Laatst online: 15:40
Even een vraagje vooraf, ben je zelf bezig met ROS of is dit een bestaand iets dat je wilt uitbreiden?

Dit is namelijk niet de manier waarop binnen ROS naar een topic geluisterd moet worden. Het kan weliswaar door het commando rostopic echo $_topic_name te gebruiken alleen is dit commando eigenlijk bedoeld om makkelijk te debuggen.

Als ik jou was dan zou ik de volgende tutorial even doorlezen: http://wiki.ros.org/ROS/T...herSubscriber%28python%29. En dan in het bijzonder de subscriber node die er als volgt uit ziet:


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def listener():

    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("chatter", String, callback) 
    rospy.spin()

if __name__ == '__main__':
    listener()


In de callback functie krijg je in principe terug wat jij wilt hebben.
Elke keer dat er een message op het "chatter" topic wordt gepubliceerd wordt de callback functie aangeroepen.

De oplossing die je nu probeert uit te werken is nogal een anti-pattern. Het feit dat het je er moeite mee hebt zegt eigenlijk al dat het makkelijker kan.

[Voor 11% gewijzigd door CCJJVV op 03-06-2020 18:47]

Alle reacties


Acties:
  • +1Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Kijk even naar de documentatie van subprocess.run: https://docs.python.org/3...ocess.html#subprocess.run
Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

[...]

If capture_output is true, stdout and stderr will be captured. When used, the internal Popen object is automatically created with stdout=PIPE and stderr=PIPE. The stdout and stderr arguments may not be supplied at the same time as capture_output. If you wish to capture and combine both streams into one, use stdout=PIPE and stderr=STDOUT instead of capture_output.
Dus:
1) Je loop is overbodig. Op het moment dat .run() returnt is het proces al klaar
2) je moet zorgen dat je zowel stdout als stderr gecaptured worden

Probeer run() eens op bovenstaande manier te draaien, en probeer dan iets als
code:
1
2
print("stdout: ", task.stdout.read())
print("stderr: ", task.stderr.read())


waarbij read() géén parameter krijgt (je wilt immers alles lezen, niet maar één byte!)

Houd er ook rekening mee dat stdout/stderr standaard bytes teruggeven en geen string:
If encoding or errors are specified, or text is true, file objects for stdin, stdout and stderr are opened in text mode using the specified encoding and errors or the io.TextIOWrapper default. The universal_newlines argument is equivalent to text and is provided for backwards compatibility. By default, file objects are opened in binary mode.

Anoniem: 1277286

Topicstarter
ValHallASW schreef op woensdag 3 juni 2020 @ 17:29:
Kijk even naar de documentatie van subprocess.run: https://docs.python.org/3...ocess.html#subprocess.run


[...]


Dus:
1) Je loop is overbodig. Op het moment dat .run() returnt is het proces al klaar
2) je moet zorgen dat je zowel stdout als stderr gecaptured worden

Probeer run() eens op bovenstaande manier te draaien, en probeer dan iets als
code:
1
2
print("stdout: ", task.stdout.read())
print("stderr: ", task.stderr.read())


waarbij read() géén parameter krijgt (je wilt immers alles lezen, niet maar één byte!)

Houd er ook rekening mee dat stdout/stderr standaard bytes teruggeven en geen string:

[...]
Bedankt voor je reactie!

Als ik mijn script aanpas a.d.h.v. jouw feedback, dan ziet het er als volgt uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
import subprocess

def next_step():
  print("Navigation goal reached!")

def check_status():
  command = "rostopic echo move_base/result/status/text"
  task = subprocess.run(command, capture_output=True, shell=True)
  print("stdout: ", task.stdout.read())
  print("stderr: ", task.stderr.read())

if __name__ == "__main__":
  check_status()


Hierbij komt er niets in de terminal te staan, terwijl als ik handmatig in een terminal het command "rostopic echo move_base/result/status/text invoer, dan krijg ik wel de verwachte uitkomst.

Verder heb ik de volgende twee varianten geprobeerd met decoding:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
import subprocess

def next_step():
  print("Navigation goal reached!")

def check_status():
  command = "rostopic echo move_base/result/status/text"
  task = subprocess.run(command, capture_output=True, shell=True)
  print("stdout: ", task.stdout.read().decode("utf-8"))
  print("stderr: ", task.stderr.read().decode("utf-8"))

if __name__ == "__main__":
  check_status()

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
import subprocess

def next_step():
  print("Navigation goal reached!")

def check_status():
  command = "rostopic echo move_base/result/status/text"
  task = subprocess.run(command, capture_output=True, shell=True)
  print("stdout: ", str(task.stdout.read().decode("utf-8")))
  print("stderr: ", str(task.stderr.read().decode("utf-8")))

if __name__ == "__main__":
  check_status()

Helaas ook zonder succes...

Acties:
  • +1Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Vreemd dat je geen enkele output krijgt. Ik heb het even lokaal geprobeerd:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> def check_status():
...   command = "rostopic echo move_base/result/status/text"
...   task = subprocess.run(command, capture_output=True, shell=True)
...   print("stdout: ", str(task.stdout.read().decode("utf-8")))
...   print("stderr: ", str(task.stderr.read().decode("utf-8")))
...
>>> check_status()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in check_status
AttributeError: 'bytes' object has no attribute 'read'


In andere woorden, stdout/stderr zijn blijkbaar geen streams maar bytestrings. Dat is overigens ook consistent met de documentatie zie ik nu.

Allereerst zou ik dus even uitzoeken waarom je die foutmelding niet krijgt: anders is het erg lastig het geheel te debuggen.

Ik krijg in ieder geval in task.stderr gewoon resultaat (weliswaar ''rostopic' is not recognized as an internal or external command', maar dat maakt niet uit voor het Python-gedeelte ;-))

Acties:
  • Beste antwoord
  • +1Henk 'm!

  • CCJJVV
  • Registratie: Maart 2016
  • Laatst online: 15:40
Even een vraagje vooraf, ben je zelf bezig met ROS of is dit een bestaand iets dat je wilt uitbreiden?

Dit is namelijk niet de manier waarop binnen ROS naar een topic geluisterd moet worden. Het kan weliswaar door het commando rostopic echo $_topic_name te gebruiken alleen is dit commando eigenlijk bedoeld om makkelijk te debuggen.

Als ik jou was dan zou ik de volgende tutorial even doorlezen: http://wiki.ros.org/ROS/T...herSubscriber%28python%29. En dan in het bijzonder de subscriber node die er als volgt uit ziet:


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def listener():

    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("chatter", String, callback) 
    rospy.spin()

if __name__ == '__main__':
    listener()


In de callback functie krijg je in principe terug wat jij wilt hebben.
Elke keer dat er een message op het "chatter" topic wordt gepubliceerd wordt de callback functie aangeroepen.

De oplossing die je nu probeert uit te werken is nogal een anti-pattern. Het feit dat het je er moeite mee hebt zegt eigenlijk al dat het makkelijker kan.

[Voor 11% gewijzigd door CCJJVV op 03-06-2020 18:47]


Anoniem: 1277286

Topicstarter
CCJJVV schreef op woensdag 3 juni 2020 @ 18:41:
Even een vraagje vooraf, ben je zelf bezig met ROS of is dit een bestaand iets dat je wilt uitbreiden?

Dit is namelijk niet de manier waarop binnen ROS naar een topic geluisterd moet worden. Het kan weliswaar door het commando rostopic echo $_topic_name te gebruiken alleen is dit commando eigenlijk bedoeld om makkelijk te debuggen.

Als ik jou was dan zou ik de volgende tutorial even doorlezen: http://wiki.ros.org/ROS/T...herSubscriber%28python%29. En dan in het bijzonder de subscriber node die er als volgt uit ziet:


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def listener():

    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("chatter", String, callback) 
    rospy.spin()

if __name__ == '__main__':
    listener()


In de callback functie krijg je in principe terug wat jij wilt hebben.
Elke keer dat er een message op het "chatter" topic wordt gepubliceerd wordt de callback functie aangeroepen.
Ik ben zelf bezig met ROS en had inderdaad de door jou gelinkte pagina ook gevonden.
Waar ik echter mee zit, is dat mijn Python script een "los" script is, en geen ROS package is. Dus ik start het script éénmaal in de terminal (python3 script.py), waarna het eindeloos in de terminal draait. Voor zover ik het begrijp, werkt jouw code alleen als je van dat script een ROS package maakt (toch?). Uiteindelijk moet deze code in een Python automatiseringsscript komen, dus vandaar dat het oneindig draait.

Edit, ter uitbreiding: ik hoef niet continu het topic te beluisteren, slechts als ik de robot onderweg stuur. Dan wil ik weten als hij is aangekomen, zodat ik dan de robotarm kan aansturen (dit gebeurt ook vanuit mijn Python script).

@ValHallASW Als ik het script in een terminal open, krijg ik geen meldingen o.i.d. te zien, maar in de IDE krijg ik dezelfde foutmeldingen die jij hebt.

[Voor 17% gewijzigd door Anoniem: 1277286 op 03-06-2020 18:53]


Acties:
  • +1Henk 'm!

  • CCJJVV
  • Registratie: Maart 2016
  • Laatst online: 15:40
Anoniem: 1277286 schreef op woensdag 3 juni 2020 @ 18:51:
[...]

Ik ben zelf bezig met ROS en had inderdaad de door jou gelinkte pagina ook gevonden.
Waar ik echter mee zit, is dat mijn Python script een "los" script is, en geen ROS package is. Dus ik start het script éénmaal in de terminal (python3 script.py), waarna het eindeloos in de terminal draait. Voor zover ik het begrijp, werkt jouw code alleen als je van dat script een ROS package maakt (toch?). Uiteindelijk moet deze code in een Python automatiseringsscript komen, dus vandaar dat het oneindig draait.

@ValHallASW Als ik het script in een terminal open, krijg ik geen meldingen o.i.d. te zien, maar in de IDE krijg ik dezelfde foutmeldingen die jij hebt.
Ik moet eerlijk bekennen dat ik zelf nog nooit ROS i.c.m. python gebruikt heb en alleen ervaring met ROS i.c.m. C++ heb. In C++ kan je in ieder geval een "ROS-node" maken zonder dat deze in een package hoeft te zitten.

Als ik het volgende antwoord mag geloven is dit voor python ook mogelijk en zou het niet uitmaken of deze in een package zit of niet.


Als je maar naar één bericht wilt luisteren kan je in de callback simpelweg unregister() aanroepen. Het zou er dan ongeveer zo uit zien:

code:
1



Edit:

Daarnaast zie ik ook nog niet waarom je jouw zogenoemde automatiseringsscript niet als ROS-package wilt hebben. Je maakt immers gebruik van ROS dus waarom niet?

[Voor 15% gewijzigd door CCJJVV op 03-06-2020 19:11]


Anoniem: 1277286

Topicstarter
CCJJVV schreef op woensdag 3 juni 2020 @ 18:55:
[...]


Ik moet eerlijk bekennen dat ik zelf nog nooit ROS i.c.m. python gebruikt heb en alleen ervaring met ROS i.c.m. C++ heb. In C++ kan je in ieder geval een "ROS-node" maken zonder dat deze in een package hoeft te zitten.

Als ik het volgende antwoord mag geloven is dit voor python ook mogelijk en zou het niet uitmaken of deze in een package zit of niet.


Als je maar naar één bericht wilt luisteren kan je in de callback simpelweg unregister() aanroepen. Het zou er dan ongeveer zo uit zien:

code:
1
2
3
4
5
def callback(msg):
   subscriber.unregister()

global subscriber
subscriber = rospy.Subscriber('my_topic,', String, callback)
Hmmm, het moet inderdaad wel kunnen om een los Python script als node te runnen. Het probleem is dat ik niet weet wanneer het bericht komt, en er verder meer informatie op het desbetreffende topic wordt geplaatst. Maar misschien dat ik dan beter op het ROS forum kan vragen naar een geschikte oplossing...

Acties:
  • +1Henk 'm!

  • CCJJVV
  • Registratie: Maart 2016
  • Laatst online: 15:40
Anoniem: 1277286 schreef op woensdag 3 juni 2020 @ 19:13:
[...]

Hmmm, het moet inderdaad wel kunnen om een los Python script als node te runnen. Het probleem is dat ik niet weet wanneer het bericht komt, en er verder meer informatie op het desbetreffende topic wordt geplaatst. Maar misschien dat ik dan beter op het ROS forum kan vragen naar een geschikte oplossing...
Dat je niet weet wanneer het bericht binnenkomt is inprincipe niet erg? Nadat je de subscriber hebt aangemaakt en in je hoofd-loop rospy.rosSpinOnce() blijft aanroepen zal voor elk bericht dat op het topic gepubliceerd wordt de callback-functie aangeroepen worden.

Daarnaast is het dan misschien handig om verschillende topics aan te maken waarbij elk topic zijn eigen taak heeft.


Aangezien je aangeeft dat je de robot aanstuurt neem ik aan dat die robot gebruik maakt van ROS omdat deze uiteindelijk ook het door jou gewilde bericht verstuurd.

Ik heb geen idee hoe gevorderd je bent in het gebruik van ROS en programmeren in het algemeen. Maar je zou naar ROS-services en ROS-actions kunnen kijken, die zijn volgens mij voor jouw doeleinde perfect.
Maar nogmaals, je gaat qua moeilijkheidsgraad dan alweer een trapje omhoog.


Edit:

In je post hieronder zie ik dat je al gebruik maakt van een ROS-action 8)7 d:)b

[Voor 3% gewijzigd door CCJJVV op 03-06-2020 19:31]


Acties:
  • +1Henk 'm!

Anoniem: 1277286

Topicstarter
Oké, ik had toch wat verder door moeten prutsen met @CCJJVV zijn post:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def listener():

    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("chatter", String, callback) 
    rospy.spin()

if __name__ == '__main__':
    listener()
Ik heb dit aangepast naar:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import rospy
from move_base_msgs.msg import MoveBaseActionResult

def callback(data):
    if data.status.text.find("Goal reached") != -1
      print("Navigation goal has been reached!")
    
def listener():

    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("/move_base/result", MoveBaseActionResult, callback) 
    rospy.spin()

if __name__ == '__main__':
    listener()

Dit werkt perfect d:)b

Dus bedankt @ValHallASW voor het meedenken w.b.h. Python gedeelte, en @CCJJVV bedankt voor het aanraden van de Python subscriber code. Had hier toch zelf wat langer mee moeten prutsen. Maar goed, het werkt :)
Pagina: 1



Google Pixel 7 Sony WH-1000XM5 Apple iPhone 14 Samsung Galaxy Watch5, 44mm Sonic Frontiers Samsung Galaxy Z Fold4 Insta360 X3 Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2022 Hosting door True

Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee