Copter: free allocations in case of allocation failure
[ardupilot.git] / libraries / AP_DDS / README.md
blob04055bdb11e09d330d6078340318ea1c6a3ac4e4
1 # Testing with DDS/micro-Ros
3 ## Architecture
5 Ardupilot contains the DDS Client library, which can run as SITL. Then, the DDS application runs a ROS 2 node, an eProsima Integration Service, and the MicroXRCE Agent. The two systems communicate over serial or UDP.
7 ```mermaid
8 ---
9 title: UDP Loopback
10 ---
11 graph LR
13   subgraph Linux Computer
15     subgraph Ardupilot SITL
16       veh[sim_vehicle.py] <--> xrceClient[EProsima Micro XRCE DDS Client]
17       xrceClient <--> port1[udp:2019]
18     end
20     subgraph DDS Application
21       ros[ROS 2 Node] <--> agent[Micro ROS Agent]
22       agent <-->port1[udp:2019]
23     end
25     loopback
27   end
28 ```
30 ```mermaid
31 ---
32 title: Hardware Serial Port Loopback
33 ---
34 graph LR
36   subgraph Linux Computer
38     subgraph Ardupilot SITL
39       veh[sim_vehicle.py] <--> xrceClient[EProsima Micro XRCE DDS Client]
40       xrceClient <--> port1[devUSB1]
41     end
43     subgraph DDS Application
44       ros[ROS 2 Node] <--> agent[Micro ROS Agent]
45       agent <--> port2[devUSB2]
46     end
48     port1 <--> port2
50   end
51 ```
54 ## Installation
56 While DDS support in Ardupilot is mostly through git submodules,
57 you must install Micro XRCE DDS Gen and create a workspace.
59 Follow the wiki [here](https://ardupilot.org/dev/docs/ros2.html)
60 to set up your environment.
62 ### Serial Only: Set up serial for SITL with DDS
64 On Linux, creating a virtual serial port will be necessary to use serial in SITL, because of that install socat.
66 ```
67 sudo apt-get update
68 sudo apt-get install socat
69 ```
71 ## Setup ardupilot for SITL with DDS
73 Set up your [SITL](https://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html).
74 Run the simulator with the following command. If using UDP, the only parameter you need to set it `DDS_ENABLE`.
76 | Name | Description | Default |
77 | - | - | - |
78 | DDS_ENABLE | Set to 1 to enable DDS, or 0 to disable | 1 |
79 | SERIAL1_BAUD | The serial baud rate for DDS | 57 |
80 | SERIAL1_PROTOCOL | Set this to 45 to use DDS on the serial port | 0 |
82 ```console
83 # Wipe params till you see "AP: ArduPilot Ready"
84 # Select your favorite vehicle type
85 sim_vehicle.py -w -v ArduPlane --console -DG --enable-dds
87 # Only set this for Serial, which means 115200 baud
88 param set SERIAL1_BAUD 115
89 # See libraries/AP_SerialManager/AP_SerialManager.h AP_SerialManager SerialProtocol_DDS_XRCE
90 param set SERIAL1_PROTOCOL 45
91 ```
93 DDS is currently enabled by default, if it's part of the build. To disable it, run the following and reboot the simulator.
94 ```
95 param set DDS_ENABLE 0
96 REBOOT
97 ```
99 ## Using the ROS 2 CLI to Read Ardupilot Data
101 After your setup is complete, do the following:
102 - Source the ROS 2 installation
103   ```console
104   source install/setup.bash
105   ```
107 Next, follow the associated section for your chosen transport, and finally you can use the ROS 2 CLI.
109 ### UDP (recommended for SITL)
111 - Run the microROS agent
112   ```console
113   cd ardupilot/libraries/AP_DDS
114   ros2 run micro_ros_agent micro_ros_agent udp4 -p 2019
115   ```
116 - Run SITL (remember to kill any terminals running ardupilot SITL beforehand)
117   ```console
118   sim_vehicle.py -v ArduPlane -DG --console --enable-dds
119   ```
121 ### Serial
123 - Start a virtual serial port with socat. Take note of the two `/dev/pts/*` ports. If yours are different, substitute as needed.
124   ```console
125   socat -d -d pty,raw,echo=0 pty,raw,echo=0
126   >>> 2023/02/21 05:26:06 socat[334] N PTY is /dev/pts/1
127   >>> 2023/02/21 05:26:06 socat[334] N PTY is /dev/pts/2
128   >>> 2023/02/21 05:26:06 socat[334] N starting data transfer loop with FDs [5,5] and [7,7]
129   ```
130 - Run the microROS agent
131   ```console
132   cd ardupilot/libraries/AP_DDS
133   # assuming we are using tty/pts/2 for DDS Application
134   ros2 run micro_ros_agent micro_ros_agent serial -b 115200 -D /dev/pts/2
135   ```
136 - Run SITL (remember to kill any terminals running ardupilot SITL beforehand)
137   ```console
138   # assuming we are using /dev/pts/1 for Ardupilot SITL
139   sim_vehicle.py -v ArduPlane -DG --console --enable-dds -A "--serial1=uart:/dev/pts/1"
140   ```
142 ## Use ROS 2 CLI
144 You should be able to see the agent here and view the data output.
146 ```bash
147 $ ros2 node list
148 /ardupilot_dds
151 Depending on what's configured, you will see something similar to this:
153 ```bash
154 $ ros2 topic list -v
155 Published topics:
156  * /ap/airspeed [geometry_msgs/msg/Vector3] 1 publisher
157  * /ap/battery [sensor_msgs/msg/BatteryState] 1 publisher
158  * /ap/clock [rosgraph_msgs/msg/Clock] 1 publisher
159  * /ap/geopose/filtered [geographic_msgs/msg/GeoPoseStamped] 1 publisher
160  * /ap/gps_global_origin/filtered [geographic_msgs/msg/GeoPointStamped] 1 publisher
161  * /ap/imu/experimental/data [sensor_msgs/msg/Imu] 1 publisher
162  * /ap/navsat [sensor_msgs/msg/NavSatFix] 1 publisher
163  * /ap/pose/filtered [geometry_msgs/msg/PoseStamped] 1 publisher
164  * /ap/tf_static [tf2_msgs/msg/TFMessage] 1 publisher
165  * /ap/time [builtin_interfaces/msg/Time] 1 publisher
166  * /ap/twist/filtered [geometry_msgs/msg/TwistStamped] 1 publisher
167  * /parameter_events [rcl_interfaces/msg/ParameterEvent] 1 publisher
168  * /rosout [rcl_interfaces/msg/Log] 1 publisher
170 Subscribed topics:
171  * /ap/cmd_gps_pose [ardupilot_msgs/msg/GlobalPosition] 1 subscriber
172  * /ap/cmd_vel [geometry_msgs/msg/TwistStamped] 1 subscriber
173  * /ap/joy [sensor_msgs/msg/Joy] 1 subscriber
174  * /ap/tf [tf2_msgs/msg/TFMessage] 1 subscriber
177 For a full list of interfaces, see [here](https://ardupilot.org/dev/docs/ros2-interfaces.html).
179 ```bash
180 $ ros2 topic hz /ap/time
181 average rate: 50.115
182         min: 0.012s max: 0.024s std dev: 0.00328s window: 52
185 ```bash
186 $ ros2 topic echo /ap/time
187 sec: 1678668735
188 nanosec: 729410000
191 ```bash
192 $ ros2 service list
193 /ap/arm_motors
194 /ap/mode_switch
195 /ap/prearm_check
196 /ap/experimental/takeoff
200 The static transforms for enabled sensors are also published, and can be received like so:
202 ```bash
203 ros2 topic echo /ap/tf_static --qos-depth 1 --qos-history keep_last --qos-reliability reliable --qos-durability transient_local --once
206 In order to consume the transforms, it's highly recommended to [create and run a transform broadcaster in ROS 2](https://docs.ros.org/en/humble/Concepts/About-Tf2.html#tutorials).
208 ## Using ROS 2 services
210 The `AP_DDS` library exposes services which are automatically mapped to ROS 2
211 services using appropriate naming conventions for topics and message and service
212 types. An earlier version of `AP_DDS` required the use of the eProsima
213 [Integration Service](https://github.com/eProsima/Integration-Service) to map
214 the request / reply topics from DDS to ROS 2, but this is no longer required.
216 List the available services:
218 ```bash
219 $ ros2 service list -t
220 /ap/arm_motors [ardupilot_msgs/srv/ArmMotors]
221 /ap/mode_switch [ardupilot_msgs/srv/ModeSwitch]
222 /ap/prearm_check [std_srvs/srv/Trigger]
223 /ap/experimental/takeoff [ardupilot_msgs/srv/Takeoff]
226 Call the arm motors service:
228 ```bash
229 $ ros2 service call /ap/arm_motors ardupilot_msgs/srv/ArmMotors "{arm: True}"
230 requester: making request: ardupilot_msgs.srv.ArmMotors_Request(arm=True)
232 response:
233 ardupilot_msgs.srv.ArmMotors_Response(result=True)
236 Call the mode switch service:
238 ```bash
239 $ ros2 service call /ap/mode_switch ardupilot_msgs/srv/ModeSwitch "{mode: 4}"
240 requester: making request: ardupilot_msgs.srv.ModeSwitch_Request(mode=4)
242 response:
243 ardupilot_msgs.srv.ModeSwitch_Response(status=True, curr_mode=4)
246 Call the prearm check service:
248 ```bash
249 $ ros2 service call /ap/prearm_check std_srvs/srv/Trigger
250 requester: making request: std_srvs.srv.Trigger_Request()
252 response:
253 std_srvs.srv.Trigger_Response(success=False, message='Vehicle is Not Armable')
257 std_srvs.srv.Trigger_Response(success=True, message='Vehicle is Armable')
260 Call the takeoff service:
262 ```bash
263 $ ros2 service call /ap/experimental/takeoff ardupilot_msgs/srv/Takeoff "{alt: 10.5}"
264 requester: making request: ardupilot_msgs.srv.Takeoff_Request(alt=10.5)
266 response:
267 ardupilot_msgs.srv.Takeoff_Response(status=True)
270 ## Commanding using ROS 2 Topics
272 The following topic can be used to control the vehicle.
274 - `/ap/joy` (type `sensor_msgs/msg/Joy`): overrides a maximum of 8 RC channels,
275 at least 4 axes must be sent. Values are clamped between -1.0 and 1.0.
276 Use `NaN` to disable the override of a single channel.
277 A channel defaults back to RC after 1 second of not receiving commands.
279 ```bash
280 ros2 topic pub /ap/joy sensor_msgs/msg/Joy "{axes: [0.0, 0.0, 0.0, 0.0]}"
282 publisher: beginning loop
283 publishing #1: sensor_msgs.msg.Joy(header=std_msgs.msg.Header(stamp=builtin_interfaces.msg.Time(sec=0, nanosec=0), frame_id=''), axes=[0.0, 0.0, 0.0, 0.0], buttons=[])
285 - `/ap/cmd_gps_pose` (type `ardupilot_msgs/msg/GlobalPosition`): sends
286 a waypoint to head to when the selected mode is GUIDED.
288 ```bash
289 ros2 topic pub /ap/cmd_gps_pose ardupilot_msgs/msg/GlobalPosition "{latitude: 34, longitude: 118, altitude: 1000}"
291 publisher: beginning loop
292 publishing #1: ardupilot_msgs.msg.GlobalPosition(header=std_msgs.msg.Header(stamp=builtin_interfaces.msg.Time(sec=0, nanosec=0), frame_id=''), coordinate_frame=0, type_mask=0, latitude=34.0, longitude=118.0, altitude=1000.0, velocity=geometry_msgs.msg.Twist(linear=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=0.0), angular=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=0.0)), acceleration_or_force=geometry_msgs.msg.Twist(linear=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=0.0), angular=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=0.0)), yaw=0.0)
295 ## Contributing to `AP_DDS` library
297 ### Adding DDS messages to Ardupilot
299 Unlike the use of ROS 2 `.msg` files, since Ardupilot supports native DDS, the message files follow [OMG IDL DDS v4.2](https://www.omg.org/spec/IDL/4.2/PDF).
300 This package is intended to work with any `.idl` file complying with those extensions.
302 Over time, these restrictions will ideally go away.
304 To get a new IDL file from ROS 2, follow this process:
306 ```bash
307 cd ardupilot
308 source /opt/ros/humble/setup.bash
310 # Find the IDL file
311 find /opt/ros/$ROS_DISTRO -type f -wholename \*builtin_interfaces/msg/Time.idl
313 # Create the directory in the source tree if it doesn't exist similar to the one found in the ros directory
314 mkdir -p libraries/AP_DDS/Idl/builtin_interfaces/msg/
316 # Copy the IDL
317 cp /opt/ros/humble/share/builtin_interfaces/msg/Time.idl libraries/AP_DDS/Idl/builtin_interfaces/msg/
319 # Build the code again with the `--enable-dds` flag as described above
322 If the message is custom for ardupilot, first create the ROS message in `Tools/ros2/ardupilot_msgs/msg/GlobalPosition.msg`.
323 Then, build ardupilot_msgs with colcon.
324 Finally, copy the IDL folder from the install directory into the source tree.
326 ### Rules for adding topics and services
328 Topics and services available from `AP_DDS` are automatically mapped into ROS 2
329 provided a few rules are followed when defining the entries in the
330 topic and service tables.
332 #### ROS 2 message and service interface types
334 ROS 2 message and interface definitions are mangled by the `rosidl_adapter` when
335 mapping from ROS 2 to DDS to avoid naming conflicts in the C/C++ libraries.
336 The ROS 2 object `namespace::Struct` is mangled to `namespace::dds_::Struct_`
337 for DDS. The table below provides some example mappings:
339 | ROS 2 | DDS |
340 | --- | --- |
341 | `rosgraph_msgs::msg::Clock` | `rosgraph_msgs::msg::dds_::Clock_` |
342 | `sensor_msgs::msg::NavSatFix` | `sensor_msgs::msg::dds_::NavSatFix_` |
343 | `ardupilot_msgs::srv::ArmMotors_Request` | `ardupilot_msgs::srv::dds_::ArmMotors_Request_` |
344 | `ardupilot_msgs::srv::ArmMotors_Response` | `ardupilot_msgs::srv::dds_::ArmMotors_Response_` |
346 Note that a service interface always requires a Request / Response pair.
348 #### ROS 2 topic and service names
350 The ROS 2 design article: [Topic and Service name mapping to DDS](https://design.ros2.org/articles/topic_and_service_names.html) describes the mapping of ROS 2 topic and service
351 names to DDS. Each ROS 2 subsystem is provided a prefix when mapped to DDS.
352 The request / response pair for services require an additional suffix.
354 | ROS 2 subsystem | DDS Prefix | DDS Suffix |
355 | --- | --- | --- |
356 | topics | rt/ | |
357 | service request | rq/ | Request |
358 | service response | rr/ | Reply |
359 | service | rs/ | |
360 | parameter | rp/ | |
361 | action | ra/ | |
363 The table below provides example mappings for topics and services
365 | ROS 2 | DDS |
366 | --- | --- |
367 | ap/clock | rt/ap/clock |
368 | ap/navsat | rt/ap/navsat |
369 | ap/arm_motors | rq/ap/arm_motorsRequest, rr/ap/arm_motorsReply |
371 Refer to existing mappings in [`AP_DDS_Topic_Table`](https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_DDS/AP_DDS_Topic_Table.h)
372 and [`AP_DDS_Service_Table`](https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_DDS/AP_DDS_Service_Table.h)
373 for additional details.
375 ### Development Requirements
377 Astyle is used to format the C++ code in AP_DDS. This is required for CI to pass the build.
378 To run the automated formatter, run:
380 ```bash
381 ./Tools/scripts/run_astyle.py
384 Pre-commit is used for other things like formatting python and XML code.
385 This will run the tools automatically when you commit. If there are changes, just add them back your staging index and commit again.
387 1. Install [pre-commit](https://pre-commit.com/#installation) python package.
388 1. Install ArduPilot's hooks in the root of the repo, then commit like normal
389   ```bash
390   cd ardupilot
391   pre-commit install
392   git commit
393   ```
395 ## Testing DDS on Hardware
397 ### With Serial
399 The easiest way to test DDS is to make use of some boards providing two serial interfaces over USB such as the Pixhawk 6X.
400 The [Pixhawk6X/hwdef.dat](../AP_HAL_ChibiOS/hwdef/Pixhawk6X/hwdef.dat) file has this info.
402 SERIAL_ORDER OTG1 UART7 UART5 USART1 UART8 USART2 UART4 USART3 OTG2
405 For example, build, flash, and set up OTG2 for DDS
406 ```bash
407 ./waf configure --board Pixhawk6X --enable-dds
408 ./waf plane --upload
409 mavproxy.py --console
410 param set DDS_ENABLE 1
411 # Check the hwdef file for which port is OTG2
412 param set SERIAL8_PROTOCOL 45
413 param set SERIAL8_BAUD 115
414 reboot
417 Then run the Micro ROS agent
418 ```bash
419 cd /path/to/ros2_ws
420 source install/setup.bash
421 cd src/ardupilot/libraries/AP_DDS
422 ros2 run micro_ros_agent micro_ros_agent serial -b 115200 -D /dev/serial/by-id/usb-ArduPilot_Pixhawk6X_210028000151323131373139-if02
425 If connection fails, instead of running the Micro ROS agent, debug the stream
426 ```bash
427 python3 -m serial.tools.miniterm /dev/serial/by-id/usb-ArduPilot_Pixhawk6X_210028000151323131373139-if02  115200 --echo --encoding hexlify
430 The same steps can be done for physical serial ports once the above works to isolate software and hardware issues.