I am still struggling with using data from the callback function, I wrote a class and trying to get the data from the callback function associated with the class, any help would be much appreciated. Do I need to use multi threading or there is an easy way to use it ? When I am calling the class the publisher initializes and then callback keep getting the updated data but I am not sure how can I use this data.
#!/usr/bin/env python3
"""OpenCV feature detectors with ros CompressedImage Topics in python.
This example subscribes to a ros topic containing sensor_msgs
CompressedImage. It converts the CompressedImage into a numpy.ndarray,
then detects and marks features in that image. It finally displays
and publishes the new image - again as CompressedImage topic.
"""
__version__= '0.1'
from moveit_commander.conversions import pose_to_list
from rospy_tutorials.msg import Floats
from rospy.numpy_msg import numpy_msg
from tf import TransformListener
from std_msgs.msg import String
import geometry_msgs.msg
import moveit_commander
import moveit_msgs.msg
from math import pi
import sys, time
import rospy
import copy
VERBOSE=False
class move_xarm:
# global my_data
def __init__(self):
'''Initialize ros publisher and subscriber'''
# publish trajectories for RViz to visualize
self.display_trajectory_publisher = rospy.Publisher('/move_group/display_planned_path',moveit_msgs.msg.DisplayTrajectory,queue_size=20)
self.data_callback_publisher = rospy.Publisher('/callback_data',numpy_msg(Floats),queue_size=1)
self.my_data = None
self.listener()
# subscribed Topic
# self.subscriber = rospy.Subscriber("marker_wrt_base_pose", numpy_msg(Floats), self.callback, queue_size = 1)
if VERBOSE :
print("subscribed to marker_wrt_base_pose")
def listener(self):
# In ROS, nodes are uniquely named. If two nodes with the same
# name are launched, the previous one is kicked off. The
# anonymous=True flag means that rospy will choose a unique
# name for our 'listener' node so that multiple listeners can
# run simultaneously.
rospy.init_node('move_group_python_interface_tutorial', anonymous=True)
rospy.Subscriber("marker_wrt_base_pose", numpy_msg(Floats), self.callback,queue_size = 1)
# spin() simply keeps python from exiting until this node is stopped
rospy.spin()
def callback(self, ros_data):
'''Callback function of subscribed topic.
Here position data get converted into float64'''
if VERBOSE :
print('received data of type: "%s"' % ros_data.format)
self.my_data = ros_data.data
# my_data = my_data.astype('float64')
self.data_callback_publisher.publish(ros_data)
if __name__ == '__main__':
mv = move_xarm()
I resolved this issue,
Create another function out of the class say name use_data(args)
Call use_data in main function and you are good to go.
One of the common problem is use of spin command,they block the main thread from exiting until ROS invokes a shutdown - via a Ctrl + C.
def use_data(args):
ic = move_xarm()
rospy.init_node('move_group_python_interface_tutorial',anonymous=True)
rate = rospy.Rate(5) # ROS Rate at 5Hz
while not rospy.is_shutdown():
do stuff using the variable ic.my_data
rate.sleep()
Related
I'm trying to write a python code, that controls a drone. I receive the position from a Rigid-body trough a rostopic, and I want to use that data in my code. How can i access it in my python code?
#!/usr/bin/env python
import numpy as np
from pycrazyswarm import *
Z = 1.0
if __name__ == "__main__":
swarm = Crazyswarm()
# get initial position for maneuver
swarm.allcfs.takeoff(targetHeight=Z, duration=1.0+Z)
swarm.timeHelper.sleep(1.5+Z)
print("press button to continue...")
swarm.input.waitUntilButtonPressed()
# After the button is pressed, I want, that the drone is aligned by a rigid body.
# Means if the rigid body moves 1m left the drone should follow
# finished following the rigid body. Get back landing
swarm.allcfs.land(targetHeight=0.02, duration=1.0+Z)
swarm.timeHelper.sleep(1.0+Z)
So after the button is pressed, I would like to use the data of the rostopic. On the host i send the data over the VRPN client of ROS
http://wiki.ros.org/vrpn_client_ros
I want to compute the data of the "tracker name"/pose topic
Yes, you can access the ROS topic data in your Python code. Take the following example:
#!/usr/bin/env python
import numpy as np
import rospy
from pycrazyswarm import *
from geometry_msgs.msg import Pose
Z = 1.0
def some_callback(msg):
rospy.loginfo('Got the message: ' + str(msg))
if __name__ == "__main__":
swarm = Crazyswarm()
# get initial position for maneuver
swarm.allcfs.takeoff(targetHeight=Z, duration=1.0+Z)
swarm.timeHelper.sleep(1.5+Z)
print("press button to continue...")
swarm.input.waitUntilButtonPressed()
#start ros node
rospy.init_node('my_node')
#Create a subscriber
rospy.Subscriber('/vrpn_client_ros/RigidBody/pose', Pose, some_callback)
# finished following the rigid body. Get back landing
swarm.allcfs.land(targetHeight=0.02, duration=1.0+Z)
swarm.timeHelper.sleep(1.0+Z)
This will create a ROS node, listen to the data coming from your topic, and print it out much like rostopic echo would.
So I have been struggling with this one error of pickle which is driving me crazy. I have the following master Engine class with the following code :
import eventlet
import socketio
import multiprocessing
from multiprocessing import Queue
from multi import SIOSerever
class masterEngine:
if __name__ == '__main__':
serverObj = SIOSerever()
try:
receiveData = multiprocessing.Process(target=serverObj.run)
receiveData.start()
receiveProcess = multiprocessing.Process(target=serverObj.fetchFromQueue)
receiveProcess.start()
receiveData.join()
receiveProcess.join()
except Exception as error:
print(error)
and I have another file called multi which runs like the following :
import multiprocessing
from multiprocessing import Queue
import eventlet
import socketio
class SIOSerever:
def __init__(self):
self.cycletimeQueue = Queue()
self.sio = socketio.Server(cors_allowed_origins='*',logger=False)
self.app = socketio.WSGIApp(self.sio, static_files={'/': 'index.html',})
self.ws_server = eventlet.listen(('0.0.0.0', 5000))
#self.sio.on('production')
def p_message(sid, message):
self.cycletimeQueue.put(message)
print("I logged : "+str(message))
def run(self):
eventlet.wsgi.server(self.ws_server, self.app)
def fetchFromQueue(self):
while True:
cycle = self.cycletimeQueue.get()
print(cycle)
As you can see I can trying to create two processes of def run and fetchFromQueue which i want to run independently.
My run function starts the python-socket server to which im sending some data from a html web page ( This runs perfectly without multiprocessing). I am then trying to push the data received to a Queue so that my other function can retrieve it and play with the data received.
I have a set of time taking operations that I need to carry out with the data received from the socket which is why im pushing it all into a Queue.
On running the master Engine class I receive the following :
Can't pickle <class 'threading.Thread'>: it's not the same object as threading.Thread
I ended!
[Finished in 0.5s]
Can you please help with what I am doing wrong?
From multiprocessing programming guidelines:
Explicitly pass resources to child processes
On Unix using the fork start method, a child process can make use of a shared resource created in a parent process using a global resource. However, it is better to pass the object as an argument to the constructor for the child process.
Apart from making the code (potentially) compatible with Windows and the other start methods this also ensures that as long as the child process is still alive the object will not be garbage collected in the parent process. This might be important if some resource is freed when the object is garbage collected in the parent process.
Therefore, I slightly modified your example by removing everything unnecessary, but showing an approach where the shared queue is explicitly passed to all processes that use it:
import multiprocessing
MAX = 5
class SIOSerever:
def __init__(self, queue):
self.cycletimeQueue = queue
def run(self):
for i in range(MAX):
self.cycletimeQueue.put(i)
#staticmethod
def fetchFromQueue(cycletimeQueue):
while True:
cycle = cycletimeQueue.get()
print(cycle)
if cycle >= MAX - 1:
break
def start_server(queue):
server = SIOSerever(queue)
server.run()
if __name__ == '__main__':
try:
queue = multiprocessing.Queue()
receiveData = multiprocessing.Process(target=start_server, args=(queue,))
receiveData.start()
receiveProcess = multiprocessing.Process(target=SIOSerever.fetchFromQueue, args=(queue,))
receiveProcess.start()
receiveData.join()
receiveProcess.join()
except Exception as error:
print(error)
0
1
...
I am building a system to connect Arduino-based sensors to a RPi via Bluetooth LE, and display the info (temperature and battery life) on a GUI. I have two main classes in my program, one that manages the GUI, and one that manages the BLE connection(class HubSensor). A HubSensor object takes the MAC address for each sensor and is supposed to emit a signal with an attached tuple that contains the temp sensed, battery life, and an index integer to let the main program know which sensor it is. HubSensor gets it's information once per second, and should be sending out the signal every time. (There is input validation already built but it's not relevant to my question.) Most of this is working fine so far.
My problem is I can't figure out how to create a slot to receive the signal so it can update the display (and later keep a log in a CSV file). I'm using the BluePy library to manage the BLE connection, which for me has it's own additional challenges.
So, this is how my program works (I think). Each thread (since I have multiple sensors) creates a HubSensor object. When the object makes the BLE connection, it then creates a MyDelegate object (subclassed from BluePy's DefaultDelegate. Inside the MyDelegate object, the Qt Signal is emitted. I need access to that signal outside of all of those classes and since I don't know the name of the MyDelegate object created, I don't know how to get to it.
I've tried having each of the above mentioned classes inherit each others' characteristics, but I'm not sure I did it right.
From trailerTempDisplay.py
import sys
from PyQt5.QtCore import *
from HubSensor import *
from PyQt5 import QtWidgets, uic
from bluepy.btle import *
from datetime import datetime
# mac addresses for the sensors. later, this will need a function to allow new devices to connect
bt_addrs = ['c1:49:02:59:ae:50', 'f3:ad:ed:46:ea:16']
app = QtWidgets.QApplication(sys.argv)
class Worker(QRunnable):
def __init__(self, macAddress, ind):
super(Worker, self).__init__()
self.macAddress = macAddress
self.ind = ind
#pyqtSlot()
#this is where each sensor exists. each object is created and runs here
def run(self):
self.sensor = HubSensor(self.macAddress, self.ind)
self.sensor.notified.connect(self.updateValues())
#close button
def buttonClicked():
app.closeAllWindows()
window = uic.loadUi("mainwindow.ui")
window.pushButton.clicked.connect(buttonClicked)
def updateValues(self):
print("value updated") # debugging
window.show()
window.threadpool = QThreadPool()
index = 0
for addr in bt_addrs:
worker = Worker(addr, index)
index += 1
window.threadpool.start(worker)
app.exec()
From HubSensor.py
from bluepy.btle import *
from PyQt5.QtCore import QObject, pyqtSignal
class MyDelegate(DefaultDelegate, QObject):
def __init__(self, index):
DefaultDelegate.__init__(self)
QObject.__init__(self)
self.index = index
# class variable for the notified signal
notified = pyqtSignal(tuple)
def handleNotification(self, cHandle, data):
# exception handling prevents bad data from being passed. cHandle is not used but might be useful later
try:
# defining the sensorData tuple. 1, 2, and 3 are dummy values
self.sensorData = (1, 2, 3)
self.notified.emit(self.sensorData) # this should emit a signal every time the function is called and send a tuple with temp, battery, and sensor index(id)
except (ValueError, IndexError):
pass
class HubSensor(MyDelegate):
# constructor. connects to device defined by mac address and position.
# uuid is static and should not change
def __init__(self, mac, index):
self.index = index # keeps track of sensor position
self.mac = mac
self.p = Peripheral(self.mac, 'random') # adafruit feathers must be 'random' for some reason
self.p.setDelegate(MyDelegate(self.index))
self.p.writeCharacteristic(35, b'\x01\x00') # writing these bits to handle '35' enables notifications
while True:
if self.p.waitForNotifications(1):
# when a bluetooth notification is received, handleNotification is called
continue
When I run the program, "value updated" is not displayed on the console. It should pop up about twice per second and just repeat. Later, I will add in the part that turns the values passed into the GUI display.
Let me apologize in advance because I am still very much a beginner. I think I've included all the relevant parts of my code, but I don't know for sure. Also, I'm pretty sure my terminology in some spots is incorrect, so I hope you all can decipher what I actually mean. Thank you in advance for any help you can give me!
Your code is confusing, for example that HubSensor is a delegate whose attribute is a Peripheral and that Peripheral has another delegate, among other problems.
So instead of relying on your code, I created the PeripheralManager class that will notify the information received by the assigned peripheral through a signal. In the case of the infinite loop, it will be handled in a thread using threading.Thread.
import threading
from PyQt5 import QtCore, QtWidgets, uic
from bluepy.btle import DefaultDelegate, Peripheral, BTLEDisconnectError
class PeripheralManager(QtCore.QObject, DefaultDelegate):
notified = QtCore.pyqtSignal(bytes)
def __init__(self, peripheral, parent=None):
super().__init__(parent)
self._peripheral = peripheral
self.peripheral.setDelegate(self)
self.peripheral.writeCharacteristic(35, b"\x01\x00")
threading.Thread(target=self._manage_notifications, daemon=True).start()
#property
def peripheral(self):
return self._peripheral
def handleNotification(self, cHandle, data):
self.notified.emit(self.data)
def _manage_notifications(self):
while self.peripheral.waitForNotifications(1):
continue
def buttonClicked():
QtWidgets.QApplication.closeAllWindows()
def updateValues(values):
print("value updated", values)
def main(args):
app = QtWidgets.QApplication(args)
bt_addrs = ["c1:49:02:59:ae:50", "f3:ad:ed:46:ea:16"]
managers = []
for addr in bt_addrs:
try:
p = Peripheral(addr, "random")
except BTLEDisconnectError as e:
print(e)
else:
manager = PeripheralManager(p)
manager.notified.connect(updateValues)
managers.append(manager)
window = uic.loadUi("mainwindow.ui")
window.pushButton.clicked.connect(buttonClicked)
window.show()
ret = app.exec_()
return ret
if __name__ == "__main__":
import sys
sys.exit(main(sys.argv))
I am trying to simulate an environment with vms and trying to run an object method in background thread. My code looks like the following.
hyper_v.py file :
import random
from threading import Thread
from virtual_machine import VirtualMachine
class HyperV(object):
def __init__(self, hyperv_name):
self.hyperv_name = hyperv_name
self.vms_created = {}
def create_vm(self, vm_name):
if vm_name not in self.vms_created:
vm1 = VirtualMachine({'vm_name': vm_name})
self.vms_created[vm_name] = vm1
vm1.boot()
else:
print('VM:', vm_name, 'already exists')
def get_vm_stats(self, vm_name):
print('vm stats of ', vm_name)
print(self.vms_created[vm_name].get_values())
if __name__ == '__main__':
hv = HyperV('temp')
vm_name = 'test-vm'
hv.create_vm(vm_name)
print('getting vm stats')
th2 = Thread(name='vm1_stats', target=hv.get_vm_stats(vm_name) )
th2.start()
virtual_machine.py file in the same directory:
import random, time, uuid, json
from threading import Thread
class VirtualMachine(object):
def __init__(self, interval = 2, *args, **kwargs):
self.vm_id = str(uuid.uuid4())
#self.vm_name = kwargs['vm_name']
self.cpu_percentage = 0
self.ram_percentage = 0
self.disk_percentage = 0
self.interval = interval
def boot(self):
print('Bootingup', self.vm_id)
th = Thread(name='vm1', target=self.update() )
th.daemon = True #Setting the thread as daemon thread to run in background
print(th.isDaemon()) #This prints true
th.start()
def update(self):
# This method needs to run in the background simulating an actual vm with changing values.
i = 0
while(i < 5 ): #Added counter for debugging, ideally this would be while(True)
i+=1
time.sleep(self.interval)
print('updating', self.vm_id)
self.cpu_percentage = round(random.uniform(0,100),2)
self.ram_percentage = round(random.uniform(0,100),2)
self.disk_percentage = round(random.uniform(0,100),2)
def get_values(self):
return_json = {'cpu_percentage': self.cpu_percentage,
'ram_percentage': self.ram_percentage,
'disk_percentage': self.disk_percentage}
return json.dumps(return_json)
The idea is to create a thread that keeps on updating the values and on request, we read the values of the vm object by calling the vm_obj.get_values() we would be creating multiple vm_objects to simulate multiple vms running in parallel and we need to get the information from a particular vm on request.
The problem, that I am facing, is that the update() function of the vm doesnot run in the background (even though the thread is set as daemon thread).
The method call hv.get_vm_stats(vm_name) waits until the completion of vm_object.update() (which is called by vm_object.boot()) and then prints the stats. I would like to get the stats of the vm on request by keeping the vm_object.update() running in the background forever.
Please share your thoughts if I am overlooking anything related to the basics. I tried looking into the issues related to the python threading library but I could not come to any conclusion. Any help is greatly appreciated. The next steps would be to have a REST api to call these functions to get the data of any vm but I am struck with this problem.
Thanks in advance,
As pointed out by #Klaus D in the comments, my mistake was using the braces when specifying the target function in the thread definition, which resulted in the function being called right away.
target=self.update() will call the method right away. Remove the () to
hand the method over to the thread without calling it.
I am currently trying to make a ROS node in Python which has both a subscriber and a publisher.
I've seen examples where a message is published within the callback, but I want it to "constantly" publish messages, and perform callbacks when it is the case.
Here is how I do it now:
#!/usr/bin/env python
import rospy
from std_msgs.msg import Empty
from std_msgs.msg import String
import numpy as np
pub = rospy.Publisher('/status', String, queue_size=1000)
def callback(data):
print "Message received"
def listener():
rospy.init_node('control', anonymous=True)
rospy.Subscriber('control_c', Empty, callback)
rospy.spin()
if __name__ == '__main__':
print "Running"
listener()
So where should I publish?
Well, I think there's a lot of solutions here, you could even make use of a python process, but what I'm proposing is a ROS approach using a ros Timer.
I am not really that efficient in python but this code may gave you a heads up.
#!/usr/bin/env python
import rospy
from std_msgs.msg import Empty
from std_msgs.msg import String
import numpy as np
last_data = ""
started = False
pub = rospy.Publisher('/status', String, queue_size=1000)
def callback(data):
print "New message received"
global started, last_data
last_data = data
if (not started):
started = True
def timer_callback(event):
global started, pub, last_data
if (started):
pub.publish(last_data)
print "Last message published"
def listener():
rospy.init_node('control', anonymous=True)
rospy.Subscriber('control_c', String, callback)
timer = rospy.Timer(rospy.Duration(0.5), timer_callback)
rospy.spin()
timer.shutdown()
if __name__ == '__main__':
print "Running"
listener()
Here, your callback will update the message and your timer will fire up every 0.5sec and publishes the last data received.
you can test this code by publishing data on "/contriol_c" every 3 seconds and configuring you timer to 0.5 sec. start an echo on /status
$ rostopic echo /status
and you'll got your message published on a 2 Hz rate.
Hope that helps !
Simply replace rospy.spin() with the following loop:
while not rospy.is_shutdown():
# do whatever you want here
pub.publish(foo)
rospy.sleep(1) # sleep for one second
Of course you can adjust the sleep duration to whatever value you want (or even remove it entirely).
According to this reference subscribers in rospy are running in a separate thread, so you don't need to call spin actively.
Note that in roscpp (i.e. when using C++) this is handled differently. There you have to call ros::spinOnce() in the while loop.