eProsima / Micro-XRCE-DDS-Client

Micro XRCE-DDS Client repository. Looking for commercial support? Contact info@eprosima.com
Apache License 2.0
130 stars 82 forks source link

Is there a direct way to communicate XRCE requesters and repliers and ROS-2 node via Micro-ROS agent #358

Open arshPratap opened 1 year ago

arshPratap commented 1 year ago

Currently I am trying to recreate the ReplyAdder example to communicate with ROS-2 nodes and was fairly successful in doing so with the ROS-2 nodes via integration services. My current question is that is there a way to directly communicate with ROS-2 nodes using the micro-ros agent (similar to how it was tackled in #250 ). Based on the docs , my current replier configuration is as follows :

    const char* replier_xml = "<dds>"
            "<replier profile_name=\"rr/my_requester\""
            "service_name=\"rs/AddTwoInts\""
            "request_type=\"example_interfaces::srv::dds_::AddTwoInts_Request\""
            "reply_type=\"example_interfaces::srv::dds_::AddTwoInts_Response\">"
            "</replier>"
            "</dds>";

But on the ROS-2 side there is no corresponding service listed when I run : ros2 service list

Any ideas how to proceed with this ? Thanks

arshPratap commented 1 year ago

@pablogs9 any idea how to proceed with this ?

arshPratap commented 1 year ago

@Acuadros95 any updates on this ? I would really like to know how to proceed with this issue

pablogs9 commented 1 year ago

Hello @arshPratap have you considered taking a look at micro-ROS RWM, there we are using XRCE to create ROS 2 interoperable services. Probably following the same approach, you will be able to use ROS 2 services directly from XRCE: https://github.com/micro-ROS/rmw_microxrcedds/blob/bc4eb312ac4601a4137c35f4a56b9b83b4b18339/rmw_microxrcedds_c/src/rmw_service.c#L30

arshPratap commented 1 year ago

Thanks for the response @pablogs9 I am currently going through the code .. if possible could this implementation be further strecthed to use ros2-actions ?

srmainwaring commented 1 year ago

Here's how we solved this for the DDS client implementation used in ArduPilot's AP_DDS library, using the ArmMotors service definition as an example:

ROS 2 service definition: ardupilot_msgs/srv/ArmMotors.srv

# This service requests the vehicle to arm or disarm its motors.

# Set true to arm motors, false to disarm motors.
bool arm
---
# True if arming/disarming request for motors was successful , false otherwise. 
bool result

IDL:

// generated from rosidl_adapter/resource/srv.idl.em
// with input from ardupilot_msgs/srv/ArmMotors.srv
// generated code does not contain a copyright notice

module ardupilot_msgs {
  module srv {
    struct ArmMotors_Request {
      @verbatim (language="comment", text=
        "This service requests the vehicle to arm or disarm its motors." "\n"
        "Set true to arm motors, false to disarm motors.")
      boolean arm;
    };
    @verbatim (language="comment", text=
      "True if arming/disarming request for motors was successful , false otherwise. ")
    struct ArmMotors_Response {
      boolean result;
    };
  };
};

The micro XRCE DDS client creates the service Replier using uxr_buffer_create_replier_ref, and the key to auto mapping the DDS messages to ROS 2 is using the following naming in the refs file:

<?xml version="1.0"?>
<profiles>
  <participant profile_name="participant_profile">
    <rtps>
      <name>ardupilot_dds</name>
    </rtps>
  </participant>
  ...
  <replier
      profile_name="arm_motors__replier"
      service_name="rs/ap/arm_motorsService"
      request_type="ardupilot_msgs::srv::dds_::ArmMotors_Request_"
      reply_type="ardupilot_msgs::srv::dds_::ArmMotors_Response_">
    <request_topic_name>rq/ap/arm_motorsRequest</request_topic_name>
    <reply_topic_name>rr/ap/arm_motorsReply</reply_topic_name>
  </replier>
</profiles>

with this choice the ArmMotors service may be called from the ROS 2 CLI without the need to run an Integration Service instance:

$ ros2 service call /ap/arm_motors ardupilot_msgs/srv/ArmMotors "{arm: True}"
requester: making request: ardupilot_msgs.srv.ArmMotors_Request(arm=True)

response:
ardupilot_msgs.srv.ArmMotors_Response(result=True)
arshPratap commented 1 year ago

Yup can confirm that the approach works.. Thanks a lot for the help @srmainwaring

Here's how we solved this for the DDS client implementation used in ArduPilot's AP_DDS library, using the ArmMotors service definition as an example:

ROS 2 service definition: ardupilot_msgs/srv/ArmMotors.srv

# This service requests the vehicle to arm or disarm its motors.

# Set true to arm motors, false to disarm motors.
bool arm
---
# True if arming/disarming request for motors was successful , false otherwise. 
bool result

IDL:

// generated from rosidl_adapter/resource/srv.idl.em
// with input from ardupilot_msgs/srv/ArmMotors.srv
// generated code does not contain a copyright notice

module ardupilot_msgs {
  module srv {
    struct ArmMotors_Request {
      @verbatim (language="comment", text=
        "This service requests the vehicle to arm or disarm its motors." "\n"
        "Set true to arm motors, false to disarm motors.")
      boolean arm;
    };
    @verbatim (language="comment", text=
      "True if arming/disarming request for motors was successful , false otherwise. ")
    struct ArmMotors_Response {
      boolean result;
    };
  };
};

The micro XRCE DDS client creates the service Replier using uxr_buffer_create_replier_ref, and the key to auto mapping the DDS messages to ROS 2 is using the following naming in the refs file:

<?xml version="1.0"?>
<profiles>
  <participant profile_name="participant_profile">
    <rtps>
      <name>ardupilot_dds</name>
    </rtps>
  </participant>
  ...
  <replier
      profile_name="arm_motors__replier"
      service_name="rs/ap/arm_motorsService"
      request_type="ardupilot_msgs::srv::dds_::ArmMotors_Request_"
      reply_type="ardupilot_msgs::srv::dds_::ArmMotors_Response_">
    <request_topic_name>rq/ap/arm_motorsRequest</request_topic_name>
    <reply_topic_name>rr/ap/arm_motorsReply</reply_topic_name>
  </replier>
</profiles>

with this choice the ArmMotors service may be called from the ROS 2 CLI without the need to run an Integration Service instance:

$ ros2 service call /ap/arm_motors ardupilot_msgs/srv/ArmMotors "{arm: True}"
requester: making request: ardupilot_msgs.srv.ArmMotors_Request(arm=True)

response:
ardupilot_msgs.srv.ArmMotors_Response(result=True)

Yup can confirm that the approach works.. Thanks a lot for the help @srmainwaring