ros-controls / roadmap

52 stars 20 forks source link

Do we need a FlexibleJointState message? #17

Closed gavanderhoorn closed 4 years ago

gavanderhoorn commented 4 years ago

Bit of a devil's advocate post, and admittedly I haven't read all the discussion around this, but what is the main rationale for wanting to use a single message (type) / topic to carry the system/joint state?

An alternative could be to use dedicated datastreams for specific types of data. That would seem to remove the need for a flexible grab bag/union type message, as producers would publish an appropriate message for their type of data and consumers would subscribe to it (ie: controllers).

New data types would result in new publications and subscriptions, not require extensions of existing messages. That would avoid the biggest issue identified with the current JointState message.

Semantics could even be derived from the (names of) topics and how they are connected between producers and consumers, instead of from the specific message types they carry. Not sure that would be desirable necessarily, but it would make things even more flexible.

I'm thinking a Mathworks Simulink type of setup, where blocks exchange primitive types and vectors of those as signals. Creating dedicated message types for those vectors would be something we can add here so type-checking can help us avoid making stupid connections (to avoid the "coffee temperature controls the throttle of my car" type of situations), but not exchanging a union type message which can contain "anything" seems like it would keep controller designs sane and implementations less complex.

(originally posted in https://github.com/ros-controls/roadmap/issues/16#issuecomment-611134465)

gavanderhoorn commented 4 years ago

Converted this into its own issue after discussing this with @bmagyar.

If this is an RTFM, then I'd be happy to do so.

mateus-amarante commented 4 years ago

Hello everyone, I've been reading the design discussions for a couple of days trying to connect the concepts of ROS2 and ros_control to see if I can contribute with opinions and suggestions at first. I am not so familiar with ROS2 yet, but I use ros_control for a while.

Regarding the FlexibleJointState message, I think it is trying to fulfill two objectives that can be separated:

1- Avoid exchange of unused data (e.g. enforce provide effort when it is not available)

In my opinion, this is a minor problem, but I think possible ways to avoid this would be:

2- Support different hardware interfaces:

Looking at the "controller_execution_management.md" draft, I feel it is trying to define a centralized way to exchange data between the Controllers and the RobotHW. The idea sounds to create a key-value map for all hardware resources grouped by hardware interfaces (by the way, the example in the "flexible_joint_states_msg.md" draft seems to mix the concept of resource/joint names and hardware interfaces). A centralized solution is a possible option, but I think it can enforce strong constraints.

Considering the scenario of exchanging data using messages (I think I am missing ROS2 concepts here, please correct me if I'm wrong), I suggest mapping resources and hardware interfaces to messages, like:

Karsten1987 commented 4 years ago

@gavanderhoorn I don't know exactly if you refer to a specific article here (either the flexible joint state message or the execution management or neither) My response here is mainly written looking at the latter, the execution management.

To take a step back here, the goal of all this is to introduce a more flexible way of composing complex, yet modular controller setups. The initial motivation was to get rid of a fixed triplet of a state in such x = [pos, vel, eff] and a single command output y = pos | vel | eff, all which is tightly bound to a joint interface construct. To overcome this, we would like to break with this constraint of having this exact triplet of predefined values and let the user specify these as they go and design their own custom setup.

There are currently two approaches I am thinking of:

I essentially opted for the second approach, because of two technical reasons:

1. Deterministic Execution While I agree that each value being sent as a ROS topic brings more flexibility and modularity, it also brings a heavy dependency on the ROS2 execution management itself. This approach requires that all values are being transported and delivered in realtime (c.f. https://github.com/ros-controls/ros2_control/issues/48). While there is quite some effort in this direction with static executors (https://github.com/ros2/rclcpp/pull/1034, https://github.com/ros2/rclc/blob/master/rclc_examples/src/example_executor.c) I still think it's not ready to be hard realtime. It seems, there is still quite some controversy for achieving determinism in the ROS2 executor (c.f. https://github.com/ros2/design/issues/259). One could argue that we could achieve this with intraprocess-only communication, but I am not aware of any intraprocess communication which does this exclusively and does not advertise the topics on the middleware as well. We could further opt for a SHM middleware (e.g. Iceoryx) but that would require further development. I think organizing a lock-free map bring in way less overhead and gives us the opportunity to focus on the essentials for ros-control without relying on a general ROS2 communication model.

2. Only publish what you need A second overhead I see is not all topics have to be published. If I think about a controller sequence where controllers are chained, I don't see a need to publish an intermediate result. We could have a dedicated (Joint-) State Publisher, which has access to the key-value map and simply publishes its content in a useful ROS2 message. That could even be publishing the values as a typical Joint State Message to not break with existing tools. I don't see the need to publish everything though. Additionally, imagining again a controller chaining, propagating through the chain might require n calls to spin(), where one subscription per controller will be executed per cycle. Having a single key-value map, could technically execute that chain in one go. Obviously, this only holds if we talk about all controllers being loaded as components within a single process and further no realtime communication between multiple computers are needed.


In general, I think also what @mateus-amarante mentioned, the map I have in mind is really only to bridge the gap on how to cycle information from the hardware through all controllers. I don't see it much as a specific ROS message in the form of generated from a .msg.

I would love to re-use something like rclcpp's intraprocess manager for the internal execution of ros-controllers. However, looking at https://github.com/ros2/rclcpp/blob/master/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp, I don't easily see how it can be used as a standalone class. I think this would further require a "intraprocess-comm-only" publisher and subscription, which does not allocate resources through the rmw interface and I wonder at this point if it's worth the effort.

What we could do in the meantime though is to design an API which adheres to pub/sub even though it's internally only accessing the map. This could be then still changed to the intraprocess communication manager in further iterations.

bmagyar commented 4 years ago

Thanks for opening this discussion. My notes:

In interest of time and moving forward with the project however, the lack of this message is a major blocker as there are several follow-up designs depending on it. I will go ahead with it but as I said, the implementation of it could be swapped out for something else (msg vs native struct, etc).

olivier-stasse commented 4 years ago

As a user, I think that the FlexibleJointState with the flat structure is a welcome and a needed addition. We are working on people developing their own power electronics and the message and its structure may change. It depends on the problem specific to the robot and the actuators: adding sensor to measure a structural flexibility, different control law, different motors. For the TALOS robot the lack of flexibility of ros_control imposed to use a fork. This imposes extra work to maintain. On the other hand from the control side, the semantic of the interface allowed me to perform introspection. This is also nice. So IMHO a good trade-off between the flexibility of the interface and the introspection capabilities is the proposed solutions Value Identifier Convention or the Constants with the Message. What I did not get from the description of the latter possibility is the way extensions are handled.

If someone wants to extend the message and that it is not inside the set of constants how is it done ? Let us assume for instance that I have an electric actuator with one motor value, one currents and that I want to add an IMU ? How do I handle this with this new framework ?

destogl commented 4 years ago

@olivier-stasse: The plan is actually not to provide a fixed set of constants, but one give a proposal for the most common one. This would be like "standard" constants in ros2-c.

On exactly how you would handle this we will provide in ros-controls/hardware_interface repository. If you look at my proposal on hardware_interface then you maybe get the idea of how the hardware composition is done.

Karsten1987 commented 4 years ago

@olivier-stasse I've tried to raise the issues of standardizing the Value Identifier Convention (VIC) in the Flexible Joint State Message design doc in section name-lookup. Yet, I am very well aware of your concern.

as @destogl mentioned, having a set of predefined and exclusive constants might not necessarily work. That's the same as extending the existing joint state message with more fields.

I guess I should make this clearer in the design doc, but I feel a nice middle ground between arbitrary strings as VICs and hardcoded constants could be established in the URDF. Taking your example, I could imagine something like the following being part of your URDF:

<transmission name="electric_motor1">
  <type>actuator_interface</type>
  <joint name="electric_motor_joint1">
     <output_hardware_interface>CurrentInterface</output_hardware_interface>
     <input_hardware_interface>EffortInterface</input_hardware_interface>
  </joint>
</transmission>

<transmission name="imu_1">
  <type>sensor_interface</type>
  <joint name="imu_fixed_joint1">
    <output_hardware_interface>FloatArrayInterface</output_hardware_interface>
  </joint>
</transmission>

The transmission parser could then generate the VICs from the URDF and populate the robot interface accordingly. That way the VICs are somehow centralized parsed and generated without much ambiguity and the only place to define them is the URDF. I am stressing out "only" in the sentence above as I think there should always be a possibility to add a random string as a VIC into the map. That would allow for custom controllers to interchange on non-standard VICs. It's the responsibility of the system designer (the user in the end) to make sure that these VICs are correctly used across the controller architecture.


@gavanderhoorn I haven't heard back from you. Are there any comments or follow-up questions from your side here? I am not sure if your points were addressed here.

olivier-stasse commented 4 years ago

@bmagyar Thanks for the feedback :+1: to have the constants in the URDF or XML file rather than header constants.

I am somewhat bit confused with the word transmission. It used both for describing the low level interface and its mechanical meaning (indicating the gear ratio).

Making this part flexible is important: we could be more precise in the quantities send to the low level control. I have seen EffortInterface used for joint torque, motor torque and motor current.

It would also allow us to send more information when needed. For instance Gain scheduling is often used, and I do not see how we can send PID gains on line with the current ros1_control API.

bmagyar commented 4 years ago

The messages were released to Foxy, I'm closing this discussion for now.