bdaiinstitute / ros_utilities

Wrappers and other utilities for ROS2
MIT License
24 stars 3 forks source link

Conversion function from bosdyn_api_msgs package not working as intended #101

Closed bobcorn closed 5 months ago

bobcorn commented 5 months ago

When trying to call the conversion function:

bosdyn_api_msgs.conversions.convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message

defined as:

def convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message(
    proto_msg: bosdyn.api.network_compute_bridge_pb2.ListAvailableModelsResponse, ros_msg: bosdyn_api_msgs.msg.ListAvailableModelsResponse
) -> None:
    """Convert from bosdyn.api.ListAvailableModelsResponse Protobuf messages to bosdyn_api_msgs/ListAvailableModelsResponse ROS messages."""

on a ListAvailableModelsResponse proto fetched from Spot, I encounter the following exception:

Exception has occurred: AttributeError (note: full exception trace is shown but execution is paused at: _run_module_as_main) 'CustomParamSpec' object has no attribute 'value' File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6234, in convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message ros_msg.spec.value = rclpy.serialization.serialize_message(typed_spec_message) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6300, in convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6271, in convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6391, in convert_bosdyn_api_one_of_param_child_spec_proto_to_bosdyn_api_msgs_one_of_param_child_spec_message convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message(proto_msg.spec, ros_msg.spec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6456, in convert_bosdyn_api_one_of_param_spec_specs_entry_proto_to_bosdyn_api_msgs_one_of_param_spec_specs_entry_message convert_bosdyn_api_one_of_param_child_spec_proto_to_bosdyn_api_msgs_one_of_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6427, in convert_bosdyn_api_one_of_param_spec_proto_to_bosdyn_api_msgs_one_of_param_spec_message convert_bosdyn_api_one_of_param_spec_specs_entry_proto_to_bosdyn_api_msgs_one_of_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6099, in convert_bosdyn_api_custom_param_spec_proto_to_bosdyn_api_msgs_custom_param_spec_message convert_bosdyn_api_one_of_param_spec_proto_to_bosdyn_api_msgs_one_of_param_spec_message(proto_msg.one_of_spec, ros_msg.spec.one_of_spec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6233, in convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message convert_bosdyn_api_custom_param_spec_proto_to_bosdyn_api_msgs_custom_param_spec_message(proto_msg.spec, typed_spec_message) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6300, in convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6271, in convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9855, in convert_bosdyn_api_model_data_proto_to_bosdyn_api_msgs_model_data_message convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message(proto_msg.custom_params, ros_msg.custom_params) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9813, in convert_bosdyn_api_available_models_proto_to_bosdyn_api_msgs_available_models_message convert_bosdyn_api_model_data_proto_to_bosdyn_api_msgs_model_data_message(input_data_item, output_data_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9780, in convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message convert_bosdyn_api_available_models_proto_to_bosdyn_api_msgs_available_models_message(proto_msg.models, ros_msg.models) File "/home/ros2_ws/build/spot_ncb_rms_actions/spot_ncb_rms_actions/list_actions.py", line 135, in timer_callback raise(e) File "/home/ros2_ws/build/spot_ncb_rms_actions/spot_ncb_rms_actions/list_actions.py", line 135, in timer_callback raise(e) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 107, in await_or_execute return callback(*args) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 351, in _execute_timer await await_or_execute(tmr.callback) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 437, in handler await call_coroutine(entity, arg) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/task.py", line 239, in call self._handler.send(None) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 736, in _spin_once_impl raise handler.exception() File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 739, in spin_once self._spin_once_impl(timeout_sec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/init.py", line 222, in spin executor.spin_once() File "/home/ros2_ws/build/spot_ncb_rms_actions/spot_ncb_rms_actions/list_actions.py", line 239, in main rclpy.spin(list_actions_node) File "/home/ros2_ws/install/spot_ncb_rms_actions/lib/spot_ncb_rms_actions/list_actions_node", line 33, in sys.exit(load_entry_point('spot-ncb-rms-actions', 'console_scripts', 'list_actions_node')()) File "/usr/lib/python3.10/runpy.py", line 86, in _run_code exec(code, run_globals) File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main (Current frame) return _run_code(code, main_globals, None, AttributeError: 'CustomParamSpec' object has no attribute 'value'

image

The exception doesn't occur for every ListAvailableModelsResponse proto, but for most of them it does. It looks like the function is not handling the proto fields properly.

I tried to dig into it a little bit, but I was not able to identify the exact cause of the issue as the call stack and the proto themselves are quite convoluted and not really straightforward to debug. Being autogenerated functions, I guess the root cause probably lies in conversions.py.jinja.

If it helps to investigate the problem, I provide attached two pickled ListAvailableModelsResponse protos, one that fails and one that succeeds. You can do a simple unpickle and provide them directly as input to the function to reproduce the bug on your side:

Both the provided ListAvailableModelsResponse protos are associated with two native Boston Dynamics models returned by Spot (they are not custom-developed models).

mhidalgo-bdai commented 5 months ago

Thanks for the report @bobcorn! We'll look into it.

mhidalgo-bdai commented 5 months ago

I took a look. ~There seems to be an issue in how dependency cycles in message definitions are broken up. Conversion API and message definition do not seem to agree on which edge of the dependency graph should be type erased.~ Hmm, no, it's not that.

mhidalgo-bdai commented 5 months ago

@bobcorn I'm unable to reproduce. Could it be that you are populating your ROS message equivalent inappropriately? The console output you shared suggests that a message field that should have been type erased wasn't.

bobcorn commented 5 months ago

@mhidalgo-bdai thank you for taking the time to investigate.

Actually, I do not populate the ROS message equivalent, but create an empty instance of it which I then pass as input to the conversion function. For example:

import pickle

from bosdyn.api.network_compute_bridge_pb2 import ListAvailableModelsResponse
from bosdyn_api_msgs import conversions as bosdyn_api_conv
from bosdyn_api_msgs.msg import ListAvailableModelsResponse as ListAvailableModelsResponseMsg

# Create an empty instance of the ROS message equivalent.
ncb_response_msg: ListAvailableModelsResponseMsg = ListAvailableModelsResponseMsg()

# Load the ListAvailableModelsResponse proto.
with open('ListAvailableModelsResponseThatFails.pkl', 'rb') as file:
    ncb_response: ListAvailableModelsResponse = pickle.load(file)

# Try to populate the ROS message equivalent with the proto information.
bosdyn_api_conv.convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message(ncb_response, ncb_response_msg)

This should reproduce the error on your side, raising the following traceback:

Traceback (most recent call last): File "/home/ros2_ws/src/spot_ncb_rms_actions/spot_ncb_rms_actions/list_actions.py", line 12, in bosdyn_api_conv.convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message(ncb_response, ncb_response_msg) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9780, in convert_bosdyn_api_list_available_models_response_proto_to_bosdyn_api_msgs_list_available_models_response_message convert_bosdyn_api_available_models_proto_to_bosdyn_api_msgs_available_models_message(proto_msg.models, ros_msg.models) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9813, in convert_bosdyn_api_available_models_proto_to_bosdyn_api_msgs_available_models_message convert_bosdyn_api_model_data_proto_to_bosdyn_api_msgs_model_data_message(input_data_item, output_data_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 9855, in convert_bosdyn_api_model_data_proto_to_bosdyn_api_msgs_model_data_message convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message(proto_msg.custom_params, ros_msg.custom_params) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6271, in convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6300, in convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6233, in convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message convert_bosdyn_api_custom_param_spec_proto_to_bosdyn_api_msgs_custom_param_spec_message(proto_msg.spec, typed_spec_message) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6099, in convert_bosdyn_api_custom_param_spec_proto_to_bosdyn_api_msgs_custom_param_spec_message convert_bosdyn_api_one_of_param_spec_proto_to_bosdyn_api_msgs_one_of_param_spec_message(proto_msg.one_of_spec, ros_msg.spec.one_of_spec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6427, in convert_bosdyn_api_one_of_param_spec_proto_to_bosdyn_api_msgs_one_of_param_spec_message convert_bosdyn_api_one_of_param_spec_specs_entry_proto_to_bosdyn_api_msgs_one_of_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6456, in convert_bosdyn_api_one_of_param_spec_specs_entry_proto_to_bosdyn_api_msgs_one_of_param_spec_specs_entry_message convert_bosdyn_api_one_of_param_child_spec_proto_to_bosdyn_api_msgs_one_of_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6391, in convert_bosdyn_api_one_of_param_child_spec_proto_to_bosdyn_api_msgs_one_of_param_child_spec_message convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message(proto_msg.spec, ros_msg.spec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6271, in convert_bosdyn_api_dict_param_spec_proto_to_bosdyn_api_msgs_dict_param_spec_message convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message(input_specs_item, output_specs_item) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6300, in convert_bosdyn_api_dict_param_spec_specs_entry_proto_to_bosdyn_api_msgs_dict_param_spec_specs_entry_message convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message(proto_msg.value, ros_msg.value) File "/opt/ros/humble/local/lib/python3.10/dist-packages/bosdyn_api_msgs/conversions.py", line 6234, in convert_bosdyn_api_dict_param_child_spec_proto_to_bosdyn_api_msgs_dict_param_child_spec_message ros_msg.spec.value = rclpy.serialization.serialize_message(typed_spec_message) AttributeError: 'CustomParamSpec' object has no attribute 'value'

Regarding this question:

Could it be that you are populating your ROS message equivalent inappropriately?

Since a DictParam.Spec can contain an arbitrary number of nested dictionaries (ideally, infinite), I'm not sure how I could populate the message differently in an agnostic way with respect to the content of the dictionaries themselves. I thought the conversion function was supposed to take care of that. Am I missing something maybe?

Thank you again for your time.

mhidalgo-bdai commented 5 months ago

Hmm, this runs deep. I was able to reproduce using released binaries. I couldn't yesterday on built from source binaries, now I can but the failure mode is different. It looks like the algorithm that breaks dependency cycles via type erasure is not stable, which results in different builds of the same sources yielding different message definitions :scream:

FYI @amessing-bdai @tcappellari-bdai

bobcorn commented 5 months ago

@mhidalgo-bdai thank you so much for your effort on this.

I pulled the new release 41b3177b73ef9a5ecff2bd932085ac25a7841b22, but unfortunately I still encounter the exact same error by running the provided sample script. I have re-checked several times that I have built the right code, and I can confirm that I am using the latest version.

Does the new release fix the error on the provided protobuf on your side? Just to understand if the issue is persisting only on my side for some reason.

mhidalgo-bdai commented 5 months ago

@bobcorn the binary release came out an hour ago over at https://github.com/bdaiinstitute/bosdyn_msgs/releases/tag/4.0.2-1. I can no longer reproduce any failures with the MRE you shared above.