우분투(Ubuntu) 18.04 한글 키보드 설치

|

iBus 기반 한글 키보드 설치

Ubuntu 18.04 LTS 버전 기준으로 한글 키보드를 설치하는 방법입니다. Ubuntu 18.04에서 한글어 입력기는 다시 ibus가 되었기 때문에 14.04나 16.04 때와는 다릅니다.

  • 메뉴에서 Language Support 실행 → 필요한 파일들 자동으로 설치 됨
  • 메뉴에서 Region & Language 실행
  • Input Sources 항목에서 기본으로 잡혀있던 English는 삭제하고, Korean(Hangul) 선택
  • 아래에 있는 설정 버튼 클릭
  • Hangul Toggle KeyAdd 버튼을 누르고 한글 키 입력(ALT_R 로 표시될 것임)


UIM 기반 한글 설정

한글 설정

Ubuntu 18.04 LTS의 한글 입력기는 iBus로 되어 있습니다. 하지만, Sublime Text나 Visual Studio Code 등의 프로그램에서 한글 입력이 되지 않는 문제가 있어서 방법을 찾아보니 UIM을 이용하면 상당부분 해소가 되는 것 같았습니다. 물론 UIM도 완벽하지는 않은 것 같습니다. Visual Studio Code에서 등의 글자 입력이 잘 안되는 경우가 있네요. Sublime이나 IntelliJ 등에서는 문제없이 동작하네요.

설치는 다음과 같습니다.

sudo apt install uim


  • Settings > Region & Language에서 Manage Installed Language 버튼 클릭.
  • 입력기를 UIM으로 변경
  • 재부팅

재부팅 후, 프로그램 메뉴에서 Input Method 실행(UIM이라고 타이핑해도 실행됩니다.)

  • Global Settings에서 Specify default IM 체크
  • Global Settings > Default Input methodByeoru로 선택
  • ToolbarDisplayNever로 설정
  • Global key bindings 1의 상단의 [Global] on[Global] off 항목을 빈 칸으로 설정
  • BVyeoru key bindings 1[Byeoru] on[Byeoru] off 키 설정을 Multi_key로 설정

chrono를 활용한 시간 측정

|

chrono 예제

#include 
#include 

using namespace std;
using namespace chrono;

const int MAX = 200000000;

long long a[MAX];

int main() {
  cout << "init ... " << endl;
  for (int i = 0; i < MAX; i++) {
    a[i] = i;
  }

  cout << "running... " << endl;

  system_clock::time_point start = system_clock::now();
  
  for (int i = 0; i < MAX; i++) {
    a[i] = a[i] * a[i];
  }
  system_clock::time_point end = system_clock::now();
  duration sec = end - start;

  cout << "Time: " << duration_cast<std::chrono::milliseconds>(sec).count() << endl;

  return 0;
}
</pre>

클래스와 구조체 차이

|

클래스와 구조체 차이

C++에서는 클래스(class)도 구조체(struct)도 모두 상속이 가능합니다. 또한 구조체를 상속한 클래스도 구현을 할 수 있고, 그 반대도 가능합니다.

클래스와 구조체의 차이를 굳이 찾으면 다음과 같습니다.

  • 클래스 멤버 변수는 기본적으로 private이지만, 구조체는 public 입니다.
  • template<class T>는 가능하지만, template<struct T>는 가능하지 않습니다. 다만, 키워드적으로 지원하지 않는 것이기 때문에 struct 대신 typename을 사용하면(ex. template<typename T>) 템플릿을 사용할 수 있습니다.

그 외에는 크게 차이가 없지만, 특정 목적에 따라 명시적으로 구분해서 사용하기도 합니다.

  • 생성자와 소멸자가 없는 데이터 타입(POD, Plain Old Data)에는 구조체를 사용
  • 메소드가 아닌 멤버 변수 위주로 사용할 때는 구조체 사용


POD

POD(Plain Old Data)는 C++11 부터는 표준 레이아웃(Standard layout) 또는 평범한 클래스(Trivial Class)라는 이름으로 대체되었습니다. 하지만 관행적으로 POD라는 용어가 쓰이고 있습니다.

