【项目背景】
在日常生活中,打扫卫生时常常需要频繁移动垃圾桶,这给使用者带来了诸多不便。为了解决这一问题,我计划设计一个行空智能跟随垃圾桶,它能够在听到我的呼唤后自动跟随我,方便我在打扫过程中随时使用。经过研究和尝试,我选择了在行空板M10上安装Snowboy库进行离线语音唤醒,并使用OpenCV进行人体检测,同时借助硅基流动平台的图像理解大模型进行图像分析,以实现这一功能。
【项目设计】
我将设计一个行空智能跟随垃圾桶,其主要功能如下:
离线语音唤醒:通过安装在行空板M10上的Snowboy库,垃圾桶能够离线识别我的唤醒词,“你好小云”,并迅速做出响应。
人体检测与图像分析:垃圾桶会旋转并使用OpenCV进行人体检测,若未检测到人体,则旋转一定角度(90度)再次检测,直至回到起点。一旦检测到人体,垃圾桶会将照片上传至硅基流动平台进行图像分析,判断是否有人拿着打扫工具。
自动跟随与避障:如果发现有人拿着打扫工具,垃圾桶会驱动电机前进,并通过超声波传感器测量与人的距离,当距离达到30cm时自动停止,确保在打扫室内卫生时能够方便地跟随使用者。
【制作步骤】
1.安装依赖库
sudo apt-get install python-pyaudio python3-pyaudio swig
2.下载代码
https://github.com/Kitt-AI/snowboy.git
3.安装puaduio库
sudo pip3 install pyaudio
4.生成个人唤醒词模型
网址https://snowboy.hahack.com/,方法:记录3个唤醒词示例,并将其提交以生成.pmdl文件。
(1)启用麦克风(Chrome需要)
(2)单击录制并等待准备就绪
(3)说出你的醒语,等待结束
(4)重复,直到你有3个例子
(5)输入模型名称,提交音频,然后单击“保存模型”按钮
(6)下载模型

5.将模型上传到行空板
【代码编写】
1.修改snowboy/examples/demo.py代码,实现直接加载指定唤醒词模型
#if len(sys.argv) == 1:
# print("Error: need to specify model name")
# print("Usage: python demo.py your.model")
# sys.exit(-1)
#model = sys.argv[1]
model = "/root/snowboy/resources/models/xiaoxing.pmdl"
2.修改snowboy/examples/demo.py/snowboydecoder.py文件,实现人体检测、图像分析、电机驱动。
(1)加载语音提醒,“我在呢”,“看到你了,我来了”
DETECT_Hello = os.path.join(TOP_DIR, "resources/xiaoxing.wav")
DETECT_COME = os.path.join(TOP_DIR, "resources/come.wav")
(2)使用OpenCV的Haar 检测器,进行人体检测。如检测到人体将此图像进行Base64编码。
import cv2
# 加载 Haar 检测器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
def camera():
global bs,cap
if bs==0:
# 打开摄像头
cap = cv2.VideoCapture(0) # 参数 0 表示使用默认摄像头
if not cap.isOpened():
print("无法打开摄像头")
return -1
bs=1
# 读取一帧
ret, frame = cap.read()
if not ret:
print("无法读取帧")
return 0
# 检测行人
# 转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
# 判断是否有人
if len(faces) > 0:
play_audio_file(DETECT_DING)
print("有人")
# 将 OpenCV 的 BGR 图像转换为 PIL 的 RGB 图像
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(frame_rgb)
# 转换为 Base64 编码
base64_image = convert_image_to_webp_base64(pil_image)
if base64_image:
print("Base64 编码成功!")
# 在这里可以处理 Base64 编码的字符串,例如打印或发送到服务器
print(base64_image[:20]) # 打印前 100 个字符作为示例
Vlcontent=QwenVL(base64_image)
print(Vlcontent)
if "是" in Vlcontent:
play_audio_file(DETECT_COME)
print("yes")
return 1
else:
print("no")
return 0
else:
print("Base64 编码失败!")
return 0
else:
play_audio_file(DETECT_DONG)
print("无人")
return 0
def convert_image_to_webp_base64(image):
"""
将图像对象转换为 WebP 格式的 Base64 编码字符串。
:param image: PIL.Image 对象
:return: Base64 编码字符串
"""
try:
byte_arr = io.BytesIO()
image.save(byte_arr, format='webp')
byte_arr = byte_arr.getvalue()
base64_str = base64.b64encode(byte_arr).decode('utf-8')
return base64_str
except IOError:
print("Error: Unable to convert the image to WebP Base64")
return None
(3)使用硅基流动的图像理解大模型,进行图像分析,检测是否有人拿着打扫工具。
def QwenVL(base64_url):
response = client.chat.completions.create(
model="Qwen/Qwen2-VL-72B-Instruct",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "data:image/jpg;base64,"+base64_url
}
},
{
"type": "text",
"text": "请看一下这张图像中,如果有一个人手中拿着打扫工具,请回复是,否则回复否。"
}
]
}],
stream=False
)
return response.choices[0].message.content
(4)唤醒后,控制电机转向并检测,识别到目标,驱动电机前进,并利用超声传感器测距。
def play_audio_file(fname=DETECT_Hello):
"""Simple callback function to play a wave file. By default it plays
a Ding sound.
:param str fname: wave file name
:return: None
"""
ding_wav = wave.open(fname, 'rb')
ding_data = ding_wav.readframes(ding_wav.getnframes())
with no_alsa_error():
audio = pyaudio.PyAudio()
stream_out = audio.open(
format=audio.get_format_from_width(ding_wav.getsampwidth()),
channels=ding_wav.getnchannels(),
rate=ding_wav.getframerate(), input=False, output=True)
stream_out.start_stream()
stream_out.write(ding_data)
time.sleep(0.2)
stream_out.stop_stream()
stream_out.close()
audio.terminate()
if fname==DETECT_Hello:
print("已唤醒")
for i in range(4):
j=camera()
if j==0:
#转向
turn(1,800)
time.sleep(0.3)
stop()
time.sleep(4)
continue
else:
break
if j==1:
print("前进")
#前进
while True:
print(urm091.distance_cm())
if urm091.distance_cm()>30:
goahead(1,800)
else:
stop()
break
(5)电机驱动
def goahead(direction,PWM):
p_p23_pwm.write_analog(PWM)
p_p24_out.write_digital(direction)
p_p9_out.write_digital(direction)
p_p21_pwm.write_analog(PWM)
p_p1_out.write_digital(1-direction)
p_p4_out.write_digital(1-direction)
def turn(direction,PWM):
p_p23_pwm.write_analog(PWM)
p_p24_out.write_digital(direction)
p_p9_out.write_digital(direction)
p_p21_pwm.write_analog(PWM)
p_p1_out.write_digital(direction)
p_p4_out.write_digital(direction)
def stop():
p_p23_pwm.write_analog(0)
p_p21_pwm.write_analog(0)
【硬件组装】
1.底盘使用麦克纳姆轮,两个两路电机驱动,两节锂电池供电。

2.使用行空板M10及扩展上的相应引脚(代码中有设置),连接电机驱动、超声波传感器。因使用了独立的电机驱动,扩展板上的电机驱动引脚未使用。

3.固定垃圾桶

4.固定摄像头

【演示视频】
【代码附件】
因附件大小限制为10M,删除了一部分无关本项目的文件。
评论