# RaspberryPi 4B + ubuntu18.04 + PIGPIO + ROS 輸出 servo motor pwm control signal ![](https://hackmd.io/_uploads/HJDcdNvv3.png =60%x) ### 使用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%} ![](https://hackmd.io/_uploads/rJna8I5x6.jpg =50%x) ## 準備軟體: 假設已擁有裝好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)? ![](https://hackmd.io/_uploads/HJwnMLBwn.jpg) ![](https://hackmd.io/_uploads/BkS6z8rw2.jpg) ================================================================ 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() ```