利用APP inventor構建一個APP作為使用者端程式,利用Flask框架結合樹莓派構建一個伺服器端程式,兩者間通訊,製作出一個木質外殼結構、帶有攝像頭和機械臂,同時具備人臉檢測和紅外目標搜尋功能的救援機器人。
橫截面為邊長1.5cm正方形的木條若干米、樹莓派4B、BST-4WD拓展板、金屬TT電機X4、金屬舵機及必要配件X6、12.6V動力鋰電池、3D列印齒輪X8、PCA9685舵機驅動板、人體熱釋紅外感測器、手機X2、杜邦線若干條、廢棄瓶蓋若干
1.通過點選APP上的方向按鈕和速度調節滑動條來操縱機器人前、後、左、右、轉向的運動以及速度調節。
2.通過點選APP的上攝像頭雲臺控制按鈕實現對攝像頭方向的水平和垂直調節,並通過APP影象顯示區域實時顯示opencv採集並處理過的視訊流,如果檢測到人臉則對人臉進行矩形框標記,從而實現對環境和人臉的感知。
3.通過點選人臉檢測按鈕,opencv採集單張影象,然後呼叫百度人臉檢測介面進行人臉檢測,將返回的資料處理後傳送到手機,最終實現在資訊顯示框檢視年齡、性別、表情、是否佩戴口罩、配戴眼鏡型別等檢測資料,APP呼叫百度語音合成介面朗讀以上資料的效果。
4.點選紅外目標搜尋按鈕,開始進行生命體搜尋,如果搜尋到紅外目標則APP語音合成提示資訊。
5.通過點選機械臂控制按鈕實現對4自由度機械臂的控制,從而達到機械臂抓取物體並放置到車體上帶回的目的。
樹莓派Python程式碼:
# main.py
from flask import Flask, render_template, Response,request
from camera import VideoCamera
from urllib.parse import urlencode
import urllib
import RPi.GPIO as GPIO
import Adafruit_PCA9685
import requests
import base64
#引腳定義
left_moto1=20
left_moto2=21
left_pwm=16
right_moto1=19
right_moto2=26
right_pwm=13
hongwai_pin=22
#變數定義
speed=0
pwm_left=None
pwm_right=None
servo_min = 150
servo_max = 600
pwm_servo=None
face_check_flag='0'
#圖片儲存路徑
pic_path='/home/pi/wifi_car/test.jpg'
#百度AI appkey secretkey
ak="qTKX7mY59YeZ1GfiW0HYv1mK"
sk="UHu5yYuQahn7L4DGxPYhi1WL6v5tjnXm"
data_str='收到此檢測訊息表明人臉檢測功能正常,請正式開始使用!'
#初始化函數
def init():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(left_pwm,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(left_moto1,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(left_moto2,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(right_pwm,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(right_moto1,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(right_moto2,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(hongwai_pin,GPIO.IN)
global pwm_left
global pwm_right
pwm_left = GPIO.PWM(left_pwm, 2000)
pwm_right = GPIO.PWM(right_pwm, 2000)
global pwm_servo
pwm_servo = Adafruit_PCA9685.PCA9685()
pwm_servo.set_pwm_freq(60)
#前進函數
def car_forward():
GPIO.output(left_moto1,GPIO.HIGH)
GPIO.output(left_moto2,GPIO.LOW)
GPIO.output(right_moto1,GPIO.HIGH)
GPIO.output(right_moto2,GPIO.LOW)
pwm_left.start(speed)
pwm_right.start(speed)
#後退函數
def car_back():
GPIO.output(left_moto1,GPIO.LOW)
GPIO.output(left_moto2,GPIO.HIGH)
GPIO.output(right_moto1,GPIO.LOW)
GPIO.output(right_moto2,GPIO.HIGH)
pwm_left.start(speed)
pwm_right.start(speed)
#左轉函數
def car_left():
GPIO.output(left_moto1,GPIO.LOW)
GPIO.output(left_moto2,GPIO.HIGH)
GPIO.output(right_moto1,GPIO.HIGH)
GPIO.output(right_moto2,GPIO.LOW)
pwm_left.start(speed)
pwm_right.start(speed)
#右轉函數
def car_right():
GPIO.output(left_moto1,GPIO.HIGH)
GPIO.output(left_moto2,GPIO.LOW)
GPIO.output(right_moto1,GPIO.LOW)
GPIO.output(right_moto2,GPIO.HIGH)
pwm_left.start(speed)
pwm_right.start(speed)
#停止函數
def car_stop():
GPIO.output(left_moto1,GPIO.LOW)
GPIO.output(left_moto2,GPIO.LOW)
GPIO.output(right_moto1,GPIO.LOW)
GPIO.output(right_moto2,GPIO.LOW)
#獲取百度AI access_token
def getAccess_token(AK,SK):
host = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id="+AK+"&client_secret="+SK
response = requests.get(host)
access_token=''
if response:
dict=response.json()
access_token=dict.get("access_token","none")
#print(dict.get("access_token","none"))
return access_token
#圖片進行base64編碼函數
def Base64(img_path):
with open(img_path, 'rb') as f:
image_data = f.read()
base64_data = base64.b64encode(image_data) # base64編碼
string=str(base64_data,"utf-8")
# print(string)
return string
#請求資料函數
def request_post(base64_code,access_token):
request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
#請求引數 年齡 性別 表情 口罩 眼鏡
params={'image':''+base64_code+'','image_type':'BASE64','face_field':'age,gender,expression,mask,glasses'}
params=urlencode(params)
request_url = request_url + "?access_token=" + access_token
request = urllib.request.Request(url=request_url,data=params.encode("utf-8"))
request.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(request)
content = response.read()
return content
#返回資料處理
def baidu_api(path,ak,sk):
global data_str
base64_code = Base64(path)
token=getAccess_token(ak,sk)
data_set=request_post(base64_code,token)
print('**********************')
print(data_set)
print('**********************')
string=bytes.decode(data_set)
#print(string)
dict_data=eval(string)
dict_data2=dict_data.get("result","none")
dict_data3=dict_data2.get("face_list","none")
dict_data4=dict_data3[0]
age=dict_data4.get("age","none")
age_str="年齡:"+str(age)+","
print(age_str)
# beauty=dict_data4.get("beauty","none")
# beauty_str="beauty:"+str(beauty)
# print(beauty_str)
gender=dict_data4.get("gender","none").get("type","none")
gender_str="性別:"+str(gender)+","
print(gender_str)
glasses=dict_data4.get("glasses","none").get("type","none")
glasses_str="眼鏡型別:"+str(glasses)+","
print(glasses_str)
mask=dict_data4.get("mask","none").get("type","none")
mask_str="是否佩戴口罩:"+str(mask)
print(mask_str)
expression=dict_data4.get("expression","none").get("type","none")
expression_str="表情:"+str(expression)+","
print(expression_str)
data_str=age_str+gender_str+glasses_str+expression_str+mask_str
#flask
app = Flask(__name__)
#預設路由
@app.route('/')
def index():
return render_template('index.html')
def gen(camera):
global face_check_flag
while True:
if face_check_flag=='1':
camera.save_pic()#儲存影象
print("save pic OK")
baidu_api(pic_path,ak,sk)
face_check_flag='0'
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
#獲取視訊流路由
@app.route('/video_feed')
def video_feed():
return Response(gen(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
#人臉檢測路由
@app.route('/face_check',methods=['GET'])
def face_check():
global face_check_flag
data=request.args.get('data')
print('The data is :',data)
#print("Type is :",type(data))
face_check_flag=data
return data_str
#運動控制路由
@app.route('/sport',methods=['GET'])
def sport():
data=request.args.get('data')
if data=='forward':
car_forward()
if data=='back':
car_back()
if data=='left':
car_left()
if data=='right':
car_right()
if data=='stop':
car_stop()
print("the data is :",data)
#print(type(data))
return 'Sport OK'
#速度調節路由
@app.route('/speed',methods=['GET'])
def getSpeed():
data=request.args.get('data')
global speed
speed=float(data)
return 'Speed OK'
#舵機1控制路由
@app.route('/servo1',methods=['GET'])
def getServo1():
data=request.args.get('data')
angle=int(data)
servo_val=int(450/270*angle)+150
pwm_servo.set_pwm(1,0,servo_val)
return 'Servo1 OK'
#舵機2控制路由
@app.route('/servo2',methods=['GET'])
def getServo2():
data=request.args.get('data')
angle=int(data)
servo_val=int(450/270*angle)+150
pwm_servo.set_pwm(2,0,servo_val)
return 'Servo2 OK'
#舵機3控制路由
@app.route('/servo3',methods=['GET'])
def getServo3():
data=request.args.get('data')
angle=int(data)
servo_val=int(450/270*angle)+150
pwm_servo.set_pwm(3,0,servo_val)
return 'Servo3 OK'
#舵機4控制路由
@app.route('/servo4',methods=['GET'])
def getServo4():
data=request.args.get('data')
angle=int(data)
servo_val=int((servo_max-servo_min)/270*angle)+150
pwm_servo.set_pwm(4,0,servo_val)
return 'Servo4 OK'
#攝像頭雲臺水平調節路由
@app.route('/camera_horizon',methods=['GET'])
def get_cam_horizon():
data=request.args.get('data')
angle=int(data)
servo_val=int(450/270*angle)+150
pwm_servo.set_pwm(5,0,servo_val)
return 'camera_horizon OK'
#攝像頭雲臺垂直調節路由
@app.route('/camera_vertical',methods=['GET'])
def get_cam_vertical():
data=request.args.get('data')
angle=int(data)
servo_val=int(450/270*angle)+150
pwm_servo.set_pwm(6,0,servo_val)
return 'camera_vertical OK'
#紅外檢測路由
@app.route('/hongwai',methods=['GET'])
def hongwai():
if GPIO.input(hongwai_pin)==True:
print("hongwai_OK")
return "hongwaiok"
else:
print("hongwai_ERROR")
return "hongwaierror"
if __name__ == '__main__':
#初始化函數呼叫
init()
#flask執行
app.run(host='192.168.43.180' ,port=8123, debug=True)
# camera.py
import cv2 as cv
#IP攝像頭地址
camera_url='http://admin:admin@192.168.43.73:8081'
class VideoCamera(object):
#範例視訊流獲取物件
def __init__(self):
self.video = cv.VideoCapture(camera_url)
def __del__(self):
self.video.release()
#影象儲存函數
def save_pic(self):
ret, image = self.video.read()
cv.imwrite('/home/pi/wifi_car/test.jpg',image)
#獲取視訊流幀 處理
def get_frame(self):
success, frame = self.video.read()
gray=cv.cvtColor(frame,cv.COLOR_BGR2GRAY)
#opencv級聯分類器檢測
face_cascade = cv.CascadeClassifier("data/haarcascade_frontalface_alt.xml")
faces=face_cascade.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=4,flags=cv.CASCADE_SCALE_IMAGE,minSize=(100, 100),maxSize=(250,250))
# print(faces)
#矩形框標記人臉
for (x,y,w,h) in faces:
frame= cv.rectangle(frame,(x,y),(x+w,y+h),(255,255,0),2)
ret, jpeg = cv.imencode('.jpg', frame)
return jpeg.tobytes()
<!--index.html 視訊顯示頁面-->
<html>
<head>
<title>Video Streaming Demonstration</title>
</head>
<body>
<img src="{{ url_for('video_feed') }}" width="100%" height="120%">
</body>
</html>
APP inventor程式碼塊(部分):
受樹莓派引腳和拓展板的限制,加裝更多的感測器很不方便,在Arduino上安裝感測器,利用串列埠將資料傳送給樹莓派理論上應當可行,但是在實際的程式設計中要將讀取功能放在Flask裡面,這卻未能達到理想效果,因此這是一個待改進的地方。