简体   繁体   English

Python实时绘制ROS数据

[英]Python real time plotting ROS data

I am trying to plot real time data coming to the computer using python.我正在尝试使用 python 绘制进入计算机的实时数据。 Data comes in a ROS topic and I use 'rospy' to subscribe to the topic in order to get data.数据来自 ROS 主题,我使用“rospy”订阅该主题以获取数据。 This is the code I wrote这是我写的代码

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()

My problem is when I call plotThrottle() with the data I got from rostopic, I get following error.我的问题是,当我使用从 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

But if I use the same function and pass some data generated within the code (some random data) plot works fine.但是,如果我使用相同的函数并传递代码中生成的一些数据(一些随机数据),则绘图工作正常。 I am an absolute beginner to python.我绝对是 python 的初学者。 I searched about this error and it says that this is because of some threading problem.我搜索了这个错误,它说这是因为一些线程问题。 But I don't understand how to fix this code.但我不明白如何修复此代码。 I am really grateful if someone can explain the problem and help fix this code.如果有人可以解释问题并帮助修复此代码,我真的很感激。

Here you have two threads running, rospy.spin() and top.mainloop() (from Tkinter, backend of matplotlib in your case).在这里,您有两个线程正在运行,rospy.spin() 和 top.mainloop()(来自 Tkinter,在您的案例中是 matplotlib 的后端)。

From this answer :这个答案

The problems stem from the fact that the _tkinter module attempts to gain control of the main thread via a polling technique when processing calls from other threads.问题源于这样一个事实,即 _tkinter 模块在处理来自其他线程的调用时试图通过轮询技术获得对主线程的控制。

Your Tkinter code in Thread-1 is trying to peek into the main thread to find the main loop, and it's not there.您在 Thread-1 中的 Tkinter 代码试图窥视主线程以找到主循环,但它不存在。

From this answer :这个答案

If there is another blocking call that keeps your program running, there is no need to call rospy.spin().如果有另一个阻塞调用使您的程序保持运行,则无需调用 rospy.spin()。 Unlike in C++ where spin() is needed to process all the threads, in python all it does is block.与在 C++ 中需要使用 spin() 来处理所有线程不同,在 python 中它所做的只是阻塞。

So you can use plt.show(block=True) to keep your program from closing, in that case you will use Tkinter mainloop, redrawing your canvas without problems.所以你可以使用plt.show(block=True)来防止你的程序关闭,在这种情况下你将使用 Tkinter 主循环,重新绘制你的画布没有问题。

The listener fuction should look like this:侦听器功能应如下所示:

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

Anyway this seems a bit a workaround for other alternatives see again this answer or simply use separate node for plotting ie ros suggested tools like rqt_graph .无论如何,这似乎是其他替代方案的一种解决方法,请再次查看此答案,或者只是使用单独的节点进行绘图,即 ros 建议的工具,例如rqt_graph

Since this is an old post and still seems to be active in the community, I am going to provide an example, in general, how can we do real-time plotting.由于这是一个旧帖子,并且似乎仍然在社区中活跃,我将提供一个示例,一般来说,我们如何进行实时绘图。 Here I used matplotlib FuncAnimation function.这里我使用了 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) 

Note: change the rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback) as you need and do necessary changes accordingly.注意:根据需要更改rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback)并相应地进行必要的更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM