owned this note
owned this note
Published
Linked with GitHub
# RaspberryPi 4B + ubuntu18.04 + PIGPIO + ROS 輸出 servo motor pwm control signal

### 使用Raspberry 4B + ubuntu、2023/07/06最後更新
## 準備硬體設備:
1、 Raspberry 4B 開發板一塊
2、 可由5V驅動的Servo Motor 一顆 (DS3235SG 3Pin為GND、VDD、SIGNAL)
3、 鍵盤(接Raspberry 4B 開發板 USB port)
4、 可透過RPI4B連接網路的環境
4、 邏輯分析儀一臺(非必要,可用來確認輸出波行是否正確)
5、 WindowsPC一臺(非必要,可接邏輯分析儀監控輸出波型)
應用:攔截網的觸發
{%youtube YpEBNVNKN0A%}

## 準備軟體:
假設已擁有裝好ubuntu18.04與ROS(Melodic Morenia版)的RaspberryPi 4B開發板,並可透過RPI4B連上網路。**無須準備其他軟體**。
## 大致分為以下五步驟:
1、在ubuntu18.04中安裝PIGPIO。
2、在ROS中創建名為cap的專案(工作區)。
3、修改CMakeLists.txt檔案使ROS的C Compiler可連結PIGPIO函式庫。
4、使用catkin_make指令編譯.c檔,產生.exe檔案,下指令執行(sudo -./xxxx.exe)。
5、透過邏輯分析儀監控指定的GPIO輸出PULSE是否正確(非必要)。
***P.S. PIGPIO函式庫中已有servo pwm pulse的相關範例與函式,可快速上手使用。***
## 1、在ubuntu18.04中安裝PIGPIO。
在ubuntu server 18.04命令列中透過以下指令下載與安裝PIGPIO
測試時是在 /home/user 位置執行下載、解壓縮並安裝
https://abyz.me.uk/rpi/pigpio/index.html <font color="blue">***PIGPIO官方網站。說明PIGPIO在RPI4B上的腳位編號(Pin Define) (RPI4B參考Type3)***</font>
### (0)前兩行是在重新安裝PIGPIO時,要先把舊版的GPIO資料夾與壓縮檔刪除
```shell=
rm master.zip
sudo rm -rf pigpio-mast
```
### (1)若是第一次安裝,則只需輸入以下五條指令
安裝Python相關工具
```shell=
sudo apt install python-setuptools python3-setuptools
```
下載pigpio最新版本
```shell=
wget https://github.com/joan2937/pigpio/archive/master.zip
```
解壓縮
```shell=
unzip master.zip
```
進入解壓縮的資料夾
```shell=
cd /home/user/pigpio-master
```
安裝pigpio
```shell=
make
sudo make install
```
### (2)安裝後進行測試
關閉所有正在執行中的pigpio node(注意指令中pigpiod結尾的"d"不要忘記打)
沒先執行這行會報錯 <font color="red">*pigpio Can't lock /var/run/pigpio.pid*</font>
```shell=
sudo killall pigpiod
```
check C I/F
```shell=
sudo ./x_pigpio
```
start daemon
```shell=
sudo pigpiod
```
check C I/F to daemon
```shell=
./x_pigpiod_if2*
```
check Python I/F to daemon
```shell=
./x_pigpio.py
```
check pigs I/F to daemon
```shell=
./x_pigs
```
check pipe I/F to daemon
```shell=
./x_pipe
```
================================================================
安裝PIGPIO之參考網頁
https://abyz.me.uk/rpi/pigpio/index.html PIGPIO官方網站。PIGPIO在不同硬體上的腳位編號(Pin Define)
https://abyz.me.uk/rpi/pigpio/ PIGPIO官方網站 (卡巴斯基會觸發警告)
https://abyz.me.uk/rpi/pigpio/ PIGPIO官方網站 下載與安裝
https://blog.csdn.net/weixin_41756645/article/details/125417549
================================================================
## 2、在ROS中創建名為cap的專案(工作區)。
在/home/user路徑下創建名為"cap"的工作資料夾
```shell=
cd /home/user
mkdir cap
```
進入剛才創建的cap資料夾
```shell=
cd /home/user/cap
```
在工作區中創建一個src資料夾用來放置package資料夾與程式檔案
```shell=
mkdir src
```
進入剛才創建的src資料夾,創建一個名為catch_net的Package
```shell=
cd /home/user/cap/src
catkin_create_pkg catch_net std_msgs rospy roscpp
```
在Package的資料夾中再創建一個src資料夾用來放置要執行的Node(程式檔案)
```shell=
mkdir src
cd /home/user/cap/src/catch_net/src
```
創建名為servo_trig.c的Node並將要執行的動作寫進.c檔案
```shell=
sudo vim servo_trig.c
```
================================================================
https://ithelp.ithome.com.tw/articles/10202723
https://ithelp.ithome.com.tw/articles/10203126
================================================================
## 3、修改CMakeLists.txt檔使ROS的C Compiler連結PIGPIO函式庫。
返回上一層,開啟CMakeLists.txt檔準備修改
```shell=
cd /home/user/cap/src/catch_net
sudo vim CMakeLists.txt
```
```shell=
//...上略
## Declare a C++ executable
## ...
## ...
## ...
add_executable(servo_trig.exe src/servo_trig.c)
target_link_libraries(servo_trig.exe ${catkin_LIBRARIES} -lpigpio -lrt)
## Rename C++ executable without prefix
//...下略
```
-lpigpio:pigpio library。連結pigpio函式庫。
-lrt:Realtime Extensions library。
## 4、catkin_make編譯.c檔,產生.exe,執行(sudo -./xxxx.exe)。
回到cap專案資料夾(工作區)並使用編譯器將.c檔編譯成.exe
```shell=
cd /home/user/cap
catkin_make
```
進入檔資料夾執行編譯後產生的.exe檔
```shell=
cd /home/user/cap/devel/lib/catch_net
sudo ./servo_trig.exe
```
P.S.
第二行
***sudo ./servo_trig.exe***
後面可以接數字,
代表要求輸出PWM的GPIO腳位編號,
**若不輸入任何數字則默認用GPIO 4腳位(pin7)輸出。**
一般來說要依據Servo控制pin所接的位置來決定要用哪個腳位輸出。
例如輸入
***sudo ./servo_trig.exe 4 22***
代表要求GPIO 4(pin7)與GPIO 22(pin15)兩個腳位同時輸出PWM訊號。
Raspberry 4b GPIO Pinout可以在官方網站查詢
https://pinout.xyz/
================================================================
================================================================
## 5、透過邏輯分析儀監控指定的GPIO輸出PULSE是否正確(非必要)。
================================================================
範例:使用邏輯分析儀檢查以下資訊
pigpio第4pin輸出的pwm訊號是否為50Hz,波寬1100us(1.1ms)/1900us(1.9ms)?


================================================================
P.S.1
以下測試用程式碼以50Hz控制頻率(PWM週期20ms),每隔400ms切換1100us/1900us pwm pulse width驅動servo轉動。
P.S.2
以下內容是從官方Servo Pulse Generator範例文件Servo_demo.c修改而來。
```shell=
#ifdef __cplusplus
extern "C"
{
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pigpio.h>
#include <time.h>
}
#endif
#include <ros/ros.h>
#include <termios.h>
#include <sys/select.h>
#include <stropts.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define NUM_GPIO 32
#define MIN_WIDTH 500
#define MAX_WIDTH 2500
#define TRIG_GPIO_NUM 4
int run=1;
int step[NUM_GPIO];
int width[NUM_GPIO];
int used[NUM_GPIO];
//===========
#define TRIG_PERIOD 400000000 //nsec
#define MID_WIDTH 1500 //usec
#define OFFSET_WIDTH 400 //usec
#define TRIG_DURATION 350000000 //nsec
#define TRIG_WIDTH 1050 //usec
#define UNTRIG_WIDTH 1420 //usec
#define CMD_LEN 1
struct timespec trig_prev, t_now;
//===========
static int peek_character = -1;
int readch(void);
int _kbhit(void);
int randint(int from, int to)
{
return (random() % (to - from + 1)) + from;
}
void stop(int signum)
{
run = 0;
}
int readch()
{
char ch;
if(peek_character != -1)
{
ch = peek_character;
peek_character = -1;
return ch;
}
read(0,&ch,1);
return ch;
}
int _kbhit()
{
static const int STDIN = 0;
static bool initialized = false;
if (!initialized)
{
termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN,TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
int main(int argc, char *argv[])
{
int i, g;
if(gpioInitialise() < 0) return -1;
gpioSetSignalFunc(SIGINT, stop);
if(argc==1) used[TRIG_GPIO_NUM]=1;
else
{
for(i=1;i<argc;i++)
{
g = atoi(argv[i]);
if((g>=0) && (g<NUM_GPIO)) used[g] = 1;
}
}
for(g=0; g<NUM_GPIO; g++)
{
if(used[g])
{
width[g]=UNTRIG_WIDTH;
gpioServo(g, width[g]);
printf("[%d %d]\n", g, width[g]);
}
}
printf("Sending servos pulses to GPIO");
printf(", control C to stop.\n");
while(run)
{
int bytes_rcv = _kbhit();
if(bytes_rcv)
{
int c[CMD_LEN];
for(int i=0;i<CMD_LEN;i++)
{
c[i] = readch();
}
printf("\nlen:%d key:%d\n",bytes_rcv,c[0]);
if(c[0]=='#')
{
printf("\nlen:%d key:%d Trigger.\n",bytes_rcv,c[0]);
for(g=0; g<NUM_GPIO; g++)
{
if(used[g])
{
width[g]=TRIG_WIDTH;
gpioServo(g, width[g]);
printf("[%d %d]\n", g, width[g]);
}
}
time_sleep((float)TRIG_DURATION/(float)1000000000);
for(g=0; g<NUM_GPIO; g++)
{
if(used[g])
{
width[g]=UNTRIG_WIDTH;
gpioServo(g, width[g]);
printf("[%d %d]\n", g, width[g]);
}
}
time_sleep((float)TRIG_DURATION/(float)1000000000);
}
}
}
printf("\ntidying up\n");
for(g=0; g<NUM_GPIO; g++)
{
if(used[g]) gpioServo(g, 0);
}
gpioTerminate();
return 0;
}
```
P.S.3
以下內容是Shaw(紹瑋)最後實際寫進ROS的Pytron程式碼,用來控制Servo轉動固定角度,觸發攔截網發射。
```shell=
import pigpio
import time
import rospy
from std_msgs.msg import Int32
servo_pin = 4 # GPIO pin number where the servo signal wire is connected
pi = pigpio.pi()
# Function to set the servo angle
fire = 0
def fire_cb(msg):
global fire
fire = msg.data
if fire == 1:
rospy.loginfo("Fire!")
pi.set_servo_pulsewidth(servo_pin, 1050)
time.sleep(2)
pi.set_servo_pulsewidth(servo_pin, 1420)
if __name__ == '__main__':
rospy.init_node('gps_init_py')
fire_sub = rospy.Subscriber('/fire', Int32, fire_cb)
# Create a PWM object with a frequency of 50Hz
pi.set_servo_pulsewidth(servo_pin, 1420)
rospy.spin()
```