Skip to content

Commit 1a1b1b1

Browse files
authored
add imu data (#9)
* add pulisher and subscriber for imu data * add dummy imu node * code format * code format
1 parent d6d1f62 commit 1a1b1b1

File tree

11 files changed

+432
-6
lines changed

11 files changed

+432
-6
lines changed

ssct_common/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ add_library(${PROJECT_NAME} SHARED
4141
# publisher
4242
src/publisher/cloud_publisher.cpp
4343
src/publisher/image_publisher.cpp
44+
src/publisher/imu_publisher.cpp
4445
# subscriber
4546
src/subscriber/image_subscriber.cpp
47+
src/subscriber/imu_subscriber.cpp
4648
# dummy_sensor
4749
src/dummy_sensor/dummy_camera_node.cpp
4850
src/dummy_sensor/dummy_lidar_node.cpp
51+
src/dummy_sensor/dummy_imu_node.cpp
4952
# utils
5053
src/calibration_params.cpp
5154
src/msg_utils.cpp
@@ -68,6 +71,11 @@ add_executable(dummy_camera_node
6871
src/dummy_sensor/dummy_camera_main.cpp)
6972
target_link_libraries(dummy_camera_node ${PROJECT_NAME})
7073

74+
# dummy imu node
75+
add_executable(dummy_imu_node
76+
src/dummy_sensor/dummy_imu_main.cpp)
77+
target_link_libraries(dummy_imu_node ${PROJECT_NAME})
78+
7179
# install include directories
7280
install(DIRECTORY include/
7381
DESTINATION include
@@ -86,6 +94,7 @@ install(TARGETS ${PROJECT_NAME}
8694
install(TARGETS
8795
dummy_lidar_node
8896
dummy_camera_node
97+
dummy_imu_node
8998
DESTINATION lib/${PROJECT_NAME}
9099
)
91100

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2024 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
#include <string>
19+
#include <vector>
20+
21+
#include "rclcpp/rclcpp.hpp"
22+
#include "ssct_common/publisher/imu_publisher.hpp"
23+
24+
namespace ssct_common
25+
{
26+
27+
class DummyImuNode
28+
{
29+
public:
30+
explicit DummyImuNode(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
31+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface()
32+
{
33+
return node_->get_node_base_interface();
34+
}
35+
36+
private:
37+
bool read_data();
38+
39+
private:
40+
rclcpp::Node::SharedPtr node_;
41+
rclcpp::TimerBase::SharedPtr timer_;
42+
std::shared_ptr<ImuPublisher> imu_pub_;
43+
// param
44+
std::string frame_id_{"imu"};
45+
std::string data_file_;
46+
double rate_{100};
47+
double timestamp_{0};
48+
bool loop_{true};
49+
// data
50+
std::vector<ImuData> imus_;
51+
size_t current_idx_{0};
52+
};
53+
} // namespace ssct_common

ssct_common/include/ssct_common/dummy_sensor/dummy_lidar_node.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 Gezp (https://github.com/gezp).
1+
// Copyright 2025 Gezp (https://github.com/gezp).
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <string>
18+
19+
#include "rclcpp/rclcpp.hpp"
20+
#include "sensor_msgs/msg/imu.hpp"
21+
22+
#include "ssct_common/sensor_data/imu_data.hpp"
23+
24+
namespace ssct_common
25+
{
26+
class ImuPublisher
27+
{
28+
public:
29+
ImuPublisher(
30+
rclcpp::Node::SharedPtr node, std::string topic_name, size_t buffer_size, std::string frame_id);
31+
32+
void publish(const ImuData & imu_data);
33+
bool has_subscribers();
34+
35+
private:
36+
rclcpp::Node::SharedPtr node_;
37+
rclcpp::Publisher<sensor_msgs::msg::Imu>::SharedPtr publisher_;
38+
std::string frame_id_;
39+
40+
sensor_msgs::msg::Imu imu_;
41+
};
42+
} // namespace ssct_common
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2025 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <Eigen/Dense>
18+
19+
namespace ssct_common
20+
{
21+
struct ImuData
22+
{
23+
double time = 0.0;
24+
Eigen::Vector3d angular_velocity;
25+
Eigen::Vector3d linear_acceleration;
26+
};
27+
28+
} // namespace ssct_common
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2025 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
#include <deque>
17+
#include <mutex>
18+
#include <string>
19+
20+
#include "ssct_common/sensor_data/imu_data.hpp"
21+
#include "rclcpp/rclcpp.hpp"
22+
#include "sensor_msgs/msg/imu.hpp"
23+
24+
namespace ssct_common
25+
{
26+
class ImuSubscriber
27+
{
28+
public:
29+
ImuSubscriber(rclcpp::Node::SharedPtr node, std::string topic_name, size_t buff_size);
30+
ImuSubscriber() = default;
31+
void read(std::deque<ImuData> & output);
32+
void clear();
33+
34+
private:
35+
void msg_callback(const sensor_msgs::msg::Imu::SharedPtr imu_msg_ptr);
36+
37+
private:
38+
rclcpp::Node::SharedPtr node_;
39+
rclcpp::Subscription<sensor_msgs::msg::Imu>::SharedPtr subscriber_;
40+
41+
std::deque<ImuData> buffer_;
42+
std::mutex buffer_mutex_;
43+
};
44+
} // namespace ssct_common

ssct_common/launch/dummy_sensors.launch.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919

2020

2121
def generate_launch_description():
22-
data_dir = os.path.join(
23-
os.environ["HOME"], "calibration_data", "SensorsCalibration"
24-
)
25-
lidar_data_dir = os.path.join(data_dir, "lidar2camera", "lidar")
26-
camera_data_dir = os.path.join(data_dir, "camera_intrinsic")
22+
data_dir = os.path.join(os.environ["HOME"], "calibration_data")
23+
lidar_data_dir = os.path.join(data_dir, "SensorsCalibration", "lidar2camera", "lidar")
24+
camera_data_dir = os.path.join(data_dir, "SensorsCalibration", "camera_intrinsic")
25+
imu_data_file = os.path.join(data_dir, "imu_data", "uncalibrated_imu_data.csv")
2726
dummy_lidar_node = Node(
2827
name="dummy_lidar_node",
2928
package="ssct_common",
@@ -44,7 +43,21 @@ def generate_launch_description():
4443
],
4544
output="screen",
4645
)
46+
dummy_imu_node = Node(
47+
name="dummy_imu_node",
48+
package="ssct_common",
49+
executable="dummy_imu_node",
50+
parameters=[
51+
{
52+
"data_file": imu_data_file,
53+
"frame_id": "center_imu",
54+
"rate": 100.0,
55+
}
56+
],
57+
output="screen",
58+
)
4759
ld = LaunchDescription()
4860
ld.add_action(dummy_lidar_node)
4961
ld.add_action(dummy_camera_node)
62+
ld.add_action(dummy_imu_node)
5063
return ld
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2025 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "ssct_common/dummy_sensor/dummy_imu_node.hpp"
16+
#include "rclcpp/rclcpp.hpp"
17+
18+
int main(int argc, char * argv[])
19+
{
20+
rclcpp::init(argc, argv);
21+
auto node = std::make_shared<ssct_common::DummyImuNode>();
22+
rclcpp::spin(node->get_node_base_interface());
23+
rclcpp::shutdown();
24+
return 0;
25+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2025 Gezp (https://github.com/gezp).
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "ssct_common/dummy_sensor/dummy_imu_node.hpp"
16+
17+
#include <fstream>
18+
#include <sstream>
19+
20+
namespace ssct_common
21+
{
22+
23+
DummyImuNode::DummyImuNode(const rclcpp::NodeOptions & options)
24+
{
25+
node_ = std::make_shared<rclcpp::Node>("dummy_imu_node", options);
26+
node_->declare_parameter("frame_id", frame_id_);
27+
node_->declare_parameter("data_file", data_file_);
28+
node_->declare_parameter("rate", rate_);
29+
node_->declare_parameter("timestamp", timestamp_);
30+
node_->declare_parameter("loop", loop_);
31+
32+
node_->get_parameter("frame_id", frame_id_);
33+
node_->get_parameter("data_file", data_file_);
34+
node_->get_parameter("rate", rate_);
35+
node_->get_parameter("timestamp", timestamp_);
36+
node_->get_parameter("loop", loop_);
37+
// read data
38+
if (data_file_.empty()) {
39+
RCLCPP_FATAL(node_->get_logger(), "data_file is empty.");
40+
return;
41+
}
42+
if (!read_data()) {
43+
RCLCPP_FATAL(node_->get_logger(), "no imus.");
44+
return;
45+
}
46+
// publisher
47+
std::string topic_name = "/sensor/" + frame_id_ + "/imu";
48+
imu_pub_ = std::make_shared<ImuPublisher>(node_, topic_name, 100, frame_id_);
49+
// create timer
50+
auto period_ms = std::chrono::milliseconds(static_cast<int64_t>(1000.0 / rate_));
51+
auto timer_callback = [this]() {
52+
if (loop_ && current_idx_ == imus_.size()) {
53+
current_idx_ = 0;
54+
}
55+
if (current_idx_ < imus_.size()) {
56+
imu_pub_->publish(imus_[current_idx_]);
57+
current_idx_++;
58+
}
59+
};
60+
timer_ = node_->create_wall_timer(period_ms, timer_callback);
61+
}
62+
63+
bool DummyImuNode::read_data()
64+
{
65+
std::ifstream ifs(data_file_);
66+
if (!ifs.is_open()) {
67+
RCLCPP_FATAL(node_->get_logger(), "failed to open file %s.", data_file_.c_str());
68+
return false;
69+
}
70+
std::string line;
71+
while (std::getline(ifs, line)) {
72+
if (line.empty() || !std::isdigit(line[0])) {
73+
// skip head line and empty line
74+
continue;
75+
}
76+
std::istringstream ss(line);
77+
std::string token;
78+
ImuData imu;
79+
try {
80+
std::getline(ss, token, ',');
81+
imu.time = timestamp_ + std::stod(token);
82+
std::getline(ss, token, ',');
83+
imu.angular_velocity.x() = std::stod(token);
84+
std::getline(ss, token, ',');
85+
imu.angular_velocity.y() = std::stod(token);
86+
std::getline(ss, token, ',');
87+
imu.angular_velocity.z() = std::stod(token);
88+
std::getline(ss, token, ',');
89+
imu.linear_acceleration.x() = std::stod(token);
90+
std::getline(ss, token, ',');
91+
imu.linear_acceleration.y() = std::stod(token);
92+
std::getline(ss, token, ',');
93+
imu.linear_acceleration.z() = std::stod(token);
94+
imus_.push_back(imu);
95+
} catch (const std::exception & e) {
96+
RCLCPP_FATAL(
97+
node_->get_logger(), "failed parse line: [%s], error: %s", line.c_str(), e.what());
98+
return false;
99+
}
100+
}
101+
ifs.close();
102+
return !imus_.empty();
103+
}
104+
105+
} // namespace ssct_common

0 commit comments

Comments
 (0)