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

[PN-97] Implement Spot's Robot State Publisher in C++ #209

Merged
merged 97 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 92 commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
c98d496
[wip] starting robot_state_publisher impl
abaker-bdai Dec 5, 2023
2cf0432
getRobotState implemented
abaker-bdai Dec 7, 2023
4ebb559
pre-commit
abaker-bdai Dec 7, 2023
770b489
[wip] implementing publisher
abaker-bdai Dec 7, 2023
e8495ad
implement publisher node
abaker-bdai Dec 13, 2023
d08ad75
naming and pre-commit
abaker-bdai Dec 13, 2023
2f583ea
Fix topic names
abaker-bdai Dec 13, 2023
0a9f44b
Add parameters for odom frame
abaker-bdai Dec 13, 2023
e52d27b
remove topic prefix and python robot state publisher
abaker-bdai Dec 14, 2023
1e5c229
clean up
abaker-bdai Dec 14, 2023
561a7f2
pre-commit
abaker-bdai Dec 15, 2023
08896fc
Merge branch 'main' into abaker/PN-94/robot-state-cpp
abaker-bdai Dec 18, 2023
ba45ec7
Merge branch 'main' into abaker/PN-94/robot-state-cpp
abaker-bdai Jan 2, 2024
053f452
moved robot_state conversions
abaker-bdai Jan 2, 2024
4b211f5
fix geometry conversions test
abaker-bdai Jan 2, 2024
1a05160
[wip] test boilerplate
abaker-bdai Jan 8, 2024
d8d8c8d
fix function naming, extend comment, add includes for IWYU
jschornak-bdai Jan 16, 2024
3e939b8
simplify ternary and cache fully-qualified odom frame ID as member
jschornak-bdai Jan 16, 2024
b1fd8b2
mark joint names map as static const
jschornak-bdai Jan 16, 2024
081fc84
fix formatting
jschornak-bdai Jan 16, 2024
6d940d6
add executables for new tests
jschornak-bdai Jan 17, 2024
3ad7ab2
add include for rclcpp/node_options.hpp as workaround for upstream bug
jschornak-bdai Jan 17, 2024
7ff4585
explicitly return std::nullopt if optional is not set
jschornak-bdai Jan 17, 2024
e536c5e
add tests for parsing BehaviorFaultState
jschornak-bdai Jan 17, 2024
fc38f7f
add tests for getEndEffectorForce
jschornak-bdai Jan 18, 2024
e7c8752
add tests for ManipulatorState conversion
jschornak-bdai Jan 18, 2024
d06675e
add tests for SystemFaultState
jschornak-bdai Jan 18, 2024
9d260e2
add getPowerState tests
jschornak-bdai Jan 18, 2024
4341157
add timestamp
jschornak-bdai Jan 18, 2024
7b9a7ff
fix errors in odom twist conversion
jschornak-bdai Jan 18, 2024
2c643b0
add tests for getOdomTwist
jschornak-bdai Jan 18, 2024
3ae3855
add test for getEstopStates
jschornak-bdai Jan 18, 2024
2b169d4
add tests for getFootState and getJointState
jschornak-bdai Jan 19, 2024
b0b11e2
add test for getJointStates when missing all kinematic data
jschornak-bdai Jan 19, 2024
b154887
be more precise about types in tests
jschornak-bdai Jan 23, 2024
97e1d05
start adding getOdom tests
jschornak-bdai Jan 23, 2024
75bca98
fix composition of frame snapshot in odometry tests
jschornak-bdai Jan 25, 2024
b085e9f
fix composition of frame snapshot in odometry tests
jschornak-bdai Jan 25, 2024
c152b88
add tests for getTf
jschornak-bdai Jan 25, 2024
17cfed4
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Jan 25, 2024
142979a
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 1, 2024
49045ce
apply clang-tidy suggestions
jschornak-bdai Feb 1, 2024
c2a78bc
add missing pragma once directives
jschornak-bdai Feb 1, 2024
349f054
rename middleware handle for robot state to be clearer
jschornak-bdai Feb 1, 2024
496c9b2
split out duplicated fake, and apply clang-tidy fixes to tests
jschornak-bdai Feb 1, 2024
d483954
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 7, 2024
b424874
fix CMake target installation
jschornak-bdai Feb 8, 2024
d7d67d8
Merge branch 'main' into abaker/PN-94/robot-state-cpp
gremigi-bdai Feb 9, 2024
8601d5d
update message conversion functions to follow consistent convention
jschornak-bdai Feb 9, 2024
78bf25a
move interfaces out of middleware handle
jschornak-bdai Feb 9, 2024
667cd7b
use a shorter name
jschornak-bdai Feb 9, 2024
ccb85ad
rename headers and variables to match class names
jschornak-bdai Feb 10, 2024
9431689
add test for TF publication
jschornak-bdai Feb 10, 2024
ff6fb9d
delete outdated comments
jschornak-bdai Feb 12, 2024
a0d5bd0
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 12, 2024
ba56c25
update copyright year
jschornak-bdai Feb 12, 2024
1fc0905
combine libraries into single target
jschornak-bdai Feb 12, 2024
c4c7db4
fix casing
jschornak-bdai Feb 13, 2024
b359a19
extend doc comments
jschornak-bdai Feb 13, 2024
187ac4e
one more comment fix
jschornak-bdai Feb 13, 2024
20a616d
fix casing in tests
jschornak-bdai Feb 13, 2024
08e9415
apply clock skew in StatePublisher instead of DefaultStateClient
jschornak-bdai Feb 14, 2024
60c8664
update docs, rearrange parameter retrival, delete extra destructor
jschornak-bdai Feb 14, 2024
b1997fa
rename struct containing robot state messages for clarify and fix doc…
jschornak-bdai Feb 14, 2024
3a2745a
delete unused Python code replaced by C++
jschornak-bdai Feb 14, 2024
bef5a36
fix RobotStateMessages type in mock, and move reused mock to own header
jschornak-bdai Feb 14, 2024
b5712c3
add matchers for ROS types
jschornak-bdai Feb 14, 2024
436fdf5
add matcher for pose
jschornak-bdai Feb 14, 2024
2f16994
add matchers for system faults and estop states
jschornak-bdai Feb 15, 2024
49b54b5
improve error output for matchers in tests
jschornak-bdai Feb 15, 2024
1d3233a
check clock skew in all applicable tests
jschornak-bdai Feb 15, 2024
84ac92d
add explanatory comment to header
jschornak-bdai Feb 15, 2024
fec6916
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 15, 2024
3b1225f
mark interface implementations final
jschornak-bdai Feb 20, 2024
407d030
Fix use of includes
jschornak-bdai Feb 20, 2024
f5cd713
remove
jschornak-bdai Feb 20, 2024
f10fb1f
Revise doc comments
jschornak-bdai Feb 20, 2024
b967a4c
mark joint names map as inline instead of static
jschornak-bdai Feb 20, 2024
cfcd2cf
make doc comments more concise
jschornak-bdai Feb 20, 2024
112a63b
add missing includes
jschornak-bdai Feb 20, 2024
51385f9
reuse more code in state conversions, and invert validation checks to…
jschornak-bdai Feb 20, 2024
9a4e809
golf code to retrieve robot state from Spot API
jschornak-bdai Feb 20, 2024
5ab3fe1
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 21, 2024
c4fbf10
fix member initialization, lambda declaration, robot state access, an…
jschornak-bdai Feb 21, 2024
48d51de
pass values by reference in test utils
jschornak-bdai Feb 21, 2024
75af76f
fix test inputs, result constness, and fixture names
jschornak-bdai Feb 21, 2024
328c9f1
return void from sendDynamicTransforms instead of tl::expected
jschornak-bdai Feb 21, 2024
72b933f
add doc comments to robot state conversion functions
jschornak-bdai Feb 21, 2024
49157bb
fix Odometry message parsing if robot state does not contain twist data
jschornak-bdai Feb 21, 2024
f2cc337
add StateClientInterface doc comment
jschornak-bdai Feb 21, 2024
08bcf39
more doxygen
jschornak-bdai Feb 21, 2024
f51874e
delete duplicated conversion functions
jschornak-bdai Feb 21, 2024
f7a6cc0
Add missing headers and revise early-returns
jschornak-bdai Feb 22, 2024
8e3f22c
sort and add missing headers
jschornak-bdai Feb 22, 2024
3556022
Merge branch 'main' into abaker/PN-94/robot-state-cpp
jschornak-bdai Feb 22, 2024
1dc9936
clang-format
jschornak-bdai Feb 22, 2024
32ca481
revise includes
jschornak-bdai Feb 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 70 additions & 19 deletions spot_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ set(THIS_PACKAGE_INCLUDE_ROS_DEPENDS
tf2_ros
tl_expected
spot_msgs
nav_msgs
)

