Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AP_DDS: Add dynamic TF subscriber support for odometry #24155

Merged
merged 1 commit into from Jul 31, 2023

Conversation

Ryanf55
Copy link
Collaborator

@Ryanf55 Ryanf55 commented Jun 28, 2023

  • This is the first step for GSOC Cartographer external odometry input

@Ryanf55 Ryanf55 added ROS For-4.5 Planned for 4.5 release labels Jun 28, 2023
@Ryanf55 Ryanf55 self-assigned this Jun 28, 2023
@Ryanf55 Ryanf55 changed the title Add dynamic TF subscriber support AP_DDS: Add dynamic TF subscriber support Jun 28, 2023
@pedro-fuoco
Copy link
Contributor

Thanks for the help! I have a few suggestions, tell me if they are in the scope of this PR

@Ryanf55
Copy link
Collaborator Author

Ryanf55 commented Jun 29, 2023

Thanks for the help! I have a few suggestions, tell me if they are in the scope of this PR

Not sure if you meant to add anything, please share when you get time.

@pedro-fuoco
Copy link
Contributor

If we use the /tf topic instead of /ap/tf we could use tf2 listeners instead of subscribing. At first glance this seems like a better way to do it as we allow users to use all of the tf2 tools.

@Ryanf55 Ryanf55 changed the title AP_DDS: Add dynamic TF subscriber support AP_DDS: Add dynamic TF subscriber support for odometry Jul 11, 2023
libraries/AP_DDS/AP_DDS_Client.cpp Show resolved Hide resolved
Comment on lines 130 to 132
.topic_profile_label = "dynamictransforms__t",
.dw_profile_label = "",
.dr_profile_label = "dynamictransforms__dr",
Copy link
Contributor

Choose a reason for hiding this comment

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

Changes to make it consistent with dds_xrce_profile.xml:

Suggested change
.topic_profile_label = "dynamictransforms__t",
.dw_profile_label = "",
.dr_profile_label = "dynamictransforms__dr",
.topic_profile_label = "dynamictf__t",
.dw_profile_label = "",
.dr_profile_label = "dynamictf___dr",

An error was appearing before and the subscriber wasn't created. With this changes it is now working:
Screenshot from 2023-06-29 11-06-10

This should solve the test dds / build (stm32h7) (pull_request) CI fail

@Ryanf55
Copy link
Collaborator Author

Ryanf55 commented Jul 12, 2023

Some updates from today's meeting regarding external odometry input into ArduPilot libs:

Add Ap_VisualOdom.h, add support in handle_vision_position_estimate with a Vector3F pos (which is either single or double precision depending on the platform). To start, do it with single precision, and upgrade it later? This would be an extreme ardupilot sort of feature at distances >100km. For GSoc, let's ignore it.

For home vs origin, see writeExtNavData. Users can set home mid mission, while the ROS convention specifies that origin and map are static. Within the EKF, there is a public orign (exposed to AHRS), absolutely static till reboot. Shadow origin is updated internally updated at 1Hz to current vehicle location. This deals doesn't deal with flat earth well.

For setting the origin, there is a mavros way to do it set_origin.py.

Ardupilot only requires a global origin, not GPS. Pedro sets origin manually in a script. This would be a good service if we want a pure ROS solution. OR, assume we initially have GPS then lose GPS and use vision. Either solution is OK for Tridge. Pedro can time to look into it. Tridge recommends just doing the GPS method because it's easier to initialize the EKF. Without GPS, the EKF gets stuck waiting for lag to determine its buffer size. get_lag(gps, delay) Visual odom library has a get_delay_ms which is a param VISO_DELAY_MS, defaulted 10mS.

For the time between sensor measurement and position estimate, it needs to be under 250mS due to Ardupilot EKF. Fusion is max 100Hz, and that is when the forward propogation happens. Further than 250mS, it degrades a t^2 and linear CPU increase.

For high lag data, there is a different API (recent addition).See AP_NavEKf3. See AP_NavEkf3_PosVelFusion with the EK3_FEATURE_POSITION_RESET. Later, we could support downsampled velocity history.

Action for pedro: Benchmark the algorithm on hardware, then we can decide.

In SITL testing, GPS_DELAY_MS could be bumped up to match the vision estimate issue. Paul put this to save RAM, but it's problematic for dual GPS. Current behavior fixes it by clamping the timestamp and fuses at the wrong time. TBD what the behavior for vision is.

In EKF source selection, set primary to GPS, secondary to Vision, and then use RC or Lua to switch if we required GPS lock.

Once we get logs working, ping Tridge to review how to look at EKF innovations and VISO message VISP. Check the VISP messages match EKF position estimates.

