3 분 소요

PCL개발 2일차

ros를 이해하기 위한 목적으로 리팩토링을 진행해보았다.

기존코드는 다음과 같다.

1.Code Refactoring

ref1_1.py

#!/usr/bin/env python3

import rospy
import random
import math
from tf.transformations import quaternion_from_euler
from geometry_msgs.msg import PoseStamped


if __name__ == '__main__':
	rospy.init_node("tf_test")
	
	pub = rospy.Publisher("pose", PoseStamped, queue_size=1)
	
	r = rospy.Rate(1)
	while not rospy.is_shutdown():
		
		msg = PoseStamped()
		
		msg.header.stamp = rospy.Time.now()
		msg.header.frame_id = "map"
		
		msg.pose.position.x = random.randint(0, 5)
		msg.pose.position.y = random.randint(0, 5)
		msg.pose.position.z = 0
		
		quat = quaternion_from_euler(
			0., 0., math.radians(random.randint(0, 180)))
			
		msg.pose.orientation.x = quat[0]
		msg.pose.orientation.y = quat[1]
		msg.pose.orientation.z = quat[2]
		msg.pose.orientation.w = quat[3]
		
		pub.publish(msg)
		
		r.sleep()

explanation>

#!/usr/bin/env python3  => 파이썬3 인터프리터로 실행하도록 플래그를 달아줌.(이거 그냥 주석 아님. 필요함ㅇ)

import rospy
import random
import math
from tf.transformations import quaternion_from_euler # tf 변환 라이브러리
from geometry_msgs.msg import PoseStamped            # msg라이브러리에서 PoseStamped라는 메시지타입을 가져옴
if __name__ == '__main__':
    rospy.init_node("tf_test") # "tf_test"라는 이름의 노드를 생성해줌

    pub = rospy.Publisher("pose", PoseStamped, queue_size=1)
    # 퍼블리셔를 생성해줌. 설정은 아래와 같음
    # 메시지타입은 PoseStamped로, 토픽명은 "pose"로, 퍼블리셔의 큐사이즈는 1로 설정

    r = rospy.Rate(1)
    # Rate를 1로 해줬으니, 초당 한번 루프가 돌게됨.
while not rospy.is_shutdown(): # False일때 실행, 즉 rospy가 멈추지 않을때 실행!

        msg = PoseStamped() #메세지타입을 갖는 객체 생성
        
        # 메시지 헤더쪽 설정
        msg.header.stamp = rospy.Time.now()
        msg.header.frame_id = "map"
        
        # 메시지 pose쪽 설정
        msg.pose.position.x = random.randint(0, 5)
        msg.pose.position.y = random.randint(0, 5)
        msg.pose.position.z = 0


        quat = quaternion_from_euler(
            0., 0., math.radians(random.randint(0, 180)))

        msg.pose.orientation.x = quat[0]
        msg.pose.orientation.y = quat[1]
        msg.pose.orientation.z = quat[2]
        msg.pose.orientation.w = quat[3]

        pub.publish(msg)

        r.sleep()

오일러 각도를 쿼터니언으로 변화하는 작업은 쿼터니언이 3D 회전표현에 있어 계산효율성과 안전성을 보장하며, 특히 짐벌락 이슈를 피할 수 있기 때문에 필요하다. 자세한 내용은 해당링크를 참고하여 이해했다.

이제 코드를 아래와 같이 1차리팩토링했다.

ref1_2.py

#!/usr/bin/env python3

import rospy
import random
import math
from tf.transformations import quaternion_from_euler
from geometry_msgs.msg import PoseStamped


def main():
    rospy.init_node("tf_test")

    pub = rospy.Publisher("pose", PoseStamped, queue_size=1)

    r = rospy.Rate(1)
    while not rospy.is_shutdown():

        msg = PoseStamped()

        msg.header.stamp = rospy.Time.now()
        msg.header.frame_id = "map"

        msg.pose.position.x = random.randint(0, 5)
        msg.pose.position.y = random.randint(0, 5)
        msg.pose.position.z = 0

        quat = quaternion_from_euler(
            0., 0., math.radians(random.randint(0, 180)))

        msg.pose.orientation.x = quat[0]
        msg.pose.orientation.y = quat[1]
        msg.pose.orientation.z = quat[2]
        msg.pose.orientation.w = quat[3]

        pub.publish(msg)

        r.sleep()

if __name__ == '__main__':
    main()
  • if __name__ == '__main__': 일단 이 부분 아래에 있던 코드들을 깡그리 main()이라는 하나의 함수로 감싸서 위로 올려버렸다. 이를 통해 엔트리 부분이 비대해져 코드의 가독성이 떨어지는 문제를 해결하였다.

다음과 같이 2차 리팩토링을 진행했다.

ref1_3.py

#!/usr/bin/env python3

import rospy
import random
import math
from tf.transformations import quaternion_from_euler
from geometry_msgs.msg import PoseStamped


def create_pose_stamped():
    """Create a PoseStamped message with random position and orientation."""
    msg = PoseStamped()

    msg.header.stamp = rospy.Time.now()
    msg.header.frame_id = "map"

    msg.pose.position.x = random.randint(0, 5)
    msg.pose.position.y = random.randint(0, 5)
    msg.pose.position.z = 0

    quat = quaternion_from_euler(0., 0., math.radians(random.randint(0, 180)))

    msg.pose.orientation.x = quat[0]
    msg.pose.orientation.y = quat[1]
    msg.pose.orientation.z = quat[2]
    msg.pose.orientation.w = quat[3]

    return msg

