개발환경 & 준비물
- 라즈베리파이3B+
- WS2812 Flexible LED (https://www.devicemart.co.kr/goods/view?no=1328618)
- ubuntu-18.04.4-preinstalled-server-armhf+raspi3.img.xz
- ROS melodic
- arduino mega
우선 라즈베리파이에 우분투와 ROS를 설치해주세요
https://chackhansaram.tistory.com/2 참고
step1. 라즈베리파이에 아두이노 설치
https://www.arduino.cc/en/software 접속 후 linux ARM 32 bits 버전 아두이노IDE zip 파일 다운로드
라즈베리파이 home에서 tool이라는 이름의 새폴더를 생성해주고 방금 받은 zip파일을 압축해제합니다. tool이 아니어도 됩니다.
아래 명령어를 통해 압축을 푼 폴더로 이동합니다
$ cd arduino-1.8.13
그리고 설치를 진행합니다
$ sudo ./install.sh
한글폰트를 설치합니다.
$ sudo apt install fonts-nanum-coding
$ sudo apt-get update
//입력 후 아두이노 재실행
step2. rosserial 설치
터미널창에 다음과 같이 입력해주세요.
sudo apt-get install ros-melodic-rosserial ros-melodic-rosserial-server ros-melodic-rosserial-arduino
sudo apt-get update
아두이노IDE실행 - 툴 - 라이브러리 관리 클릭
Adafruit NeoPixel
rosserial
두가지 라이브러리를 설치해줍니다.
혹시 이미 ros.lib이 설치돼있다면 삭제해주세요.
저는 여기까지 진행했을 때 아두이노에서 rosserial 라이브러리에 있는 아무 예제나 컴파일 하면 다음과 같이 에러가 떴습니다.
In file included from /home/kk/Arduino/libraries/ros_lib/std_msgs/Time.h:7:0,
from /home/kk/Arduino/libraries/ros_lib/ros/node_handle.h:40,
from /home/kk/Arduino/libraries/ros_lib/ros.h:38,
from /home/kk/Arduino/libraries/ros_lib/examples/HelloWorld/HelloWorld.pde:6:
/home/kk/Arduino/libraries/ros_lib/ros/msg.h:40:10: fatal error: cstring: No such file or directory
#include <cstring>
^~~~~~~~~
compilation terminated.
exit status 1
Error compiling for board Arduino Leonardo.
~/Arduino/libraries/ros_lib/ros 이 경로에 있는 msg.h라는 파일을 실행하고
#include <cstring> 을 #include <string.h>로 바꿔주고
std::memcpy() 을 memcpy()로 바꿔주니 아두이노 컴파일 에러가 없어졌습니다
참고 : https://answers.ros.org/question/361930/rosserial-arduino-compilation-error-no-cstring/
step3. 아두이노 - LED 연결
led의 gnd선과 아두이노 보드의 gnd
led의 5V선과 아두이노 보드의 5V
led의 data선과 아두이노 보드의 6번 핀을 연결해줍니다.
참고 : https://blog.naver.com/no1_devicemart/221325255307
터미널 블럭 연결에 대해 :
지금 실행할 코드의 경우 LED에 따로 전원을 입력해주지 않아도 아두이노 보드만 pc와 연결돼있으면 작동합니다.
파워서플라이로 LED 터미널 블럭과 연결하여 전원을 주면 밝기가 더 밝아졌습니다. 그런데 보드판에서 연기가 나길래 사용하지 않았습니다. 파워서플라이와 LED 전원선 사이에 저항과 커패시터를 추가하면 괜찮을 것 같기도 합니다.
step4. 아두이노 코드 컴파일, 업로드
컴파일하기 전에 아두이노 맨 위 툴바에 tool 에서 보드와 포트 설정을 해주세요.
#include <Adafruit_NeoPixel.h>
#include <Arduino.h>
#include <ros.h>
#include <std_msgs/UInt16MultiArray.h>
//루프문에 불켜는걸 콜백에다가 넣어보자 안되면
//문자열 데이터
#include <std_msgs/String.h>
#define rePublishTime 1000 // (ms)
//초기 스위치 값
//버튼핀은 define으로 핀 번호를 설정한 뒤에 boolean newState2 = digitalRead(BUTTON_PIN2);으로 값을 받아오자
//ㄴㄴ 위에처럼 하지 말자
//수동 스위치 개념 버리기 버튼 핀을 실제 버튼으로 입력받을 필요가 없음
// 컨트롤 핀
#define PIN 6
// WS2812B 의 연결된 개수
#define NUMPIXELS 576 //아두이노 uno의 경우 데이터처리 한계 때문에 461까지만 가능
#ifdef AVR
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// 컨트롤 핀
#define PIN 6
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);
// pixels.Color(a, b, c, d) = pixels.Color(Red, Green, Blue, White)
// Red(빨강), Green(초록), Blue(파랑), White(하양) 의 색상을 각 255단계로 제어
const int init_switch = 0;
boolean newState2 = init_switch;
boolean newState3 = init_switch;
boolean newState4 = init_switch;
// boolean newState5 = init_switch; //를 쓰려면 아마 uint32를 써야할거 같은 느낌
// int ledPin = 13; 굳이 정의 안해도 아두이노에서 정해져 있는듯.
//인풋 스위치 변수 지정
boolean oldState2 = HIGH; //풀업 인풋은 HIGH가 끊겨있는거임
boolean oldState3 = HIGH;
boolean oldState4 = HIGH;
// boolean oldState5 = HIGH;
uint32_t b = pixels.Color(0,0,0,255);
uint32_t c = pixels.Color(255,0,0,0);
uint32_t d = pixels.Color(0,0,0,0);
uint32_t e = pixels.Color(0,255,0,0);
uint32_t f = pixels.Color(0,0,255,0);
// int inputcmd[3] = { newState2, newState3, newState4 };
//ros 노드 핸들
ros::NodeHandle nh;
/*
* ROS 콜백 함수
* 스위치 데이터 받기 / 이게 문자열로 명령을 못 받을 수도 있음
* 그리고 서보 블로그 참고 예제에서는 servo함수 자체에 포함돼서 그런지
* 위에서 미리 선언하지 않은 변수를 바로 콜백함수에서 사용함
* cmd를 switch로 바꿔주자
*/
//a_led방식으로 트라이
//void switch_cb( const std_msgs::Byte& switch_msg){
// int i;
//
// for (i=0; i<3; i++)
// {
// inputcmd[i] = switch_msg.data[i];
// digitalWrite(13, HIGH-digitalRead(13));
// }
//}
void switch_cb( const std_msgs::UInt16MultiArray& switch_msg){
newState2 = switch_msg.data[0];
newState3 = switch_msg.data[1];
newState4 = switch_msg.data[2];
// newState5 = switch_msg.data[3];
digitalWrite(13, HIGH-digitalRead(13));
}
// Subscriber switch 메시지 입력과 콜백 함수와 연결
ros::Subscriber<std_msgs::UInt16MultiArray> sub("switch", switch_cb);
//ros::Subscriber<std_msgs::Byte> sub("switch", switch_cb);
// Publisher echo_hello 생성
std_msgs::String str_msg;
ros::Publisher pubTester("echo_hello", &str_msg);
/*
* 초기 설정
*/
void setup() {
//nh.subscribe(sub);
pixels.begin(); // This initializes the NeoPixel library.
pixels.setBrightness(20); // LED 밝기 : 255가 최대 0이 최소 입니다.
pixels.show();
// LED 핀 설정
pinMode(13, OUTPUT);
// 노드 설정
nh.initNode();
//Subscriber 설정
nh.subscribe(sub);
//Publisher 설정
nh.advertise(pubTester);
}
void loop() {
//이전 시간 기억(ms)
static unsigned long prevTime = 0;
//현재 시간 기억(ms)
unsigned long currentTime;
//ROS 콜백 함수 newstate에 명령이 입력된다.
nh.spinOnce();
delay(1);
currentTime = millis();
//publish 시간 체크
if((currentTime - prevTime) >= rePublishTime)
{
str_msg.data = "Hello, This is Testing for ROS Publisher";
//'hello' 전송
pubTester.publish(&str_msg);
//현재 시간 이전 시간으로 기억
prevTime = currentTime;
}
// 하양
if((newState2 == LOW) && (newState3 == LOW) && (newState4 == LOW) ) {
delay(20);
pixels.fill(b,0,NUMPIXELS);
pixels.show();
}
else if((newState2 == LOW) && (oldState2 == HIGH)){
delay(20);
// else if(newState2 == LOW){
// pixels.fill(d,0,NUMPIXELS);
// pixels.show();
// }
}
// // 검정
// if((newState2 == LOW) && (newState3 == LOW)&&(newState4 == LOW) && (newState5 == HIGH) ) {
//
//
// delay(20);
//
//
// pixels.fill(d,0,NUMPIXELS);
// pixels.show();
//
// }
// else if((newState5 == LOW) && (oldState5 == HIGH)){
// //GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
// delay(20);
// if(newState5 == LOW){
// pixels.fill(d,0,NUMPIXELS);
//
// }
// }
// 빨강
if((newState2 == HIGH) && (newState3 == LOW)&&(newState4 == LOW) ) {
delay(20);
pixels.fill(c,0,NUMPIXELS);
pixels.show();
}
else if((newState2 == LOW) && (oldState2 == HIGH)){
//GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
delay(20);
if(newState2 == LOW){
pixels.fill(d,0,NUMPIXELS);
}
}
//초록
if((newState3 == HIGH)&& (newState2 == LOW)&&(newState4 == LOW)) {
// Short delay to debounce button. HZ to GND
delay(20);
pixels.fill(e,0,NUMPIXELS);
pixels.show();
}
else if((newState3 == LOW) && (oldState3 == HIGH)){
//GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
delay(20);
if(newState3 == LOW){
pixels.fill(d,0,NUMPIXELS);
pixels.show();
}
}
//파랑
if((newState4 == HIGH)&& (newState2 == LOW)&&(newState3 == LOW) ) {
delay(20);
pixels.fill(f,0,NUMPIXELS);
pixels.show();
}
else if((newState4 == LOW) && (oldState4 == HIGH)){
delay(20);
if(newState4 == LOW){
pixels.fill(d,0,NUMPIXELS);
pixels.show();
}
}
// PIN2와 PIN3 동시에 ON
if((newState2 == HIGH) &&(newState3 == HIGH) &&(newState4 == LOW) )
{
delay(20);
pixels.fill(c,0,NUMPIXELS);
pixels.show();
delay(500);
pixels.fill(d,0,NUMPIXELS);
pixels.show();
}
// PIN2와 PIN4 동시에 ON
if((newState2 == HIGH) &&(newState3 == LOW) &&(newState4 == HIGH) )
{
// Short delay to debounce button. HZ to GND
delay(20);
pixels.fill(e,0,NUMPIXELS);
pixels.show();
delay(500);
pixels.fill(d,0,NUMPIXELS);
pixels.show();
}
// PIN3와 PIN4 동시에 ON
if((newState2 == LOW) &&(newState3 == HIGH) &&(newState4 == HIGH) )
{
// Short delay to debounce button. HZ to GND
delay(20);
pixels.fill(f,0,NUMPIXELS);
pixels.show();
delay(500);
pixels.fill(d,0,NUMPIXELS);
pixels.show();
}
// PIN2와 PIN3 PIN4 동시에 ON
if((newState2 == HIGH) &&(newState3 == HIGH) &&(newState4 == HIGH) )
{
// Short delay to debounce button. HZ to GND
delay(20);
theaterChaseRainbow(50);
}
// Set the last-read button state to the old state.
oldState2 = newState2;
oldState3 = newState3;
oldState4 = newState4;
// oldState5 = newState5;
delay(300); // 깜빡이는 주기
}
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<NUMPIXELS; c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / NUMPIXELS;
uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); // hue -> RGB
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
실행 순서
roscore
-> 아두이노 코드 업로드
sudo chmod 766 /dev/ttyACM0
rosrun rosserial_python serial_node.py __name:=arduino _port:=/dev/ttyACM0 _baud:=57600
rostopic pub /switch std_msgs/UInt16MultiArray "{data: [0, 0, 1]}" --once
입력해주는 데이터 행렬에 따라 다음과 같이 동작합니다.
RED | GREEN | BLUE | RED BLINK | GREEN BLINK | BLUE BLINK | LED SHOW | |
1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
2 | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
3 | 0 | 0 | 1 | 0 | 1 | 1 | 1 |
<수정 버전>
0 : white
1 : 초록 전진 100
2 : 초록 후진 010
4 : 알맹이가 아니라 첨부터 끝까지 드르륵 001
3 : red blink 110
5 : 블링크 대신 쓸 수 있을거 같은 빨강 왔다갔다 101
6 : black 011
7 : rainbow 111
- 기존 코드와 다르게 led show가 복잡해졌다. 제어기가 pixel의 led를 바꾸고 밝히는 코드를 수행할 때는 rosserial 호출을 못함 -> 호출 기능을 여러군데에 넣기 시도
- spinOnce를 여러군데 넣어도 여러군데에서 호출 기능이 작동하지 않았다.
- 함수(absss) 안에 spinOnce 명령을 넣으면 제대로 기능한다. -> 함수 군데군데 absss 많이 넣음
white | 초록 전진 | 초록 후진 | 알맹이가 아니라 첨부터 끝까지 드르륵 | red blink | 블링크 대신 쓸 수 있을거 같은 빨강 왔다갔다 | black | rainbow | |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
2 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
3 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 |
끝
'작업' 카테고리의 다른 글
라즈베리파이에서 ydlidar 구동하기 (0) | 2021.09.08 |
---|