###### 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()
```