一直想尝试自己动手diy一个像Loona、Vector、Comoz等一样的宠物机器人,毕竟在网上见识过这类产品的人都知道它们的功能是非常神奇的存在,宠物机器人内置了丰富人机交互功能,会有情绪波动,会对人撒娇,会调皮捣蛋搞破坏,让人感觉仿佛这些真的是有生命的小精灵。然而这些产品的售价也不菲,而且网上许多购买过该产品的用户后来也都转二手卖出了,原因之一就是虽然这些宠物机器人有着像真宠物一样的行为,可实际时间久了就会发现其实它的功能来来回回就那几样,这时过了新鲜感的用户逐渐意识到机器人的一个一个表现其实不过是一串又一串预先配置的冷冰冰的代码罢了,因而自然也不会再被其所吸引。由于目前技术所限,宠物机器人还不足以真正做到“有生命”,这就是目前这一类产品的缺陷所在。
我想自己做一个,其中一方面就是为了能自由开发和更新升级机器的功能,另一方面也是想尝试用自己的想法去仿一个简单的宠物行为,探索其中的奥妙。当然,我目前是刚刚入手,想做到像上述的产品那样的功能还是十分困难的,在这一个项目中,我计划设定的功能都是比较简单的,目前想的是先实现简单的交互后再慢慢升级,探索开发复杂的功能。当然这个小机器人同时也是我给女朋友准备的礼物,女孩子当然是无法拒绝一只乖巧可爱富有活力的小“宠物”的。
以K210为主控,以迷你版的stm32为协处理器,K210与stm32采用串口通信。K210主要用于处理图像识别等大计算量的任务,stm32则是用于控制底层执行器,这次的执行器布置比较简单,只有两个舵机,一个负责让机器人抬头低头,一个负责左右转。
这里K210是在一款名为K210视觉识别模块的产品中,在某橙色软件可以搜到。该产品可以外形像一个小相机,自带了摄像头和LCD屏幕。在购买到手把玩一段时间后我发现恰好拆了亚克力板把摄像头扭向下,整个相机倒过来,恰好就可以做一个小机器人的头,摄像头做眼睛,屏幕做脸,而其中内置的K210芯片,能够跑轻量级视觉算法,恰巧能做机器人的大脑。
这款stm32尺寸只有25.40*22.86mm大小,可以说是非常小巧玲珑了,非常适合用来做小体积的项目,它芯片的具体型号是stm32f103c8t6,其实就是最小系统板常用的那款型号。
这是自己绘制的用于连接的扩展板,结构非常简单,就两个舵机接口,以及串口通信接口,还有两个电源引脚线。
用的是MG90s,非常常见的小舵机。(某宝直接截的图,忽略图中的水印哈哈哈哈)
程序主要是在K210官方例程的基础上改的,大体的布局就是K210跑视觉算法将相应的参数如识别出的人脸位置以及长度宽度,还有两个舵机的转角通过串口通信传输给stm32,而stm32则是将来自K210的转角值输入两个舵机中,从而是K210这颗大脑能指挥两个关节行动。
表情图像用的是B站一位up主何时登陆何时还 开源的oled表情包,详情可点击下面链接。开源代码教程stm32的oled表情显示
不过我这边不像视频那样做,我直接把表情图像存入SD卡,再让K210从SD卡中读取图片并显示在LCD上,当然本质上都是一样的,就是逐帧显示照片形成动画。
stm32所用的程序也是在官方例程的基础上改的,比较简单,只是增加了两个舵机函数,让stm32不断地执行从K210读取到转角命令。以下是stm32的主程序代码,其余全部源码将在项目完成后公开。
#include "AllHeader.h"
char buff_com[50];
msg_k210 k210_msg;
int main()
{
SystemInit();
Servo_Init1();
delay_init();
led_int();
USART2_init(115200);
LED = 0;
while(1)
{OLED_ShowNum(4, 1, k210_msg.class_n, 3);
if (k210_msg.class_n != 0)
{
if(k210_msg.class_n == 5)
{
Servo_SetAngle1(k210_msg.p1);
Servo_SetAngle0(k210_msg.p0);
k210_msg.class_n = 0;
delay_ms(10);
}
}
}
}
K210使用的编译软件是CanMV,以下代码的主要功能是让机器人醒着的时候盯着人看,当醒着的时间到4000秒(约67分钟)时,它会进入睡眠状态,当睡够3000秒即50分钟时会自然苏醒,当然睡眠状态下也可以通过触碰触摸屏将它唤醒。
import sensor, image, time, lcd
import touchscreen as ts
from maix import KPU
from modules import ybserial
import binascii
import gc
import random
serial = ybserial()
file_template = "/sd/EMO/{:04d}.bmp"
img_paths_mode1 = []
sleep =[]
for i in range(8):
img_paths_mode1.append(file_template.format(i))
for i in range(10, 22, 1):
sleep.append(file_template.format(i))
img_paths_mode2 = [
"/sd/EMO/{:04d}.bmp".format(i) for i in range(0, 38, 5)
]
def choose_display_mode():
s=random.random()
if s < 0.7:
print(s)
return img_paths_mode1
else:
print(s)
return img_paths_mode2
def str_int(data_str):
bb = binascii.hexlify(data_str)
bb = str(bb)[2:-1]
hex_1 = int(bb[0])*16
hex_2 = int(bb[1],16)
return hex_1+hex_2
def send_data(x,y,w,h,p0,p1,msg):
start = 0x24
end = 0x23
length = 5
class_num = 0x05
class_group = 0xBB
data_num = 0x00
fenge = 0x2c
crc = 0
data = []
low = x & 0xFF
high = x >> 8& 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
low = y & 0xFF
high = y >> 8& 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
low = w & 0xFF
high = w >> 8& 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
low = h & 0xFF
high = h >> 8& 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
low = p0 & 0xFF
high = p0 >> 8 & 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
low = p1 & 0xFF
high = p1 >> 8 & 0xFF
data.append(low)
data.append(fenge)
data.append(high)
data.append(fenge)
if msg !=None:
for i in range(len(msg)):
hec = str_int(msg[i])
data.append(hec)
data.append(fenge)
data_num = len(data)
length += len(data)
send_merr = [length,class_num,class_group,data_num]
for i in range(data_num):
send_merr.append(data[i])
for i in range(len(send_merr)):
crc +=send_merr[i]
crc = crc%256
send_merr.insert(0,start)
send_merr.append(crc)
send_merr.append(end)
global send_buf
send_buf = send_merr
last_time = time.time()
send_buf = []
x_ = 0
y_ = 0
w_ = 0
h_ = 0
p0_= 100
p0_0= 100
p1_= 57
p1_1= 57
k0=0.06
k1=0.04
X0=160
Y0=120
i = 0
sleepy =1
current_img_paths = choose_display_mode()
lcd.init()
ts.init()
status_last = ts.STATUS_IDLE
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 100)
clock = time.clock()
od_img = image.Image(size=(320,256))
anchor = (0.893, 1.463, 0.245, 0.389, 1.55, 2.58, 0.375, 0.594, 3.099, 5.038, 0.057, 0.090, 0.567, 0.904, 0.101, 0.160, 0.159, 0.255)
kpu = KPU()
kpu.load_kmodel("/sd/KPU/yolo_face_detect/yolo_face_detect.kmodel")
kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320, net_h=256, layer_w=10, layer_h=8, threshold=0.7, nms_value=0.3, classes=1)
img_paths = [
"/sd/EMO/{:04d}.bmp".format(i) for i in range(0,511,2)
]
print(img_paths)
while True:
gc.collect()
clock.tick()
img = sensor.snapshot()
a = od_img.draw_image(img, 0,0)
od_img.pix_to_ai()
kpu.run_with_output(od_img)
dect = kpu.regionlayer_yolo2()
fps = clock.fps()
for l in dect :
a = img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
x_ = l[0]
y_ = l[1]
w_ = l[2]
h_ = l[3]
p0_=round(p0_-(x_ +(h_/2)-X0)*k0+1)
p1_=round(p1_+(y_ +(w_/2)-Y0)*k0+1.5)
print(p0_)
print((x_ +(h_/2)-X0)*k0+1)
if sleepy==0:
if p0_>180:
p0_0=180
p0_=p0_0
elif p0_<50:
p0_0=50
p0_=p0_0
else:
p0_0=p0_
if p1_>82:
p1_1=82
p1_=p1_1
elif p1_<57:
p1_1=57
p1_=p1_1
else:
p1_1=p1_
else:
p0_0=100
p1_1=82
print(p0_0)
send_data(x_,y_,w_,h_,p0_0,p1_1,None)
serial.send_bytearray(send_buf)
print(p0_0)
(status, j, k) = ts.read()
if status_last != status:
print(status, j, k)
status_last = status
if status == ts.STATUS_MOVE:
sleepy=0
if sleepy==1:
if i <len(sleep):
img_path = sleep[i]
img_read = image.Image(img_path)
img2 = img_read.resize(300, 150)
img2 = img2.rotation_corr(z_rotation=180)
i=i+1
lcd.display(img2)
else:
i=0
else:
if i <len(current_img_paths):
img_path = current_img_paths[i]
img_read = image.Image(img_path)
img2 = img_read.resize(300, 150)
img2 = img2.rotation_corr(z_rotation=180)
i=i+1
lcd.display(img2)
else:
i=0
current_img_paths = choose_display_mode()
current_time = time.time()
if (current_time - last_time >= 4000)and(sleepy==0):
last_time = current_time
sleepy=1
print("I fall asleep", time.time())
elif (current_time - last_time >= 3000)and(sleepy==1):
last_time = current_time
sleepy=0
print("I wake up", time.time())
time.sleep(0.01)
kpu.deinit()
为了突出效果,视频中我把机器人的睡眠时间和清醒时间都调短了,大约每过五六秒就会睡着,视频里看起来像是它要睡觉但我一直骚扰它。
如图是用solidworks建的模型,结构比较简单,仅两个自由度,一个在头部,用于抬头和低头,一个在身体,用于转身。
如图为头部剖视图,里面放置了K210模块和舵机,灰色部分是轴承座。
如下图为身体的剖视图,ministm32安置在“心部”,可以说是它的心脏了,下边的舵机用于转动整个机身,再往下则是身体通过轴承与底座连接在一起。
相关知识
[DIY]用STM32实现桌面宠物功能#开源
基于STM32的智能物流机器人控制设计
AI智能新物种,桌面级别电子宠物来了,乐天派桌面机器人体验
基于STM32的智能宠物监测自动喂养
STM32单片机做智能宠物狗项目
年轻人的第一台桌面电子宠物!乐天派桌面机器人功能详解
用Python写个桌面挂件,手把手带你做只桌面宠物~
赛博宠物:桌面娱乐机器人的新革命
Python实现桌面挂件,做一只可爱的桌面宠物~
stm32毕设项目选题大全
网址: 用K210和stm32做一只桌面小机器人 https://m.mcbbbk.com/newsview747581.html
上一篇: 武隆县宠物保健品企业大全 |
下一篇: 宠物智能机器人的设计与实现 |