Как скормить данные, полученные от rospy.Subscriber, в переменную?

Я написал образец подписчика. Я хочу передать данные, полученные от rospy.Subscriber, в другую переменную, чтобы потом использовать их в программе для обработки. На данный момент я мог видеть, что подписчик работает, поскольку я могу видеть, что подписанные значения печатаются, когда я использую функцию rospy.loginfo (). Хотя я не знаю, как сохранить эти данные в другой переменной. Я попытался назначить его напрямую переменной с помощью оператора присваивания '=', но получаю сообщение об ошибке.

Я попытался написать функцию обратного вызова с rospy.loginfo, чтобы распечатать данные позиции из подписанного объекта. Я подписался на JointState, и он содержит массивы заголовка, положения, скорости и усилий. с помощью rospy.loginfo я могу проверить, подписывается ли подписчик. Но когда я попытался назначить его напрямую переменной, я получил сообщение об ошибке.

Я показываю логин из функции обратного вызова следующим образом

def callback(data):
   rospy.loginfo(data.position)
   global listen
    listen = rospy.Subscriber("joint_states", JointState, 
    callback)
    rospy.spin()

и это прекрасно работает. Но когда я немного изменяю код для присвоения подписанных значений, я получаю следующую ошибку, т.е.

   listen1 = rospy.Subscriber("joint_states", JointState, 
   callback=None)
   listen = listen1.position
   #rospy.loginfo(listen)
   print(listen)
   rospy.spin()```

The error is as follows, 
 ```listen = listen1.position
    AttributeError: 'Subscriber' object has no attribute 'position'

РЕДАКТИРОВАТЬ: вот мой узел, который я определил в своей программе,

    #rospy.loginfo(msg.data)
    global tactile_states
    tactile_states = data.data

def joint_callback(data):
    #rospy.loginfo(data.position)
    global g_joint_states 
    global g_position
    global g_pos1
    g_joint_states = data
    #for i in len(data.position):
        #g_position[i] = data.position[i]
    g_position = data.position
    if len(data.position) > 0:
        print("jointstate more than 0")
        g_pos1 = data.position[0]
    #print(g_position)


def joint_modifier(*args):
    #choice describes what the node is supposed to do whether act as publisher or subscribe to joint states or tactile sensors
    rospy.init_node('joint_listener_publisher', anonymous=True)
    pub1 = rospy.Publisher('joint_states', JointState, queue_size = 10)
    if(len(args)>1):
        choice = args[0]
        joint_name = args[1]
        position = args[2]
    else:
        choice = args[0]
    if (choice == 1):
        rate = rospy.Rate(1)
        robot_configuration = JointState()
        robot_configuration.header = Header()
        robot_configuration.name = [joint_name]
        robot_configuration.position = [position]
        robot_configuration.velocity = [10]
        robot_configuration.effort = [100]
        while not rospy.is_shutdown():
            robot_configuration.header.stamp = rospy.Time.now()
            rospy.loginfo(robot_configuration)
            break
        pub1.publish(robot_configuration)
        rospy.sleep(2)
    if (choice == 2):
        #rospy.Timer(rospy.Duration(2), joint_modifier)
        listen = rospy.Subscriber("joint_states", JointState, joint_callback)
        rospy.spin()
    if (choice == 3):
        #rospy.Timer(rospy.Duration(2), joint_modifier)
        tactile_sub = rospy.Subscriber("/sr_tactile/touch/ff", Float64, tactile_callback)
        rospy.spin()

Вот как я вызываю узел внутри основной части программы,

           joint_modifier(2)
           print("printing g_position")
           print(g_position)#to check the format of g_position
           print("printed g _position")
           leg_1 = Leg_attribute(g_position[0], g_position[1], g_position[2], velocity1 = 10, velocity2 = 10, velocity3 = 10, effort1 = 100, effort2 = 100, effort3 = 100, acceleration=1)

При таком вызове программа застревает на joint_modifier(2), поскольку эта функция имеет rospy.spin().



person Sai Raghava    schedule 30.07.2019    source источник


Ответы (1)


Стиль, который вы используете, не очень стандартный. Я предполагаю, что вы видели пример в вики ROS, я изменил его, чтобы продемонстрировать стандартное использование ниже.

В основном, обращаясь к опубликованному вами коду, вам нужно было сделать listen глобальной областью действия вне функции обратного вызова. Это нужно для хранения data, которое вы хотите, а не для объекта подписчика. Rospy.spin () никогда не выполняет обратный вызов, только функция / раздел основного узла. Объект подписчика listen1, который используется нечасто, ничего не возвращает и не сохраняет полученные данные. То есть вам нужно, чтобы Subscriber () имел обратный вызов, отличный от None. Это больше похоже на bind, отдавая data callback вместо того, чтобы возвращать его от подписчика. Вот почему listen1 (подписчик) не имеет атрибут position (JointState).

