Python для робототехники: как создать приложение для локализации робота Pepper

На днях я попытался сгенерировать приложение, используя шаблон pythonapp из проекта Github Jumpstarter(https://github.com/aldebaran/robot-jumpstarter), чтобы выполнить локализацию Pepper. Моя основная идея состоит в том, чтобы объединить модуль LandmarkDetector в сгенерированное приложение «Lokalisierung» (локализация немецкого языка). mainlandmarkdetection

Вы можете прочитать весь код «LandmarkDetector.py», «main.py» и «MainLandmarkDetection.py» здесь:

"LandmarkDetector.py":

#! /usr/bin/env python
# -*- encoding: UTF-8 -*-

"""Пример: демонстрирует способ локализации робота с помощью ALLandMarkDetection"""

import qi
import time
import sys
import argparse
import math
import almath


class LandmarkDetector(object):
"""
We first instantiate a proxy to the ALLandMarkDetection module
Note that this module should be loaded on the robot's naoqi.
The module output its results in ALMemory in a variable
called "LandmarkDetected".
We then read this ALMemory value and check whether we get
interesting things.
After that we get the related position of the landmark compared to robot.
"""
def __init__(self, app):
    """
    Initialisation of qi framework and event detection.
    """
    super(LandmarkDetector, self).__init__()

    app.start()
    session = app.session
    # Get the service ALMemory.
    self.memory = session.service("ALMemory")
    # Connect the event callback.

    # Get the services ALMotion & ALRobotPosture.

    self.motion_service = session.service("ALMotion")
    self.posture_service = session.service("ALRobotPosture")

    self.subscriber = self.memory.subscriber("LandmarkDetected")
    print "self.subscriber = self.memory.subscriber(LandmarkDetected)"
    self.subscriber.signal.connect(self.on_landmark_detected)
    print "self.subscriber.signal.connect(self.on_landmark_detected)"
    # Get the services ALTextToSpeech, ALLandMarkDetection and ALMotion.
    self.tts = session.service("ALTextToSpeech")
    self.landmark_detection = session.service("ALLandMarkDetection")
  #  print "self.landmark_detection" is repr(self.landmark_detection)
    self.motion_service = session.service("ALMotion")
    self.landmark_detection.subscribe("LandmarkDetector", 500, 0.0 )
    print "self.landmark_detection.subscribe(LandmarkDetector, 500, 0.0 )"
    self.got_landmark = False
    # Set here the size of the landmark in meters.
    self.landmarkTheoreticalSize = 0.06 #in meters 0  #.05 or 0.06?
    # Set here the current camera ("CameraTop" or "CameraBottom").
    self.currentCamera = "CameraTop"

def on_landmark_detected(self, markData):
    """
    Callback for event LandmarkDetected.
    """
    while markData == [] :  # empty value when the landmark disappears
        self.got_landmark = False
        self.motion_service.moveTo(0, 0, 0.1 * math.pi)

    if not self.got_landmark:  # only speak the first time a landmark appears
        self.got_landmark = True

#stop.motion_service.moveTo

        print "Ich sehe eine Landmarke! "
        self.tts.say("Ich sehe eine Landmarke! ")

        # Retrieve landmark center position in radians.
        wzCamera = markData[1][0][0][1]
        wyCamera = markData[1][0][0][2]

        # Retrieve landmark angular size in radians.
        angularSize = markData[1][0][0][3]

        # Compute distance to landmark.
        distanceFromCameraToLandmark = self.landmarkTheoreticalSize / ( 2 * math.tan( angularSize / 2))

        # Get current camera position in NAO space.
        transform = self.motion_service.getTransform(self.currentCamera, 2, True)
        transformList = almath.vectorFloat(transform)
        robotToCamera = almath.Transform(transformList)

        # Compute the rotation to point towards the landmark.
        cameraToLandmarkRotationTransform = almath.Transform_from3DRotation(0, wyCamera, wzCamera)

        # Compute the translation to reach the landmark.
        cameraToLandmarkTranslationTransform = almath.Transform(distanceFromCameraToLandmark, 0, 0)

        # Combine all transformations to get the landmark position in NAO space.
        robotToLandmark = robotToCamera * cameraToLandmarkRotationTransform *cameraToLandmarkTranslationTransform

#    robotTurnAroundAngle = almath.rotationFromAngleDirection(0, 1, 1, 1)
#        print "robotTurnAroundAngle = ", robotTurnAroundAngle

        print "x " + str(robotToLandmark.r1_c4) + " (in meters)"
        print "y " + str(robotToLandmark.r2_c4) + " (in meters)"
        print "z " + str(robotToLandmark.r3_c4) + " (in meters)"

def run(self):
    """
    Loop on, wait for events until manual interruption.
    """

    # Wake up robot
    self.motion_service.wakeUp()

    # Send robot to Pose Init
    self.posture_service.goToPosture("StandInit", 0.5)

    # Example showing how to get a simplified robot position in world.
    useSensorValues = False
    result = self.motion_service.getRobotPosition(useSensorValues)
    print "Robot Position", result

    # Example showing how to use this information to know the robot's diplacement.
    useSensorValues = False
    #   initRobotPosition = almath.Pose2D(self.motion_service.getRobotPosition(useSensorValues))

    # Make the robot move
    for i in range(1, 12, 1):
        self.motion_service.moveTo(0, 0, 0.1 * math.pi)
        print "self.motion_service.moveTo(0, 0, (0.1)*math.pi)"

    print "Starting LandmarkDetector"
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print "Interrupted by user, stopping LandmarkDetector"
        self.landmark_detection.unsubscribe("LandmarkDetector")
        #stop
        sys.exit(0)


if __name__ == "__main__":


    parser = argparse.ArgumentParser()
    parser.add_argument("--ip", type=str, default="10.0.0.10",
                    help="Robot IP address. On robot or Local Naoqi: use 
'10.0.0.10'.")
    parser.add_argument("--port", type=int, default=9559,
                    help="Naoqi port number")

    args = parser.parse_args()
    try:
        # Initialize qi framework.
        connection_url = "tcp://" + args.ip + ":" + str(args.port)
        app = qi.Application(["LandmarkDetector", "--qi-url=" + connection_url])
    except RuntimeError:
        print ("Can't connect to Naoqi at ip \"" + args.ip + "\" on port " + str(args.port) +".\n"
               "Please check your script arguments. Run with -h option for help.")
        sys.exit(1)
    landmark_detector = LandmarkDetector(app)
    landmark_detector.run()

"main.py":

""" Пример, показывающий, как сделать скрипт Python в виде приложения. """

версия = "0.0.8"

copyright = "Авторское право 2015, Aldebaran Robotics" автор = 'ВАШЕ ИМЯ' электронная почта = 'ВАША ПОЧТА@aldebaran.com'

import stk.runner
import stk.events
import stk.services
import stk.logging

class Activity(object):

"Пример автономного приложения, демонстрирующий простое использование Python" APP_ID = "com.aldebaran.lokalisierung"

def __init__(self, qiapp):
    self.qiapp = qiapp
    self.events = stk.events.EventHelper(qiapp.session)
    self.s = stk.services.ServiceCache(qiapp.session)
    self.logger = stk.logging.get_logger(qiapp.session, self.APP_ID)

def on_touched(self, *args):
    "Callback for tablet touched."
    if args:
        self.events.disconnect("ALTabletService.onTouchDown")
        self.logger.info("Tablet touched: " + str(args))
        self.s.ALTextToSpeech.say("Yay!")
        self.stop()

def on_start(self):
    "Ask to be touched, waits, and exits."
    # Two ways of waiting for events
    # 1) block until it's called

    self.s.ALTextToSpeech.say("Touch my forehead.")
    self.logger.warning("Listening for touch...")
    while not self.events.wait_for("FrontTactilTouched"):
        pass

    # 2) explicitly connect a callback
    if self.s.ALTabletService:
        self.events.connect("ALTabletService.onTouchDown", self.on_touched)
        self.s.ALTextToSpeech.say("okay, now touch my tablet.")
        # (this allows to simltaneously speak and watch an event)
    else:
        self.s.ALTextToSpeech.say("touch my tablet ... oh. " + \
            "I don't haave one.")
        self.stop()

def stop(self):
    "Standard way of stopping the application."
    self.qiapp.stop()

def on_stop(self):
    "Cleanup"
    self.logger.info("Application finished.")
    self.events.clear()

if __name__ == "__main__":


    stk.runner.run_activity(Activity)

"MainLandmarkDetection.py":

#! /usr/bin/env python
# -*- encoding: UTF-8 -*-

"""Пример создания скрипта Python в качестве приложения для локализации робота с помощью ALLandMarkDetection"""

версия = "0.0.8"

copyright = "Авторское право 2015, Aldebaran Robotics"

автор = 'ВАШЕ ИМЯ'

электронная почта = '[email protected]'

import stk.runner
import stk.events
import stk.services
import stk.logging
import time
import sys
import math
import almath

class Activity(object):

"Пример автономного приложения, демонстрирующий простое использование Python" APP_ID = "com.aldebaran.lokalisierung"

def __init__(self, qiapp):

    self.qiapp = qiapp
    self.events = stk.events.EventHelper(qiapp.session)
    self.s = stk.services.ServiceCache(qiapp.session)
    self.logger = stk.logging.get_logger(qiapp.session, self.APP_ID)
    self.qiapp.start()
    session = qiapp.session
    # Get the service ALMemory.
    self.memory = session.service("ALMemory")
    # Connect the event callback.

    # Get the services ALMotion & ALRobotPosture.

    self.motion_service = session.service("ALMotion")
    self.posture_service = session.service("ALRobotPosture")

    self.subscriber = self.memory.subscriber("LandmarkDetected")
    print "self.subscriber = self.memory.subscriber(LandmarkDetected)"
    self.subscriber.signal.connect(self.on_landmark_detected)
    print "self.subscriber.signal.connect(self.on_landmark_detected)"
    # Get the services ALTextToSpeech, ALLandMarkDetection and ALMotion.
    self.tts = session.service("ALTextToSpeech")
    self.landmark_detection = session.service("ALLandMarkDetection")
    #  print "self.landmark_detection" is repr(self.landmark_detection)
    self.motion_service = session.service("ALMotion")
    self.landmark_detection.subscribe("Activity", 500, 0.0)
    print "self.landmark_detection.subscribe(Activity, 500, 0.0 )"
    self.got_landmark = False
    # Set here the size of the landmark in meters.
    self.landmarkTheoreticalSize = 0.06  # in meters 0  #.05 or 0.06?
    # Set here the current camera ("CameraTop" or "CameraBottom").
    self.currentCamera = "CameraTop"

def on_landmark_detected(self, markData):
    """
    Callback for event LandmarkDetected.
    """
    while markData == []:  # empty value when the landmark disappears
        self.got_landmark = False
    #           self.motion_service.moveTo(0, 0, 0.1 * math.pi)

    if not self.got_landmark:  # only speak the first time a landmark appears
        self.got_landmark = True

        # stop.motion_service.moveTo

        print "Ich sehe eine Landmarke! "
        #          self.tts.say("Ich sehe eine Landmarke! ")

        # Retrieve landmark center position in radians.
        wzCamera = markData[1][0][0][1]
        wyCamera = markData[1][0][0][2]

        # Retrieve landmark angular size in radians.
        angularSize = markData[1][0][0][3]

        # Compute distance to landmark.
        distanceFromCameraToLandmark = self.landmarkTheoreticalSize / (2 * math.tan(angularSize / 2))

        # Get current camera position in NAO space.
        transform = self.motion_service.getTransform(self.currentCamera, 2, True)
        transformList = almath.vectorFloat(transform)
        robotToCamera = almath.Transform(transformList)

        # Compute the rotation to point towards the landmark.
        cameraToLandmarkRotationTransform = almath.Transform_from3DRotation(0, wyCamera, wzCamera)

        # Compute the translation to reach the landmark.
        cameraToLandmarkTranslationTransform = almath.Transform(distanceFromCameraToLandmark, 0, 0)

        # Combine all transformations to get the landmark position in NAO space.
        robotToLandmark = robotToCamera * cameraToLandmarkRotationTransform * cameraToLandmarkTranslationTransform

        #    robotTurnAroundAngle = almath.rotationFromAngleDirection(0, 1, 1, 1)
        #        print "robotTurnAroundAngle = ", robotTurnAroundAngle

        print "x " + str(robotToLandmark.r1_c4) + " (in meters)"
        print "y " + str(robotToLandmark.r2_c4) + " (in meters)"
        print "z " + str(robotToLandmark.r3_c4) + " (in meters)"

def run(self):
    """
    Loop on, wait for events until manual interruption.
    """

    # Wake up robot
    self.motion_service.wakeUp()

    # Send robot to Pose Init
    self.posture_service.goToPosture("StandInit", 0.5)

    # Example showing how to get a simplified robot position in world.
    useSensorValues = False
    result = self.motion_service.getRobotPosition(useSensorValues)
    print "Robot Position", result

    # Example showing how to use this information to know the robot's diplacement.
    useSensorValues = False
    #   initRobotPosition = almath.Pose2D(self.motion_service.getRobotPosition(useSensorValues))

    # Make the robot move
    for i in range(1, 20, 1):
        self.motion_service.moveTo(0, 0, 0.1 * math.pi)
        print "self.motion_service.moveTo(0, 0, (0.1)*math.pi)"

    print "Starting Activity"
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print "Interrupted by user, stopping Activity"
        self.landmark_detection.unsubscribe("Activity")
        # stop
        sys.exit(0)

    def stop(self):
        "Standard way of stopping the application."
        self.qiapp.stop()

    def on_stop(self):
        "Cleanup"
        self.logger.info("Application finished.")
        self.events.clear()

if __name__ == "__main__":



    stk.runner.run_activity(Activity)

    landmark_detector = Activity()

    landmark_detector.run()

Вот как это работает в cmd.exe:

bugs_for_mainlandmarkdetection02bugs_for_mainlandmarkdetection03 И у меня вопрос к параметру "landmark_detector = Activity()" в строке 157 почти в конце программы из-за ошибки в изображении, которую я должен пройти. После прочтения ответов на аналогичный вопрос здесь StackoverflowPython: TypeError : __init__() принимает ровно 2 аргумента (1 указан), я все еще в замешательстве. Я думаю, что это должно быть значение, которое присваивается «qiapp», но какое значение?

С уважением, Фредерик


person Frederik Best    schedule 04.07.2018    source источник


Ответы (1)


На самом деле, когда вы звоните

stk.runner.run_activity(Activity)

... он создаст экземпляр этого объекта активности для вас с правильными параметрами, вам не нужно ориентир_детектор = Activity() и т. д. в MainLandmarkDetector.py

И если у этого объекта есть метод on_start, этот метод будет вызван, как только все будет готово (поэтому вам может понадобиться только переименовать метод run() в on_start()).

Также обратите внимание, что вместо вызова sys.stop() вы можете просто вызвать self.stop() или self.qiapp.stop() (что немного чище с точки зрения разрешения вызова кода очистки в on_stop, если вы нужно отписаться от вещей и т.д.)

См. здесь некоторую документацию по stk.runner.

Обратите также внимание, что вместо выполнения

self.motion_service = session.service("ALMotion")
(...)
transform = self.motion_service.getTransform(self.currentCamera, 2, True)

вы можете напрямую сделать

transform = self.s.ALMotion.getTransform(self.currentCamera, 2, True)

... что (на мой взгляд) облегчает просмотр того, что именно делается (и уменьшает количество переменных).

person Emile    schedule 05.07.2018
comment
Спасибо, это было действительно полезно! - person Frederik Best; 05.07.2018
comment
Что делать, если мне нужно, чтобы выходные данные, которые могут быть выведены в cmd.exe, также отображались в окне просмотра журнала Choregraphe? Нужно ли мне редактировать исполняемый файл приложения «Выполнить» вручную, например, добавлять некоторые выходные значения, или это можно сделать автоматически с помощью вашего шаблона? (Я уже изменил соответствующий executable_id и executable_name в поле «Выполнить исполняемый файл приложения python») - person Frederik Best; 06.07.2018
comment
Я не уверен, как получить эти журналы в Choregraphe, это может быть невозможно в NAOqi 2.1, вам придется попробовать кое-что (может быть, они уже есть? в противном случае проверьте /var/log/naoqi, но я не думаю, что вы их там найдёте) - person Emile; 09.07.2018
comment
В порядке. это NAOqi2.5 здесь. Вы имеете в виду логи на роботе, где я должен мешать Connection-Advanced-File Transfer... в Choregraphe? По пути /var/log/ есть только одна папка с именем rabbitmaq...... - person Frederik Best; 09.07.2018
comment
Если вы подключаетесь по SSH к роботу с NAOqi 2.5 (проверьте, какая версия установлена ​​на вашем роботе с помощью lsb_release -a), вы должны найти журналы в /var/log/naoqi. - person Emile; 24.07.2018
comment
Это могло произойти с моим Pepper так, как сказано в официальной документации aldebaran: поскольку каталог /var/log является изменчивым, он сбрасывается при каждом отключении питания. :) - person Frederik Best; 24.07.2018
comment
Журналы создаются, как только запускается naoqi, поэтому, если вы не отключили naoqi на своем роботе (я бы даже не знал, как это сделать), у вас должны быть вещи в /var/log/naoqi... странно. - person Emile; 24.07.2018
comment
Поскольку я не могу опубликовать изображение в качестве комментария к вашему ответу, мне пришлось поделиться с вами изображением окон SSH в другом ответе... - person Frederik Best; 24.07.2018