【问题标题】:Python real time plotting ROS dataPython实时绘制ROS数据
【发布时间】:2016-02-02 05:14:32
【问题描述】:

我正在尝试使用 python 绘制传入计算机的实时数据。数据来自 ROS 主题,我使用“rospy”订阅主题以获取数据。 这是我写的代码

import rospy
from sensor_msgs.msg import ChannelFloat32
import matplotlib.pyplot as plt

N = 200
i = 0

topic = "chatter"

x = range(N)
lmotor = [0]*N
rmotor = [0]*N

plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim([0,N])
ax.set_ylim([-1,1])

line1, = ax.plot(lmotor, 'r-')
line2, = ax.plot(rmotor, 'g')

def plotThrottle(data):
    global x, lmotor, rmotor, i

    [x[i],lmotor[i],rmotor[i], tmp] = data

    line1.set_ydata(lmotor)
    line1.set_xdata(x)
    line2.set_ydata(rmotor)
    line2.set_xdata(x)

    fig.canvas.draw()

def callBack(packet):
    data = list(packet.values)
    plotThrottle(data)


def listner():
    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber(topic, ChannelFloat32, callBack)
    rospy.spin()

if __name__ == '__main__':
    listner()

我的问题是当我使用从 rostopic 获得的数据调用 plotThrottle() 时,出现以下错误。

[ERROR]
[WallTime: 1454388985.317080] bad callback: <function callBack at 0x7f13d98ba6e0>
Traceback (most recent call last):
  File "/opt/ros/indigo/lib/python2.7/dist-packages/rospy/topics.py", line 720, in _invoke_callback
    cb(msg)
  File "dummy2.py", line 41, in callBack
    plotThrottle(data)
  File "dummy2.py", line 37, in plotThrottle
    fig.canvas.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 349, in draw
    tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 13, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop

但是,如果我使用相同的函数并传递代码中生成的一些数据(一些随机数据),则绘图工作正常。 我是 python 的绝对初学者。我搜索了这个错误,它说这是因为一些线程问题。但我不明白如何修复此代码。如果有人可以解释问题并帮助修复此代码,我将不胜感激。

【问题讨论】:

    标签: python multithreading matplotlib python-multithreading rospy


    【解决方案1】:

    这里有两个线程在运行,rospy.spin() 和 top.mainloop()(来自 Tkinter,在你的例子中是 matplotlib 的后端)。

    来自this answer

    问题源于 _tkinter 模块试图 通过轮询技术获得对主线程的控制权 处理来自其他线程的调用。

    您在 Thread-1 中的 Tkinter 代码正试图窥探主线程 找到主循环,它不存在。

    来自this answer

    如果有另一个阻塞调用使您的程序保持运行, 无需调用 rospy.spin()。不像在 C++ 中 spin() 是 需要处理所有线程,在python中它所做的只是阻塞。

    所以你可以使用plt.show(block=True) 来阻止你的程序关闭,在这种情况下你将使用 Tkinter 主循环,重新绘制你的画布没有问题。

    监听函数应该是这样的:

        def listener():
            rospy.init_node('listener', anonymous=True)
            rospy.Subscriber(topic, ChannelFloat32, callBack)
            # rospy.spin()
            plt.show(block=True)
    

    无论如何,这似乎是其他替代方案的一种解决方法,请再次查看 this answer 或简单地使用单独的节点进行绘图,即 ros 建议的工具,如 rqt_graph

    【讨论】:

      【解决方案2】:

      由于这是一个旧帖子,并且似乎在社区中仍然很活跃,所以我将提供一个示例,一般来说,我们如何进行实时绘图。这里我使用了 matplotlib FuncAnimation 函数。

      import matplotlib.pyplot as plt
      import rospy
      import tf
      from nav_msgs.msg import Odometry
      from tf.transformations import quaternion_matrix
      import numpy as np
      from matplotlib.animation import FuncAnimation
      
      
      class Visualiser:
          def __init__(self):
              self.fig, self.ax = plt.subplots()
              self.ln, = plt.plot([], [], 'ro')
              self.x_data, self.y_data = [] , []
      
          def plot_init(self):
              self.ax.set_xlim(0, 10000)
              self.ax.set_ylim(-7, 7)
              return self.ln
          
          def getYaw(self, pose):
              quaternion = (pose.orientation.x, pose.orientation.y, pose.orientation.z,
                      pose.orientation.w)
              euler = tf.transformations.euler_from_quaternion(quaternion)
              yaw = euler[2] 
              return yaw   
      
          def odom_callback(self, msg):
              yaw_angle = self.getYaw(msg.pose.pose)
              self.y_data.append(yaw_angle)
              x_index = len(self.x_data)
              self.x_data.append(x_index+1)
          
          def update_plot(self, frame):
              self.ln.set_data(self.x_data, self.y_data)
              return self.ln
      
      
      rospy.init_node('lidar_visual_node')
      vis = Visualiser()
      sub = rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback)
      
      ani = FuncAnimation(vis.fig, vis.update_plot, init_func=vis.plot_init)
      plt.show(block=True) 
      

      注意:根据需要更改rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback),并相应地进行必要的更改。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-17
        • 2014-06-25
        • 2021-09-07
        • 2015-09-15
        • 2021-06-24
        • 1970-01-01
        • 2017-11-14
        相关资源
        最近更新 更多