find_package(ament_cmake REQUIRED)
Expand All @@ -28,33 +29,50 @@ foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_ROS_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()

###
# Spot API
###

add_library(spot_api
src/api/default_kinematic_api.cpp
src/api/default_image_client.cpp
src/api/default_spot_api.cpp
src/api/default_state_client.cpp
src/api/default_time_sync_api.cpp
src/api/spot_image_sources.cpp
src/conversions/common_conversions.cpp
src/conversions/geometry.cpp
src/conversions/kinematic_conversions.cpp
src/conversions/robot_state.cpp
src/images/spot_image_publisher.cpp
src/images/images_middleware_handle.cpp
src/images/spot_image_publisher_node.cpp
src/interfaces/rclcpp_logger_interface.cpp
src/interfaces/rclcpp_node_interface.cpp
src/interfaces/rclcpp_parameter_interface.cpp
src/interfaces/rclcpp_tf_interface.cpp
src/interfaces/rclcpp_wall_timer_interface.cpp
src/conversions/common_conversions.cpp
src/conversions/kinematic_conversions.cpp
src/kinematic/kinematic_node.cpp
src/kinematic/kinematic_service.cpp
src/kinematic/kinematic_middleware_handle.cpp
src/images/spot_image_publisher.cpp
src/images/images_middleware_handle.cpp
src/images/spot_image_publisher_node.cpp
src/robot_state/state_middleware_handle.cpp
src/robot_state/state_publisher.cpp
src/robot_state/state_publisher_node.cpp
)

