한이음/GPS 좌표 트래킹을 통한 자율주행 로봇

ROS에서 Arduino로 값 전달 (ROS (PUB)-> Arduino (SUB))

MZ 아이종 2022. 2. 2. 22:21

 

ROS끼리의 Publish, Subscribe는 Custom message로도 쉽게 가능하지만, Arduino와의 통신은 Custom message를 사용할 경우 번거로운 점이 많다. 

그래서 geometry_msgs/Twist message를 이용해 ROS에서 Adruino로 값을 전달해 보려고 한다.

코드 자체는 그대로 두고 message 파일만 변경해서 ROS - Arduino rosserial통신을 시도한다.

 

 

사용버전. 노트북. 아두이노. 모터 드라이버. 모터

˙ Samsung Ultrabook i7

˙ Ubuntu 18.04.6 LTS

˙ Ros Melodic

˙ Arduino Mega 2560

˙ L298N

˙ RB35GM 21TYPE 1/100 DC 12V 

단계

1. geometry_msgs/Twist

2. package.xml 수정

3. publisher 파일 수정

4. subscriber 파일 수정

5. CMakeLists.txt 파일 수정

6. publish, subscribe 확인

7. H/W 연결

8. arduino 코드 작성

9. 실행

 

1. geometry_msgs/Twist

geometry_msgs는 linear와 angular로 구성되어 있다.

각각의 변수를 본인의 필요에 맞게 사용해도 되지만 보통의 경우 선속도 / 각속도 등의 정보에 사용된다.

작성자의 경우에는 motor를 동작 시켜보기위해 값을 전달해 주는 용도로 사용해 보겠다.

 

 

아래의 Vector3를 클릭하면 위와같은 화면이 나오는데, 구조는 아래와 같다고 생각하면 이해가 쉽다.

struct Twist
{
	float64 x;
	float64 y;
	float64 z;
};

 

 

2. package.xml 수정

이전 포스팅에서 작성한 package.xml과 비교했을 때 custom message를 사용하지 않고, std_msgs가 아닌 geometry_msgs를 사용하는것이 차이점이다. 이를생각해서 package.xml 파일을 수정해 보자.

 

#원본
<?xml version="1.0"?>
<package format="2">
  <name>basic</name>
  <version>0.0.0</version>
  <description>The basic package</description>

  <maintainer email="8ajs0114@gmail.com">js</maintainer>

  <license>BSD</license>

  <url type="website">http://wiki.ros.org/basic</url>

  <author email="8ajs0114@gmail.com">Jane Doe</author>

  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>

  <export>

  </export>
</package>

#수정본
<?xml version="1.0"?>
<package format="2">
  <name>basic</name>
  <version>0.0.0</version>
  <description>The basic package</description>

  <maintainer email="8ajs0114@gmail.com">js</maintainer>

  <license>BSD</license>

  <url type="website">http://wiki.ros.org/basic</url>

  <author email="8ajs0114@gmail.com">Jane Doe</author>

  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>geometry_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>geometry_msgs</build_export_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>geometry_msgs</exec_depend>

  <export>

  </export>
</package>

작성자에 대한 정보는 수정하지 않고, build 쪽 내용만 수정해 주면 된다.

 

 

3. publisher 파일 수정

publish와 subscribe 파일도 message를 수정해 주어야 한다.

message를 수정할 때는 message 변수들의 형식을 맞춰주는것이 중요하다. 변수는 linear.x를 사용한다.

//원본
#include <ros/ros.h> 
#include "basic/basic_msg.h" //패키지이름 / 메세지파일.h 

int main(int argc, char **argv) //main 함수의 원문
{
    ros::init(argc, argv, "basic_pub"); //초기화 "사용할 node 이름"
    ros::NodeHandle nh; //ROS시스템과 통신을 위한 노드핸들 선언
 
    ros::Publisher pub = nh.advertise<basic::basic_msg>("basic_topic",1000); 
    //nh.advertise<message파일 포함 package 이름 :: message파일 이름>("사용할 topic 이름",que 개수);
    
    ros::Rate loop_rate(20); //루프 주기를 20Hz로 설정 (1초에 20번)
    
    basic::basic_msg msg; //(message파일 포함 package 이름)::(message파일 이름) (사용하려는 변수명); 
    
    int count = 0;
 
    while(ros::ok()) //ros 가 활성화되면
    {
	for(count <= 0; count < 255 ;count++)
	{   
	    msg.data = count; //count 변수 값을 msg 하위 data 메세지에 담는다.
	    ROS_INFO("send msg = %d", msg.data);
	 
	    pub.publish(msg);
	    loop_rate.sleep();
	}

	for(count >= 255; count > 0 ;count--)
	{   
	    msg.data = count; //count 변수 값을 msg 하위 data 메세지에 담는다.
	    ROS_INFO("send msg = %d", msg.data);
	 
	    pub.publish(msg);
	    loop_rate.sleep();
	}
    }
    return 0;
}

