I am doing rosject of course " ROS Basics in 5 Days" and in third part I create a new action server with new action type:
# goal
# result
geometry_msgs/Point32[] list_of_odoms
# feedback
float32 current_total
when I test this action with axclient tool, I meet with 2 problem:
this new action has not defined goal. So how can I “send a goal” to axclient? Just leave it blank and directly press send a goal? 2022-11-21 19-15-07 的屏幕截图1920×1080 216 KB
If the first problem is just solved as what I said, then I get another error:
AttributeError: 'float' object has no attribute 'current_total'
I am not sure what caused this. I don’t think it is because I somewhere wrongly call the attribute. Here is my code:
#! /usr/bin/env python
import rospy
import actionlib
import time
import math
from section_action.msg import OdomRecordResult, OdomRecordFeedback, OdomRecordAction
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Point32
class Odom_Record_Class():
def __init__(self):
# define action server of 'odom record'
self.odom_record_action_server = actionlib.SimpleActionServer(
"odom_record_action_server", OdomRecordAction, self.action_goal_callback, False)
self.odom_record_action_server.start()
rospy.loginfo('the action of odom recording is ready......')
# define subscriber of '/odom'
self.odom_sub = rospy.Subscriber('/odom', Odometry, self.sub_callback)
# in order to get infomation in '/odom'
self.odom_data = Odometry()
# in order to restore the info (x,y,theta) from '/dom'
self.current_pose = Point32()
self.previous_pose = Point32()
self.result_point32_list = OdomRecordResult()
# in order to feedback the current total distance that robot has moved so far
self.feedback = OdomRecordFeedback()
def sub_callback(self, msg):
# get pose info from '/odom'
self.odom_data = msg
def action_goal_callback(self, goal):
# get initial pose
self.current_pose.x = self.odom_data.pose.pose.position.x
self.current_pose.y = self.odom_data.pose.pose.position.y
self.current_pose.z = self.odom_data.pose.pose.orientation.z
# robot doesn't move any distance at the beginning
self.feedback = 0
# records has nothing at the beginning
self.result_point32_list = []
# if the robot hasn't finished one lap, then keep moving
while self.feedback <= 2:
# send pose into previous_pose for distance calculation
self.previous_pose.x = self.current_pose.x
self.previous_pose.y = self.current_pose.y
self.previous_pose.z = self.current_pose.z
# update pose info
self.current_pose.x = self.odom_data.pose.pose.position.x
self.current_pose.y = self.odom_data.pose.pose.position.y
self.current_pose.z = self.odom_data.pose.pose.orientation.z
# calculate and publish current total distance
interval_x = self.current_pose.x - self.previous_pose.x
interval_y = self.current_pose.y - self.previous_pose.y
interval_dist = math.sqrt(interval_x ** 2 + interval_y ** 2)
self.feedback += interval_dist
rospy.loginfo('current total distance is: ' +
str(self.feedback))
self.odom_record_action_server.publish_feedback(
self.feedback)
# record pose every 1 second
self.result_point32_list.append(self.current_pose)
time.sleep(1)
# return the records with all pose info
self.odom_record_action_server.set_succeeded(self.result_point32_list)
def main(self):
rospy.loginfo('call action......')
rospy.spin()
if __name__ == '__main__':
rospy.init_node("odom_record_1_node")
odom_record = Odom_Record_Class()
odom_record.main()
the complete error info shows as below:
[ERROR] [1669054503.135606, 4361.412000]: Exception in your execute callback: 'float' object has no attribute 'current_total'
Traceback (most recent call last):
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/simple_action_server.py", line 289, in executeLoop
self.execute_callback(goal)
File "/home/user/catkin_ws/src/section_action/scripts/ActionSection_OdomRecord_1.py", line 120, in action_goal_callback
self.odom_record_action_server.publish_feedback(
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/simple_action_server.py", line 175, in publish_feedback
self.current_goal.publish_feedback(feedback)
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/server_goal_handle.py", line 213, in publish_feedback
self.action_server.publish_feedback(self.status_tracker.status, feedback)
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/action_server.py", line 195, in publish_feedback
self.feedback_pub.publish(af)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/topics.py", line 882, in publish
self.impl.publish(data)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/topics.py", line 1066, in publish
serialize_message(b, self.seq, message)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/msg.py", line 152, in serialize_message
msg.serialize(b)
File "/home/user/catkin_ws/devel/lib/python3/dist-packages/section_action/msg/_OdomRecordActionFeedback.py", line 152, in serialize
_x = self.feedback.current_total
AttributeError: 'float' object has no attribute 'current_total'
the catkin_make is successful. Please help…
this new action has not defined goal. So how can I “send a goal” to axclient? Just leave it blank and directly press send a goal?
Yes! Just click on the “Send a Goal” button with goal blank text box.
MeineLiebeAxt:
You get this error because self.feedback = OdomRecordFeedback() just initializes self.feedback as a feedback message container. Therefore doing self.feedback = 0.0 makes no sense.
It must be self.feedback.current_total = 0.0
Try this fix and let me know if it worked.
– Girish
Hi @girishkumar.kannan ,
Thank you for you reply. Unfortunately it still doesn’t work.
My code is reviewed as:
import rospy
import actionlib
import time
import math
from section_action.msg import OdomRecordResult, OdomRecordFeedback, OdomRecordAction
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Point32
class Odom_Record_Class():
def __init__(self):
# define action server of 'odom record'
self.odom_record_action_server = actionlib.SimpleActionServer(
"odom_record_action_server", OdomRecordAction, self.action_goal_callback, False)
self.odom_record_action_server.start()
rospy.loginfo(
'the action /odom_record_action_server of odom recording is ready......')
# define subscriber of '/odom'
self.odom_sub = rospy.Subscriber('/odom', Odometry, self.sub_callback)
# in order to get infomation in '/odom'
self.odom_data = Odometry()
# in order to restore the info (x,y,theta) from '/dom'
self.current_pose = Point32()
self.previous_pose = Point32()
self.result_point32_list = OdomRecordResult()
# in order to feedback the current total distance that robot has moved so far
self.feedback = OdomRecordFeedback()
def sub_callback(self, msg):
# get pose info from '/odom'
self.odom_data = msg
def action_goal_callback(self, goal):
# get initial pose
self.current_pose.x = self.odom_data.pose.pose.position.x
self.current_pose.y = self.odom_data.pose.pose.position.y
self.current_pose.z = self.odom_data.pose.pose.orientation.z
# robot doesn't move any distance at the beginning
self.feedback.current_total = 0
# records has nothing at the beginning
self.result_point32_list = []
# if the robot hasn't finished one lap, then keep moving
while self.feedback.current_total <= 2:
# send pose into previous_pose for distance calculation
self.previous_pose.x = self.current_pose.x
self.previous_pose.y = self.current_pose.y
self.previous_pose.z = self.current_pose.z
# update pose info
self.current_pose.x = self.odom_data.pose.pose.position.x
self.current_pose.y = self.odom_data.pose.pose.position.y
self.current_pose.z = self.odom_data.pose.pose.orientation.z
# calculate and publish current total distance
interval_x = self.current_pose.x - self.previous_pose.x
interval_y = self.current_pose.y - self.previous_pose.y
interval_dist = math.sqrt(interval_x ** 2 + interval_y ** 2)
self.feedback.current_total += interval_dist
rospy.loginfo('current total distance is: ' +
str(self.feedback.current_total))
self.odom_record_action_server.publish_feedback(
self.feedback.current_total)
# record pose every 1 second
self.result_point32_list.append(self.current_pose)
time.sleep(1)
# return the records with all pose info
self.odom_record_action_server.set_succeeded(self.result_point32_list)
def main(self):
rospy.loginfo('call action......')
rospy.spin()
if __name__ == '__main__':
rospy.init_node("odom_record_1_node")
odom_record = Odom_Record_Class()
odom_record.main()
The only difference is to add current_total to self.feedback.
And axclient still shows not correct, and the error info also:
[ERROR] [1669108780.594454, 67.138000]: Exception in your execute callback: 'float' objecthas no attribute 'current_total'
Traceback (most recent call last):
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/simple_action_server.py", line 289, in executeLoop
self.execute_callback(goal)
File "/home/user/catkin_ws/src/section_action/scripts/ActionSection_OdomRecord_1.py", line 121, in action_goal_callback
self.odom_record_action_server.publish_feedback(
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/simple_action_server.py", line 175, in publish_feedback
self.current_goal.publish_feedback(feedback)
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/server_goal_handle.py", line 213, in publish_feedback
self.action_server.publish_feedback(self.status_tracker.status, feedback)
File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/action_server.py", line 195, in publish_feedback
self.feedback_pub.publish(af)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/topics.py", line 882, in publish
self.impl.publish(data)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/topics.py", line 1066, in publish
serialize_message(b, self.seq, message)
File "/opt/ros/noetic/lib/python3/dist-packages/rospy/msg.py", line 152, in serialize_message
msg.serialize(b)
File "/home/user/catkin_ws/devel/lib/python3/dist-packages/section_action/msg/_OdomRecordActionFeedback.py", line 152, in serialize
_x = self.feedback.current_total
AttributeError: 'float' object has no attribute 'current_total'
I have an idea:
I follow the course book to import this new action with:
from section_action.msg import OdomRecordResult, OdomRecordFeedback, OdomRecordAction
But actually the steps in course book have not told us to specificly make a msg directionary, but only action directionary. The case in course book with Fibonacci in actionlib_tutorials shows differently: it has both action and msg for Fibonacci:
But after catkin_make, I found the msg of my new action in devel/share/section_action/msg. Should I manually copy this msg directionary to the same path with action directionary? Is this the problem of float has no attribute of current_total?