POD는 C++98에서 정의된 용어로 C언어에서 사용되는 평범한 데이터 타입을 의미합니다.

Ros2 Custom Message And Service

|

layout: post title: ROS2 커스텀 Message 및 Service category: ROS2 tag: [ROS, Python] —

ROS2 커스텀 Message 및 Service

ROS2에서 사용자 정의 Message와 Service를 정의하는 방법입니다.


package.xml

<?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>tree_service</name>
  <version>0.4.0</version>
  <description>Message and Service of Tree-Service</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  
  <build_depend>builtin_interfaces</build_depend>
  <build_depend>rosidl_default_generators</build_depend>
  
  <exec_depend>builtin_interfaces</exec_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>

  <member_of_group>rosidl_interface_packages</member_of_group>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(tree_service)

if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
endif()

find_package(ament_cmake REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rosidl_default_generators REQUIRED)

set(msg_files
    "msg/TreeEvent.msg"
    "msg/TreeData.msg"
    "msg/TreeNode.msg"
)

set(srv_files
    "srv/Tree.srv"
)

rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
  ${srv_files}
  DEPENDENCIES builtin_interfaces
)

ament_package()


Message는 ‘msg’라는 서브 디렉토리 밑에, Service는 ‘srv’라는 서브 디렉토리 밑에 위치시킵니다. (위 CMakeLists.txt 파일 참고)

각각의 예제 파일들은 다음과 같습니다. 상단에 상수값을 정의하면 나중에 C++이나 Python에서 해당 메시지나 서비스를 사용할 때 해당 상수값을 이용할 수 있습니다. TOPIC_NAME이라던지 이벤트 ID 등을 상수로 정의하면 편리합니다.


msg/TreeNode.msg

string NAME = "TreeNode"

string id
string parent_id
string type
string name


srv/TreeData

string NAME = "TreeData"
int64 REQUEST_TO_PUBLISH_TREE_DATA = 1

int64 request_id
---
int64 request_id
int64 response

Python Simple Service Recevier

|

layout: post title: ROS2 Simple Service Receiver on Python category: ROS2 tag: [ROS, Python] —

ROS2 Simple Service Receiver on Python

간단한 ROS2 Service를 수신하고, 커스텀 메시지 배열을 Publish 하는 예제입니다.


from simple_service.srv import SimpleService
from simple_service.msg import Position
from simple_service.msg import RoutingData

import rclpy

global g_publisher


def add_position(positions, x, y, theta):
    pos = Position()
    pos.x = x;
    pos.y = y;
    pos.theta = theta
    positions.append(pos)


def callback(request, response):
    request_id = request.request_id
    print("Simple Service Request: {0} ".format(request_id))

    if request_id == Navigation.Request.REQUEST_DEMO_MOVE_PATH_1:
        msg = RoutingData()
        msg.command = RoutingData.COMMAND_MOVE_ALONG_PATH

        add_position(msg.positions, 100, 100, 10)
        add_position(msg.positions, 500, 200, 10)
        add_position(msg.positions, 1000, 1000, 1)

        g_publisher.publish(msg)

    elif request_id == Navigation.Request.REQUEST_DEMO_MOVE_PATH_2:
        msg = RoutingData()
        msg.command = RoutingData.COMMAND_MOVE_ALONG_PATH

        add_position(msg.positions, -100, -100, 10)
        add_position(msg.positions, -500, -200, 10)
        add_position(msg.positions, -1000, -1000, 1)

        g_publisher.publish(msg)

    response.response = 200

    return response


def main(args=None):
    global g_publisher

    rclpy.init(args=args)

    node = rclpy.create_node('simple_service_receiver')
    g_publisher = node.create_publisher(RoutingData, RoutingData.NAME)

    srv = node.create_service(SimpleService, SimpleService.Request.NAME, callback)

    rclpy.spin(node)
    rclpy.shutdown()


if __name__ == '__main__':
    main()