def main():
    rospy.init_node("tf_test")

    pub = rospy.Publisher("pose", PoseStamped, queue_size=1)

    r = rospy.Rate(1)
    while not rospy.is_shutdown():
        msg = create_pose_stamped()
        pub.publish(msg)
        r.sleep()

if __name__ == '__main__':
    main()
while not rospy.is_shutdown():
	msg = create_pose_stamped()

이전 코드를 보면 while문 아래에 msg라는 객체를 생성해서 거기다 필요한 것들을 집어넣는 작업을 코드화 시켜주었다. 이것도 가독성이 떨어지는게,, 메세지 안에 header와 pose등을 설정하는 작업을 계속 일일이 반복한다는 느낌을 주기 때문에, 설정을 반복해주는 뉘앙스를 주므로,

def create_pose_stamped():

위와 같이 아예 클래스를 생성하여 일일이 설정하는 작업들은 여기서 하도록 뺴버렸다. 이러니 신경쓰이는게 줄어들었다.

마지막으로 다음과 같이 3차 리팩토링을 진행했다.

ref1_4.py

#!/usr/bin/env python3

import rospy
# import random
# import math
# from tf.transformations import quaternion_from_euler
from geometry_msgs.msg import PoseStamped
from class_test import create_pose_stamped



def main():
    rospy.init_node("tf_test")

    pub = rospy.Publisher("pose", PoseStamped, queue_size=1)

    r = rospy.Rate(1)
    while not rospy.is_shutdown():
        msg = create_pose_stamped()
        pub.publish(msg)
        r.sleep()

if __name__ == '__main__':
    main()

class_test.py

#!/usr/bin/env python3

import rospy
import random
import math
from tf.transformations import quaternion_from_euler
from geometry_msgs.msg import PoseStamped


def create_pose_stamped():
    """Create a PoseStamped message with random position and orientation."""
    msg = PoseStamped()

    msg.header.stamp = rospy.Time.now()
    msg.header.frame_id = "map"

    msg.pose.position.x = random.randint(0, 5)
    msg.pose.position.y = random.randint(0, 5)
    msg.pose.position.z = 0

    quat = quaternion_from_euler(0., 0., math.radians(random.randint(0, 180)))

    msg.pose.orientation.x = quat[0]
    msg.pose.orientation.y = quat[1]
    msg.pose.orientation.z = quat[2]
    msg.pose.orientation.w = quat[3]

    return msg

이는 굳이 안해줘도 되지만, 나중에 코드의 확장성을 고려해서 표현한 것이다. 나중에 코드가 비대해지면, 저 클래스 선언하는 부분들이 넘나 많아지고 이게 신경쓰인다. (쉽게 말해서 그냥 꼴보기가 싫다는 거다. 저거 아니어도 가뜩이나 코드가 복잡한데 말이다) 그래서 파일을 하나더 만들어서 여기서 참조하도록 하는게 더 마음이 편하다.

마지막으로 아래와 같이 분할해서 파일을 실행해보았고, 결과는 문제없이 출력되었다.

# terminal_1
jacksmith@jacksmith-desktop:~$ roscore

# terminal_2
jacksmith@jacksmith-desktop:~/catkin_ws/src/rviz_template/src$ rosrun rviz_template src/ref1_4.py

# terminal_3
jacksmith@jacksmith-desktop:~$ rviz

2.pcl 대회과제로 넘어와서,,

다시 대회과제로 넘어왔다.

brave_ZzF8oDNa3g

위와 같이 설정하는 절차는 다음과 같다.

  1. 세개의 터미널 창을 켜고 아래 명령어를 각각 수행
    # terminal_1
    jacksmith@jacksmith-desktop:~$ roscore
       
    # terminal_2
    jacksmith@jacksmith-desktop:~/Downloads$ rosbag play 2024-07-22-15-13-50.bag --loop
       
    # terminal_3
    jacksmith@jacksmith-desktop:~$ rviz
    
  2. rviz > Global Options > Fixed Frame: os_lidar로 설정

  3. rviz > Add > By topic > /ouster/points/PointCloud2 (물론 이것만 쓸거라서 귀찮으니까 그냥 config_as 해서 rviz로 저장해놓으려고 한다. 설정 자동화!)

3.연구용 패키지를 생성헀다.

catkin_ws/src 아래에 rosbag을 테스트할 목적의 패키지를 생성했다.

# 1.디렉토리 이동
cd ~/catkin_ws/src

# 2.패키지 생성: 특정 패키지에 의존하도록 패키지 생성
# format
catkin_create_pkg [생성할 패키지명] [의존성패키지1] [의존성패키지2] , ...
# me
catkin_create_pkg rosbag_test_pkg std_msgs rospy rosbag

# 3.패키지 빌드: 패키지의 코드를 실행할수있는 파일로 변형하기 위해, 작업공간을 다시 빌드
cd ~/catkin_ws
catkin_make

# 4.패키지 사용: 변형한 파일을 실행기가 참조할 수 있게 하기 위해, 환경변수 설정
source devel/setup.bash

이후 터미널을 분할실행하여, 원하는 출력을 얻을 수 있었다.

# terminal_1
jacksmith@jacksmith-desktop:~$ roscore

# terminal_2
jacksmith@jacksmith-desktop:~/catkin_ws/src/rosbag_test_pkg/src$ rosrun rosbag_test_pkg src/play_bag.py

# terminal_3
jacksmith@jacksmith-desktop:~$ rviz

우분투 터미널에서 사용할 수 있는 특이한 단축키를 찾았다.

터미널창에서 작동하는 특이한 단축키: ctrl + shift + z

창하나를 클릭하고 위 단축키를 누르면 커지게 할 수 있다.

전>

brave_oeyVh2z5Wy

후>

brave_ZqUBLARaKE

댓글남기기