feat(slam): add cartographer_ros
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package cartographer_rviz
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
2.0.9003 (2024-04-17)
|
||||
---------------------
|
||||
|
||||
2.0.9002 (2024-03-15)
|
||||
---------------------
|
||||
* Fix a warning when building against newer Ogre.
|
||||
* Fix warnings when building against recent ROS 2 releases.
|
||||
* Contributors: Chris Lalancette
|
||||
|
||||
2.0.9001 (2023-04-13)
|
||||
---------------------
|
||||
|
||||
2.0.9000 (2022-03-31)
|
||||
---------------------
|
||||
* Update to latest upstream, as well as new port to ROS 2.
|
||||
|
||||
0.3.0 (2017-11-23)
|
||||
------------------
|
||||
* https://github.com/googlecartographer/cartographer_ros/compare/0.2.0...0.3.0
|
||||
|
||||
0.2.0 (2017-06-19)
|
||||
------------------
|
||||
* https://github.com/googlecartographer/cartographer_ros/compare/0.1.0...0.2.0
|
||||
|
||||
0.1.0 (2017-05-18)
|
||||
------------------
|
||||
* First unstable development release
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright 2016 The Cartographer Authors
|
||||
# Copyright 2022 Wyca Robotics (for the ROS2 conversion)
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(cartographer_rviz)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
# Default to C++17
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra)
|
||||
endif()
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(cartographer REQUIRED)
|
||||
include("${CARTOGRAPHER_CMAKE_DIR}/functions.cmake")
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
option(BUILD_GRPC "build features that require Cartographer gRPC support" false)
|
||||
google_initialize_cartographer_project()
|
||||
|
||||
find_package(absl REQUIRED)
|
||||
find_package(cartographer_ros REQUIRED)
|
||||
find_package(cartographer_ros_msgs REQUIRED)
|
||||
find_package(Eigen3 REQUIRED)
|
||||
find_package(pluginlib REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rviz_common REQUIRED)
|
||||
find_package(rviz_ogre_vendor REQUIRED)
|
||||
find_package(rviz_rendering REQUIRED)
|
||||
find_package(tf2_ros REQUIRED)
|
||||
find_package(tf2_eigen REQUIRED)
|
||||
find_package(pcl_conversions REQUIRED)
|
||||
find_package(urdf REQUIRED)
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
set(rviz_plugins_headers_to_moc
|
||||
include/cartographer_rviz/drawable_submap.h
|
||||
include/cartographer_rviz/ogre_slice.h
|
||||
include/cartographer_rviz/submaps_display.h
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
src/drawable_submap.cpp
|
||||
src/ogre_slice.cpp
|
||||
src/submaps_display.cpp
|
||||
${rviz_plugins_headers_to_moc}
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
absl::synchronization
|
||||
cartographer
|
||||
cartographer_ros::cartographer_ros
|
||||
Eigen3::Eigen
|
||||
rclcpp::rclcpp
|
||||
rviz_common::rviz_common
|
||||
rviz_rendering::rviz_rendering
|
||||
tf2_ros::tf2_ros
|
||||
tf2_eigen::tf2_eigen
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
pluginlib::pluginlib
|
||||
)
|
||||
|
||||
pluginlib_export_plugin_description_file(rviz_common rviz_plugin_description.xml)
|
||||
register_rviz_ogre_media_exports(DIRECTORIES "ogre_media/materials/glsl120" "ogre_media/materials/scripts")
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
ament_export_include_directories(include/${PROJECT_NAME})
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET)
|
||||
ament_export_dependencies(
|
||||
absl
|
||||
cartographer
|
||||
cartographer_ros
|
||||
eigen
|
||||
rclcpp
|
||||
rviz_common
|
||||
rviz_rendering
|
||||
tf2_ros
|
||||
)
|
||||
|
||||
ament_package()
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CARTOGRAPHER_RVIZ_SRC_DRAWABLE_SUBMAP_H_
|
||||
#define CARTOGRAPHER_RVIZ_SRC_DRAWABLE_SUBMAP_H_
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <dlfcn.h>
|
||||
#include "Eigen/Core"
|
||||
#include "Eigen/Geometry"
|
||||
#include "OgreSceneManager.h"
|
||||
#include "OgreSceneNode.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "cartographer/io/submap_painter.h"
|
||||
#include "cartographer/mapping/id.h"
|
||||
#include "cartographer/transform/rigid_transform.h"
|
||||
#include "cartographer_ros/submap.h"
|
||||
#include "cartographer_ros_msgs/msg/submap_entry.hpp"
|
||||
#include "cartographer_ros_msgs/srv/submap_query.hpp"
|
||||
#include "cartographer_rviz/ogre_slice.h"
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rviz_common/display_context.hpp>
|
||||
#include <rviz_common/frame_manager_iface.hpp>
|
||||
#include "rviz_rendering/objects/axes.hpp"
|
||||
#include "rviz_rendering/objects/movable_text.hpp"
|
||||
#include <rviz_common/properties/bool_property.hpp>
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
// Contains all the information needed to render a submap onto the final
|
||||
// texture representing the whole map.
|
||||
class DrawableSubmap : public QObject{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DrawableSubmap(const ::cartographer::mapping::SubmapId& submap_id,
|
||||
::rviz_common::DisplayContext* display_context,
|
||||
Ogre::SceneNode* map_node, ::rviz_common::properties::Property* submap_category,
|
||||
bool visible, const bool pose_axes_visible,
|
||||
float pose_axes_length, float pose_axes_radius);
|
||||
~DrawableSubmap() override;
|
||||
DrawableSubmap(const DrawableSubmap&) = delete;
|
||||
DrawableSubmap& operator=(const DrawableSubmap&) = delete;
|
||||
|
||||
// Updates the 'metadata' for this submap. If necessary, the next call to
|
||||
// MaybeFetchTexture() will fetch a new submap texture.
|
||||
void Update(const ::std_msgs::msg::Header& header,
|
||||
const ::cartographer_ros_msgs::msg::SubmapEntry& metadata);
|
||||
|
||||
// If an update is needed, it will send an RPC using 'client' to request the
|
||||
// new data for the submap and returns true.
|
||||
bool MaybeFetchTexture(rclcpp::Client<cartographer_ros_msgs::srv::SubmapQuery>::SharedPtr client,
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor);
|
||||
|
||||
// Returns whether an RPC is in progress.
|
||||
bool QueryInProgress();
|
||||
|
||||
// Sets the alpha of the submap taking into account its slice height and the
|
||||
// 'current_tracking_z'. 'fade_out_start_distance_in_meters' defines the
|
||||
// distance in z direction in meters, before which the submap will be shown
|
||||
// at full opacity.
|
||||
void SetAlpha(double current_tracking_z, float fade_out_distance_in_meters);
|
||||
|
||||
// Sets the visibility of a slice. It will be drawn if the parent submap
|
||||
// is also visible.
|
||||
void SetSliceVisibility(size_t slice_index, bool visible);
|
||||
|
||||
::cartographer::mapping::SubmapId id() const { return id_; }
|
||||
int version() const { return metadata_version_; }
|
||||
bool visibility() const { return visibility_->getBool(); }
|
||||
void set_visibility(const bool visibility) {
|
||||
visibility_->setBool(visibility);
|
||||
}
|
||||
void set_pose_markers_visibility(const bool visibility) {
|
||||
pose_axes_visible_ = visibility;
|
||||
TogglePoseMarkerVisibility();
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
// RPC request succeeded.
|
||||
void RequestSucceeded();
|
||||
|
||||
private Q_SLOTS:
|
||||
// Callback when an rpc request succeeded.
|
||||
void UpdateSceneNode();
|
||||
void ToggleVisibility();
|
||||
void TogglePoseMarkerVisibility();
|
||||
|
||||
private:
|
||||
const ::cartographer::mapping::SubmapId id_;
|
||||
|
||||
absl::Mutex mutex_;
|
||||
::rviz_common::DisplayContext* const display_context_;
|
||||
Ogre::SceneNode* const submap_node_;
|
||||
Ogre::SceneNode* const submap_id_text_node_;
|
||||
std::vector<std::unique_ptr<OgreSlice>> ogre_slices_;
|
||||
::cartographer::transform::Rigid3d pose_ GUARDED_BY(mutex_);
|
||||
::rviz_rendering::Axes pose_axes_;
|
||||
bool pose_axes_visible_;
|
||||
::rviz_rendering::MovableText submap_id_text_;
|
||||
std::chrono::milliseconds last_query_timestamp_ GUARDED_BY(mutex_);
|
||||
bool query_in_progress_ GUARDED_BY(mutex_) = false;
|
||||
int metadata_version_ GUARDED_BY(mutex_) = -1;
|
||||
std::future<void> rpc_request_future_;
|
||||
std::unique_ptr<::cartographer::io::SubmapTextures> submap_textures_
|
||||
GUARDED_BY(mutex_);
|
||||
float current_alpha_ = 0.f;
|
||||
std::unique_ptr<::rviz_common::properties::BoolProperty> visibility_;
|
||||
};
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
|
||||
#endif // CARTOGRAPHER_RVIZ_SRC_DRAWABLE_SUBMAP_H_
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CARTOGRAPHER_RVIZ_SRC_OGRE_SLICE_H_
|
||||
#define CARTOGRAPHER_RVIZ_SRC_OGRE_SLICE_H_
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include "Eigen/Geometry"
|
||||
#include "OgreManualObject.h"
|
||||
#include "OgreMaterial.h"
|
||||
#include "OgreQuaternion.h"
|
||||
#include "OgreSceneManager.h"
|
||||
#include "OgreSceneNode.h"
|
||||
#include "OgreTexture.h"
|
||||
#include "Ogre.h"
|
||||
#include "cartographer/io/submap_painter.h"
|
||||
#include "cartographer/mapping/id.h"
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
Ogre::Vector3 ToOgre(const Eigen::Vector3d& v);
|
||||
Ogre::Quaternion ToOgre(const Eigen::Quaterniond& q);
|
||||
|
||||
// A class containing the Ogre code to visualize a slice texture of a submap.
|
||||
// Member functions are expected to be called from the Ogre thread.
|
||||
class OgreSlice {
|
||||
public:
|
||||
// Attaches a node visualizing the submap 'id' to the 'submap_node' which is
|
||||
// expected to represent the submap frame.
|
||||
OgreSlice(const ::cartographer::mapping::SubmapId& id, int slice_id,
|
||||
Ogre::SceneManager* const scene_manager,
|
||||
Ogre::SceneNode* const submap_node);
|
||||
~OgreSlice();
|
||||
|
||||
OgreSlice(const OgreSlice&) = delete;
|
||||
OgreSlice& operator=(const OgreSlice&) = delete;
|
||||
|
||||
// Updates the texture and pose of the submap using new data from
|
||||
// 'submap_texture'.
|
||||
void Update(const ::cartographer::io::SubmapTexture& submap_texture);
|
||||
|
||||
// Changes the opacity of the submap to 'alpha'.
|
||||
void SetAlpha(float alpha);
|
||||
|
||||
// Sets the local visibility of this slice.
|
||||
void SetVisibility(bool visibility);
|
||||
|
||||
// Updates the SceneNode to be visible if the submap and this slice are
|
||||
// visible.
|
||||
void UpdateOgreNodeVisibility(bool submap_visibility);
|
||||
|
||||
private:
|
||||
// TODO(gaschler): Pack both ids into a struct.
|
||||
const ::cartographer::mapping::SubmapId id_;
|
||||
const int slice_id_;
|
||||
Ogre::SceneManager* const scene_manager_;
|
||||
Ogre::SceneNode* const submap_node_;
|
||||
Ogre::SceneNode* const slice_node_;
|
||||
Ogre::ManualObject* const manual_object_;
|
||||
Ogre::TexturePtr texture_;
|
||||
Ogre::MaterialPtr material_;
|
||||
bool visibility_ = true;
|
||||
};
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
|
||||
#endif // CARTOGRAPHER_RVIZ_SRC_OGRE_SLICE_H_
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CARTOGRAPHER_RVIZ_SRC_SUBMAPS_DISPLAY_H_
|
||||
#define CARTOGRAPHER_RVIZ_SRC_SUBMAPS_DISPLAY_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <dlfcn.h>
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "cartographer/common/port.h"
|
||||
#include "cartographer_ros_msgs/msg/submap_list.hpp"
|
||||
#include "cartographer_rviz/drawable_submap.h"
|
||||
#include <rviz_common/message_filter_display.hpp>
|
||||
#include <rviz_common/properties/bool_property.hpp>
|
||||
#include <rviz_common/properties/float_property.hpp>
|
||||
#include "tf2_ros/buffer.h"
|
||||
#include "tf2_ros/transform_listener.h"
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
// TODO(gaschler): This should be a private class in SubmapsDisplay,
|
||||
// unfortunately, QT does not allow for this. Move the logic out of the struct
|
||||
// and use just one slot for all changes.
|
||||
struct Trajectory : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Trajectory(std::unique_ptr<::rviz_common::properties::BoolProperty> property,
|
||||
bool pose_markers_enabled);
|
||||
|
||||
std::unique_ptr<::rviz_common::properties::BoolProperty> visibility;
|
||||
std::unique_ptr<::rviz_common::properties::BoolProperty> pose_markers_visibility;
|
||||
std::map<int, std::unique_ptr<DrawableSubmap>> submaps;
|
||||
|
||||
private Q_SLOTS:
|
||||
void AllEnabledToggled();
|
||||
void PoseMarkersEnabledToggled();
|
||||
};
|
||||
|
||||
// RViz plugin used for displaying maps which are represented by a collection of
|
||||
// submaps.
|
||||
//
|
||||
// We show an X-ray view of the map which is achieved by shipping textures for
|
||||
// every submap containing pre-multiplied alpha and grayscale values, these are
|
||||
// then alpha blended together.
|
||||
class SubmapsDisplay
|
||||
: public ::rviz_common::MessageFilterDisplay<::cartographer_ros_msgs::msg::SubmapList>, rclcpp::Node {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SubmapsDisplay();
|
||||
~SubmapsDisplay() override;
|
||||
|
||||
|
||||
|
||||
SubmapsDisplay(const SubmapsDisplay&) = delete;
|
||||
SubmapsDisplay& operator=(const SubmapsDisplay&) = delete;
|
||||
|
||||
private Q_SLOTS:
|
||||
void Reset();
|
||||
void AllEnabledToggled();
|
||||
void PoseMarkersEnabledToggled();
|
||||
void ResolutionToggled();
|
||||
|
||||
private:
|
||||
void CreateClient();
|
||||
|
||||
// These are called by RViz and therefore do not adhere to the style guide.
|
||||
void onInitialize() override;
|
||||
void reset() override;
|
||||
void processMessage(
|
||||
const ::cartographer_ros_msgs::msg::SubmapList::ConstSharedPtr msg) override;
|
||||
void update(float , float) override;
|
||||
|
||||
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group_;
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor_;
|
||||
rclcpp::Client<cartographer_ros_msgs::srv::SubmapQuery>::SharedPtr client_;
|
||||
::rviz_common::properties::StringProperty* submap_query_service_property_;
|
||||
std::unique_ptr<std::string> map_frame_;
|
||||
::rviz_common::properties::StringProperty* tracking_frame_property_;
|
||||
Ogre::SceneNode* map_node_ = nullptr; // Represents the map frame.
|
||||
std::map<int, std::unique_ptr<Trajectory>> trajectories_ GUARDED_BY(mutex_);
|
||||
absl::Mutex mutex_;
|
||||
::rviz_common::properties::BoolProperty* slice_high_resolution_enabled_;
|
||||
::rviz_common::properties::BoolProperty* slice_low_resolution_enabled_;
|
||||
::rviz_common::properties::Property* trajectories_category_;
|
||||
::rviz_common::properties::BoolProperty* visibility_all_enabled_;
|
||||
::rviz_common::properties::BoolProperty* pose_markers_all_enabled_;
|
||||
::rviz_common::properties::FloatProperty* fade_out_start_distance_in_meters_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
|
||||
#endif // CARTOGRAPHER_RVIZ_SRC_SUBMAPS_DISPLAY_H_
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
fragment_program cartographer_ros/glsl120/submap.frag glsl
|
||||
{
|
||||
source submap.frag
|
||||
}
|
||||
|
||||
vertex_program cartographer_ros/glsl120/submap.vert glsl
|
||||
{
|
||||
source submap.vert
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#version 120
|
||||
|
||||
varying vec2 out_submap_texture_coordinate;
|
||||
|
||||
uniform sampler2D u_submap;
|
||||
uniform float u_alpha;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texture_value = texture2D(u_submap, out_submap_texture_coordinate).rg;
|
||||
float value = u_alpha * texture_value.r;
|
||||
float alpha = u_alpha * texture_value.g;
|
||||
gl_FragColor = vec4(value, value, value, alpha);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#version 120
|
||||
|
||||
attribute vec4 uv0;
|
||||
|
||||
varying vec2 out_submap_texture_coordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_submap_texture_coordinate = vec2(uv0);
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
material cartographer_ros/Submap
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_program_ref cartographer_ros/glsl120/submap.vert {}
|
||||
|
||||
fragment_program_ref cartographer_ros/glsl120/submap.frag
|
||||
{
|
||||
param_named u_submap int 0
|
||||
param_named u_alpha float 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copyright 2016 The Cartographer Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<package format="3">
|
||||
<name>cartographer_rviz</name>
|
||||
<!--
|
||||
As of 2022-03-31, the versioning for cartographer_ros is in a complicated situation.
|
||||
Upstream https://github.com/cartographer-project/cartographer_ros seems to be moribund,
|
||||
with no commits for close to a year. The master branch there is only for ROS 1, and
|
||||
that branch has not had a released tag since 1.0.0 in 2018.
|
||||
|
||||
In the meantime, we've done a port to ROS 2 in https://github.com/ros2/cartographer_ros
|
||||
that targets upstream cartographer 2.0.0.
|
||||
|
||||
Since we have no response from upstream in a long time, we are going to go ahead and
|
||||
assume that the 1.x series of tags from upstream cartographer_ros will always be used
|
||||
to target ROS 1. Further we are going to assume that the 2.x series of tags
|
||||
from upstream cartographer_ros (none of which exist yet) will be used to target ROS 2.
|
||||
If upstream comes alive and chooses a different versioning scheme, we can adjust later
|
||||
as necessary. The outcome of all of this is that we are assuming a starting "upstream"
|
||||
release of 2.0.0, which then plays into the rest of the explanation below.
|
||||
|
||||
We add 900 to the patch part of the version and then multiply it by 10,
|
||||
i.e. our version = `(upstream_patch_version + 900) * 10`,
|
||||
so we can have intermediate releases as well as release any future official 2.0.x versions.
|
||||
|
||||
This is basically packing the patch part of the version and a fourth version part together
|
||||
into the third part of the version.
|
||||
|
||||
The use of `900` instead of something else like `100` is arbitrary, but it might
|
||||
help people recognize that this is a "special" version number.
|
||||
It is needed however, because we cannot have a leading `0` in our patch version.
|
||||
|
||||
Consider these possible future versions as an example:
|
||||
|
||||
2.0.9000 -> current state of this repository, 2.0.0 + some commits from us
|
||||
2.0.9010 -> upstream 2.0.1
|
||||
2.0.9011 -> upstream 2.0.1 + additional commits from upstream or us
|
||||
2.0.9012 -> upstream 2.0.1 + additional commits from 2.0.1011 + more new commits
|
||||
2.0.9020 -> upstream 2.0.2
|
||||
and so on...
|
||||
-->
|
||||
<version>2.0.9003</version>
|
||||
<description>
|
||||
Cartographer is a system that provides real-time simultaneous localization
|
||||
and mapping (SLAM) in 2D and 3D across multiple platforms and sensor
|
||||
configurations. This package provides Cartographer's RViz integration.
|
||||
</description>
|
||||
<maintainer email="clalancette@openrobotics.org">Chris Lalancette</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<url>https://github.com/cartographer-project/cartographer_ros</url>
|
||||
|
||||
<author email="google-cartographer@googlegroups.com">
|
||||
The Cartographer Authors
|
||||
</author>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>cartographer</depend>
|
||||
<depend>cartographer_ros</depend>
|
||||
<depend>cartographer_ros_msgs</depend>
|
||||
<depend>eigen</depend>
|
||||
<depend>libabsl-dev</depend>
|
||||
<depend>pluginlib</depend>
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rviz_common</depend>
|
||||
<depend>rviz_ogre_vendor</depend>
|
||||
<depend>rviz_rendering</depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
<rviz plugin="rviz_plugin_description.xml" />
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
Copyright 2016 The Cartographer Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<library path="cartographer_rviz">
|
||||
<class name="cartographer_rviz/SubmapsDisplay"
|
||||
type="cartographer_rviz::SubmapsDisplay"
|
||||
base_class_type="rviz_common::Display">
|
||||
<description>
|
||||
Displays submaps as a unified map in RViz.
|
||||
https://github.com/cartographer-project/cartographer_ros
|
||||
</description>
|
||||
</class>
|
||||
</library>
|
||||
@@ -0,0 +1,2 @@
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cartographer_rviz/drawable_submap.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include "Eigen/Geometry"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "cartographer/common/port.h"
|
||||
#include "cartographer_ros/msg_conversion.h"
|
||||
#include "cartographer_ros_msgs/srv/submap_query.hpp"
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::chrono::milliseconds kMinQueryDelayInMs(250);
|
||||
constexpr float kAlphaUpdateThreshold = 0.2f;
|
||||
|
||||
const Ogre::ColourValue kSubmapIdColor(Ogre::ColourValue::Red);
|
||||
const Eigen::Vector3d kSubmapIdPosition(0.0, 0.0, 0.3);
|
||||
constexpr float kSubmapIdCharHeight = 0.2f;
|
||||
constexpr int kNumberOfSlicesPerSubmap = 2;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
DrawableSubmap::DrawableSubmap(const ::cartographer::mapping::SubmapId& id,
|
||||
::rviz_common::DisplayContext* const display_context,
|
||||
Ogre::SceneNode* const map_node,
|
||||
::rviz_common::properties::Property* const submap_category,
|
||||
const bool visible, const bool pose_axes_visible,
|
||||
const float pose_axes_length,
|
||||
const float pose_axes_radius)
|
||||
: id_(id),
|
||||
display_context_(display_context),
|
||||
submap_node_(map_node->createChildSceneNode()),
|
||||
submap_id_text_node_(submap_node_->createChildSceneNode()),
|
||||
pose_axes_(display_context->getSceneManager(), submap_node_,
|
||||
pose_axes_length, pose_axes_radius),
|
||||
pose_axes_visible_(pose_axes_visible),
|
||||
submap_id_text_(QString("(%1,%2)")
|
||||
.arg(id.trajectory_id)
|
||||
.arg(id.submap_index)
|
||||
.toStdString()),
|
||||
last_query_timestamp_(0){
|
||||
for (int slice_index = 0; slice_index < kNumberOfSlicesPerSubmap;
|
||||
++slice_index) {
|
||||
ogre_slices_.emplace_back(absl::make_unique<OgreSlice>(
|
||||
id, slice_index, display_context->getSceneManager(), submap_node_));
|
||||
}
|
||||
// DrawableSubmap creates and manages its visibility property object
|
||||
// (a unique_ptr is needed because the Qt parent of the visibility
|
||||
// property is the submap_category object - the BoolProperty needs
|
||||
// to be destroyed along with the DrawableSubmap)
|
||||
visibility_ = absl::make_unique<::rviz_common::properties::BoolProperty>(
|
||||
"" /* title */, visible, "" /* description */, submap_category,
|
||||
SLOT(ToggleVisibility()), this);
|
||||
submap_id_text_.setCharacterHeight(kSubmapIdCharHeight);
|
||||
submap_id_text_.setColor(kSubmapIdColor);
|
||||
submap_id_text_.setTextAlignment(::rviz_rendering::MovableText::H_CENTER,
|
||||
::rviz_rendering::MovableText::V_ABOVE);
|
||||
submap_id_text_node_->setPosition(ToOgre(kSubmapIdPosition));
|
||||
submap_id_text_node_->attachObject(&submap_id_text_);
|
||||
TogglePoseMarkerVisibility();
|
||||
connect(this, SIGNAL(RequestSucceeded()), this, SLOT(UpdateSceneNode()));
|
||||
}
|
||||
|
||||
DrawableSubmap::~DrawableSubmap() {
|
||||
// 'query_in_progress_' must be true until the Q_EMIT has happened. Qt then
|
||||
// makes sure that 'RequestSucceeded' is not called after our destruction.
|
||||
if (QueryInProgress()) {
|
||||
rpc_request_future_.wait();
|
||||
}
|
||||
display_context_->getSceneManager()->destroySceneNode(submap_node_);
|
||||
display_context_->getSceneManager()->destroySceneNode(submap_id_text_node_);
|
||||
}
|
||||
|
||||
void DrawableSubmap::Update(
|
||||
const ::std_msgs::msg::Header& header,
|
||||
const ::cartographer_ros_msgs::msg::SubmapEntry& metadata) {
|
||||
(void) header; // TODO: remove unused arg ?
|
||||
absl::MutexLock locker(&mutex_);
|
||||
metadata_version_ = metadata.submap_version;
|
||||
pose_ = ::cartographer_ros::ToRigid3d(metadata.pose);
|
||||
submap_node_->setPosition(ToOgre(pose_.translation()));
|
||||
submap_node_->setOrientation(ToOgre(pose_.rotation()));
|
||||
display_context_->queueRender();
|
||||
visibility_->setName(
|
||||
QString("%1.%2").arg(id_.submap_index).arg(metadata_version_));
|
||||
visibility_->setDescription(
|
||||
QString("Toggle visibility of this individual submap.<br><br>"
|
||||
"Trajectory %1, submap %2, submap version %3")
|
||||
.arg(id_.trajectory_id)
|
||||
.arg(id_.submap_index)
|
||||
.arg(metadata_version_));
|
||||
}
|
||||
|
||||
bool DrawableSubmap::MaybeFetchTexture(
|
||||
rclcpp::Client<cartographer_ros_msgs::srv::SubmapQuery>::SharedPtr const client,
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor) {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
// Received metadata version can also be lower if we restarted Cartographer.
|
||||
const bool newer_version_available =
|
||||
submap_textures_ == nullptr ||
|
||||
submap_textures_->version != metadata_version_;
|
||||
const std::chrono::milliseconds now =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
const bool recently_queried =
|
||||
last_query_timestamp_ + kMinQueryDelayInMs > now;
|
||||
if (!newer_version_available || recently_queried || query_in_progress_) {
|
||||
return false;
|
||||
}
|
||||
query_in_progress_ = true;
|
||||
last_query_timestamp_ = now;
|
||||
|
||||
rpc_request_future_ = std::async(std::launch::async, [this, client, callback_group_executor]() {
|
||||
std::unique_ptr<::cartographer::io::SubmapTextures> submap_textures =
|
||||
::cartographer_ros::FetchSubmapTextures(id_, client, callback_group_executor, std::chrono::milliseconds(10000));
|
||||
absl::MutexLock locker(&mutex_);
|
||||
query_in_progress_ = false;
|
||||
if (submap_textures != nullptr) {
|
||||
// We emit a signal to update in the right thread, and pass via the
|
||||
// 'submap_texture_' member to simplify the signal-slot connection
|
||||
// slightly.
|
||||
submap_textures_ = std::move(submap_textures);
|
||||
Q_EMIT RequestSucceeded();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrawableSubmap::QueryInProgress() {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
return query_in_progress_;
|
||||
}
|
||||
|
||||
void DrawableSubmap::SetAlpha(const double current_tracking_z,
|
||||
const float fade_out_start_distance_in_meters) {
|
||||
const float fade_out_distance_in_meters =
|
||||
2.f * fade_out_start_distance_in_meters;
|
||||
const double distance_z =
|
||||
std::abs(pose_.translation().z() - current_tracking_z);
|
||||
const double fade_distance =
|
||||
std::max(distance_z - fade_out_start_distance_in_meters, 0.);
|
||||
const float target_alpha = static_cast<float>(
|
||||
std::max(0., 1. - fade_distance / fade_out_distance_in_meters));
|
||||
|
||||
if (std::abs(target_alpha - current_alpha_) > kAlphaUpdateThreshold ||
|
||||
target_alpha == 0.f || target_alpha == 1.f) {
|
||||
current_alpha_ = target_alpha;
|
||||
}
|
||||
for (auto& slice : ogre_slices_) {
|
||||
slice->SetAlpha(current_alpha_);
|
||||
}
|
||||
display_context_->queueRender();
|
||||
}
|
||||
|
||||
void DrawableSubmap::SetSliceVisibility(size_t slice_index, bool visible) {
|
||||
ogre_slices_.at(slice_index)->SetVisibility(visible);
|
||||
ToggleVisibility();
|
||||
}
|
||||
|
||||
void DrawableSubmap::UpdateSceneNode() {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
for (size_t slice_index = 0; slice_index < ogre_slices_.size() &&
|
||||
slice_index < submap_textures_->textures.size();
|
||||
++slice_index) {
|
||||
ogre_slices_[slice_index]->Update(submap_textures_->textures[slice_index]);
|
||||
}
|
||||
display_context_->queueRender();
|
||||
}
|
||||
|
||||
void DrawableSubmap::ToggleVisibility() {
|
||||
for (auto& ogre_slice : ogre_slices_) {
|
||||
ogre_slice->UpdateOgreNodeVisibility(visibility_->getBool());
|
||||
}
|
||||
display_context_->queueRender();
|
||||
}
|
||||
|
||||
void DrawableSubmap::TogglePoseMarkerVisibility() {
|
||||
submap_id_text_node_->setVisible(pose_axes_visible_);
|
||||
pose_axes_.getSceneNode()->setVisible(pose_axes_visible_);
|
||||
}
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cartographer_rviz/ogre_slice.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OgreGpuProgramParams.h"
|
||||
#include "OgreImage.h"
|
||||
#include "OgreMaterialManager.h"
|
||||
#include "OgreTechnique.h"
|
||||
#include "OgreTextureManager.h"
|
||||
#include "cartographer/common/port.h"
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kManualObjectPrefix[] = "ManualObjectSubmap";
|
||||
constexpr char kSubmapSourceMaterialName[] = "cartographer_ros/Submap";
|
||||
constexpr char kSubmapMaterialPrefix[] = "SubmapMaterial";
|
||||
constexpr char kSubmapTexturePrefix[] = "SubmapTexture";
|
||||
|
||||
std::string GetSliceIdentifier(
|
||||
const ::cartographer::mapping::SubmapId& submap_id, const int slice_id) {
|
||||
return (std::to_string(submap_id.trajectory_id) + "-" + std::to_string(submap_id.submap_index) + "-" + std::to_string(slice_id));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Ogre::Vector3 ToOgre(const Eigen::Vector3d& v) {
|
||||
return Ogre::Vector3(v.x(), v.y(), v.z());
|
||||
}
|
||||
|
||||
Ogre::Quaternion ToOgre(const Eigen::Quaterniond& q) {
|
||||
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
|
||||
}
|
||||
|
||||
OgreSlice::OgreSlice(const ::cartographer::mapping::SubmapId& id, int slice_id,
|
||||
Ogre::SceneManager* const scene_manager,
|
||||
Ogre::SceneNode* const submap_node)
|
||||
: id_(id),
|
||||
slice_id_(slice_id),
|
||||
scene_manager_(scene_manager),
|
||||
submap_node_(submap_node),
|
||||
slice_node_(submap_node_->createChildSceneNode()),
|
||||
manual_object_(scene_manager_->createManualObject(
|
||||
kManualObjectPrefix + GetSliceIdentifier(id, slice_id))) {
|
||||
material_ = Ogre::MaterialManager::getSingleton().getByName(kSubmapSourceMaterialName);
|
||||
material_ = material_->clone(
|
||||
kSubmapMaterialPrefix + GetSliceIdentifier(id_, slice_id_), true, "General");
|
||||
material_->setReceiveShadows(false);
|
||||
material_->getTechnique(0)->setLightingEnabled(false);
|
||||
material_->setCullingMode(Ogre::CULL_NONE);
|
||||
material_->setDepthBias(-1.f, 0.f);
|
||||
material_->setDepthWriteEnabled(false);
|
||||
slice_node_->attachObject(manual_object_);
|
||||
}
|
||||
|
||||
OgreSlice::~OgreSlice() {
|
||||
Ogre::MaterialManager::getSingleton().remove(material_->getHandle());
|
||||
if (texture_) {
|
||||
Ogre::TextureManager::getSingleton().remove(texture_->getHandle());
|
||||
texture_.reset();
|
||||
}
|
||||
scene_manager_->destroySceneNode(slice_node_);
|
||||
scene_manager_->destroyManualObject(manual_object_);
|
||||
}
|
||||
|
||||
void OgreSlice::Update(
|
||||
const ::cartographer::io::SubmapTexture& submap_texture) {
|
||||
slice_node_->setPosition(ToOgre(submap_texture.slice_pose.translation()));
|
||||
slice_node_->setOrientation(ToOgre(submap_texture.slice_pose.rotation()));
|
||||
// The call to Ogre's loadRawData below does not work with an RG texture,
|
||||
// therefore we create an RGB one whose blue channel is always 0.
|
||||
std::vector<char> rgb;
|
||||
CHECK_EQ(submap_texture.pixels.intensity.size(),
|
||||
submap_texture.pixels.alpha.size());
|
||||
for (size_t i = 0; i < submap_texture.pixels.intensity.size(); ++i) {
|
||||
rgb.push_back(submap_texture.pixels.intensity[i]);
|
||||
rgb.push_back(submap_texture.pixels.alpha[i]);
|
||||
rgb.push_back(0);
|
||||
}
|
||||
|
||||
manual_object_->clear();
|
||||
const float metric_width = submap_texture.resolution * submap_texture.width;
|
||||
const float metric_height = submap_texture.resolution * submap_texture.height;
|
||||
manual_object_->begin(material_->getName(),
|
||||
Ogre::RenderOperation::OT_TRIANGLE_STRIP);
|
||||
// Bottom left
|
||||
manual_object_->position(-metric_height, 0.0f, 0.0f);
|
||||
manual_object_->textureCoord(0.0f, 1.0f);
|
||||
// Bottom right
|
||||
manual_object_->position(-metric_height, -metric_width, 0.0f);
|
||||
manual_object_->textureCoord(1.0f, 1.0f);
|
||||
// Top left
|
||||
manual_object_->position(0.0f, 0.0f, 0.0f);
|
||||
manual_object_->textureCoord(0.0f, 0.0f);
|
||||
// Top right
|
||||
manual_object_->position(0.0f, -metric_width, 0.0f);
|
||||
manual_object_->textureCoord(1.0f, 0.0f);
|
||||
manual_object_->end();
|
||||
|
||||
Ogre::DataStreamPtr pixel_stream;
|
||||
pixel_stream.reset(new Ogre::MemoryDataStream(rgb.data(), rgb.size()));
|
||||
|
||||
if (texture_) {
|
||||
Ogre::TextureManager::getSingleton().remove(texture_->getHandle());
|
||||
texture_.reset();
|
||||
}
|
||||
const std::string texture_name =
|
||||
kSubmapTexturePrefix + GetSliceIdentifier(id_, slice_id_);
|
||||
texture_ = Ogre::TextureManager::getSingleton().loadRawData(
|
||||
texture_name, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
pixel_stream, submap_texture.width, submap_texture.height,
|
||||
Ogre::PF_BYTE_RGB, Ogre::TEX_TYPE_2D, 0);
|
||||
|
||||
Ogre::Pass* const pass = material_->getTechnique(0)->getPass(0);
|
||||
pass->setSceneBlending(Ogre::SBF_ONE, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA);
|
||||
Ogre::TextureUnitState* const texture_unit =
|
||||
pass->getNumTextureUnitStates() > 0 ? pass->getTextureUnitState(0)
|
||||
: pass->createTextureUnitState();
|
||||
|
||||
texture_unit->setTextureName(texture_->getName());
|
||||
texture_unit->setTextureFiltering(Ogre::TFO_NONE);
|
||||
}
|
||||
|
||||
void OgreSlice::SetAlpha(const float alpha) {
|
||||
const Ogre::GpuProgramParametersSharedPtr parameters =
|
||||
material_->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
|
||||
parameters->setNamedConstant("u_alpha", alpha);
|
||||
}
|
||||
|
||||
void OgreSlice::SetVisibility(bool visibility) { visibility_ = visibility; }
|
||||
|
||||
void OgreSlice::UpdateOgreNodeVisibility(bool submap_visibility) {
|
||||
slice_node_->setVisible(submap_visibility && visibility_);
|
||||
}
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2016 The Cartographer Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cartographer_rviz/submaps_display.h"
|
||||
|
||||
#include "OgreResourceGroupManager.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
|
||||
#include "cartographer/mapping/id.h"
|
||||
#include "cartographer_ros_msgs/msg/submap_list.hpp"
|
||||
#include "cartographer_ros_msgs/srv/submap_query.hpp"
|
||||
#include <geometry_msgs/msg/transform_stamped.hpp>
|
||||
#include <pluginlib/class_list_macros.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp/version.h>
|
||||
#include <rviz_common/display_context.hpp>
|
||||
#include <rviz_common/frame_manager_iface.hpp>
|
||||
#include <rviz_common/properties/bool_property.hpp>
|
||||
#include <rviz_common/properties/string_property.hpp>
|
||||
#include <rviz_common/message_filter_display.hpp>
|
||||
#include <ament_index_cpp/get_package_share_directory.hpp>
|
||||
|
||||
namespace cartographer_rviz {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kMaxOnGoingRequestsPerTrajectory = 6;
|
||||
constexpr char kMaterialsDirectory[] = "/ogre_media/materials";
|
||||
constexpr char kGlsl120Directory[] = "/glsl120";
|
||||
constexpr char kScriptsDirectory[] = "/scripts";
|
||||
constexpr char kDefaultTrackingFrame[] = "base_link";
|
||||
constexpr char kDefaultSubmapQueryServiceName[] = "/submap_query";
|
||||
|
||||
} // namespace
|
||||
|
||||
SubmapsDisplay::SubmapsDisplay() : rclcpp::Node("submaps_display") {
|
||||
submap_query_service_property_ = new ::rviz_common::properties::StringProperty(
|
||||
"Submap query service", kDefaultSubmapQueryServiceName,
|
||||
"Submap query service to connect to.", this, SLOT(Reset()));
|
||||
tracking_frame_property_ = new ::rviz_common::properties::StringProperty(
|
||||
"Tracking frame", kDefaultTrackingFrame,
|
||||
"Tracking frame, used for fading out submaps.", this);
|
||||
slice_high_resolution_enabled_ = new ::rviz_common::properties::BoolProperty(
|
||||
"High Resolution", true, "Display high resolution slices.", this,
|
||||
SLOT(ResolutionToggled()), this);
|
||||
slice_low_resolution_enabled_ = new ::rviz_common::properties::BoolProperty(
|
||||
"Low Resolution", false, "Display low resolution slices.", this,
|
||||
SLOT(ResolutionToggled()), this);
|
||||
|
||||
callback_group_ = this->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false);
|
||||
callback_group_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
callback_group_executor_->add_callback_group(callback_group_, this->get_node_base_interface());
|
||||
client_ = this->create_client<::cartographer_ros_msgs::srv::SubmapQuery>(
|
||||
kDefaultSubmapQueryServiceName,
|
||||
#if RCLCPP_VERSION_GTE(17, 0, 0)
|
||||
rclcpp::ServicesQoS(),
|
||||
#else
|
||||
rmw_qos_profile_services_default,
|
||||
#endif
|
||||
callback_group_
|
||||
);
|
||||
trajectories_category_ = new ::rviz_common::properties::Property(
|
||||
"Submaps", QVariant(), "List of all submaps, organized by trajectories.",
|
||||
this);
|
||||
visibility_all_enabled_ = new ::rviz_common::properties::BoolProperty(
|
||||
"All", true,
|
||||
"Whether submaps from all trajectories should be displayed or not.",
|
||||
trajectories_category_, SLOT(AllEnabledToggled()), this);
|
||||
pose_markers_all_enabled_ = new ::rviz_common::properties::BoolProperty(
|
||||
"All Submap Pose Markers", true,
|
||||
"Whether submap pose markers should be displayed or not.",
|
||||
trajectories_category_, SLOT(PoseMarkersEnabledToggled()), this);
|
||||
fade_out_start_distance_in_meters_ =
|
||||
new ::rviz_common::properties::FloatProperty("Fade-out distance", 1.f,
|
||||
"Distance in meters in z-direction beyond "
|
||||
"which submaps will start to fade out.",
|
||||
this);
|
||||
const std::string package_path = ament_index_cpp::get_package_share_directory("cartographer_rviz");
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
package_path + kMaterialsDirectory, "FileSystem", "cartographer_rviz");
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
package_path + kMaterialsDirectory + kGlsl120Directory, "FileSystem",
|
||||
"cartographer_rviz");
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
package_path + kMaterialsDirectory + kScriptsDirectory, "FileSystem",
|
||||
"cartographer_rviz");
|
||||
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
|
||||
|
||||
tf_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());
|
||||
tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
|
||||
}
|
||||
|
||||
SubmapsDisplay::~SubmapsDisplay() {
|
||||
client_.reset();
|
||||
trajectories_.clear();
|
||||
scene_manager_->destroySceneNode(map_node_);
|
||||
}
|
||||
|
||||
void SubmapsDisplay::Reset() { reset(); }
|
||||
|
||||
void SubmapsDisplay::CreateClient() {
|
||||
client_ = this->create_client<::cartographer_ros_msgs::srv::SubmapQuery>(
|
||||
submap_query_service_property_->getStdString(),
|
||||
#if RCLCPP_VERSION_GTE(17, 0, 0)
|
||||
rclcpp::ServicesQoS(),
|
||||
#else
|
||||
rmw_qos_profile_services_default,
|
||||
#endif
|
||||
callback_group_
|
||||
);
|
||||
}
|
||||
|
||||
void SubmapsDisplay::onInitialize() {
|
||||
MFDClass::onInitialize();
|
||||
map_node_ = scene_manager_->getRootSceneNode()->createChildSceneNode();
|
||||
CreateClient();
|
||||
}
|
||||
|
||||
void SubmapsDisplay::reset() {
|
||||
MFDClass::reset();
|
||||
absl::MutexLock locker(&mutex_);
|
||||
client_.reset();
|
||||
trajectories_.clear();
|
||||
CreateClient();
|
||||
}
|
||||
|
||||
void SubmapsDisplay::processMessage( const ::cartographer_ros_msgs::msg::SubmapList::ConstSharedPtr msg) {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
map_frame_ = absl::make_unique<std::string>(msg->header.frame_id);
|
||||
// In case Cartographer node is relaunched, destroy trajectories from the
|
||||
// previous instance.
|
||||
for (const ::cartographer_ros_msgs::msg::SubmapEntry& submap_entry : msg->submap) {
|
||||
const size_t trajectory_id = submap_entry.trajectory_id;
|
||||
if (trajectories_.count(trajectory_id) == 0) {
|
||||
continue;
|
||||
}
|
||||
const auto& trajectory_submaps = trajectories_[trajectory_id]->submaps;
|
||||
const auto it = trajectory_submaps.find(submap_entry.submap_index);
|
||||
if (it != trajectory_submaps.end() &&
|
||||
it->second->version() > submap_entry.submap_version) {
|
||||
// Versions should only increase unless Cartographer restarted.
|
||||
trajectories_.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
using ::cartographer::mapping::SubmapId;
|
||||
std::set<SubmapId> listed_submaps;
|
||||
std::set<int> listed_trajectories;
|
||||
for (const ::cartographer_ros_msgs::msg::SubmapEntry& submap_entry : msg->submap) {
|
||||
const SubmapId id{submap_entry.trajectory_id, submap_entry.submap_index};
|
||||
listed_submaps.insert(id);
|
||||
listed_trajectories.insert(submap_entry.trajectory_id);
|
||||
if (trajectories_.count(id.trajectory_id) == 0) {
|
||||
trajectories_.insert(std::make_pair(
|
||||
id.trajectory_id,
|
||||
absl::make_unique<Trajectory>(
|
||||
absl::make_unique<::rviz_common::properties::BoolProperty>(
|
||||
QString("Trajectory %1").arg(id.trajectory_id),
|
||||
visibility_all_enabled_->getBool(),
|
||||
QString(
|
||||
"List of all submaps in Trajectory %1. The checkbox "
|
||||
"controls whether all submaps in this trajectory should "
|
||||
"be displayed or not.")
|
||||
.arg(id.trajectory_id),
|
||||
trajectories_category_),
|
||||
pose_markers_all_enabled_->getBool())));
|
||||
}
|
||||
auto& trajectory_visibility = trajectories_[id.trajectory_id]->visibility;
|
||||
auto& trajectory_submaps = trajectories_[id.trajectory_id]->submaps;
|
||||
auto& pose_markers_visibility =
|
||||
trajectories_[id.trajectory_id]->pose_markers_visibility;
|
||||
if (trajectory_submaps.count(id.submap_index) == 0) {
|
||||
// TODO(ojura): Add RViz properties for adjusting submap pose axes
|
||||
constexpr float kSubmapPoseAxesLength = 0.3f;
|
||||
constexpr float kSubmapPoseAxesRadius = 0.06f;
|
||||
trajectory_submaps.emplace(
|
||||
id.submap_index,
|
||||
absl::make_unique<DrawableSubmap>(
|
||||
id, context_, map_node_, trajectory_visibility.get(),
|
||||
trajectory_visibility->getBool(),
|
||||
pose_markers_visibility->getBool(), kSubmapPoseAxesLength,
|
||||
kSubmapPoseAxesRadius));
|
||||
trajectory_submaps.at(id.submap_index)
|
||||
->SetSliceVisibility(0, slice_high_resolution_enabled_->getBool());
|
||||
trajectory_submaps.at(id.submap_index)
|
||||
->SetSliceVisibility(1, slice_low_resolution_enabled_->getBool());
|
||||
}
|
||||
trajectory_submaps.at(id.submap_index)->Update(msg->header, submap_entry);
|
||||
}
|
||||
// Remove all deleted trajectories not mentioned in the SubmapList.
|
||||
for (auto it = trajectories_.begin(); it != trajectories_.end();) {
|
||||
if (listed_trajectories.count(it->first) == 0) {
|
||||
it = trajectories_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// Remove all submaps not mentioned in the SubmapList.
|
||||
for (const auto& trajectory_by_id : trajectories_) {
|
||||
const int trajectory_id = trajectory_by_id.first;
|
||||
auto& trajectory_submaps = trajectory_by_id.second->submaps;
|
||||
for (auto it = trajectory_submaps.begin();
|
||||
it != trajectory_submaps.end();) {
|
||||
if (listed_submaps.count(
|
||||
SubmapId{static_cast<int>(trajectory_id), it->first}) == 0) {
|
||||
it = trajectory_submaps.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubmapsDisplay::update(const float , const float) {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
// Schedule fetching of new submap textures.
|
||||
for (const auto& trajectory_by_id : trajectories_) {
|
||||
int num_ongoing_requests = 0;
|
||||
for (const auto& submap_entry : trajectory_by_id.second->submaps) {
|
||||
if (submap_entry.second->QueryInProgress()) {
|
||||
++num_ongoing_requests;
|
||||
}
|
||||
}
|
||||
for (auto it = trajectory_by_id.second->submaps.rbegin();
|
||||
it != trajectory_by_id.second->submaps.rend() &&
|
||||
num_ongoing_requests < kMaxOnGoingRequestsPerTrajectory;
|
||||
++it) {
|
||||
if (it->second->MaybeFetchTexture(client_, callback_group_executor_)) {
|
||||
++num_ongoing_requests;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map_frame_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the fading by z distance.
|
||||
const auto klatest = this->get_clock()->now();
|
||||
try {
|
||||
const ::geometry_msgs::msg::TransformStamped transform_stamped =
|
||||
tf_buffer_->lookupTransform(
|
||||
*map_frame_, tracking_frame_property_->getStdString(), tf2::TimePointZero,std::chrono::milliseconds(100));
|
||||
for (auto& trajectory_by_id : trajectories_) {
|
||||
for (auto& submap_entry : trajectory_by_id.second->submaps) {
|
||||
submap_entry.second->SetAlpha(
|
||||
transform_stamped.transform.translation.z,
|
||||
fade_out_start_distance_in_meters_->getFloat());
|
||||
}
|
||||
}
|
||||
} catch (const tf2::TransformException& ex) {
|
||||
RCLCPP_WARN(this->get_logger(), "Could not compute submap fading: %s", ex.what());
|
||||
}
|
||||
// Update the map frame to fixed frame transform.
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
if (context_->getFrameManager()->getTransform(*map_frame_, klatest, position,
|
||||
orientation)) {
|
||||
map_node_->setPosition(position);
|
||||
map_node_->setOrientation(orientation);
|
||||
context_->queueRender();
|
||||
}
|
||||
}
|
||||
|
||||
void SubmapsDisplay::AllEnabledToggled() {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
const bool visible = visibility_all_enabled_->getBool();
|
||||
for (auto& trajectory_by_id : trajectories_) {
|
||||
trajectory_by_id.second->visibility->setBool(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void SubmapsDisplay::PoseMarkersEnabledToggled() {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
const bool visible = pose_markers_all_enabled_->getBool();
|
||||
for (auto& trajectory_by_id : trajectories_) {
|
||||
trajectory_by_id.second->pose_markers_visibility->setBool(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void SubmapsDisplay::ResolutionToggled() {
|
||||
absl::MutexLock locker(&mutex_);
|
||||
for (auto& trajectory_by_id : trajectories_) {
|
||||
for (auto& submap_entry : trajectory_by_id.second->submaps) {
|
||||
submap_entry.second->SetSliceVisibility(
|
||||
0, slice_high_resolution_enabled_->getBool());
|
||||
submap_entry.second->SetSliceVisibility(
|
||||
1, slice_low_resolution_enabled_->getBool());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trajectory::AllEnabledToggled() {
|
||||
const bool visible = visibility->getBool();
|
||||
for (auto& submap_entry : submaps) {
|
||||
submap_entry.second->set_visibility(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void Trajectory::PoseMarkersEnabledToggled() {
|
||||
const bool visible = pose_markers_visibility->getBool();
|
||||
for (auto& submap_entry : submaps) {
|
||||
submap_entry.second->set_pose_markers_visibility(visible);
|
||||
}
|
||||
}
|
||||
|
||||
Trajectory::Trajectory(std::unique_ptr<::rviz_common::properties::BoolProperty> property,
|
||||
const bool pose_markers_enabled)
|
||||
: visibility(std::move(property)) {
|
||||
::QObject::connect(visibility.get(), SIGNAL(changed()), this,
|
||||
SLOT(AllEnabledToggled()));
|
||||
// Add toggle for submap pose markers as the first entry of the visibility
|
||||
// property list of this trajectory.
|
||||
pose_markers_visibility = absl::make_unique<::rviz_common::properties::BoolProperty>(
|
||||
QString("Submap Pose Markers"), pose_markers_enabled,
|
||||
QString("Toggles the submap pose markers of this trajectory."),
|
||||
visibility.get());
|
||||
::QObject::connect(pose_markers_visibility.get(), SIGNAL(changed()), this,
|
||||
SLOT(PoseMarkersEnabledToggled()));
|
||||
}
|
||||
|
||||
} // namespace cartographer_rviz
|
||||
|
||||
PLUGINLIB_EXPORT_CLASS(cartographer_rviz::SubmapsDisplay, rviz_common::Display)
|
||||
Reference in New Issue
Block a user