I am writing a ROS node to read CAN data (linear x speed, and direction of motion i.e. rev or fwd) from my vehicle and my steer-by-wire system (encoder data for angular z). Both CAN interfaces are on separate CAN bus. I am using that data to generate odometry. We will call this node odom node.The odom node has 2 threads : one thread to generate linear velocity and another one to generate angular z velocity. The node also has a publisher for each of those parameters as well.
I also have a planning algorithm that works fine without the odom node, however, when I run my planning algorithm with my odom node, the planning algorithm crashes.
I opened up htop to see if its some sort of memory issue, I noticed that while the odom node is running, the number of threads keep on increasing (please refer to the image attached, 10740 threads).
I am not sure why the number of threads keep increasing, since Python has GIL, the first thread should get terminated before the second one is started. Could someone please help me figure out what am I doing wrong?
Code :
#!/usr/bin/env python3
import cantools
import can
from pprint import pprint
import rospy
from geometry_msgs.msg import Twist
import numpy as np
import fixedint
import std_msgs.msg
from std_msgs.msg import Float64
from nav_msgs.msg import Odometry
import math
import threading
import unittest
db = cantools.database.load_file('dbc_files/kia.dbc')
vehicle_can_bus = can.interface.Bus('can0', bustype='socketcan')
steer_by_wire_can_bus = can.interface.Bus('can1', bustype="socketcan")
class Telemetry:
def __init__(self, linear_x, angular_z, encoder_angle,direction):
self.linear_x = linear_x
self.angular_z = angular_z
self.encoder_angle = encoder_angle
self.direction = direction
try :
self.linear_rate = rospy.get_param('~linear_vel_rate')
self.angular_rate = rospy.get_param('~angular_vel_rate')
except KeyError:
print ("Could not set angular and linear thread rate parameters!!!")
rospy.Timer(rospy.Duration(1.0 / self.linear_rate), self.linear_velocity_loop)
rospy.Timer(rospy.Duration(1.0 / self.angular_rate), self.angular_velocity_loop)
def linear_velocity_loop(self,event):
while not rospy.is_shutdown() :
# reader = can.BufferedReader()
# notifier = can.Notifier(vehicle_can_bus, [reader], 10) # bus, listeners, time
# message = reader.get_message(10) # timeout
# if (message.arbitration_id == 213786525):
# rospy.loginfo_throttle((1/10),message.arbitration_id)
message = vehicle_can_bus.recv()
if message.arbitration_id == 317284680: # ID of ShtlDisp
# this message will print every 0.05 seconds (1/20)
rospy.loginfo_throttle((1/10), "Shuttle msg")
shuttle_data = db.decode_message('ShtlDisp', message.data)
ShtlSymbol = shuttle_data['ShtlSymbol']
if (ShtlSymbol == 3) : # if the shuttle is in rev position, ShtlSymbol == 1 is fwd, 2 is neutral, 3 is rev
self.direction = -1
# rospy.loginfo("The shuttle status is reverse")
elif ShtlSymbol == 1 :
self.direction = 1
# rospy.loginfo("The shuttle status is forward")
# else:
# self.direction = 0
# rospy.loginfo("The shuttle status is neutral")
if message.arbitration_id == 213786525: # ID converted to HEX. Make sure if CAN ID is 19 or 29 bit. ID of DbmSpeedDisplay 213786525
rospy.loginfo_throttle((1/10), "Received speed from CAN")
readable_data = db.decode_message('DbmSpeedDisplay', message.data)
SpeedDisplay_H = readable_data['SpeedDisplay_H']
SpeedDecimalPoint = readable_data['SpeedDecimalPoint']
SpeedDisplay_L = readable_data['SpeedDisplay_L'] # to kmph (dib by 100) and then m/sec (div 3.6)
PtoRpm_H = readable_data['PtoRpm_H']
PtoRpm_L = readable_data['PtoRpm_L']
if (SpeedDisplay_H == 0):
if (SpeedDisplay_L < 0):
SpeedDisplay_L = 255 + SpeedDisplay_L
if (SpeedDisplay_H == 1):
if (SpeedDisplay_L > 0):
SpeedDisplay_L = 255 + SpeedDisplay_L
velocity = SpeedDisplay_L*self.direction
self.linear_x = velocity/(100*3.6) # 3.6 to kmph >> m/s and div by 100 to chnage from 116 m/h (from CAN) to 1.16 kmph
# while not reader.buffer.empty():
# reader.buffer.get()
def angular_velocity_loop(self,event):
while not rospy.is_shutdown() :
dbw_reader = can.BufferedReader()
dbw_notifier = can.Notifier(steer_by_wire_can_bus, [dbw_reader], 0.1) # bus, listeners, time
encoder_message = dbw_reader.get_message(0.1) # timeout
# encoder_message = steer_by_wire_can_bus.recv()
if encoder_message and encoder_message.arbitration_id == 1:
#rospy.loginfo_throttle (duration (n), message) # publish the message every n seconds
rospy.loginfo_throttle((1/10), "Recieved Encoder Data") # this message will print every 0.05 seconds (1/20)
encoder_data = np.uint16(encoder_message.data)
xx = encoder_data[3] | (encoder_data[4] << 8)
xx &= 0x0FFF
if xx & 0x0800:
xx |= 0xF000
angle = np.int16(xx) * (-360.0 / 4096.0)
if angle > 0.0:
angle *= 1.21582733812
angle = angle *(3.14/180)
self.encoder_angle = angle
# angular speed (z) calculation
wheelbase = rospy.get_param('~wheelbase')
self.angular_z = self.linear_x * math.tan(self.encoder_angle)/wheelbase
# rospy.sleep(rospy.Duration(2))
# clear CAN message buffer to always get the latest message
while not dbw_reader.buffer.empty():
dbw_reader.buffer.get()
def telematics_publish(telemetry):
pub = rospy.Publisher('/vehicle_telematics/odom', Odometry, queue_size=10)
encoder_pub = rospy.Publisher('/vehicle_telematics/encoder', Float64, queue_size=10)
try:
pub_rate = rospy.get_param('~publishing_rate')
rate = rospy.Rate(pub_rate)
except KeyError:
rospy.logerr("Could not set publishing rate !!!")
# rate = rospy.Rate(10)
twist = Twist()
odom = Odometry()
while not rospy.is_shutdown():
odom.header.stamp = rospy.Time.now()
odom.header.frame_id = 'base_odom'
odom.pose.pose.position.x = 0
odom.pose.pose.position.y = 0
odom.pose.pose.position.z = 0.0
odom.pose.pose.orientation.x = 0
odom.pose.pose.orientation.y = 0
odom.pose.pose.orientation.z = 0
odom.pose.pose.orientation.w = 0
odom.child_frame_id = "odom"
odom.twist.twist.linear.x = telemetry.linear_x
odom.twist.twist.linear.y = 0
odom.twist.twist.angular.z = telemetry.angular_z
# rospy.loginfo(odom)
# rospy.loginfo(telemetry.encoder_angle)
pub.publish(odom)
rospy.loginfo_throttle((1/10), "Odom published")
encoder_pub.publish(telemetry.encoder_angle)
rospy.loginfo_throttle((1/10), "Encoder published") # if rospy.loginfo seems to slow the node down then change 1/rate to 1/10. 1/20 seems to work great for all rospy.loginfo in this code.
rate.sleep()
if __name__ == '__main__':
try:
rospy.init_node('vehicle_telematics')
tele = Telemetry(0,0,0,1)
telematics_publish(tele)
except rospy.ROSInterruptException:
pass