ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

How to pass complex arguments to rostopic (Python subprocess)?

asked 2018-06-14 04:52:45 -0500

thinwybk gravatar image

updated 2018-06-15 03:46:40 -0500

I want to call ros_comm tools (mainly rostopic to publish messages right now) via Python subprocess and pass complex YAML arguments (nested, containing message fields of all field types, etc.) to it.

Let's say I want to publish messages of type geometry_msgs/PointStamped

$ rosmsg show geometry_msgs/PointStamped
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
geometry_msgs/Point point
  float64 x
  float64 y
  float64 z

on a topic /ptusing explicit YAML dictionary syntax like described in the wiki section "4.3 Command-line and messages (dictionaries)" which would be called on the command line like follows:

rostopic pub pt geometry_msgs/PointStamped '{header: {stamp: now, frame_id: base_link}, point: {x: 1.0, y: 2.0, z: 3.0}}'

Using the YAML list syntax works well but is not really convenient to work with and is limited to easier cases I guess:

import subprocess
yaml_list_header = '[0, now, base_link]'
yaml_list_point = '[1.0, 2.0, 3.0]'
sp = subprocess.Popen(['rostopic', 'pub', 'pt', 'geometry_msgs/PointStamped', yaml_list_header, yaml_list_point, '-r', '2'])

EDIT: Probably not obvious... nesting with YAML list syntax is possible as well.

import subprocess
yaml_list_header = '[0, [0, 0], base_link]'
yaml_list_point = '[1.0, 2.0, 3.0]'
sp = subprocess.Popen(['rostopic', 'pub', 'pt', 'geometry_msgs/PointStamped', yaml_list_header, yaml_list_point, '-r', '2'])

What's the best approach (most flexible, least error prone) to publish messages via rostopic called via Python subprocess? It is probably best to pass the cmd as list to subprocess.Popen(cmd). (Not to use shlex.split(<cmd-as-string>) to generate cmd for subprocess.Popen(cmd) due to little control about what the result looks like and if it suites the syntax.) (As YAML is a valid super-set of JSON it's probably possible to use some string to JSON parser for conversion.)

edit retag flag offensive close merge delete

Comments

2

I'm not sure it's a best practice (I dislike using the command line tools from Python/C++ in any case), but what about using the regular msg classes, populate their fields, serialise to yaml and pass that string to rostopic pub?

gvdhoorn gravatar image gvdhoorn  ( 2018-06-14 06:08:41 -0500 )edit

I dislike using the command line tools as well. However for now it is the best approach to get fast results. " serialise to yaml and pass that string to rostopic pub?" That's exactly what I don't know how to do in detail yet.

thinwybk gravatar image thinwybk  ( 2018-06-14 06:16:26 -0500 )edit

2 Answers

Sort by ยป oldest newest most voted
1

answered 2018-06-14 14:14:06 -0500

I definitely agree with @gvdhoorn that this is likely not best practice to begin with, but his suggestion of serializing to YAML seems to work great. Example:

import yaml
import subprocess
from geometry_msgs.msg import PointStamped
import time

p = PointStamped()
p.header.frame_id = "test"
p.point.x = 2.0
y = yaml.load(str(p))
args = ['rostopic', 'pub', '/pt', 'geometry_msgs/PointStamped', '-r', '2', str(y)]
sp = subprocess.Popen(args)
time.sleep(2)
sp.kill()
edit flag offensive delete link more

Comments

I totally agree. Shelling out is not best practice. I changed the question title accordingly. Thx for the example. As this answer is matching the current question title I'll accept this one. Thx for all other response so far as well (@ahendrix, @gvdhoorn).

thinwybk gravatar image thinwybk  ( 2018-06-15 04:00:14 -0500 )edit

Side note: If you want to get an insight into messages (here) PointStamped go for:

In [1]: from geometry_msgs.msg import PointStamped
In [2]: PointStamped??
thinwybk gravatar image thinwybk  ( 2018-06-15 04:08:02 -0500 )edit
1

answered 2018-06-14 15:11:17 -0500

ahendrix gravatar image

I'd strongly recommend using the rospy API instead of shelling out to the command-line tool.

import rospy
from geometry_msgs.msg import PointStamped

p = PointStamped()
p.header.frame_id = 'test'
p.point.x = 2.0

rospy.init_node('point_pub')
pub = rospy.Publisher('pt', PointStamped, queue_size=10)

for i in range(4):
    pub.publish(p)
    rospy.sleep(0.5)
edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2018-06-14 04:52:45 -0500

Seen: 1,261 times

Last updated: Jun 15 '18