###### tags: 樹莓派,控制實驗 [TOC] # Raspberry sample code Week1. 1. LED開關、閃爍 2. 讀取按鈕、PWM控制(漸亮、閃爍) 實作1:LED閃爍 實作2:切換RGB Week2. 1. 電阻控制馬達加減速(ADC) 2. 樹莓派與Arduino通訊控制馬達 3. 樹莓派 or Arduino計時 實作:控制馬達走矩形 ## Week_1 ### LED開關控制 ```python= import RPi.GPIO as gpio #引用RPi.GPIO函式庫 import time #引用time函式庫 LED_white = 14 #定義LED_white腳位 gpio.setwarnings(False) #關閉程式重複執行警告訊息 gpio.setmode(gpio.BCM) #腳位定義為BCM gpio.setup(LED_white, gpio.OUT) #定義為輸出腳位 def led_High_Low(): gpio.output(LED_white, 1) #設定LED_white腳位為高電位 time.sleep(1) #延遲1秒 gpio.output(LED_white, 0) #設定LED_white腳位為低電位 time.sleep(1) print("End.") led_High_Low() #GPIO.cleanup() ``` ### 按鈕 + LED開關控制 ```python= import RPi.GPIO as gpio #引用RPi.GPIO函式庫 import time #引用time函式庫 LED_white = 14 #定義LED_white腳位 Buttun_0 = 15 #定義Buttun腳位 gpio.setwarnings(False) #關閉程式重複執行警告訊息 gpio.setmode(gpio.BCM) #腳位定義為BCM gpio.setup(LED_white, gpio.OUT) #定義為輸出腳位 gpio.setup(Buttun_0, gpio.IN) #定義為輸入腳位 def Btn_LED(hl): gpio.output(LED_white, hl) def Btn_test(): while True: Btn_LED(1-gpio.input(Buttun_0)) #按鈕按下讀取到的數值為0 #按鈕放開讀取到的數值為1 Btn_test() ``` ### PWM控制LED ```python= import RPi.GPIO as gpio #引用RPi.GPIO函式庫 import time #引用time函式庫 LED_RGB = 14 #定義LED_white腳位 gpio.setwarnings(False) #關閉程式重複執行警告訊息 gpio.setmode(gpio.BCM) #腳位定義為BCM gpio.setup(LED_RGB, gpio.OUT) #定義為輸出腳位 #建立LED_RGB為PWM輸出通道,頻率設定為50 PWM = gpio.PWM(LED_RGB, 50) #啟動PWM,占空比為0 PWM.start(0) def PWM_LED_Control(): while True: dc = 0 while(dc < 100): dc += 5 PWM.ChangeDutyCycle(dc)#改變PWM占空比 time.sleep(0.5) print("PWM: " + str(dc)) PWM_LED_Control() ``` ### 控制RGB LED ```python= import RPi.GPIO as gpio #引用RPi.GPIO函式庫 import time #引用time函式庫 RED_LED_PIN = 2 #定義RED_LED腳位 GREEN_LED_PIN = 3 #定義GREEN_LED腳位 BLUE_LED_PIN = 4 #定義BLUE_LED腳位 PWM_FREQ = 50 #定義PWM頻率 gpio.setwarnings(False) #關閉程式重複執行警告訊息 gpio.setmode(gpio.BCM) #腳位定義為BCM gpio.setup(LED_RGB, gpio.OUT) #定義為輸出腳位 gpio.setup(RED_LED_PIN, gpio.OUT) gpio.setup(BLUE_LED_PIN, gpio.OUT) gpio.setup(GREEN_LED_PIN, gpio.OUT) #建立RED_LED_PIN為PWM輸出通道,頻率設定為PWM_FREQ red_pwm = gpio.PWM(RED_LED_PIN, PWM_FREQ) red_pwm.start(0)#啟動PWM,占空比為0 #建立BLUE_LED_PIN為PWM輸出通道,頻率設定為PWM_FREQ blue_pwm = gpio.PWM(BLUE_LED_PIN, PWM_FREQ) blue_pwm.start(0)#啟動PWM,占空比為0 #建立GREEN_LED_PIN為PWM輸出通道,頻率設定為PWM_FREQ green_pwm = gpio.PWM(GREEN_LED_PIN, PWM_FREQ) green_pwm.start(0)#啟動PWM,占空比為0 def setColor(r=0, g=0, b=0): red_pwm.ChangeDutyCycle(100-int(r/255*100)) green_pwm.ChangeDutyCycle(100-int(g/255*100)) blue_pwm.ChangeDutyCycle(100-int(b/255*100)) def RGB_LED_LOOP(): while True: setColor(255, 0, 0) #R time.sleep(1) setColor(0, 255, 0) #G time.sleep(1) setColor(0, 0, 255) #B time.sleep(1) RGB_LED_LOOP() ``` ## Week 2 ### 馬達歸零校正 ```cpp= int servoPin = 12; //設定servoPin由12腳位控制 /*此腳位只能控制單一馬達,要校正另一邊,請記得更改腳位*/ void setup(){ pinMode(servoPin, OUTPUT);//設定12腳位為輸出 } void loop(){ digitalWrite(servoPin, HIGH); delayMicroseconds(1500);//維持1.5ms digitalWrite(servoPin, LOW); delay(20); } ``` ### Servo(SG90) ```cpp= #include <Servo.h> //載入函式庫 Servo myservo; // 建立SERVO物件 void setup(){ myservo.attach(8); // 設定要將伺服馬達接到哪一個PIN腳 } void loop(){ myservo.write(0); //旋轉到0度,就是一般所說的歸零 delay(1000); myservo.write(90); //旋轉到90度 delay(1000); myservo.write(180); //旋轉到180度 delay(1000); myservo.write(90); delay(1000); } ``` ### Motor control in Arduino ```cpp== #include <Servo.h> //載入函式庫 #define Pin_Motor_Left 12 //定義左邊馬達腳位 #define Pin_Motor_Right 13 //定義右邊馬達腳位 Servo Motor_Left; // 建立SERVO物件 Servo Motor_Right; // 建立SERVO物件 char receive_msg; void setup() { Serial.begin(115200);//設定通訊鮑率 // 設定要將伺服馬達接到哪一個PIN腳 Motor_Left.attach(Pin_Motor_Left); Motor_Right.attach(Pin_Motor_Right); } void loop() { if(Serial.available()) //有訊息進來的時候才去做讀取的動作 { receive_msg = Serial.Read(); //讀取字元 switch(receive_msg) //判斷讀取字元 { case 'X': ServoControl(1600, 1400); //馬達後退 break; case 'A': ServoControl(1400, 1400); //馬達左轉 break; case 'W': ServoControl(1400, 1600); //馬達前進 break; case 'D': ServoControl(1600, 1600); //馬達右轉 break; case 'S': ServoControl(1500, 1500); //馬達停止 break; } } } void ServoControl(int speed_L, int speed_R) { Motor_Left.writeMicroseconds(speed_L); Motor_Right.writeMicroseconds(speed_R); } ``` ### Motor speed control in Arduino ```cpp= #include <Servo.h> #define Pin_Motor_Left 12 #define Pin_Motor_Right 13 Servo Motor_Left; Servo Motor_Right; char receive_char; const uint8_t analogPin = A0; //#define analogPin A0 int resistance_value; int servo_speed_Forward; int servo_speed_Reverse; void setup() { Serial.begin(115200); Motor_Left.attach(Pin_Motor_Left); Motor_Right.attach(Pin_Motor_Right); } void loop() { resistance_value = analogRead(analogPin); // Serial.println(resistance_value); servo_speed_Forward = map(resistance_value, 0, 255, 1500, 1700); servo_speed_Reverse = map(resistance_value, 0, 255, 1500, 1300); // Serial.print(servo_speed_Forward); // Serial.print(","); // Serial.println(servo_speed_Reverse); if(Serial.available()) { receive_char = Serial.read(); } switch(receive_char) { case 'X': ServoControl(servo_speed_Forward, servo_speed_Reverse); break; case 'A': ServoControl(servo_speed_Reverse, servo_speed_Reverse); break; case 'W': ServoControl(servo_speed_Reverse, servo_speed_Forward); break; case 'D': ServoControl(servo_speed_Forward, servo_speed_Forward); break; case 'S': ServoControl(1500, 1500); break; } } void ServoControl(int speed_L, int speed_R) { Motor_Left.writeMicroseconds(speed_L); Motor_Right.writeMicroseconds(speed_R); } ``` ### 時間函數-計時 ```cpp= long last_time = 0; long count = 0; void setup() { Serial.begin(115200); last_time = millis(); } void loop() { //判斷目前時間減去上次紀錄時間,是否大於1000ms if(millis() - last_time > 1000){ Serial.println(count ++); last_time = millis();//更新並儲存時間 } } ``` ## Week 3 ### 超音波控制 ```cpp= #define RPi3 1 #define RPi4 0 #if RPi3 const int trig_1 = 10; const int trig_2 = 11; #endif #if RPi4 const int trig_1 = 8; const int echo_1 = 9; const int trig_2 = 10; const int echo_2 = 11; #endif void setup() { Serial.begin(115200); #if RPi4 pinMode(trig_1, OUTPUT); pinMode(echo_1, INPUT); pinMode(trig_2, OUTPUT); pinMode(echo_2, INPUT); #endif } void loop(){ #if RPi3 Serial.print(ReadDistance(trig_1)); Serial.print(","); Serial.println(ReadDistance(trig_2)); #endif #if RPi4 Serial.print(ReadDistance(trig_1, echo_1)); Serial.print(","); Serial.println(ReadDistance(trig_1, echo_1)); #endif } long ReadDistance(int trig_pin) //For Rpi3 { pinMode(trig_pin, OUTPUT); long distance = 0; digitalWrite(trig_pin, LOW); delayMicroseconds(2); digitalWrite(trig_pin, HIGH); delayMicroseconds(10); pinMode(trig_pin, INPUT); distance = pulseIn(trig_pin, HIGH)/2/29; return distance; } long ReadDistance(int trig_pin, int echo_pin) //For Rpi4 { long distance = 0; digitalWrite(trig_pin, LOW); delayMicroseconds(2); digitalWrite(trig_pin, HIGH); delayMicroseconds(10); distance = pulseIn(echo_pin, HIGH)/2/29; return distance; } ``` ```cpp= #define Nothing 1 #define PI_Control 0 const int trig = 8; const int echo = 9; long Distance = 0; unsigned int base_speed = 50; uint8_t Kp = 10; unsigned int Sent_Motor = 0; unsigned int V_Err = 0; #include <Servo.h> //載入函式庫 #define Pin_Motor_Left 12 //定義左邊馬達腳位 #define Pin_Motor_Right 13 //定義右邊馬達腳位 Servo Motor_Left; // 建立SERVO物件 Servo Motor_Right; // 建立SERVO物件 void setup() { Serial.begin(115200); pinMode(trig, OUTPUT); pinMode(echo, INPUT); Motor_Left.attach(Pin_Motor_Left); Motor_Right.attach(Pin_Motor_Right); } void loop(){ Distance = ReadDistance(trig, echo); #if Nothing if ( Distance > 12) { //需前進 ServoControl(1400, 1600); //馬達前進 } else if( Distance < 12) { //需後退 ServoControl(1600, 1400); //馬達後退 } else { //需停止 ServoControl(1500, 1500); //馬達停止 } #endif //========================================== #if PI_Control if(Distance > 30) Distance = 30; V_Err = 12 - Distance; if (V_Err < 0){ V_Err = V_Err * (-1); Sent_Motor = V_Err * Kp + base_speed; if (Sent_Motor > 100) Sent_Motor = 100; Forward_Left(Sent_Motor); Backward_Right(Sent_Motor); } else if (V_Err > 0 ){ Sent_Motor = V_Err * Kp + base_speed; if (Sent_Motor > 100) Sent_Motor = 100; Backward_Left(Sent_Motor); Forward_Right(Sent_Motor); } else{ Backward_Left(0); Forward_Right(0); } #endif } void Forward_Right(int speed_percent) { Motor_Right.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1700)); } void Forward_Left(int speed_percent) { Motor_Left.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1700)); } void Backward_Right(int speed_percent) { Motor_Right.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1300)); } void Backward_Left(int speed_percent) { Motor_Left.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1300)); } void ServoControl(int speed_L, int speed_R) { Motor_Left.writeMicroseconds(speed_L); Motor_Right.writeMicroseconds(speed_R); } long ReadDistance(int trig_pin, int echo_pin) { long distance = 0; digitalWrite(trig_pin, LOW); delayMicroseconds(2); digitalWrite(trig_pin, HIGH); delayMicroseconds(10); distance = pulseIn(echo_pin, HIGH)/2/29; return distance; } ``` ## Week_4 ### 視訊調整縮放 ```python= #!/usr/bin/env python # -*- coding: utf8 -*- import cv2 print("cv2 version: " + cv2.__version__) import numpy as np cap = cv2.VideoCapture(0) #開啟鏡頭裝置影像,利用變數cap作為視訊擷取後存放空間 ret, frame = cap.read() #擷取一張影像,存到陣列frame,是否成功存到布林變數ret #影像初始大小為640x480 #cap.get(3) 確認影像寬度,cap.get(4) 確認影像高度 print(cap.get(3), cap.get(4)) cv2.imshow('image', frame) cap.set(3, 320) #改變影像寬度 cap.set(4, 240) #改變影像高度 #改變大小可降低後續影像處理資料量 print(cap.get(3), cap.get(4)) while True: ret, hframe = cap.read() #擷取影像,存到陣列hframe cv2.imshow('half image', hframe) #利用視窗顯示hframe #waitKey 函數是用來等待與讀取使用者按下的按鍵,而其參數是等待時間(單位為毫秒), #若設定為 0 就表示持續等待至使用者按下按鍵為止, #此處設定為字母q,表示等待1秒確認是否按下按鍵, #這樣當我們按下任意按鍵之後,就會呼叫 #cv2.destroyAllWindows 關閉所有 OpenCV 的視窗 if cv2.waitKey(1) & 0xFF == ord('q'): break #在python opencv 中要補捉鍵盤事件的話可以使用 cv2.waitKey(), #它會在給定的時間內監聽鍵盤事件,給定的時間到了 #cv2.waitKey() 回傳按下按鍵的數字,沒有按鍵按下的話會回傳-1 cap.release() #釋放攝影機 cv2.destroyAllWindows() #關閉所有OpenCV 視窗 ``` ### 樹梅派 相機 拍照 [ord函數](https://www.runoob.com/python/python-func-ord.html) [OpenCV 擷取網路攝影機串流影像,處理並寫入影片檔案教學](https://blog.gtwang.org/programming/opencv-webcam-video-capture-and-file-write-tutorial/) ```python= #!/usr/bin/env python # -*- coding: utf8 -*- import cv2 print("cv2 version: " + cv2.__version__) import RPi.GPIO as GPIO import time import numpy as np GPIO.setmode(GPIO.BCM) GPIO.setup(17,GPIO.IN) cap = cv2.VideoCapture(0) ret, frame = cap.read() print(cap.get(3), cap.get(4)) picture_num = 0 pre_btn_state = 0 while(True): ret, hframe = cap.read() cv2.imshow('image', hframe) btn_state = GPIO.input(17) if (not btn_state) and pre_btn_state: time.sleep(0.3) print('photograph') cv2.imwrite('Output.jpg', hframe) // 輸出圖片(存檔路徑為目前.py路徑底下) ''' // picture_num = picture_num + 1 print('photograph') path = '/home/pi/Desktop/Control_exp_v_2/' //設定存檔絕對路徑 cv2.imwrite(path + 'output_' + str(picture_num) + '.jpg', hframe) // ''' pre_btn_state = btn_state if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` [[Python+OpenCV] 圖片縮放 resize](https://shengyu7697.github.io/python-opencv-resize/) ### 影像圖片放大縮小 ```python= #!/usr/bin/env python # -*- coding: utf8 -*- import cv2 print("cv2 version: " + cv2.__version__) import numpy as np img = cv2.imread('123.jpg') print(img.shape) rows, cols, channel = img.shape ''' 圖像的大小可以通過其shape屬性來獲取 第一個元素表示圖像的高度, 第二個表示圖像的寬度, 第三個表示圖元的通道數。 ''' dst = cv2.resize(img, (2*cols, rows), interpolation = cv2.INTER_CUBIC) dst1 = cv2.resize(img, (cols, 2*rows), interpolation = cv2.INTER_LINEAR) dst2 = cv2.resize(img, (int(cols/2), int(rows/2)), interpolation = cv2.INTER_AREA) cv2.imshow('Original', img) cv2.imshow('INTER_CUBIC', dst) cv2.imshow('INTER_LINEAR', dst1) cv2.imshow('INTER_AREA', dst2) cv2.waitKey(0) cv2.destroyAllWindows() ``` [Pyplot tutorial](https://matplotlib.org/stable/tutorials/introductory/pyplot.html) [python數位圖像處理](https://www.cnblogs.com/denny402/p/5122594.html) [[Python+OpenCV] 影像二值化 Image Thresholding](https://shengyu7697.github.io/python-opencv-threshold/) ### 圖片二值化 ```python= #!/usr/bin/env python # -*- coding: utf8 -*- import cv2 print("cv2 version: " + cv2.__version__) import numpy as np import matplotlib matplotlib.use('Tkagg', warn = False, force = True) from matplotlib import pyplot as plt img = cv2.imread('gradient.png') ret, thresh1 = cv2.threshold(img, 127, 155, cv2.THRESH_BINARY) ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) ret, thresh3 = cv2.threshold(img, 127, 5, cv2.THRESH_TRUNC) ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV) titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV'] images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] for i in range: plt.subplot(2,3,i+1), plt.imshow(images[i]) ''' 使用 subplot 可以在一幅圖中生成多个子圖,其參數為: plt.subplot(numrows, numcols, fignum) ''' plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ```