ROS에서 Arduino로 값 전달 (ROS (PUB)-> Arduino (SUB))
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가 된다.
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
모터가 구동됨을 확인하여 rosserial 통신을 확인할 수 있었다.