//수정본
#include <ros/ros.h> 
#include <geometry_msgs/Twist.h> //패키지이름 / 메세지파일.h 

int main(int argc, char **argv) //main 함수의 원문
{
    ros::init(argc, argv, "basic_pub"); //초기화 "사용할 node 이름"
    ros::NodeHandle nh; //ROS시스템과 통신을 위한 노드핸들 선언
 
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("basic_topic",1000); 
    //nh.advertise<message파일 포함 package 이름 :: message파일 이름>("사용할 topic 이름",que 개수);
    
    ros::Rate loop_rate(20); //루프 주기를 20Hz로 설정 (1초에 20번)
    
    geometry_msgs::Twist msg; //(message파일 포함 package 이름)::(message파일 이름) (사용하려는 변수명); 
    
    float count = 0;
 
    while(ros::ok()) //ros 가 활성화되면
    {
	for(count <= 0; count < 255 ;count++)
	{   
	    msg.linear.x = count; //count 변수 값을 msg 하위 data 메세지에 담는다.
	    ROS_INFO("send msg = %f", msg.linear.x);
	 
	    pub.publish(msg);
	    loop_rate.sleep();
	}

	for(count >= 255; count > 0 ;count--)
	{   
	    msg.linear.x = count; //count 변수 값을 msg 하위 data 메세지에 담는다.
	    ROS_INFO("send msg = %f", msg.linear.x);
	 
	    pub.publish(msg);
	    loop_rate.sleep();
	}
    }
    return 0;
}

 

 

4. subscriber 파일 수정

publish 파일과 동일하게 subscribe 파일도 수정해 준다. 

//원본
#include <ros/ros.h>
#include "basic/basic_msg.h"

void msgCallback(const basic::basic_msg &msg)  //(const message파일 포함 package 이름:: message파일 이름 &사용하려는 변수명)
{
    ROS_INFO("receive msg = %d", msg.data);
}
 
int main(int argc, char **argv)
{
    ros::init(argc, argv, "basic_sub"); //pub 쪽 node와 이름이 같지 않도록 주의 
    ros::NodeHandle nh;
    
    ros::Subscriber sub = nh.subscribe("basic_topic",100,msgCallback); //pub쪽 topic과 이름이 같도록 주의  
    ros::spin(); //어떤 값이 들어오기 전까지 대기 (다시 위로 올라감)
    return 0;
}

//수정본
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

void msgCallback(const geometry_msgs::Twist &msg)  //(const message파일 포함 package 이름:: message파일 이름 &사용하려는 변수명)
{
    ROS_INFO("receive msg = %f", msg.linear.x);
}
 
int main(int argc, char **argv)
{
    ros::init(argc, argv, "basic_sub"); //pub 쪽 node와 이름이 같지 않도록 주의 
    ros::NodeHandle nh;
    
    ros::Subscriber sub = nh.subscribe("basic_topic",100,msgCallback); //pub쪽 topic과 이름이 같도록 주의  
    ros::spin(); //어떤 값이 들어오기 전까지 대기 (다시 위로 올라감)
    return 0;
}

 

 

5. CMakeLists.txt 파일 수정

CMakeLists.txt 파일의 경우 message 부분만 수정해 주면 된다. 

add_message_files의 경우 msg파일의 message 파일을 사용하지 않으니 지워줘도 된다.

각각의 경우에서 std_msgs은 사용하지 않으니 geometry_msgs으로 변경해 준다.

#원본
cmake_minimum_required(VERSION 3.0.2)
project(basic)

find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  std_msgs
)

add_message_files(
   FILES
   basic_msg.msg
)

 generate_messages(
   DEPENDENCIES
   std_msgs
 )

catkin_package(
  LIBRARIES basic
  CATKIN_DEPENDS roscpp std_msgs
)


include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)

add_executable(basic_pub src/basic_pub.cpp)
add_executable(basic_sub src/basic_sub.cpp)

add_dependencies(basic_pub ${basic_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(basic_sub ${basic_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(basic_pub
   ${catkin_LIBRARIES}
 )
target_link_libraries(basic_sub
   ${catkin_LIBRARIES}
 )
 
#수정본
cmake_minimum_required(VERSION 3.0.2)
project(basic)

find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  geometry_msgs
)

 generate_messages(
   DEPENDENCIES
   geometry_msgs
 )

catkin_package(
  LIBRARIES basic
  CATKIN_DEPENDS roscpp geometry_msgs
)

include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)

add_executable(basic_pub src/basic_pub.cpp)
add_executable(basic_sub src/basic_sub.cpp)

add_dependencies(basic_pub ${basic_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(basic_sub ${basic_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(basic_pub
   ${catkin_LIBRARIES}
 )
target_link_libraries(basic_sub
   ${catkin_LIBRARIES}
 )

 

 

6. publish, subscribe 확인

수정을 마쳤으면 make 후 roscore를 실행해 동작을 확인한다.

$ cd ~/catkin_ws && catkin_make #원래 입력해야할 명령어
$ cm #cs나 cw같이 ROS 환경설정 시에 단축어로 설정해 두었으면 입력하면 정상작동 한다.
     #소스를 컴파일 하는 과정이라고 생각하면 된다. 오류가 있다면 오류의 위치와 내용을 알려준다.
     #자주 사용하므로 cs, cw, cm 3가지는 설정해 두는것이 좋다.

# ================================================================================

#1st Terminal
$ roscore

#2nd Terminal
$ rosrun basic basic_pub

#3rd Terminal
$ rosrun basic basic_sub

#rosrun의 경우 package의 이름 node의 이름을 차례로 입력해 준다.
#생성한 publish 파일과 subscribe 파일이 각각 publisher, subscriber node가 된다.

roscore / rosrun basic basic_pub / rosrun basic basic_sub

 

 

7. H/W 연결

- 7.1 RB35GM 21TYPE 1/100 DC 12V

6개 색의 선이 존재한다. 

빨간색 : Motor VCC

검은색 : Motor GND

갈색 : Encoder VCC

초록색 : Encoder GND

파란색 : Encoder B

보라색 : Encoder A

 

 

- 7.2 L298N

 

2개의 모터를 동시에 제어할 수 있는 모터 드라이버이다.

모터에서 Motor VCC, GND는 드라이버 좌우에 하나씩 있는 핀에 연결하는데, 연결하는 방법에 따라 모터 회전 방향이 바뀐다. 

최대 24V까지 입력이 가능하며, 정면 왼쪽에 있는 3개의 핀 중 VCC, GND에 각각 연결한다.

정면 오른쪽에 10개의 핀 중 ENA, IN1, IN2를 통해 소프트웨어적으로 모터의 회전방향을 결정한다. ENB, IN3, IN4를 통해 오른쪽에 연결된 모터를 제어할 수 있다.

 

 

- 7.3 연결

ARDUINO <-> MOTOR / DRIVER

MEGA 2560 5V GND 3 4 8 9 10
MOTOR ENCODER
VCC
ENCODER
GND
ENCODER A ENCODER B      
DRIVER         IN2 IN1 ENA

 

POWER SUPPLY <-> DRIVER <-> MOTOR

POWER SUPPLY VCC GND    
MOTOR     MOTOR VCC MOTOR GND
DRIVER VCC GND OUT 1 OUT 2

 

8. arduino 코드 작성

먼저 Arduino를 PC와 연결한 뒤 Arduino를 실행하고 권한을 부여한다.

$ arduino #Arduino 실행
$ sudo chmod a+rw /dev/ttyACM0 #권한 부여 / 작성자의 경우 사용 포트가 ACM0

 

Publish된 값을 받아서 모터를 동작하는 arduino 코드는 아래와 같다.

기본적으로 ROS Subscribe 코드와 비슷하지만 arduino 형식에 맞춰서 작성되었다.

//ros
#include <Arduino.h>
#include <ros.h>
#include <geometry_msgs/Twist.h>

//motor
#define ENABLE_A  10  
#define IN1_A  9   
#define IN2_A  8   
 
ros::NodeHandle  nh;

void MOTOR( const geometry_msgs::Twist& msg)
{
  digitalWrite(IN1_A, HIGH);  
  digitalWrite(IN2_A, LOW);  
  analogWrite(ENABLE_A, msg.linear.x); 
}

ros::Subscriber<geometry_msgs::Twist> sub("basic_topic", &MOTOR);
//topic의 이름은 publisher의 topic이름과 동일하도록 주의한다.

void setup()
{
  pinMode(IN1_A, OUTPUT); // 방향1
  pinMode(IN2_A, OUTPUT); // 방향2
  pinMode(ENABLE_A, OUTPUT); // 속도

  nh.initNode();
  nh.subscribe(sub);
}

void loop()
{
  nh.spinOnce();
  delay(1);
}

 

 

9. 실행

roscore를 실행하고 publishc하는 부분은 같지만, subscribe 하는 방식이 다르다. 

arduino 코드를 보드에 업로드한 뒤 아래의 명령어를 각각의 터미널에서 실행한다.

#1st Terminal
$ roscore

#2nd Terminal
$ rosrun ros_motor ros_motor_pub

#3rd Terminal
$ rosrun rosserial_python serial_node.py _port:=/dev/ttyACM0 _baud:=57600

 

roscore / rosrun basic basic_pub / arduino subscribe

 

모터가 구동됨을 확인하여 rosserial 통신을 확인할 수 있었다.

 

'한이음 > GPS 좌표 트래킹을 통한 자율주행 로봇' 카테고리의 다른 글

OpenCR 사용하기  (0) 2022.02.05
IMU (Myahrs+) 사용하기  (0) 2022.02.03
ROS package 생성  (2) 2022.02.01
Chrome 다운  (0) 2022.01.19
Arduino 설치  (0) 2022.01.19