Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change servo namespacing logic #2354

Merged
merged 11 commits into from
Oct 22, 2020

Conversation

jliukkonen
Copy link
Contributor

The namespacing logic seemed odd when running multiple servo servers within their own namespaces. This PR attempts to fix/change the namespacing logic within Servo to make it more intuitive. I listed before/after comparisons below to better illustrate the changes made in this PR.

Before

  • Service namespaces were a bit surprising for the services Servo advertises. Namespacing the node that made use of Servo into namespace foo resulted to service topics like this: /foo/foo/<nodeName>/change_drift_dimensions when I would have expected simple /foo/<nodeName>/change_drift_dimensions.
  • User was forced to load parameters into separate parameter namespace that matched the node name. This was a bit confusing since the node namespace is where the parameters are loaded by default.
  • The topic names that Servo subscribed to had, by default, in the example files the name of the Servo server node (for example servo_server/delta_twist_cmds). And to be more precise, the topic names had to match the node name. I think it it better to keep relative topic names in config files without the requirement to use node name as part of them. This way user doesn't have to change config file topic names if the node name is changed in the launch file.
  • Joint states subscriber subscribed to topic relative to the node handle namespace. Normally this is OK but if you wanted to place all the topics into the node namespace by initializing Servo using ros::NodeHandle nh("~") the joint states subscriber would start listening to /<nodeName>/joint_states topic which, I would argue, is rarely what one wants. And if more complex namespacing was used, there was no easy way (that I know of) to drop the <nodeName> part out of the full topic name just by modifying the topic name in the config file.

After

  • The service namespacing problem is solved by always advertising the services in the namespace of the node.
  • I made parameter namespacing optional since it is nice to be able to sometimes stash the Servo specific parameter under a sub-namespace within the node namespace. So user can now leave the parameters into node namespace for example like this /servo_server/<parameters> or use sub-namespace /servo_server/my_own_servo_param_namespace/<parameters>
  • The topic names in the Servo config file do not need to know about the node name that calls Servo. One can still use absolute names such as/foo/bar/servo/delta_twist_cmds just like before. Only the implicit requirement of specifying the calling node name as part of the topic names is removed, that is, delta_twist_cmds will now suffice for as value for cartesian_command_in_topic parameter.
  • Joint states subscriber listens always to the node parent namespace + topic name specified in the configuration file. For nodes running in global namespace the topic will default to /joint_states, for more complex namespaces the subscribed topic might be, for example, /foo/bar/baz/joint_states. Note that user is still able to specify absolute topic names in config file just like before.

In addition to the changes described in before/after examples, the servo server and cpp example files as well as the launch and config files have been modified to work with the changes in this PR.