Copy link
Contributor

@pedro-fuoco pedro-fuoco left a comment

Choose a reason for hiding this comment

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

Changes for the code to build

Screenshot from 2023-07-14 00-32-49

libraries/AP_DDS/AP_DDS_Topic_Table.h Outdated Show resolved Hide resolved
uint32_t* count_ptr = (uint32_t*) args;
(*count_ptr)++;
if (rx_dynamic_transforms_topic.transforms_size > 0) {
GCS_SEND_TEXT(MAV_SEVERITY_INFO,"Received tf2_msgs/TFMessage of length: %lu",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
GCS_SEND_TEXT(MAV_SEVERITY_INFO,"Received tf2_msgs/TFMessage of length: %lu",
GCS_SEND_TEXT(MAV_SEVERITY_INFO,"Received tf2_msgs/TFMessage of length: %u",

Copy link
Contributor

Choose a reason for hiding this comment

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

image

Copy link
Contributor

@pedro-fuoco pedro-fuoco left a comment

Choose a reason for hiding this comment

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

After building ardupilot and ardupilot_sitl I ran the iris_maze launch from ardupilot_gz. In order to test the tf subscriber, I remapped the robot_state_publisher node from /tf to /ap/tf.

AP_DDS_Client received a message but this apparently caused a seg fault, any idea why that might be? Here's the log:

[dds_udp.parm --synthetic-clock -4] ERROR: segmentation fault - aborting
[dds_udp.parm --synthetic-clock -4] Running: sh dumpstack.sh 97015 >dumpstack.sh_arducopter.97015.out 2>&1
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] AP: Received tf2_msgs/TFMessage of length: 6
[dds_udp.parm --synthetic-clock -4] Failed
[dds_udp.parm --synthetic-clock -4] Running: sh dumpcore.sh 97015 >dumpcore.sh_arducopter.97015.out 2>&1
[dds_udp.parm --synthetic-clock -4] Failed
[dds_udp.parm --synthetic-clock -4] Aborted (core dumped)
[ERROR] [dds_udp.parm --synthetic-clock -4]: process has died [pid 97012, exit code 134, cmd 'arducopter --model json --speedup 1 --slave 0 --sim-address=127.0.0.1 --instance 0 --defaults /home/fuoco/ros2_ws/install/ardupilot_sitl/share/ardupilot_sitl/config/default_params/gazebo-iris.parm,/home/fuoco/ros2_ws/install/ardupilot_sitl/share/ardupilot_sitl/config/default_params/dds_udp.parm --synthetic-clock '].
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] EOF on TCP socket
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] Attempting reconnect
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] [Errno 111] Connection refused sleeping
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] [Errno 111] Connection refused sleeping
[mavproxy.py --out 127.0.0.1:14550 --out 127.0.0.1:14551 --master tcp:127.0.0.1:5760 --sitl 127.0.0.1:5501 --non-interactive -5] Attempting reconnect

The crash doesn't happen if I don't remap the topic (presumably because nothing reaches the subscriber handler)

@srmainwaring
Copy link
Contributor

srmainwaring commented Jul 14, 2023

@pedro-fuoco have you tried publishing TF from the command line?

ros2 topic pub /ap/tf tf2_msgs/msg/TFMessage 'transforms: [{header: {stamp: {sec: 1, nanosec: 0}, frame_id: "base_link"}, child_frame_id: "GPS_0", transform: {translation: {x: 0.0, y: 0.0, z: 0.0}, rotation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}, {header: {stamp: {sec: 2, nanosec: 0}, frame_id: "base_link"}, child_frame_id: "GPS_0", transform: {translation: {x: 0.0, y: 0.0, z: 0.0}, rotation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}, {header: {stamp: {sec: 3, nanosec: 0}, frame_id: "base_link"}, child_frame_id: "GPS_0", transform: {translation: {x: 0.0, y: 0.0, z: 0.0}, rotation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}, {header: {stamp: {sec: 4, nanosec: 0}, frame_id: "base_link"}, child_frame_id: "GPS_0", transform: {translation: {x: 0.0, y: 0.0, z: 0.0}, rotation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}, {header: {stamp: {sec: 5, nanosec: 0}, frame_id: "base_link"}, child_frame_id: "GPS_0", transform: {translation: {x: 0.0, y: 0.0, z: 0.0}, rotation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}]' --once

ap-dds-tf-sub

I'm running a rebased version of the PR with the fixes you noted included: https://github.com/srmainwaring/ardupilot/tree/wips/wip-tf-subscriber-rebased-fixes

Update

  1. Can replicate the segfault when forwarding TF from the example:
ros2 launch ardupilot_gz_bringup iris_runway.launch.py`

I suspect it's a combination of the message size and rate.

  • /tf is published from Gazebo at approx. 1000 Hz (i.e. 0.001 s time step in the physics engine).
  • The iris tf tree contains 8 items. This seems to be about the limit of what will be processed when publishing using the ROS 2 CLI.
  1. Looks like message size. This will cause a segfault:

Run SITL session (no Gazebo):

ros2 launch ardupilot_sitl sitl_dds_udp.launch.py transport:=udp4 refs:=$(ros2 pkg prefix ardupilot_sitl)/share/ardupilot_sitl/config/dds_xrce_profile.xml synthetic_clock:=True wipe:=False model:=quad speedup:=1 slave:=0 instance:=0 defaults:=$(ros2 pkg prefix ardupilot_sitl)/share/ardupilot_sitl/config/default_params/copter.parm,$(ros2 pkg prefix ardupilot_sitl)/share/ardupilot_sitl/config/default_params/dds_udp.parm sim_address:=127.0.0.1 master:=tcp:127.0.0.1:5760 sitl:=127.0.0.1:5501

Send single TF (6 items of the 8 published from the iris example):

ros2 topic pub /ap/tf tf2_msgs/msg/TFMessage 'transforms: [
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/tilt_link, transform: {translation: {x: 0.019984060916003184, y: -0.020015926647110993, z: -0.09998399286175172}, rotation: {x: -0.4999997609802308, y: 0.49999975742835057, z: 0.4996019204931798, w: 0.5003982440325244}}},
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/roll_link,
transform: {translation: {x: -2.2827193214895528e-11, y: -2.3581028733108545e-08, z: -0.09999990013228542}, rotation: {x: 0.0002823774170257476, y: 0.7068251247013926,
z: -0.0002807098464664981, w: 0.70738821346969}}},
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/gimbal_link, transform: {translation: {x: -7.861318384707755e-19, y: -0.01, z: -0.11}, rotation: {x: 0.5000000095078997, y: 0.4996016684687246, z: 0.500000009775746, w: 0.500397995179458}}},
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/rotor_0, transform: {translation: {x: 0.13000000000182527, y: -0.2199999999962856, z: 0.023000096313720075}, rotation: {x: -6.217004150247587e-12, y: 1.5109553369859743e-12, z: -0.00010602049440067152, w: 0.9999999943798272}}},
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/rotor_1, transform: {translation: {x: -0.12999999999845072, y: 0.20000000000354365, z: 0.023000096309283957}, rotation: {x: -6.217004150247587e-12, y: 1.5109553369859743e-12, z: -0.00010602049440067152, w: 0.9999999943798272}}},
{header: {stamp: {sec: 227, nanosec: 91000000}, frame_id: iris}, child_frame_id: iris/rotor_3, transform: {translation: {x: -0.12999999999818787, y: -0.1999999999964565, z: 0.02300009631425745}, rotation: {x: -6.217004150247587e-12, y: 1.5109553369859743e-12, z: -0.00010602049440067152, w: 0.9999999943798272}}}]' --once

Additional notes:

Using topic_tools can throttle the TF from the iris_example to a reasonable rate - still get the segfault as before, confirming message size is the issue:

# throttle from 1000 Hz to 1 Hz and relay to /ap/tf 
ros2 run topic_tools throttle messages /tf 1 /ap/tf

uint32_t* count_ptr = (uint32_t*) args;
(*count_ptr)++;
if (rx_dynamic_transforms_topic.transforms_size > 0) {
GCS_SEND_TEXT(MAV_SEVERITY_INFO,"Received tf2_msgs/TFMessage of length: %zu",
Copy link
Contributor

Choose a reason for hiding this comment

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

Apparently the Ardupilot C compiler doesn't support %z. It failed CI and I also wasn't able to compile it locally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Which board is it failing for you on?

Copy link
Contributor

Choose a reason for hiding this comment

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

Pixhawk4

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With the last commit, it passes CI with the use of a static cast.
Please approve if you are happy.

@pedro-fuoco
Copy link
Contributor

pedro-fuoco commented Jul 28, 2023

Tested it with the ardupilot_gz example iris_maze.launch like before. The crash problem seems to be solved, I received the message correctly. LGTM on CI pass and squash.

* This is the first step for GSOC Cartographer external odometry input
* Moved subscriber data members to class member variables

Signed-off-by: Ryan Friedman <ryanfriedman5410+github@gmail.com>
@tridge tridge merged commit 01c5f44 into ArduPilot:master Jul 31, 2023
81 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For-4.5 Planned for 4.5 release ROS
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

4 participants