target_include_directories(spot_api PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_link_libraries(spot_api PUBLIC bosdyn::bosdyn_client_static)
set_property(TARGET spot_api PROPERTY POSITION_INDEPENDENT_CODE ON)
ament_target_dependencies(spot_api PUBLIC ${THIS_PACKAGE_INCLUDE_ROS_DEPENDS})

###
# Spot image publisher
###

# Create executable to allow running SpotImagePublisherNode directly as a ROS 2 node
add_executable(spot_image_publisher_node src/images/spot_image_publisher_node_main.cpp)
target_include_directories(spot_image_publisher_node
Expand All @@ -78,28 +96,60 @@ rclcpp_components_register_node(
PLUGIN "spot_ros2::images::SpotImagePublisherNode"
EXECUTABLE spot_image_publisher_node_component)

###
# Spot state publisher
###

# Create executable to allow running StatePublisherNode directly as a ROS 2 node
add_executable(state_publisher_node src/robot_state/state_publisher_node_main.cpp)
target_link_libraries(state_publisher_node PUBLIC spot_api)
target_include_directories(state_publisher_node
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

# Register a composable node to allow loading StatePublisherNode in a component container
add_library(state_publisher_component SHARED src/robot_state/state_publisher_component.cpp)
target_include_directories(state_publisher_component
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(state_publisher_component PUBLIC spot_api)
ament_target_dependencies(state_publisher_component PUBLIC rclcpp_components)

rclcpp_components_register_node(
state_publisher_component
PLUGIN "spot_ros2::StatePublisherNode"
EXECUTABLE state_publisher_node_component)

###
# Spot IK
###

# Create executable to allow running KinematicNode directly as a ROS 2 node.
add_executable(kinematic_node src/kinematic/kinematic_node_main.cpp)
target_include_directories(kinematic_node
add_executable(spot_inverse_kinematics_node src/kinematic/kinematic_node_main.cpp)
target_include_directories(spot_inverse_kinematics_node
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(kinematic_node PUBLIC spot_api)
target_link_libraries(spot_inverse_kinematics_node PUBLIC spot_api)

# Register a composable node to allow loading KinematicNode in a component container
add_library(kinematic_component SHARED src/kinematic/kinematic_component.cpp)
target_include_directories(kinematic_component
add_library(spot_inverse_kinematics_component SHARED src/kinematic/kinematic_component.cpp)
target_include_directories(spot_inverse_kinematics_component
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(kinematic_component PUBLIC spot_api)
target_link_libraries(spot_inverse_kinematics_component PUBLIC spot_api)

rclcpp_components_register_node(
kinematic_component
spot_inverse_kinematics_component
PLUGIN "spot_ros2::kinematic::KinematicNode"
EXECUTABLE kinematic_node_component)
EXECUTABLE spot_inverse_kinematics_node_component)

ament_python_install_package(${PROJECT_NAME})
install(
Expand Down Expand Up @@ -131,23 +181,24 @@ install(
TARGETS
spot_api
spot_image_publisher_component
kinematic_component
spot_inverse_kinematics_component
state_publisher_component
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)

# Install Binaries
# Install Executables
install(
TARGETS
spot_image_publisher_node
spot_image_publisher_node_component
kinematic_node
kinematic_node_component
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
spot_inverse_kinematics_node
spot_inverse_kinematics_node_component
state_publisher_node
state_publisher_node_component
RUNTIME DESTINATION lib/${PROJECT_NAME}
)

Expand Down
5 changes: 5 additions & 0 deletions spot_driver/include/spot_driver/api/default_spot_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <bosdyn/client/robot/robot.h>
#include <bosdyn/client/sdk/client_sdk.h>

#include <spot_driver/api/state_client_interface.hpp>
#include <spot_driver/api/time_sync_api.hpp>

#include <memory>
Expand All @@ -22,12 +24,15 @@ class DefaultSpotApi : public SpotApi {
tl::expected<bool, std::string> hasArm() const override;
std::shared_ptr<KinematicApi> kinematicApi() const override;
std::shared_ptr<ImageClientInterface> image_client_interface() const override;
std::shared_ptr<StateClientInterface> stateClientInterface() const override;
std::shared_ptr<TimeSyncApi> timeSyncInterface() const override;

private:
std::unique_ptr<::bosdyn::client::ClientSdk> client_sdk_;
std::unique_ptr<::bosdyn::client::Robot> robot_;
std::shared_ptr<KinematicApi> kinematicApi_;
std::shared_ptr<ImageClientInterface> image_client_interface_;
std::shared_ptr<StateClientInterface> state_client_interface_;
std::shared_ptr<TimeSyncApi> time_sync_api_;
std::string robot_name_;
};
Expand Down
34 changes: 34 additions & 0 deletions spot_driver/include/spot_driver/api/default_state_client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2024 Boston Dynamics AI Institute LLC. All rights reserved.

#pragma once

#include <bosdyn/client/robot_state/robot_state_client.h>
#include <spot_driver/api/state_client_interface.hpp>

jschornak-bdai marked this conversation as resolved.
Show resolved Hide resolved
#include <string>

namespace spot_ros2 {

class DefaultStateClient final : public StateClientInterface {
public:
/**
* @brief constructor for DefaultStateClient.
*
* @param client A pointer to Spot's RobotStateClient. A DefaultStateClient SHOULD NOT delete this pointer since it
* does not take ownership.
jschornak-bdai marked this conversation as resolved.
Show resolved Hide resolved
*/
explicit DefaultStateClient(::bosdyn::client::RobotStateClient* client);

/**
* @brief Retrieve Spot's most recent robot state data.
* @return Returns an expected which contains a RobotState message if the request was completed successfully or an
* error message if the request could not be completed.
*/
tl::expected<bosdyn::api::RobotState, std::string> getRobotState() override;

private:
/** @brief A pointer to a RobotStateClient provided to this class during construction. */
::bosdyn::client::RobotStateClient* client_;
};

} // namespace spot_ros2
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace spot_ros2 {
class DefaultTimeSyncApi : public TimeSyncApi {
public:
explicit DefaultTimeSyncApi(std::shared_ptr<::bosdyn::client::TimeSyncThread> time_sync_thread);
~DefaultTimeSyncApi() = default;

tl::expected<builtin_interfaces::msg::Time, std::string> convertRobotTimeToLocalTime(
const google::protobuf::Timestamp& robot_timestamp) override;
Expand Down
9 changes: 9 additions & 0 deletions spot_driver/include/spot_driver/api/spot_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#pragma once

#include <spot_driver/api/kinematic_api.hpp>
#include <spot_driver/api/state_client_interface.hpp>
#include <spot_driver/api/time_sync_api.hpp>
#include <spot_driver/interfaces/image_client_interface.hpp>
#include <tl_expected/expected.hpp>

Expand All @@ -20,5 +22,12 @@ class SpotApi {
virtual tl::expected<bool, std::string> hasArm() const = 0;
virtual std::shared_ptr<KinematicApi> kinematicApi() const = 0;
virtual std::shared_ptr<ImageClientInterface> image_client_interface() const = 0;

/**
* @brief Get a StateClientInterface that communicates with Spot's robot state server.
* @return A shared_ptr to an instance of StateClientInterface which is owned by this object.
*/
virtual std::shared_ptr<StateClientInterface> stateClientInterface() const = 0;
gbrooks-bdai marked this conversation as resolved.
Show resolved Hide resolved
virtual std::shared_ptr<TimeSyncApi> timeSyncInterface() const = 0;
};
} // namespace spot_ros2
27 changes: 27 additions & 0 deletions spot_driver/include/spot_driver/api/state_client_interface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2024 Boston Dynamics AI Institute LLC. All rights reserved.

#pragma once

#include <bosdyn/api/robot_state.pb.h>
#include <tl_expected/expected.hpp>

#include <string>

namespace spot_ros2 {

/**
* @brief Interface class to interact with Spot SDK's Robot State Client
*/
class StateClientInterface {
public:
virtual ~StateClientInterface() = default;

/**
* @brief Retrieve Spot's most recent robot state data.
* @return Returns an expected which contains a RobotState message if the request was completed successfully. If the
* request could not be completed, or if the response does not contain a RobotState message, return an error message
* describing the failure.
*/
virtual tl::expected<bosdyn::api::RobotState, std::string> getRobotState() = 0;
};
} // namespace spot_ros2
2 changes: 2 additions & 0 deletions spot_driver/include/spot_driver/api/time_sync_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ inline builtin_interfaces::msg::Time applyClockSkew(const google::protobuf::Time

class TimeSyncApi {
public:
virtual ~TimeSyncApi() = default;

virtual tl::expected<builtin_interfaces::msg::Time, std::string> convertRobotTimeToLocalTime(
const google::protobuf::Timestamp& robot_timestamp) = 0;

Expand Down
27 changes: 27 additions & 0 deletions spot_driver/include/spot_driver/conversions/geometry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2024 Boston Dynamics AI Institute LLC. All rights reserved.

#pragma once

#include <bosdyn/api/geometry.pb.h>
#include <bosdyn/math/frame_helpers.h>
#include <builtin_interfaces/msg/time.hpp>
#include <geometry_msgs/msg/point.hpp>
jschornak-bdai marked this conversation as resolved.
Show resolved Hide resolved
#include <geometry_msgs/msg/transform_stamped.hpp>

#include <string>

namespace spot_ros2::conversions {
/**
* @brief Convert SE3Pose messages to ROS TransformStamped messages
*
* @param transform Spot SE3Pose proto message
* @param parent_frame transform's parent frame
* @param child_frame transform's child frame
* @param tf_time transform's timestamp
*/

geometry_msgs::msg::TransformStamped toTransformStamped(const ::bosdyn::api::SE3Pose& transform,
const std::string& parent_frame, const std::string& child_frame,
const builtin_interfaces::msg::Time& tf_time);

} // namespace spot_ros2::conversions
Loading
Loading