import rospy
from sensor_msgs.msg import JointState

# Subscribers
#     joint_sub (sensor_msgs/JointState): "joint_states"

# This is where you store all your data you recieve
g_joint_states = None
g_positions = None
g_pos1 = None

def timer_callback(event): # Type rospy.TimerEvent
    print('timer_cb (' + str(event.current_real) + '): g_positions is')
    print(str(None) if g_positions is None else str(g_positions))

def joint_callback(data): # data of type JointState
    # Each subscriber gets 1 callback, and the callback either
    # stores information and/or computes something and/or publishes
    # It _does not!_ return anything
    global g_joint_states, g_positions, g_pos1
    rospy.loginfo(data.position)
    g_joint_states = data
    g_positions = data.position
    if len(data.position) > 0:
        g_pos1 = data.position[0]
    print(g_positions)

# In your main function, only! here do you subscribe to topics
def joint_logger_node():
    # Init ROS
    rospy.init_node('joint_logger_node', anonymous=True)

    # Subscribers
    # Each subscriber has the topic, topic type, AND the callback!
    rospy.Subscriber('joint_states', JointState, joint_callback)
    # Rarely need to hold onto the object with a variable: 
    #     joint_sub = rospy.Subscriber(...)
    rospy.Timer(rospy.Duration(2), timer_callback)

    # spin() simply keeps python from exiting until this node is stopped
    # This is an infinite loop, the only code that gets ran are callbacks
    rospy.spin()
    # NO CODE GOES AFTER THIS, NONE! USE TIMER CALLBACKS!
    # unless you need to clean up resource allocation, close(), etc when program dies

if __name__ == '__main__':
    joint_logger_node()

Изменить 1: Кажется, есть некоторая путаница в том, что делают Subscriber (), spin () и _callback (s). В Python это немного скрыто, но есть основная программа, которая управляет всеми узлами и отправляет узлы между ними. В каждом узле мы регистрируем в этой главной программе, что узел существует, а также какие издатели и подписчики у него есть. Регистрируясь, это означает, что мы говорим главной программе: «Эй, я хочу эту тему!»; в вашем случае для вашего (необъявленного) подписчика Joint_sub: «Эй, мне нужны все JointState сообщения из темы joint_states!» Основная программа каждый раз, когда получает (от какого-то издателя) новое joint_states JointState сообщение, отправляет его этому подписчику. Подписчик обрабатывает, обрабатывает и обрабатывает сообщение (данные) с помощью обратного вызова: когда (!) Я получаю сообщение, запускайте обратный вызов.

Итак, основная программа получает новое joint_states JointState сообщение от какого-то издателя. Затем он, поскольку мы зарегистрировали на него подписчика, отправляет его на этот узел. rospy.spin () - это бесконечный цикл, ожидающий этих данных. Вот что он делает (в основном):

def rospy.spin():
    while rospy.ok():
        for new_msg in get_new_messages from master():
            if I have a subscriber to new_msg:
                my_subscriber.callback(new_msg)

rospy.spin () - это то место, где на самом деле вызывается и выполняется ваш обратный вызов, Joint_callback (и / или timer_callback и т. д.). Он только запускается, когда для него есть данные.

Если говорить более фундаментально, я думаю, что из-за этой путаницы структура вашей программы ошибочна; ваши функции не делают того, что вы думаете. Вот как вы должны сделать свой узел.

  1. Сделайте свою математическую часть (весь реальный код, не относящийся к ros), который выполняет NN, в отдельный модуль и создайте функцию для его запуска.
  2. Если вы хотите запускать его только при получении данных, запустите его в обратном вызове. Если вы хотите опубликовать результат, опубликуйте в обратном вызове.
  3. Не вызывайте основную функцию! if __init__ == '__main__': my_main_function() должен быть единственным местом, где он вызывается, и это вызовет ваш код. Повторяю: основная функция, объявляющая подписчиков / издателей / init / таймеров / параметров, выполняется только в if __init__ ..., и эта функция запускает ваш код. Чтобы он запускал ваш код, поместите свой код в обратный вызов. Обратные вызовы таймера удобны для этого.

Надеюсь, этот пример кода проясняет:

import rospy
from std_msgs.msg import Header
from sensor_msgs.msg import JointState
import my_nn as nn # nn.run(data)

# Subscribers
#     joint_sub (sensor_msgs/JointState): "joint_states"