// joint topic parameter can be explicitly set to "/baz/my_joint_state_topic".
// **Note!** Make sure your planning scene monitor subscribes to correct joint states topic when using absolute
// joint state topic name *or* custom relative joint state topic name (for example, "my_joint_states").
ros::NodeHandle nh_parent_ns = ros::NodeHandle("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this still be a private NodeHandle following the described logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have used wrong terminology, so just to confirm that we are talking about the same thing: If the node would have name servo_node and it would exist within namespace foo. Then I would argue that the joint_states it listens to should be /foo/joint_states and not /foo/servo_node/joint_states. If the explanation is confusing, can you propose an alternative wording?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, your solution totally makes sense

cartesian_command_in_topic: delta_twist_cmds # Topic for incoming Cartesian twist commands
joint_command_in_topic: delta_joint_cmds # Topic for incoming joint angle commands
joint_topic: joint_states
status_topic: status # Publish status to this topic
command_out_topic: joint_group_position_controller/command # Publish outgoing commands here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't the controller topics namespaced as well? A usual setup would have these at root level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I need to check the namespacing for this one.

Copy link
Member

@AndyZe AndyZe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Fixing the service namespacing is important. Please rebase.

Unfortunately, it looks like this tutorial does not work with the changes--

https://ros-planning.github.io/moveit_tutorials/doc/realtime_servo/realtime_servo_tutorial.html

I think the problem is, Servo is publishing on this topic: /servo_server/joint_group_position_controller/command

But the controller expects this topic: /joint_group_position_controller/command

So make sure topics in the yaml files have a leading /.

I'll need to test on our dual-arm projects after you rebase.

The service namespacing problem is solved by always advertising the services in the namespace of the node.

What if we're using the C++ interface, and two Servo objects are instantiated in the node? Might need to put the service topic in a yaml file, too :(

@AndyZe
Copy link
Member

AndyZe commented Oct 12, 2020

Overall I'm kind of lukewarm about this PR. It would be really nice to fix service namespacing but I think the other changes will lead to a lot of existing users who are missing / in their yaml files. What would you think about reducing the scope of the PR to focus on the services only?

@jliukkonen
Copy link
Contributor Author

jliukkonen commented Oct 13, 2020

The pose tracking PR brought in more changes than I realized. I have to go over those changes and see how they fit together with this PR. The pose tracking interface seems to utilize separate parameter namespace variable to its constructor while I would argue for using node handle. Sorry I didn't bring this up earlier, I simply hadn't considered namespacing back then. But I'll make the required changes here and then we can continue discussion when there is something more concrete to talk about.

As for breaking changes, anyone who depends on master branch of any repo implicitly accepts that sometimes their code breaks and they have to accommodate for the changes in the repos they rely on. Only breaking changes that should not be merged are the ones that can potentially be dangerous, for instance, when using old configuration files.

And, if this PR grows much more, then maybe it is best to concentrate on just the services but I will at least try to get this right in one PR first.

@jliukkonen jliukkonen force-pushed the change-servo-namespacing-logic branch from e6d892f to 6b63a8f Compare October 13, 2020 22:42
@jliukkonen
Copy link
Contributor Author

jliukkonen commented Oct 13, 2020

Well, this is a bit of a wall of text but I'm hoping this helps in the reviewing process. I listed below examples of how to use Servo with this PR and how the different parameters, topics and services end up looking like. As for the tutorials, I would rather concentrate on getting the namespacing right and once it is right, then it should be trivial to make the tiny fixes to the tutorials and examples that may still need updating.

How to run two Servo instances in a single node:

  ros::NodeHandle nh("~");
  
  // Left hand side
  ros::NodeHandle left_nh = ros::NodeHandle(nh, "left_side");
  moveit_servo::Servo left_servo(left_nh, planning_scene_monitor);
  left_servo.start();

  // Right hand side
  ros::NodeHandle right_nh = ros::NodeHandle(nh, "right_side");
  moveit_servo::Servo right_servo(right_nh, planning_scene_monitor);
  right_servo.start();

Example launch file:

<group ns="foo">
   	<node name="servo_server" pkg="moveit_servo" type="cpp_interface_example" output="screen" >
    	<param name="parameter_ns" type="string" value="sub_namespace" /> 
    	<rosparam command="load" file="$(find moveit_servo)/config/ur_simulated_config.yaml" ns="left_side/sub_namespace"/>
    	<rosparam command="load" file="$(find moveit_servo)/config/pose_tracking_settings.yaml" ns="left_side/sub_namespace" />
    	<rosparam command="load" file="$(find moveit_servo)/config/ur_simulated_config.yaml" ns="right_side/sub_namespace"/>
    	<rosparam command="load" file="$(find moveit_servo)/config/pose_tracking_settings.yaml" ns="right_side/sub_namespace" />
  	</node>
</group>

Parameters:

One example per each instance.

/foo/servo_server/left_side/sub_namespace/<param_name>
/foo/servo_server/right_side/sub_namespace/<param_name>

Topics:

Note! Especially pay attention to Servo internal topics which are fixed by this PR to truly be internal to that specific Servo instance. Also, The joint_group_position_controller/command is still within the node namespace to make sure the receiving end can differentiate between sources. If both instances published to /foo/servo_server/joint_group_position_controller/command` I'd say that will not work correctly.

/foo/attached_collision_object
/foo/collision_object
/foo/joint_states
/foo/planning_scene
/foo/planning_scene_world
/foo/servo_server/left_side/delta_joint_cmds
/foo/servo_server/left_side/delta_twist_cmds
/foo/servo_server/left_side/internal/collision_velocity_scale
/foo/servo_server/left_side/internal/worst_case_stop_time
/foo/servo_server/left_side/joint_group_position_controller/command
/foo/servo_server/left_side/status
/foo/servo_server/planning_scene_monitor/parameter_descriptions
/foo/servo_server/planning_scene_monitor/parameter_updates
/foo/servo_server/right_side/delta_joint_cmds
/foo/servo_server/right_side/delta_twist_cmds
/foo/servo_server/right_side/internal/collision_velocity_scale
/foo/servo_server/right_side/internal/worst_case_stop_time
/foo/servo_server/right_side/joint_group_position_controller/command
/foo/servo_server/right_side/status

Services:

/foo/servo_server/get_loggers
/foo/servo_server/left_side/change_control_dimensions
/foo/servo_server/left_side/change_drift_dimensions
/foo/servo_server/left_side/reset_servo_status
/foo/servo_server/planning_scene_monitor/set_parameters
/foo/servo_server/right_side/change_control_dimensions
/foo/servo_server/right_side/change_drift_dimensions
/foo/servo_server/right_side/reset_servo_status
/foo/servo_server/set_logger_level

How to run single Servo instance in a single node:

ros::NodeHandle nh("~");

// Left hand side
moveit_servo::Servo servo(nh, planning_scene_monitor);
servo.start();

Parameters:

/foo/servo_server/sub_namespace/<parameter_name>

Topics:

/foo/attached_collision_object
/foo/collision_object
/foo/joint_states
/foo/planning_scene
/foo/planning_scene_world
/foo/servo_server/delta_joint_cmds
/foo/servo_server/delta_twist_cmds
/foo/servo_server/internal/collision_velocity_scale
/foo/servo_server/internal/worst_case_stop_time
/foo/servo_server/joint_group_position_controller/command
/foo/servo_server/planning_scene_monitor/parameter_descriptions
/foo/servo_server/planning_scene_monitor/parameter_updates
/foo/servo_server/status

Services:

/foo/servo_server/change_control_dimensions
/foo/servo_server/change_drift_dimensions
/foo/servo_server/get_loggers
/foo/servo_server/planning_scene_monitor/set_parameters
/foo/servo_server/reset_servo_status
/foo/servo_server/set_logger_level

Multiple nodes

Running multiple nodes with multiple servos (or just one) in each of them is just a special case of the examples above.
Only thing that would change is the servo_server node name which would have to be unique for each node.

What about running the PoseTracker then? Well, it is exactly the same as the examples above except there is one more topic:
/foo/servo_server/target_pose

More than one tracker?
Want to have two trackers (or more) inside a single node? Same logic as above applies:

ros::NodeHandle nh("~");

// Left hand side
ros::NodeHandle left_nh = ros::NodeHandle(nh, "left_side");
moveit_servo::PoseTracking right_tracker(left_nh, planning_scene_monitor);

// Make a publisher for sending pose commands
ros::Publisher target_pose_pub =
  left_nh.advertise<geometry_msgs::PoseStamped>("target_pose", 1 /* queue */, true /* latch */);

// Subscribe to servo status (and log it when it changes)
StatusMonitor status_monitor(left_nh, "status");

// Right hand side
ros::NodeHandle right_nh = ros::NodeHandle(nh, "right_side");
moveit_servo::PoseTracking right_tracker(right_nh, planning_scene_monitor);

ros::Publisher target_pose_pub =
  right_nh.advertise<geometry_msgs::PoseStamped>("target_pose", 1 /* queue */, true /* latch */);

StatusMonitor status_monitor(right_nh, "status");

@codecov
Copy link

codecov bot commented Oct 14, 2020

Codecov Report

Merging #2354 into master will decrease coverage by 0.07%.
The diff coverage is 97.30%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2354      +/-   ##
==========================================
- Coverage   56.29%   56.23%   -0.06%     
==========================================
  Files         287      287              
  Lines       24433    24437       +4     
==========================================
- Hits        13752    13739      -13     
- Misses      10681    10698      +17     
Impacted Files Coverage Δ
.../moveit_servo/include/moveit_servo/pose_tracking.h 100.00% <ø> (ø)
...os/moveit_servo/include/moveit_servo/servo_calcs.h 100.00% <ø> (ø)
moveit_ros/moveit_servo/src/pose_tracking.cpp 45.95% <95.84%> (-0.76%) ⬇️
moveit_ros/moveit_servo/src/servo.cpp 62.78% <97.73%> (-2.29%) ⬇️
moveit_ros/moveit_servo/src/collision_check.cpp 80.29% <100.00%> (ø)
moveit_ros/moveit_servo/src/servo_calcs.cpp 59.14% <100.00%> (ø)
moveit_core/robot_model/src/joint_model_group.cpp 58.03% <0.00%> (-2.46%) ⬇️
...e/src/parameterization/model_based_state_space.cpp 70.40% <0.00%> (-2.39%) ⬇️
...ove_group_interface/src/wrap_python_move_group.cpp 40.18% <0.00%> (-1.54%) ⬇️
...ipulation/pick_place/src/manipulation_pipeline.cpp 72.35% <0.00%> (-1.06%) ⬇️
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f9b2e2b...b449495. Read the comment docs.

@AndyZe
Copy link
Member

AndyZe commented Oct 15, 2020

I'm waiting to test this on two more robot setups (one with dual arms). Looks good otherwise!

Copy link
Member

@AndyZe AndyZe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I don't have a good dual-arm setup to test with any more, but the changes seem like a net benefit. +1

// joint topic parameter can be explicitly set to "/baz/my_joint_state_topic".
// **Note!** Make sure your planning scene monitor subscribes to correct joint states topic when using absolute
// joint state topic name *or* custom relative joint state topic name (for example, "my_joint_states").
ros::NodeHandle nh_parent_ns = ros::NodeHandle("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, your solution totally makes sense

@AndyZe AndyZe merged commit 1ebdb2d into moveit:master Oct 22, 2020
@tylerjw tylerjw mentioned this pull request Oct 23, 2020
57 tasks
tylerjw pushed a commit to tylerjw/moveit that referenced this pull request Oct 23, 2020
* Publish services correctly in the namespace of the node.

* Set relative joint state topic names be relative to the parent namespace of the servo node.

* Make parameter sub-namespace optional. By default load the parameters into node namespace.

* Fix node handles for the servo server node and the cpp interface example to use node namespace instead of the parent namespace.

* Fix formatting

* Reading ee_frame_name parameter got lost somewhere along the way. Add it back.

* Remove explicit parameter_ns variables and used namespaced node handles instead.

* Fix internal namespace to truly be internal even in case where two or more Servo instances are launched from a single node.

* Fix command_out_topic parameter in ur_simulated_config.yaml example config.

* Apply namespacing fixes also to tests.

* Shorten the comment about joint states topic names.
tylerjw pushed a commit to tylerjw/moveit that referenced this pull request Oct 27, 2020
* Publish services correctly in the namespace of the node.

* Set relative joint state topic names be relative to the parent namespace of the servo node.

* Make parameter sub-namespace optional. By default load the parameters into node namespace.

* Fix node handles for the servo server node and the cpp interface example to use node namespace instead of the parent namespace.

* Fix formatting

* Reading ee_frame_name parameter got lost somewhere along the way. Add it back.

* Remove explicit parameter_ns variables and used namespaced node handles instead.

* Fix internal namespace to truly be internal even in case where two or more Servo instances are launched from a single node.

* Fix command_out_topic parameter in ur_simulated_config.yaml example config.

* Apply namespacing fixes also to tests.

* Shorten the comment about joint states topic names.
tylerjw pushed a commit that referenced this pull request Nov 19, 2020
* Publish services correctly in the namespace of the node.

* Set relative joint state topic names be relative to the parent namespace of the servo node.

* Make parameter sub-namespace optional. By default load the parameters into node namespace.

* Fix node handles for the servo server node and the cpp interface example to use node namespace instead of the parent namespace.

* Fix formatting

* Reading ee_frame_name parameter got lost somewhere along the way. Add it back.

* Remove explicit parameter_ns variables and used namespaced node handles instead.

* Fix internal namespace to truly be internal even in case where two or more Servo instances are launched from a single node.

* Fix command_out_topic parameter in ur_simulated_config.yaml example config.

* Apply namespacing fixes also to tests.

* Shorten the comment about joint states topic names.
@tylerjw tylerjw mentioned this pull request Apr 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants