Skip to content

WIP: Implementation of components for controlling robots#101

Closed
destogl wants to merge 29 commits intoros-controls:masterfrom
destogl:hardware_control_components-design
Closed

WIP: Implementation of components for controlling robots#101
destogl wants to merge 29 commits intoros-controls:masterfrom
destogl:hardware_control_components-design

Conversation

@destogl
Copy link
Copy Markdown
Member

@destogl destogl commented Jun 12, 2020

This PR replaces #80 .

There are a few things to consider:

  1. A package name is work-in-progress. I am not (yet?) comfortable with having hardware word in the name since the components should be completely hardware agnostic and provide the abstraction for controller and controller manager.
  2. Currently, I am planning to add Robot-Component inside RobotHardware. This component should take care of parsing the urdf and gluing sensors and actuators. In the final version, there should be another way around, i.e. Component has reference to the Hardware. Nevertheless, starting as proposed in this PR should reduce number of braking changes and hopefully simplify integration.

@destogl destogl changed the title Implementation of components for controlling robots WIP: Implementation of components for controlling robots Jun 12, 2020
{
// Classes
template < typename T >
class ROS2ControlLoaderPluginlib
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd propose to leave out the plugin loading functionality as for now until we've figured out an API for the components.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added loading of Hardware already in Demo repository, and we will need this very soon for Sensors and Actuators.

Copy link
Copy Markdown
Contributor

@olivier-stasse olivier-stasse left a comment

Choose a reason for hiding this comment

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

Dear @destogl thanks again for this second iteration.
As a general feedback, I understand better the framework. Mimizing the dependency to the middleware is for me a great force of ros_control. Making it also in ros2_control would be super nice.
As far as I understood and IMHO the message targeted for the controllers should not be at the level of the Robot class. But their C++ mapping are definitely needed.

Thanks again !

#include <vector>

#include "control_msgs/msg/dynamic_joint_state.hpp"
#include "control_msgs/msg/interface_value.hpp"
Copy link
Copy Markdown
Contributor

@olivier-stasse olivier-stasse Jun 16, 2020

Choose a reason for hiding this comment

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

@destogl I am not sure that we want to put straight at this level the middleware.
In the ROS1 interface the middleware was present only for loading and unloading the controllers not at the robot level.
Right now I do not see the functionnal need to have it here.

IMHO if you want to have a robot compound through a middleware (OpenRTM, Micro-DDS) it should be implemented in another object.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To precise: dynamic_joint_state and interface_value are a convenient way to communicate with the controllers but to be agnostic they could be map to std::map<> and std::vector<> containers in the C++ space of the robot.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I agree to do this if there is a use case for it

#include "control_msgs/msg/dynamic_joint_state.hpp"
#include "control_msgs/msg/interface_value.hpp"

#include "rclcpp/macros.hpp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The convenient macros to handle the shared pointers could be exposed to avoid a dependency to rclcpp for this specific object.

#include "control_msgs/msg/interface_value.hpp"

#include "rclcpp/macros.hpp"
#include "rclcpp/rclcpp.hpp"
Copy link
Copy Markdown
Contributor

@olivier-stasse olivier-stasse Jun 16, 2020

Choose a reason for hiding this comment

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

Could you clarify why rclcpp is needed ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it will be used for logging.

{
//TODO: Add catch
try {
ComponentInfo robot_info = parse_robot_from_urdf(urdf_string);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think that this is very nice idea, but maybe not at the right place.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Where would you put it?

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 16, 2020

As far as I understood and IMHO the message targeted for the controllers should not be at the level of the Robot class. But their C++ mapping are definitely needed.

If you asked me few years ago I would be with you on this one, nevertheless always when I separate this I ended up with doubling code, but still using it in ROS...

Is anybody using ros_control outside ROS? And what are exactly issues including ROS-main headers? I understand your reasoning and we should clarify this for sure. I am also interesting if there is really a use case where you cannot afford to load a few ROS header (some microcontrollers or something).

@Karsten1987
Copy link
Copy Markdown
Contributor

And what are exactly issues including ROS-main headers?

Having small dependencies is always desirable for various reasons. But I guess we have to distinguish here between a strong dependency to something like a rclcpp::Node and a header for convenience functions.
The biggest argument for me is testability. I don't want to set up a complete ROS network to test my camera image or some controller behavior. Mocking a node is hard, mocking some primitive parameter values is easy.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 16, 2020

OK, I agree, should we then remove completely dependency to rclcpp? I find it important for logging functions...

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 16, 2020

@Karsten1987 I added interfaces and initial implementations of sensors and actuators. For this simple data, I believe we should make data-management directly in Sensor and Actuator interfaces since it will be shared functionality by many user components.

Is this a good direction? Did you have another concept in mind?

@olivier-stasse
Copy link
Copy Markdown
Contributor

I do agree that logging is important for real-time system. For this very reason, some implementation are carefully crafted to meet real-time constraints.

My only point is that the object Robot should be as lean as possible, and then derived class, or class matching polymorphic use offers various design patterns. (Thanks for the link @Karsten1987)

For more details you can see:
RTRobMultiAxisControl: A Framework for Real-Time Multiaxis and Multirobot Control
H Fischer, M Vulliez, P Laguillaumie, P Vulliez, JP Gazeau
IEEE Transactions on Automation Science and Engineering 16 (3), 1205-1217
for reat time evaluation. It might have changed.

@Karsten1987 Karsten1987 force-pushed the hardware_control_components-design branch from 54165d3 to aa885a2 Compare June 16, 2020 20:42
@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 17, 2020

@olivier-stasse @Karsten1987

in the meeting, I understood that we should not use ROS2 logging infrastructure. So what are alternative? Do we need to implement some soft of real-time logger?

@olivier-stasse
Copy link
Copy Markdown
Contributor

@destogl For the logging infra-structure this is something which can be illustrate in the ros2_control_demos repository. PAL-robotics has a package called dynamic_introspection I believe.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 22, 2020

As described in destogl/ros2-control#1 there are some still things to clarify.

The main open questions are:

  • How resources (actuators) are claimed?
  • Which type of pointers should be used in read and write functions? (doubles or more complex data)
  • Do we need separate functions for Accessing actuators data from Hardware and Controllers? E.g. set_command (from controllers), set_current_value (from Hardware)

And TODO:

  • add constant with common interface types (for now: position, velocity, effort)

@Karsten1987
Copy link
Copy Markdown
Contributor

Karsten1987 commented Jun 23, 2020

I was thinking a bit more about the resource management and came up with the following diagram:

ResourceManager (1)

The "Resource Manager" loads the actuators/sensors/systems from the URDF and each component registers their handles. That is, each component can (statically) allocate the data storage it needs and give access to it via the handle.
The resource manager can then "loan" a handle to individual controllers. This loaning of resources can be done quite transparently and ensures that each resource is only accessible via one controller. So we have a 1:1 relationship here. I guess we could have a 1:N relationship for sensor data and have the sensor handle access the data read-only. But I consider this details for now.
Whenever a controller is done (either via a call to shutdown or going out of scope) that loaned handle is returned and made available for other controllers to borrow.

The registered resources are fixed on runtime, making it hard to reload new components but I guess that's okay and usually the hardware is fixed within a setup - opposite to controllers, where we would like to dynamically load/start/stop them.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 25, 2020

@Karsten1987 thanks for sharing your idea. This sounds sensible to me this means that our "Container" would actually be RessourceManager. In this way we do not need robots to "have" other robots, but they would be composed over RessourceManager? (At the moment I do not see problems with this approach).

I would like that we get more concrete on the point how RessourceManager is started. Is this an objects in ControllerManager? Or how you imagine this?

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 25, 2020

The building is failing on test_robot_hardware package so I will wait for #109 before trying to repair it.

Copy link
Copy Markdown
Member

@bmagyar bmagyar left a comment

Choose a reason for hiding this comment

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

couple of styling comments going forward


namespace
{
constexpr const auto kRobotTag = "robot";
Copy link
Copy Markdown
Member

@bmagyar bmagyar Jun 26, 2020

Choose a reason for hiding this comment

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

Is it just me, being the hungarian who doesn't like hungarian notation? We also aren't using it anywhere else on the codebase (thank god) but only for some constants. Do we even need this? I know some people don't like shouty capital-case constants but they are not that bad nor would it be horrific to simply drop the gibberish from these variable names and go with robot_tag in this instance...

@Karsten1987 ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's the Google style guide. We've been using it in the ROS2 code base as such.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should I change something here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please just stay consistent at least. You've been using constants in three different places, all with their own style :D

@v-lopez
Copy link
Copy Markdown
Contributor

v-lopez commented Jun 26, 2020

@destogl For the logging infra-structure this is something which can be illustrate in the ros2_control_demos repository. PAL-robotics has a package called dynamic_introspection I believe.

@olivier-stasse It was rewritten as https://github.com/pal-robotics/pal_statistics but, it is still ROS1 only and it's useful for publishing numerical data in a RT safe way, not messages.

So not useful here right now I believe.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jun 29, 2020

Before I do some more coding, first 1-2 questions.

@Karsten1987: Can you look at the ResourceManager? Is this along with your proposition?

What would be the best way to manage hardware in ResourceManger? At the current point, we would need to crate to maps, one with hardware and other with components, and then give hardware the reference to the components. This would create some additional interfaces... @Karsten1987 what was your idea to solve this? What is the purpose of SensorInterface is this representation of hardware? (I just want to check that we are thinking int he same direction...)

Comment on lines +32 to +41
std::string name;
std::string type;
std::string class_type;
std::string joint;
std::vector<std::string> interface_names;
std::unordered_map<std::string, std::string> parameters;
std::vector<ComponentInfo> subcomponents;

std::string hardware_class_type;
std::unordered_map<std::string, std::string> hardware_parameters;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should be limited to what's currently being used in this PR.
I also strongly advise against having a recursive data structure here. A component info should be a POD, with a flat structure and describing one specific component (either actuator or sensor).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK. I added than SystemInfo to realize structural dependencies in URDF. is this OK? I extended test URDF and tests to support hardware class types in components.


namespace
{
constexpr const auto kRobotTag = "robot";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's the Google style guide. We've been using it in the ROS2 code base as such.

@destogl destogl requested a review from bmagyar July 4, 2020 21:50
@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jul 4, 2020

@Karsten1987 @bmagyar I hope I addressed all your comments. is build failing because the master is also failing?

@destogl destogl requested a review from Karsten1987 July 4, 2020 21:51
@Karsten1987
Copy link
Copy Markdown
Contributor

Looking at the CI output, it seems like two tests are failing:

  [5.922s] The following tests FAILED:
  [5.922s] 	  3 - test_component_parser (Failed)
  [5.922s] 	  8 - uncrustify (Failed)
  [5.922s] Errors while running CTest

The return code for the test_component_parser is -11 which regularly indicates a segmentation fault. You might want to look into these once more.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jul 7, 2020

I was thinking a bit more about the resource management and came up with the following diagram:

I agree with the diagram and have a few questions...

  1. We lost the word "Robot" from here. Will this confuse users?
  2. What about Actuators/ActuatorHandles? I am not sure this will work smooth with transmissions. Maybe we should have reference to Transmissions and Actuators in the RessourceManager?

The resource manager can then "loan" a handle to individual controllers. This loaning of resources can be done quite transparently and ensures that each resource is only accessible via one controller. So we have a 1:1 relationship here. I guess we could have a 1:N relationship for sensor data and have the sensor handle access the data read-only. But I consider this details for now.

I agree.

The registered resources are fixed on runtime, making it hard to reload new components but I guess that's okay and usually the hardware is fixed within a setup - opposite to controllers, where we would like to dynamically load/start/stop them.

This is OK for now. I can only imagine that this registering resources at runtime would be interesting for tool changers. Nevertheless, this could be extended in the future.

@jordan-palacios
Copy link
Copy Markdown
Member

  1. What about Actuators/ActuatorHandles? I am not sure this will work smooth with transmissions. Maybe we should have reference to Transmissions and Actuators in the RessourceManager?

Given that robot_hardwarealready holds the Joints and Actuators handles, adding Transmissions there seems like the sensible thing to do.

As @Karsten1987 mentioned, I think focusing on the Joints and Sensor interfaces should be the only scope of the current PR. The ResourceManager and any other feature should come later.

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jul 14, 2020

Hi guys, I renamed Actuator to Joint. I still have problems with some tests and I am not able to find the error. In the test node, everything works fine...

Can somebody help me with the output of the tests? I tried using the following command:

colcon test --packages-select hardware_interface --ctest-args -VV -R test_component_parser --output-on-failure

but there is no additional output. Therefore it is very hard to pin-point the error(s).

@olivier-stasse
Copy link
Copy Markdown
Contributor

olivier-stasse commented Jul 14, 2020

@destogl From the logs of the github action run

The output is:

   Starting >>> rclcpp_lifecycle
  --- output: hardware_interface
  [0.363s] CMake Error at CMakeLists.txt:112:
  [0.363s]   Parse error.  Expected "(", got right paren with text ")".
  [0.363s] 
  [0.363s] 
  [0.363s] -- Configuring incomplete, errors occurred!
  ---
  --- stderr: hardware_interface
  CMake Error at CMakeLists.txt:112:
    Parse error.  Expected "(", got right paren with text ")".

it seems to come from a problem with rclcpp_lifecycle.
I was a bit surprise too but it seems that the colcon-mixin is actually fetching the dependencies and then compile all of them. This is quite expensive.

What I do not get it that CMakeLists.txt:112 seems to be a comment in rclcpp_lifecycle CMakeLists.txt:
https://github.com/ros2/rclcpp/blob/e3490a29cd076bff320f6b5f02b013eb93c8d29d/rclcpp_lifecycle/CMakeLists.txt#L112
Not sure that helps.

Copy link
Copy Markdown
Contributor

@Karsten1987 Karsten1987 left a comment

Choose a reason for hiding this comment

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

are you planning on adding tests to the APIs introduced here? There's quite some new stuff in here which doesn't have any corresponding test.

Comment on lines +67 to +70
/**
* \brief (optional) key-value pairs for components hardware.
*/
std::unordered_map<std::string, std::string> hardware_parameters;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what's the difference here to parameters?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

parameters are for component and hardware_parameters are hardware-specific stuff. It could be that in some cases one have the same parameter for the component but uses different hardware/interface.

/**
* \brief constants for types of components.
*/
constexpr const auto robotType = "robot";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this be systemType?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not really. Those types are for definitions for ResourceManager to get correct type from type property when initializing components.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not sure I understand this.

Comment on lines +95 to +98
/**
* \brief list of subcomponents in the system, i.e., list of sensors and actuators.
*/
std::vector<ComponentInfo> subcomponents;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I still don't understand why we need this. Can a system not be treated as a single black-box component?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

According to your figure yes. You are right, this is not the best way, at least it shouldn't be placed inside SystemInfo type. Nevertheless, we need one top "container" which corresponds to the robot tag in URDF, as there could be System, Sensor and Joints defined, and they are like "subcomponents" of this robot.

The SystemInfo here represents exactly this robot level in URDF. IMHO it is better name than RobotInfo since one can define a good deal of different stuff with URDF, not only robots.

I hope you got my idea, and if you have any proposal, I would be happy to discuss it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fair enough. I am cross-posting here as I just went through #121 but I think we definitely need a piece of URDF here which dictates how the individual components as well as the systems are supposed to be specified.

Comment on lines +20 to +22
constexpr const auto HW_IF_POSITION = "position";
constexpr const auto HW_IF_VELOCITY = "velocity";
constexpr const auto HW_IF_EFFORT = "effort";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why do we need these values? I thought they'd be defined as constants in the control_msgs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is not relevant in #121 anymore.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what does that mean for this PR?


namespace
{
constexpr const auto kRobotTag = "robot";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please just stay consistent at least. You've been using constants in three different places, all with their own style :D

<description>The main package for `ros2_control`-concept testing. The package implements the most important classes and tests them with `demo_robot` to enable functionality and test driven development.</description>
<maintainer email="denis@stogl.de">Denis Štogl</maintainer>
<license>Apache License 2.0</license>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jul 27, 2020

are you planning on adding tests to the APIs introduced here? There's quite some new stuff in here which doesn't have any corresponding test.

I will add tests in the follow-up PRs starting with #121 with small example.

@Karsten1987
Copy link
Copy Markdown
Contributor

@destogl what's the state on this PR here?

@destogl
Copy link
Copy Markdown
Member Author

destogl commented Jul 28, 2020

It can be closed. Everything is moved in #121, #122, #123

@destogl destogl closed this Jul 29, 2020
destogl pushed a commit to b-robotized-forks/ros2_control that referenced this pull request Aug 11, 2022
* joint velocity controller initial class version with tests

* Set velocity to zero when deactivating controller

* Renaming to joint_group_velocity_controller

* Default initialize logger

* Improved error message log
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.

6 participants