# Publishers
#     joint_pub (sensor_msgs/JointState): "target_joint_states"

joint_pub = None

def joint_callback(data): # data of type JointState
    pub_msg = JointState() # Make a new msg to publish results
    pub_msg.header = Header()
    pub_msg.name = data.name
    pub_msg.velocity = [10] * len(data.name)
    pub_msg.effort = [100] * len(data.name)
    # This next line might not be quite right for what you want to do,
    # But basically, run the "real code" on the data, and get the
    # result to publish back out
    pub_msg.position = nn.run(data.position) # Run NN on data, store results
    joint_pub.publish(pub_msg) # Send it when ready!

if __name__ == '__main__':
    # Init ROS
    rospy.init_node('joint_logger_node', anonymous=True)
    # Subscribers
    rospy.Subscriber('joint_states', JointState, joint_callback)
    # Publishers
    joint_pub = rospy.Publisher('target_joint_states', JointState, queue_size = 10)
    # Spin
    rospy.spin()
    # No more code! This is not a function to call, but its
    # own program! This is an executable! Run your code in
    # a callback!

Обратите внимание, что модуль python, который мы разрабатываем как узел ros, не имеет никаких функций для вызова. Он имеет определенную структуру обратных вызовов и общих данных между ними, которые инициализированы и зарегистрированы в __init__.

