Help with python action client

I have been stuck on this python script for two weeks and was wondering if anyone could help. I am doing the first rosject and have this code for my action client:

#! /usr/bin/env python

import rospy
import actionlib
import turtle2.msg
from turtle2.msg import turtlemvmtAction, turtlemvmtGoal, turtlemvmtResult, turtlemvmtFeedback
import rospy
import sensor_msgs.msg
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
from std_msgs.msg import String

#Get information from /scan into turtlemvmtGoal

class ActionClient(): #prereq instead of of object , or self???

def __init__(self, dist): #Constructors are used to initialize the object’s state. The task of constructors is to initialize(assign values) to the data members of the class when an object of class is created. Like methods, a constructor also contains collection of statements(i.e. instructions) that are executed at time of Object creation. It is run as soon as an object of a class is instantiated. The method is useful to do any initialization you want to do with your object.
    self.dist = dist
    self.sub = rospy.Subscriber('/scan', LaserScan, self.callback)

def callback(self, msg) :
    dist = (msg.ranges[180])
    print ("point 1", dist)
    self.call_server()        
    
            
def feedback_cb(self):
    print ("point 4: completed")


def call_server(self):
    print ("point 2, server called")

    print ("point 3", self.dist)

    self.dist = turtlemvmtGoal()

    client = actionlib.SimpleActionClient('sharedspace', turtlemvmtAction)

    client.wait_for_server()

    client.send_goal(self.dist, feedback_cb = self.feedback_cb)

    client.wait_for_result()

    result = client.get_result()

    return result

if name == ‘main’:
try:
rospy.init_node(‘action_client’)
j = ActionClient(dist=None)
print (‘The result is:’,)
rospy.spin()
except rospy.ROSInterruptException as e:
print (‘Something went wrong:’, e)

Everything seems ok until point 3, where my ranges end up empty :slight_smile: image even though I called on self.dist.

My script failes to continue to point 4, which I assume is because of the error I’m getting to point 3,
When I investigate to see

rosmsg list /turtle2/turtlemvntGoal

I get

I think my problem is on how I am calling a variable within the class but I cant crack the code.

Would really appreciate any help!

Caveat - I’m not familiar with the details of this Rosject. Regardless, here’s what I noticed:

You initiate your action client object with a dist of None

therefore self.dist is set as None as a result of this:

When you get a scan result, you assign the distance to one of the ranges

Note that you do not make an assignment to self.dist. Here dist is simply a local variable. Self.dist is still equal to None.

I am a bit confused as to then why when you do this:

You actually get “ranges: ” and not just None back. Did you leave anything out in what you posted?

I’m also a little confused by your data flow - as far as I can tell you’ll call the action client when you get a sensor message. The comment in your program says:

But further down you have

So here you are redefining self.dist as a turtlemovementGoal object. But you don’t have any data in it when you then send it to the action server.

Hope these observations help.
/K

2 Likes

These are super helpful thank you so much for your time and patience!

Note that you do not make an assignment to self.dist . Here dist is simply a local variable. Self.dist is still equal to None.

  • Thank you! I think I had that initially then went ot make changes and forgot to reset and dug myself into a hole. That was really helpful!

You actually get “ranges: ” and not just None back. Did you leave anything out in what you posted?
This really confused me!!! I dont know why I got that. On my .action file I have it as a float so its werid that it shows up like that. Either way, thats resolved with the above changes

So here you are redefining self.dist as a turtlemovementGoal object. But you don’t have any data in it when you then send it to the action server.

Now that I made the correction above (self.dist instead of dist), I feel that I should have data in it. When I run the action client, I get
image

HOWEVER, when I run rostopic echo on sharedspace/goal I come up empty
image
Shouldnt ranges be filled with the data from self.dist???

Also, if we continue on the script, when we get to:

client.send_goal(self.dist, feedback_cb = self.feedback_cb)

It should sent it to the function feedback_cb and it should print “point 4 completed”
HOWEVER it never gets to this point. Why is that?

Again, thank you so much for your help, this has been driving me crazy!

updated code just for your reference

Great.

No. When you do this: self.dist = turtlemvmtGoal() what you are doing is re-assigning the variable self.dist to be a turtlemvmtGoal message object. The distance that you had stored is no longer there. You just have an empty message object. That’s OK - you actually need that to send the message to the action server. Since this is a whole message object, you’ll need to assign a value to the part of the object that contains the distance. As I said - I’m not familiar with the specifics of this Rosject. Did you make a custom message? What is the structure of the message? Usually you do something like this:

goal_message = turtlemovementGoal()
goal_message.distance = distance_value

Since you are taking a reading from where you already are, and then sending that distance to a goal, I’m not sure that you’ll actually go anywhere unless your action server is programmed to move a certain distance away from the “goal”.

Hope that helps.
/K

2 Likes

A million thanks!

To answer your questions: I made a custom action (but didnt specifically create a custom message) with the structure as follows:
image

Could this be my error? my call server code is

def call_server(self): print ("point 2, server called")
    print ("point 3", self.dist)

    goal_message = turtlemvmtGoal()

    goal_message.ranges = self.dist

    print ("point3a",goal_message.ranges)

    client = actionlib.SimpleActionClient('sharedspace', turtlemvmtAction)

    client.wait_for_server()

    client.send_goal(goal_message.ranges, feedback_cb = self.feedback_cb)

    client.wait_for_result()

    result = client.get_result()

    return result

but I get the following error:

  • I also tried client.send_goal(goal_message, feedback_cb = self.feedback_cb) but got errors as well

I dont fully understand it because it goes outside of the catkin workspace but im getting the impression that one of my variables or objects is configured incorrectly. If I need to make a custom message to address then then I can work on trying to do that.

The goal of the overall project is the move the robot in a square around a room while maintaning a certain distance from the wall. In my action server Im trying to program the robot to move forward and made adjustments in its path if it senses the wall less than a certain value

image

Thank you very much - I dont have much program experience prior to this endeavor and your help really make me understand the concepts better

1 Like

So you did actually create a custom message as part of that. But your goal is a list of float 32 (float32), which probably isn’t what you want. What value does the action server expect? Probably the distance from the wall so it can adjust the path. So now I see why you’d send the current distance.

I’d work backward from what the action server wants and make sure you’re sending the right data type.
/K

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.