会员申请ID:feige
1、申 请 I D:feige2、个人邮箱:lbt0355.com@outlook.com
3、原创技术文章:
图像识别循迹
架构设计
传输层NRF24L01
感知层MSP430F5529、OPENMV、7路灰度、基本模型、直流电机
理论设计:先通过opencv采集数字图片数据
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
author:Administrator
datetime:2018/5/11/011 19:49
software: PyCharm
'''
import cv2
import numpy as np
cap = cv2.VideoCapture(1)
cap.set(3,320)
cap.set(4,240)
ret, frame = cap.read()
rows, cols, channels = frame.shape
print(cols, rows, channels)
def get_point(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK:
print(x,y)
cv2.namedWindow("image")
cv2.setMouseCallback("image", get_point)
# 图像预处理
def img_p(img):
# 灰度化
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 平滑滤波
blur = cv2.blur(gray_img, (3,3))
# 二值化
ret1, th1 = cv2.threshold(blur, 190, 255, cv2.THRESH_BINARY)
# 透视变换
b = 50
pts1 = np.float32([, , , ])
pts2 = np.float32([, , , ])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(th1, M, (cols, rows))
return dst
num = input("num:")
print(num)
while True:
# 读取图像
ret, frame = cap.read()
dst = img_p(frame)
k = cv2.waitKey(10)
if k == ord('q'):
break
elif k == ord('s'):
filename = r'./num_data/' + num + '.jpg'
cv2.imwrite(filename, dst)
print(filename)
num = input("num:")
print(num)
cv2.imshow("image", dst)
cap.release()
cv2.destroyAllWindows()
在通过对图像的预处理和透射变换等操作来采集操作处理后的图像,采集到的数据数量有限,要用卷神经网络来识别数字图片需要大量的数据,所以可以用keras的图像预处理api来对数据进行扩增。接下来搭建卷神经网络来训练数字1-8的图片分类器。经过训练后,识别数字的成功率在90%以上。
代码设计:图像识别:import sensor, image, time, pyb, tf, math
from pyb import Servo
from pyb import UART
from pyb import LED
red_led = LED(1)
green_led = LED(2)
blue_led= LED(3)
ir_led = LED(4)
#串口初始化
uart = UART(3, 115200)
#加载数字训练模型
net = "Road.tflite"
labels =
net1 = "First.tflite"
labels1 =
#目标病房号
goal_home = "0"
#赛道红色阈值
red_threshold = (7, 43, 3, 43, -1, 31)
#筛选出数字边框
black_threshold = (0, 51, -18, 5, -11, 24)
good_roi = (42, 1, 99, 119)
road_one_roi = (28, 1, 110, 119)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) # 使用QQVGA的分辨率160*120
sensor.skip_frames(10) # 让新的设置生效。
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # 跟踪FPS帧率
#初始化舵机,设置占空比
s1 = Servo(1) # P7 #左右舵机
s2 = Servo(2) # P8 #勿动,上下舵机
s1_pwm = 1500
s1.pulse_width(s1_pwm)
s2.pulse_width(2150)
#舵机固定角参数
s1_left_1 = 1660 #看左侧,(交叉路口只有2个数字时)
s1_right_1 = 1100 #看右侧,(交叉路口只有2个数字时)
s1_left_2 = 1800 #看左侧,(交叉路口有4个数字时)
s1_right_2 = 1140 #看右侧,(交叉路口有4个数字时)
#设定图像的中心
image_center_x = sensor.width()/2
image_center_y = sensor.height()/2
#图像高宽
w = sensor.width()
h = sensor.height()
#交叉路口出现标志(用于过滤异常图像)
cross_flag = 0
cross_flag_count = 2 #过滤次数,识别路口超过5次,判断有效
#交叉路口出现次数(用于判断当前处于第几个交叉路口)
cross_count = 0
#矫正图像的配置属性
TARGET_POINTS = [(0, 0), # (x, y) CHANGE ME!
(w-1, 0), # (x, y) CHANGE ME!
(w-1, h-1), # (x, y) CHANGE ME!
(0, h-1)] # (x, y) CHANGE ME!
X_OFFSET = 0
Y_OFFSET = 0
ZOOM_AMOUNT = 1
FOV_WINDOW = 25
x_rotation_counter = 180
y_rotation_counter = 180
z_rotation_counter = 0
#数字识别函数(数字识别时,不识别赛道)(数字分割采用舵机旋转固定角度将镜头对准,然后使用矩形框选数字,分割出来进行识别)
def Find_Number(x,y):
global s1
success = False
#舵机复位(上电机向下,下电机居中)
s1.pulse_width(s1_pwm)
global cross_count
cross_count = cross_count + 1
Send_Cross_Count(cross_count)
if cross_count == 1 :
print("The first cross ... jump \n")
pyb.delay(2000) #延时1秒,跳过初次检测,1,2固定
return
elif cross_count == 2 :#================================有2个数字的交叉路口=====================
Cn_Stop() #发送停止命令
red_led.on()
#舵机转向,摄像头看向左侧 注意数据发送完毕后,摄像头恢复
s1.pulse_width(s1_left_1)
print("Begin Left ...\n")
success = Find_Number_one(0)
#舵机转向,摄像头看向右侧
if(success == False):
print("Begin Right ...\n")
s1.pulse_width(s1_right_1)
success = Find_Number_one(1)
red_led.off()
elif cross_count == 3 :#有4个数字的交叉路口
Cn_Stop() #发送停止命令
blue_led.on()
#舵机转向,摄像头看向左侧 注意数据发送完毕后,摄像头恢复
s1.pulse_width(s1_left_1)
print("Begin Left ...\n")
success = Find_Number_one(0)
#舵机转向,摄像头看向右侧
if(success == False):
print("Begin Right ...\n")
s1.pulse_width(s1_right_1)
success = Find_Number_one(1)
blue_led.off()
elif cross_count >= 4 :#有2个数字的交叉路口 第四个交叉路口
Cn_Stop() #发送停止命令
green_led.on()
#舵机转向,摄像头看向左侧
s1.pulse_width(s1_left_1)
print("Begin Left ...\n")
Find_Number_one(0)
#舵机转向,摄像头看向右侧
print("Begin Right ...\n")
s1.pulse_width(s1_right_1)
Find_Number_one(1)
green_led.off()
if success == False:
send = "GG"
print(send)
rev = " "
uart.write(send)
while(1):
if (uart.any()):
rev = uart.readline()
print("receive data is %s" % rev)
break
s1.pulse_width(s1_pwm)
pyb.delay(3000) #延时3秒,跳过路口检测
return
#数字识别一级函数,用于识别数字
def Find_Number_one(direct): #0左1右 2初始状态
result = "0" #识别结果
same = 0.0 #相似程度
same_flag = 5 #相似过滤,只有相同次数达到10次,才算有效
same_count = 0 #相似次数
temp_result = "0" #暂存识别结果
temp_same = 0.0 #暂存相似程度
three_same = 0.0 #数字3的相似度,用于分辨5
six_same = 0.0 #数字6的相似度,用于分辨5
seven_same = 0.0 #数字7的相似度,用于分辨1
global s1
if direct == 2 :
#=====在此处添加手持数字训练模型识别————————————————————————————————————————
while(True):
img = sensor.snapshot().rotation_corr(x_rotation = x_rotation_counter, \
y_rotation = y_rotation_counter, \
z_rotation = z_rotation_counter, \
x_translation = X_OFFSET, \
y_translation = Y_OFFSET, \
zoom = ZOOM_AMOUNT, \
fov = FOV_WINDOW, \
corners = TARGET_POINTS)
blobs = img.find_blobs(, roi = good_roi,pixels_threshold = 100, merge = True, margin = 10)
#pixels_threshold为像素个数阈值,如果色块像素小于这个值,会被过滤掉
if blobs:
for black in blobs:
#画出矩形进行标注
img.draw_rectangle(black)
#画出矩形中心
#img.draw_cross(black, black)
#找矩形
for r in img.find_rects(black,threshold = 15000):
#img.draw_rectangle(r.rect(), color = (0, 255, 0))
##找出矩形框的中心点,备用
#img_x = r.x()
#img_w = r.w()
#center_x = img_x + img_w/2
##print(r) # default settings just do one detection... change them to search the image...
for obj in tf.classify(net1, img,black ,min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
#print("**********\nPredictions at " % obj.rect())
img.draw_rectangle(obj.rect())
predictions_list = list(zip(labels1, obj.output()))
temp_result = "0" #暂存识别结果
temp_same = 0.0 #暂存相似程度
for i in range(len(predictions_list)):
#predictions_list #识别结果 predictions_list #相似度
#print("%s = %f" % (predictions_list, predictions_list)) #调式可用,输出识别数字的相似度
if predictions_list > temp_same :
temp_same = predictions_list #记录相似度
temp_result = predictions_list #记录相似结果
if(temp_result == '3'):
three_same = three_same + temp_same
elif(temp_result == '6'):
six_same = six_same + temp_same
elif(temp_result == '7'):
seven_same = seven_same + temp_same
print(temp_result)
if (temp_result == result):
result = temp_result
same_count = same_count + 1
if same_count > same_flag :
global goal_home
#过滤错误识别的数字3,6,7(3->5,7->1,6->5)
if(result == '5'):
three_same = three_same / same_flag
six_same = six_same / same_flag
if(three_same > 0.15):
result = '3'
elif(six_same > 0.3):
result = '6'
elif(result == '1'):
seven_same = seven_same / same_flag
if(seven_same > 0.2):
result = '7'
goal_home = result #存储目标病房
print("The result is %s" % result)
send = "F" + result
print(send)
rev = " "
uart.write(send)
while(1):
if (uart.any()):
rev = uart.readline()
print("receive data is %s" % rev)
break
pyb.delay(2000) #等待被识别纸片被拿走
return
else:
same_count = 0
result = temp_result
three_same = 0.0 #数字3的相似度,用于分辨5
six_same = 0.0 #数字6的相似度,用于分辨5
seven_same = 0.0 #数字7的相似度,用于分辨1
while(True):
img = sensor.snapshot().rotation_corr(x_rotation = x_rotation_counter, \
y_rotation = y_rotation_counter, \
z_rotation = z_rotation_counter, \
x_translation = X_OFFSET, \
y_translation = Y_OFFSET, \
zoom = ZOOM_AMOUNT, \
fov = FOV_WINDOW, \
corners = TARGET_POINTS)
blobs = img.find_blobs(, roi = good_roi,pixels_threshold = 200, merge = True, margin = 10)
#pixels_threshold为像素个数阈值,如果色块像素小于这个值,会被过滤掉
if blobs:
for black in blobs:
#画出矩形进行标注
img.draw_rectangle(black)
#画出矩形中心
#img.draw_cross(black, black)
#找矩形
for r in img.find_rects(threshold = 8000,roi = road_one_roi):
for obj in tf.classify(net, img, r.rect(),min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
#print("**********\nPredictions at " % obj.rect())
img.draw_rectangle(obj.rect())
predictions_list = list(zip(labels, obj.output()))
temp_result = "0" #暂存识别结果
temp_same = 0.0 #暂存相似程度
for i in range(len(predictions_list)):
#predictions_list #识别结果 predictions_list #相似度
#print("%s = %f" % (predictions_list, predictions_list)) #调式可用,输出识别数字的相似度
if predictions_list > temp_same :
temp_same = predictions_list #记录相似度
temp_result = predictions_list #记录相似结果
print(temp_result)
#过滤混淆数字
if (temp_result == result):
result = temp_result
same_count = same_count + 1
if same_count > same_flag :
print("The result is %s" % result)
if result == goal_home :
s1.pulse_width(s1_pwm)
send = " "
if(direct == 0):
send = "L" + result
elif(direct == 1):
send = "R" + result
print(send)
rev = " "
uart.write(send)
while(1):
if (uart.any()):
rev = uart.readline()
print("receive data is %s" % rev)
break
return True
else:
return False
else:
same_count = 0
result = temp_result
#停止命令发送
def Cn_Stop():
send = "SS"
print("send data is %s" % send)
uart.write(send)
print("send cross succes")
while(1):
if (uart.any()):
rev = uart.readline()
print(rev)
break
print("begin find number")
#发送经过路口次数
def Send_Cross_Count(count):
send = "Z" + str(count)
print("send data is %s" % send)
uart.write(send)
print("send cross_count succes")
while(1):
if (uart.any()):
rev = uart.readline()
print(rev)
break
return
#识别手持图片
Find_Number_one(2)
while(True):
#接收串口数据的方法
#if(uart.any()))
#a = uart.readline()
#print(a)
img = sensor.snapshot().rotation_corr(x_rotation = x_rotation_counter, \
y_rotation = y_rotation_counter, \
z_rotation = z_rotation_counter, \
x_translation = X_OFFSET, \
y_translation = Y_OFFSET, \
zoom = ZOOM_AMOUNT, \
fov = FOV_WINDOW, \
corners = TARGET_POINTS)
#显示图像中心
#img.draw_cross(int(image_center_x), int(image_center_y))
blobs = img.find_blobs(, pixels_threshold = 100, merge = True, margin = 10)
#find_blobs函数
#第一个为需要寻找的颜色的阈值范围
#pixels_threshold为像素个数阈值,如果色块像素小于这个值,会被过滤掉
#merge值为True时会将所有寻找到的色块用一个合并的矩形框起来,默认值为False
if blobs:
for red in blobs:
#画出矩形进行标注
img.draw_rectangle(red)
#画出矩形中心
img.draw_cross(red, red)
red_center_x = red #记录中心坐标
red_center_y = red
red_rect_wide = red#记录红色色块矩形的长宽
red_rect_height = red
print("x and y",red,"",red,"\n") #打印输出寻找色块的中心坐标
print("wide and height",red,"",red,"\n") #打印输出寻找色块的中心坐标
if red_rect_wide > 80: #出现交叉路口
cross_flag = cross_flag + 1
print("%dcross\n" % cross_flag)
if cross_flag >= cross_flag_count :
Find_Number(image_center_x,image_center_y)
else:
cross_flag = 0
#颜色追踪
temp = red_center_x - image_center_x
#云台测试,小车控制不需要此处代码
#if(temp < -10): #左转
#uart.write("A")
#if (s1_pwm + 40) < 2000 :
#s1_pwm = s1_pwm + 40
#s1.pulse_width(s1_pwm)
#if(temp > 10):
#uart.write("a")
#if (s1_pwm - 40) > 1000 :
#s1_pwm = s1_pwm - 40
#s1.pulse_width(s1_pwm)
驱动:#include <msp430f5529.h>
void cn_TurnLeft_Big(); //控制左转(大角度方向改变)
void cn_TurnRight_Big(); //控制右转(大角度方向改变)
void cn_SlowDown(); //小车减速
void cn_Stop(); //小车停止
void cn_Forward(); //小车直行
void cn_SaveHistoryData(); //存储小车当前的运行状态存储,当作历史数据
void cn_RunHistoryData(); //运行小车上一个历史PWM数据
//定义参数
//0°电机状态
//0°10sPWM输出参数
volatile unsigned int Zero10 = { { 15, 100 }, { 15, 0 },
{ 30, 0 },
{ 15, 0 }, //前行
{ 0, 100 }, { 100, 0 }, { 100, 0 },
{ 0, 0 }, //左转
{ 100, 50 }, { 0, 0 }, { 0, 0 }, { 100,
0 }, //右转
};
//0°12sPWM输出参数
volatile unsigned int Zero12 = { { 25, 100 }, { 25, 0 }, { 40, 0 },
{ 30, 0 }, //前行
{ 10, 100 }, { 100, 0 }, { 100, 0 }, {
10, 0 }, //左转
{ 100, 50 }, { 10, 0 }, { 10, 0 }, {
100, 0 }, //右转
};
//0°15sPWM输出参数
volatile unsigned int Zero15 = { { 35, 100 }, { 35, 0 }, { 30, 0 },
{ 35, 0 }, //前行
{ 15, 100 }, { 100, 0 }, { 100, 0 }, {
15, 0 }, //左转
{ 100, 50 }, { 10, 0 }, { 15, 0 }, {
100, 0 }, //右转
};
unsigned int flag = 0;//声明变量是随时可变的,系统不要去优 化这个值
unsigned int i = 0;//声明变量是随时可变的,系统不要去优 化这个值
unsigned int j = 0;//声明变量是随时可变的,系统不要去优 化这个值
unsigned int k = 0;//声明变量是随时可变的,系统不要去优 化这个值
//存放小车运行PWM历史数据,主要目的,稳住起步速度
extern volatile unsigned int motor1 = 20;
extern volatile unsigned int motor2 = 20;
extern volatile unsigned int motor22 = 100;
extern volatile unsigned int motor3 = 30;
extern volatile unsigned int motor4 = 20;
//存储小车转向数据的中间变量,中断函数会控制数据变化
extern volatile unsigned int forward1 = 25;
extern volatile unsigned int forward2 = 25;
extern volatile unsigned int forward3 = 40;
extern volatile unsigned int forward4 = 30;
extern volatile unsigned int left1 = 10;
extern volatile unsigned int left2 = 100;
extern volatile unsigned int left3 = 100;
extern volatile unsigned int left4 = 10;
extern volatile unsigned int Right1 = 100;
extern volatile unsigned int Right2 = 10;
extern volatile unsigned int Right3 = 10;
extern volatile unsigned int Right4 = 100;
void main(void)
{
forward1 = 15;
forward2 = 15;
forward3 = 30;
forward4 = 15;
left1 = 100;
left2 = 0;
left3 = 100;
left4 = 0;
Right1 = 0;
Right2 = 50;
Right3 = 0;
Right4 = 100;
//设置 PWM 周期参数,const声明此值不允许改变.该数值太大,会导致 LED 闪烁
unsigned const PWMPeriod = 100;
//电机PWM初始化
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗
P1DIR |= (BIT3 + BIT2 + BIT4 + BIT5 + BIT0); // 设置 该引脚为输出 //BIT0为LED灯 //P1引脚配置
P1SEL |= BIT3; // 设置 P1.3为 TA0.2输出
P1SEL |= BIT2; // 设置 P1.3为 TA0.1输出
P1SEL |= BIT4; // 设置 P1.3为 TA0.3输出
P1SEL |= BIT5; // 设置 P1.3为 TA0.4输出
P2DIR |= (BIT4); //motor2的后退引脚 //P2.2为输入状态
P2SEL |= (BIT4);
TA0CCR0 = PWMPeriod; // 设置定时器0的 PWM 周期(4个舵机的前进引脚)
TA2CCR0 = PWMPeriod; //设置定时器2的PWM周期(Motor2)的后退引脚
TA0CCTL2 = OUTMOD_7; // 设置 PWM 输出模式为:7 - PWM复位/置位模 式, // 即输出电平在 TAR的值等于 CCR2时复位为0,当 TAR的值等于 CCR0时 置位为1,改变 CCR2,从而产生 PWM。其实模式2也可以
TA0CCTL1 = OUTMOD_7;
TA0CCTL3 = OUTMOD_7;
TA0CCTL4 = OUTMOD_7;
TA2CCTL1 = OUTMOD_7; //设置PWM的输出模式
TA0CTL = TASSEL_2 + MC_1; // 设置 TIMERA 的时钟源为 SMCLK, 计数模 式为 up,到 CCR0再自动从0开始计数
TA2CTL = TASSEL_2 + MC_1; // 设置 TIMERC 的时钟源为 SMCLK, 计数模 式为 up,到 CCR0再自动从0开始计数
//电机初始化结束
//按键配置
P2IE |= BIT1; //P2.2开启中断
P2IES |= BIT1; //P2为下降沿触发
P2IFG &= ~ BIT1; //清除 P2口的中断标志
P2REN |= BIT1;
P2OUT |= BIT1;
__enable_interrupt();
//按键配置结束
//红外传感器初始化
P6DIR |= (0x00); //配置P6为输入状态 //P3引脚配置
P6SEL |= 0x00;
P6IN = 0x00;
P1OUT &= ~ BIT0; //LED灯的输出位清零
P4DIR |= (BIT7);
P4OUT &= ~(BIT7); //LED2的输出位清零
//红外传感器初始化结束
P1OUT &= ~ BIT0;
while (1) //P6.5为右转标志 //P6.4为左转标志
{
switch (P6IN & 0x7F)
{
case (0x00): //空白标志,运行上一步历史数据
cn_RunHistoryData();
break;
case (BIT1): //前行标志
// cn_SlowDown();
break;
case (BIT1 + BIT2): //左转(小角度)标志
// break;
case (BIT2): //左转(大角度)标志
case (BIT3):
case (BIT4):
case (BIT3 + BIT4):
case (BIT2 + BIT3):
cn_TurnLeft_Big();
break;
case (BIT0 + BIT1): //右转(小角度)标志
// break;
case (BIT0): //右转(大角度)标志
case (BIT5):
case (BIT6):
case (BIT0 + BIT5):
case (BIT5 + BIT6):
cn_TurnRight_Big();
break;
case (0x7F): //停止标志(全黑为停)
case (BIT0 + BIT1 + BIT2): //停止标记
// cn_Stop();
// P8OUT = BIT1;
// for(i=0;i<100;i++)
// for(j=0;j<100;j++)
// for(k=0;k<5;k++);
// P8OUT &=~ BIT1;
// return;
default:
// cn_Stop();
// P8OUT = BIT1;
// for(i=0;i<100;i++)
// for(j=0;j<100;j++)
// for(k=0;k<5;k++);
// P8OUT &=~ BIT1;
// return;
break;
}
cn_SaveHistoryData();
}
}
//固定格式,声明中断向量地址
//void cn_SlowDown() //减速
//{
// TA0CCR2 = forward2; //motor2
// TA2CCR1 = 100;
//
// TA0CCR1 = forward1; //motor1
// TA0CCR3 = forward3; //motor3
// TA0CCR4 = forward4; //motor4
//}
void cn_Stop()
{
TA0CCR2 = 100; //motor2
TA2CCR1 = 100;
TA0CCR1 = 100; //motor1
TA0CCR3 = 100; //motor3
TA0CCR4 = 100; //motor4
}
void cn_Forward()
{
TA0CCR2 = 0; //motor2
TA2CCR1 = 100;
TA0CCR1 = 0; //motor1
TA0CCR3 = 0; //motor3
TA0CCR4 = 0; //motor4
}
void cn_TurnLeft_Big() //控制左转(大角度方向改变)
{
TA0CCR2 = left2; //motor2
TA2CCR1 = 100;
TA0CCR1 = left1; //motor1 //如果要大转弯(大角度),将此参数调为100
TA0CCR3 = left3; //motor3 //motor3比motor20 大30 (该电机特别吸电)
TA0CCR4 = left4; //motor4
}
void cn_TurnRight_Big() //控制右转(大角度方向改变)
{
TA0CCR2 = Right2; //motor2 //如果要大转弯(大角度),将此参数调为100
TA2CCR1 = 50;
TA0CCR1 = Right1; //motor1
TA0CCR3 = Right3; //motor3
TA0CCR4 = Right4; //motor4
}
void cn_RunHistoryData() //存储小车上一个PWM数据,即小车运行状态
{
TA0CCR2 = motor2; //motor2
TA2CCR1 = motor22;
TA0CCR1 = motor1; //motor1
TA0CCR3 = motor3; //motor3
TA0CCR4 = motor4; //motor4
}
void cn_SaveHistoryData() //存储小车当前的运行状态存储,当作历史数据
{
motor1 = TA0CCR1;
motor22 = TA2CCR1;
motor2 = TA0CCR2;
motor3 = TA0CCR3;
motor4 = TA0CCR4;
}
电路图及实物展示:
链接:https://pan.baidu.com/s/1s9j7MwEnY9Y9KrJrOdNyqw
提取码:0355
超出审核水平了,能通俗易懂的讲解一下吗。。。 Hmily 发表于 2023-2-1 10:40
超出审核水平了,能通俗易懂的讲解一下吗。。。
这是在大学做的一个项目。。呃可以当作酒店智能机器人来看,没那么高端,但是能够实现基本的配送功能的一个小车。用单片机msp430当主控,小车集成了摄像头红外蓝牙电源模块,嗯.就是这样 游客 183.95.248.x 发表于 2023-2-11 14:55
这是在大学做的一个项目。。呃可以当作酒店智能机器人来看,没那么高端,但是能够实现基本的配送功能的一 ...
申请账号的时候应该先测试好账号有效性。
页:
[1]