Open robberthofmanfm opened 3 years ago
Something else that I tried and that didn't work, are variations on the code in skiros2/skiros2_skill/nodes/utils/cmd.
Instantiating a SkillLayerInterface and getting all of its agents always results in an empty dictionary for me.
I'm getting closer with the rosservice call
method. execution_id
should be -1
, otherwise skill_manager.py
L428, _command_cb(self, msg)
does not add a task to the skill manager.
Then, in skiros2_ws/src/skiros2/skiros2_common/src/skiros2_common/ros/utils.py
L171, in the decodeParam(p)
method, I added a line if v is not None:
, and am now able to call parameterless skills.
I will now try to find out what's going on with decodeParam(p)
to make complex skills with parameters working too.
One a side note, I suspect error handling could be better when you pass a command to rosservice call
: if you do that with the correct skill description class name (e.g. DummyPrimitive
), but wrong skill implementation class name (e.g. dummy_primitive_spelled_wrong
), the skill_manager_node terminal shows that it's trying to execute this wrongly spelled skill over and over, even if you use the Qt GUI to execute a different skill. I suspect this is because of skiros2_ws/src/skiros2/skiros2_skill/src/skiros2_skill/core/skill_instanciator.py
, L68, method assign_instance(self, skill, ignore_list)
.
It calls skill.setInstance(self.add_instance(skill.label))
there, which I would split into two lines (add_instance
can fail too), and maybe also surround with try/except blocks.
Hi @robberthofmanfm , I would always suggest to use the SkillLayerInterface to interface with the skill manager. If the agents results in an empty dictionary, it could be a problem of namespace. It uses a simple discovery system ( see skiros2_skill/src/skiros2_skill/ros/discovery_interface.py ). You have to initialize the SkillLayerInterface with the right namespace, "skill_managers". If you make it work, you will not have to dig into the serialization/deserialization of params yourself.
Hi @frvd , thanks a lot for your reply, it made me look in the right direction.
The namespace was not the problem, since that's hard coded in the SkillLayerInterface itself, L13:
self.init_discovery("skill_managers", self._on_active, self._on_inactive)
However, I understand now that all this information is communicated over ROS messages, so the solution was just to wait until those messages arrive. Before, my code would execute in a few milliseconds and end without success. Now, it waits for the ROS message, picks up the agent and asks the agent to execute a (parameterless) skill. Now I just need to get the parameters working.
The relevant piece of code is the following:
_sli = sli.SkillLayerInterface('/external_interface')
while(_sli.agents == dict()):
pass
manager = _sli.agent
type_in = "skiros:DummyPrimitive"
name = "dummy_primitive"
sk = SkillHolder(manager, type_in, name, params_in=None, children=None)
print(_sli.agent.execute(skill_list=[sk]))
I'm not sure if that while
loop is considered good practice (I don't want to rospy_spin
because this code will be called from within an endless loop), but this seems to work.
Maybe we can document this as a tutorial in the wiki, together with some rosservice call
commands?
Thanks again for your help, it's much appreciated.
Hi again, I am glad you found the solution :)
Indeed, waiting with a while loop is the most straightforward solution I can think about at the moment. In the future we could implement an Event
to wait.
To execute skills with parameters, you can use first <agent>.get_skill(<name>)
. It returns a skill holder with all parameters initialized to defaults. You can then pass it to the execute
, like in your example.
I agree that a tutorial on this would be helpful. Do you think it would be better to describe the usage of SkillLayerInterface
, or the use of raw services?
Awesome! That seems to work.
I suppose both SkillLayerInterface
and ROS services have their advantages, where the first can be used to easily integrate services based on python (and is imo cleaner and less environment dependent), and the latter can be used for interoperability with any language that can call the equivalent of python's os.system()
.
Personally, I'm moving forward with the SkillLayerInterface
approach since our 'external' service is also written in python :)
Hello @robberthofmanfm do you consider the problem solved ? Should we close the issue ?
Thanks :)
Hi @dhled , yes the issue itself is resolved, but I think it might be useful to create a tutorial out of this in the wiki. I don't know if there's some standard process for this. If another wiki tutorial is not preferred then I guess users can always find this issue :)
I am working on an integration where I want an external python script to initiate the execution of a skill available in SkiROS2. On the wiki home page, it says
While everything works fine using the SkiROS GUI, I haven't got the ROS service working yet.
I just type in
rosservice call
, and then use tab completion until I see the name of the robot that lives in SkiROS. This give me four options:/robot_name/command
/robot_name/get_skills
/robot_name/get_loggers
/robot_name/set_logger_level
of which I assume only the first two to be relevant for this problem.
Calling
rosservice call /robot_name/get_skills
prints a quite verbose list of skills with their parameters.Then, further using tab completion on
rosservice call /robot_name/command
results in a multi-line completion in my terminal, with some parameters to be filled in:author
, where I put my own name between the quotesaction
, where I put lower-casestart
between the quotesexecution_id
, where I've tried-1
,0
, and some bigger integersskills
, which expands totype
,name
andparams
To keep things simple, I made a
dummy_primitive
, which takes no parameters, and on execution just printsdummy_primitive called
and then returns success.For
type
, I setskiros:DummyPrimitive
, just like I named the SkillDescription class, and just like how it appears in the world model. Forname
, I setdummy_primitive
, like I named it in the primitives file, and also how it appears in the world model. Theparams
section, I either leave unchanged, or remove entirely.Executing this command results in
ok: True
and yields the following output in the skill_manager_node terminal:However, when executing this very same primitive through the use of the GUI, we can already observe a difference:
It seems that the GUI also makes sure that the SkillManager adds a task, which is then executed.
So to summarize, my question is: how can I execute a skill from an external python script or command line?