https://cafe.naver.com/openrt/24450
보통 처음엔 Hello world를 출력해 본다.
ROS에서는 메시지를 전송해 보는 것이 그 역할이다.
ROS 2 패키지 생성 명령어는 다음과 같다.(참고로, 예전에 만들었던 workspace에서 실행해야 한다. 나의 경우 ros2_ws)
ros2 pkg create [패키지이름] --build-type [빌드 타입] --dependencies [의존하는패키지1][의존하는패키지n]
$ cd ~/ros2_ws/src/
Python
$ ros2 pkg create my_first_ros_rclpy_pkg --build-type ament_python --dependencies rclpy std_msgs
의존하는 패키지 1: rclpy(ROS에서 Python을 사용하기 위한 클라이언트 라이브러리)
의존하는 패키지 2: std_msgs(ROS의 표준 메시지 패키지)
C++
$ ros2 pkg create my_first_ros_rclcpp_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
의존하는 패키지 1: rclcpp(ROS에서 C++를 사용하기 위한 클라이언트 라이브러리)
의존하는 패키지 2: std_msgs
3.1 패키지 설정 파일 (package.xml)
들어가기 전, .xml파일에 대한 간략히 알아보자(잘 모른다)
.xml파일은 엑셀, .json파일과 비슷한 개념이다. 데이터를 쉽게 구조화해 전달하기 위한 문서 종류이다.
<태그이름 속성1="값" 속성2="값"... >내용</태그이름>
과 같은 구조로 이루어진다.
주석을 달고 싶다면
(velog가 html문법을 따르기 때문에 저것도 주석 처리가 되어 표시가 안 돼서 사진으로 넣었다..)
Python
ros2_ws의 my_first_ros_rclpy_pkg에 들어가서 package.xml파일을 찾으면 나의 경우 다음과 같은 내용을 확인할 수 있었다.
(오로카 포스팅에서는 작성하라고 했는데, 이미 생성되어 있었다. 이름, 메일 주소 등 개인정보를 제외하고는 내 파일 내용과 거의 다르지 않아서 일단 바꾸지 않기로 했다)
7번 실행에서 에러가 나서, 결국 오로카 포스팅대로 바꾸었다.
원래 내 파일 내용
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_first_ros_rclpy_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hcw@todo.todo">hcw</maintainer>
<license>TODO: License declaration</license>
<depend>rclpy</depend>
<depend>std_msgs</depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
오로카 포스팅 참고해서 수정
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_first_ros_rclpy_pkg</name>
<version>0.0.2</version>
<description>ROS 2 rclpy basic package for the ROS 2 seminar</description>
<maintainer email="hcw@todo.todo">hcw</maintainer>
<license>Apache License 2.0</license>
<author email="mikael@osrfoundation.org">Mikael Arguedas</author>
<author email="hcw@todo.todo">hcw</author>
<depend>rclpy</depend>
<depend>std_msgs</depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
C++
원래 내 파일 내용
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_first_ros_rclcpp_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hcw@todo.todo">hcw</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
오로카 포스팅 참고해서 수정
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_first_ros_rclcpp_pkg</name>
<version>0.0.1</version>
<description>ROS 2 rclcpp basic package for the ROS 2 seminar</description>
<maintainer email="hcw@todo.todo">hcw</maintainer>
<license>Apache License 2.0</license>
<author>Mikael Arguedas</author>
<author>Morgan Quigley</author>
<author email="jacob@openrobotics.org">Jacob Perron</author>
<author email="hcw@todo.todo">hcw</author>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
3.2 <파이썬> 패키지 설정 파일 (setup.py)
원래 내 파일 내용
from setuptools import setup
package_name = 'my_first_ros_rclpy_pkg'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='hcw',
maintainer_email='hcw@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
],
},
)
오로카 포스팅 참고해서 수정
from setuptools import find_packages
from setuptools import setup
package_name = 'my_first_ros_rclpy_pkg'
setup(
name=package_name,
~~//버전은 package.xml과 같이 0.0.0으로 남겨두기로 했다.~~
//아니..그냥 0.0.2로 바꾸기로 했다.
version='0.0.2',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
//author부분은 일단 필요없어 보여 삭제하기로 했다.
//author='Mikael Arguedas, Pyo',
//author_email='mikael@osrfoundation.org, pyo@robotis.com',
maintainer='hcw',
maintainer_email='hcw@todo.todo',
keywords=['ROS'],
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Topic :: Software Development',
],
description='ROS 2 rclpy basic package for the ROS 2 seminar',
~~//license는 내 것으로 바꿔 주었다~~
//아니..원래 것으로 바꿔라. 이게 오류의 원인이었던 듯
license='Apache License, Version 2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'helloworld_publisher = my_first_ros_rclpy_pkg.helloworld_publisher:main',
'helloworld_subscriber = my_first_ros_rclpy_pkg.helloworld_subscriber:main',
],
},
)
뭔가 마음대로 바꾼 것 같은데 이게 어떤 문제를 초래할지는 모르겠다..
test package니까 이 정도 변동은 괜찮겠지.
음...역시 마음대로 하면 문제가 생긴다.
3.2 <C++> 빌드 설정 파일(CMakeLists.txt)
원래 내 파일 내용
cmake_minimum_required(VERSION 3.5)
project(my_first_ros_rclcpp_pkg)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
오로카 포스팅 참고해서 수정
# Set minimum required version of cmake, project name and compile options
cmake_minimum_required(VERSION 3.5)
project(my_first_ros_rclcpp_pkg)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# Find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
# Build
add_executable(helloworld_publisher src/helloworld_publisher.cpp)
ament_target_dependencies(helloworld_publisher rclcpp std_msgs)
add_executable(helloworld_subscriber src/helloworld_subscriber.cpp)
ament_target_dependencies(helloworld_subscriber rclcpp std_msgs)
# Install
install(TARGETS
helloworld_publisher
helloworld_subscriber
DESTINATION lib/${PROJECT_NAME})
# Test
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
# Macro for ament package
ament_package()
3.3 <파이썬> 패키지 환경 설정 파일 (setup.cfg)
이 파일은 오로카 포스팅과 내 파일이 완전히 똑같다. 수정할 필요 없다.
[develop]
script-dir=$base/lib/my_first_ros_rclpy_pkg
[install]
install-scripts=$base/lib/my_first_ros_rclpy_pkg
Python
~/ros2_ws/src/my_first_ros_rclpy_pkg/my_first_ros_rclpy_pkg/
폴더에
helloworld_publisher.py
라는 이름으로 소스 코드 파일을 직접 생성하여 넣어주자.
터미널에서 해당 폴더로 이동한 후 gedit helloworld_publisher.py를 통해 생성했다.
$ cd ~/ros2_ws/src/my_first_ros_rclpy_pkg/my_first_ros_rclpy_pkg/
$ gedit helloworld_publisher.py
파일 내용
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile
from std_msgs.msg import String
class HelloworldPublisher(Node):
def __init__(self):
super().__init__('helloworld_publisher')
qos_profile = QoSProfile(depth=10)
self.helloworld_publisher = self.create_publisher(String, 'helloworld', qos_profile)
self.timer = self.create_timer(1, self.publish_helloworld_msg)
self.count = 0
def publish_helloworld_msg(self):
msg = String()
msg.data = 'Hello World: {0}'.format(self.count)
self.helloworld_publisher.publish(msg)
self.get_logger().info('Published message: {0}'.format(msg.data))
self.count += 1
def main(args=None):
rclpy.init(args=args)
node = HelloworldPublisher()
try:
rclpy.spin(node)
except KeyboardInterrupt:
node.get_logger().info('Keyboard Interrupt (SIGINT)')
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
나중에 포스팅에 있는 코드 설명을 보고 코드를 적는 것으로 복습을 해 보자.
C++
~/ros2_ws/src/my_first_ros_rclcpp_pkg/src/
폴더에
helloworld_publisher.cpp
라는 이름으로 소스 코드 파일을 직접 생성하여 넣어주자.
$ cd ~/ros2_ws/src/my_first_ros_rclcpp_pkg/src/
$ gedit helloworld_publisher.cpp
파일 내용
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
class HelloworldPublisher : public rclcpp::Node
{
public:
HelloworldPublisher()
: Node("helloworld_publisher"), count_(0)
{
auto qos_profile = rclcpp::QoS(rclcpp::KeepLast(10));
helloworld_publisher_ = this->create_publisher<std_msgs::msg::String>(
"helloworld", qos_profile);
timer_ = this->create_wall_timer(
1s, std::bind(&HelloworldPublisher::publish_helloworld_msg, this));
}
private:
void publish_helloworld_msg()
{
auto msg = std_msgs::msg::String();
msg.data = "Hello World: " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Published message: '%s'", msg.data.c_str());
helloworld_publisher_->publish(msg);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr helloworld_publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<HelloworldPublisher>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
Python
$ cd ~/ros2_ws/src/my_first_ros_rclpy_pkg/my_first_ros_rclpy_pkg/
$ gedit helloworld_subscriber.py
파일 내용
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile
from std_msgs.msg import String
class HelloworldSubscriber(Node):
def __init__(self):
super().__init__('Helloworld_subscriber')
qos_profile = QoSProfile(depth=10)
self.helloworld_subscriber = self.create_subscription(
String,
'helloworld',
self.subscribe_topic_message,
qos_profile)
def subscribe_topic_message(self, msg):
self.get_logger().info('Received message: {0}'.format(msg.data))
def main(args=None):
rclpy.init(args=args)
node = HelloworldSubscriber()
try:
rclpy.spin(node)
except KeyboardInterrupt:
node.get_logger().info('Keyboard Interrupt (SIGINT)')
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
C++
$ cd ~/ros2_ws/src/my_first_ros_rclcpp_pkg/src/
$ gedit helloworld_subscriber.cpp
파일 내용
#include <functional>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
class HelloworldSubscriber : public rclcpp::Node
{
public:
HelloworldSubscriber()
: Node("Helloworld_subscriber")
{
auto qos_profile = rclcpp::QoS(rclcpp::KeepLast(10));
helloworld_subscriber_ = this->create_subscription<std_msgs::msg::String>(
"helloworld",
qos_profile,
std::bind(&HelloworldSubscriber::subscribe_topic_message, this, _1));
}
private:
void subscribe_topic_message(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "Received message: '%s'", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr helloworld_subscriber_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<HelloworldSubscriber>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
//workspace내 모든 패키지 빌드
$ cd ~/ros2_ws && colcon build --symlink-install
//특정 패키지 빌드
$ cd ~/ros2_ws && colcon build --symlink-install --packages-select [패키지 이름1] [패키지 이름2] [패키지 이름N]
//특정 패키지 및 의존성 패키지를 함께 빌드
$ cd ~/ros2_ws && colcon build --symlink-install --packages-up-to [패키지 이름]
Python
my_first_ros_rclpy_pkg 패키지만 빌드해 보자.
특정 패키지의 첫 빌드 때는 빌드 후 실행 가능한 패키지의 노드 설정들을 해줘야 빌드된 노드를 실행할 수 있다.
$ cd ~/ros2_ws
$ colcon build --symlink-install --packages-select my_first_ros_rclpy_pkg
$ . ~/ros2_ws/install/local_setup.bash
처음에 이런 에러가 나서, 3번으로 돌아가 파일 내용을 수정해 주었다.
그랬더니 잘 되었다.
C++
my_first_ros_rclcpp_pkg 패키지만 빌드해 보자.
$ cd ~/ros2_ws
$ colcon build --symlink-install --packages-select my_first_ros_rclcpp_pkg
$ . ~/ros2_ws/install/local_setup.bash
Python
$ ros2 run my_first_ros_rclpy_pkg helloworld_subscriber
$ ros2 run my_first_ros_rclpy_pkg helloworld_publisher
C++
$ ros2 run my_first_ros_rclcpp_pkg helloworld_subscriber
$ ros2 run my_first_ros_rclcpp_pkg helloworld_publisher
terminal에서 6, 7을 통한 빌드와 실행은 잘 되는데,
VScode에서 helloworld_subscriber.cpp 코드 실행을 해 보면 include에서부터 에러가 난다.
따배씨에서 했던 걸 생각해 보면 hpp파일이 같은 프로젝트 폴더 내에 있었던 것 같기는 하다.
같은 폴더 내에 저 hpp파일이 없어서 에러가 나는 건가.
나는 아직도 IDE라는 것을 잘 모르겠다..하핫
일단 실행은 그냥 terminal로 시켜보는 걸로 하고,
VScode는 코드 뷰와 주석 달기를 편리하게 하는 용도로 일단 사용하자^^
천리길도 한 걸음부터........