person JWCS    schedule 30.07.2019
comment
Привет, извините за беспокойство еще раз, в примере, который вы упомянули, какой тип данных для g_position, это массив или объект? при попытке получить к нему доступ я получаю, что объект NoneType не подлежит подписке. Пожалуйста, помогите мне. Спасибо - person Sai Raghava; 06.08.2019
comment
Если щелкнуть ссылку (JointState), будет показано, что JointState.position (data.position) представляет собой массив двойников. g_positions просто присвоено это значение в обратном вызове. Теперь, если g_positions a NoneType / None, значит, вы еще не получили никаких обратных вызовов, он не получил никаких данных. Вы должны сначала проверить, нет ли он, а затем, если это не так, проверить длину, а затем получить доступ к своим данным. - person JWCS; 06.08.2019
comment
Если вы не знакомы с ros, использование c ++ - неплохой вариант, потому что он заставит вас правильно указать типы в начале и всегда знать, что это за типы переменных. Ошибки типа - это определенно то, в чем я больше всего ошибаюсь. - person JWCS; 06.08.2019
comment
Большое тебе спасибо. да, я прочитал эту документацию JointState, хотя я не был уверен в том, что data.position является массивом, потому что, когда я получил доступ к отдельному элементу в массиве, я получил эту ошибку, как вы сказали, это может быть вызвано тем, что он не получил никаких данных, но меня это тоже смутило, потому что в первой строке обратного вызова функции мы попытались записать данные data.position и значения позиции, отображаемые на терминале. - person Sai Raghava; 06.08.2019
comment
Итак, я не понял, что вызывает это? это должно быть проблема с назначением data.position для g_positions. Если у вас есть предварительный диагноз, на который я должен обратить внимание, пожалуйста, дайте мне знать. Спасибо - person Sai Raghava; 06.08.2019
comment
Если мы посмотрим на data, полученный обратным вызовом типа JointPositions, у него есть член .position. Это массив, а data не может быть None. Если вы получили ошибку при попытке использовать g_positions в другом обратном вызове / функции, g_positions может быть None до первого joint_callback. Если значения положения, печать / запись data.position выглядят правильно, проблема в том, как вы обрабатываете данные. - person JWCS; 06.08.2019
comment
Здравствуйте, меня до сих пор поражает этот фрагмент кода. Я прошел через руководства для подписчиков и понял, почему вы сделали каждый из этих шагов. Хотя у меня возникла проблема, когда я пытаюсь позвонить g_positions на более поздних этапах после вызова подписчика, он все равно возвращает None. Я считаю, что он должен содержать data.position значений, поскольку мы передали g_positions = data.position в функции обратного вызова. - person Sai Raghava; 13.08.2019
comment
Но я мог видеть, что команда print(g_positions) выполняется, поскольку она печатает значения положения на экране. Поэтому мне интересно, почему g_position сохраняет значения data.position только при вызове подписчика, и как только узел подписчика останавливается для дальнейших вычислений с полученным g_position, его значение остается равным None. - person Sai Raghava; 13.08.2019
comment
Я считаю, что из-за типа g_position g_position хранит значения data.position только до тех пор, пока узел работает, и после его остановки возвращается ли он к None. правда ли это причина? - person Sai Raghava; 13.08.2019
comment
Вы наполовину, верно, извините, я допустил серьезную ошибку с этой публикацией. Я отредактировал его для дальнейшего использования. В C ++ (я немного больше делаю в / с) это правильно. В Python в пределах Joint_callback все g_joint_states, g_positions и g_pos1 являются локальными переменными, не ссылаясь на (никогда не устанавливаемые) глобальные переменные. Вы можете либо обернуть / использовать в классе, либо в таком коротком скрипте, используя (действительно) только одну глобальную переменную, добавить строку (к обратному вызову) global g_positions(, g_joint_states, g_pos1). Это может быть достаточно элегантным, если хорошо спроектировано; вам просто нужно быть явным при использовании глобальных переменных. - person JWCS; 13.08.2019
comment
Спасибо, уже пробовал, объявив их глобальными. Хотя проблема все еще сохраняется, когда я пытаюсь получить доступ к g_position, он печатает None вместо значений data.position. Я также проверил JointState документацию, и значение позиции должно быть float64 []. Хотя, когда я пытаюсь print(g_position) внутри функции обратного вызова, он успешно печатает углы соединения, но при обращении к g_position на следующем шаге после вызова подписчика он возвращает None. - person Sai Raghava; 13.08.2019
comment
Вы можете помочь мне указать, что вызывает эту аномалию? я неправильно использую тип None ?? - person Sai Raghava; 13.08.2019
comment
Я снова отредактировал код, добавив таймер. Таймер обычно (помимо других обратных вызовов) - это то, как вы используете переменные. Я закомментировал печать в Joint_callback, а timer_callback распечатал значение. Я тестировал его с тремя терминальными приложениями: одно запускало roscore, одно запускало python demo.py (это), одно запускало rostopic pub /joint_states sensor_msgs/JointState "...(stuff)" (которое вам не нужно вводить, оно должно выполняться автоматически). У меня все это сработало. Я не знаю, что ты делаешь ... подожди минутку - person JWCS; 13.08.2019
comment
Не помещайте свой код в основную функцию. Основная функция * EXCLUSIVELY * инициализирует узел, объявляет подписчиков, объявляет издателей, объявляет таймеры, считывает параметры и инициализирует инициализаторы, а затем выполняет вращение. Он не запускает серьезного кода, он не запускает код, обрабатывающий данные, он переходит в состояние бесконечного зависания (намеренно) и никогда больше не запускается. Если вы хотите периодически обрабатывать данные на основе ваших глобальных данных, наиболее распространенная парадигма ros заключается в том, чтобы обратный вызов получал сообщение, сохранял данные глобально и чтобы timer_callback периодически вычислял / публиковал материал на основе данных. - person JWCS; 13.08.2019
comment
Вызов подписчика, его инициализация, не запускает обратный вызов. Он также не будет обновлять (следовательно) глобальные переменные, поэтому, конечно, g_positions все равно будет none - это начальное значение. - person JWCS; 13.08.2019
comment
Извините, что вы имеете в виду, когда называете абонента, инициализация которого не приводит к обратному вызову? так что вызов функции joint_logger_node() не запускает обратный вызов? Как уже упоминалось, я не обрабатывал никакой информации после строки rospy.spin() внутри тела функции. Хотя в моей программе мне нужно считывать углы соединения, а затем использовать NN для их обработки, а затем снова публиковать обработанный вывод обратно в робота. - person Sai Raghava; 14.08.2019
comment
Итак, чтобы прочитать совместные углы, когда я вызываю joint_logger_node(), он застревает в этом rospy.spin() цикле, и, следовательно, код не переходит к следующим строкам, где я обрабатываю эти g_positions. Я редактирую вопрос, указав определение функции узла и способ их вызова. Пожалуйста, дайте мне знать, в чем я ошибаюсь, поскольку я застрял здесь долгое время, и я приближаюсь к крайнему сроку моей диссертации. - person Sai Raghava; 14.08.2019
comment
Не вызывайте Joint_logger_node ()! Не вызывайте Joint_logger_node ()! Не вызывайте Joint_logger_node ()! Это не функция, ничего не возвращает. Это функция main для исполняемого файла: она - это тот, кто вызывает ваш код, а вы его не вызываете. rospy.spin () должен запускать ваш код. Код rospy.spin () запускает каждый из подписчиков и таймеров. rospy.spin () запускает подписчика только тогда, когда есть новое сообщение, и таймер, когда это подходящее время. Если вы хотите читать с клавиатуры, поместите код чтения с клавиатуры в таймер; преобразование данных либо в подписчике, либо в таймере. См. Правка 1. - person JWCS; 14.08.2019
comment
Большое тебе спасибо. Это пока самое ясное объяснение, которое у меня есть. - person Sai Raghava; 14.08.2019