给机器人开发个功能让它帮我照看宝宝

本文首发于古月居

这篇博客主要讲述了如何通过OriginBot来看护宝宝,当宝宝的脸不在摄像头的范围之内时,发送消息到钉钉群组,通知家人及时查看。


前言

我在上个月有了宝宝,为了方便照看宝宝,就买了一个带有宝宝看护功能的摄像头,但是产品做的不怎么样,最重要的脸部遮挡检测功能用不了,后来就退货了。退货后就萌生了自己用OriginBot做一个类似功能的想法,于是就有了这篇博客~


功能流程图(架构图)

具体的流程或者说架构如下:

在这里插入图片描述

其实整体也不复杂,OriginBot上有一个MIPI摄像头,然后利用地平线TogetheROS.Bot的人体检测和跟踪来实时检测摄像头中有没有脸部,如果没有脸部,就向后端发送一条数据,然后后端会向钉钉群组发消息告诉家人。钉钉群组里面需要事先创建一个webhook。

下面会分为人体检测、判断有无脸部、后端操作三个部分来记录。


人体检测

这一部分用的是地平线TogetheROS.Bot现成的功能,启动OriginBot之后,在命令行中运行如下命令:

# 配置tros.b环境
source /opt/tros/setup.bash# 从tros.b的安装路径中拷贝出运行示例需要的配置文件。
cp -r /opt/tros/lib/mono2d_body_detection/config/ .# 配置MIPI摄像头
export CAM_TYPE=mipi# 启动launch文件
ros2 launch mono2d_body_detection mono2d_body_detection.launch.py

此时可以通过http://IP:8000查看检测效果,这个模块检测了人体、人头、人脸、人手检测框、检测框类型和目标跟踪ID以及人体关键点等,我只取其中的人脸部分,当然了,以后也可以增加如人体检测等。

上面的命令运行之后,在OriginBot上执行ros2 topic list,应该会有一个topic叫做hobot_mono2d_body_detection, 这个就是我们需要的,我们会订阅这个话题,然后分析其中发送数据来判断有没有脸部


判断摄像头中有没有脸部

按照TogetheROS.Bot的文档说明,hobot_mono2d_body_detection的消息类型是ai_msgs.msg.PerceptionTargets, 具体如下:

# 感知结果# 消息头
std_msgs/Header header# 感知结果的处理帧率
# fps val is invalid if fps is less than 0
int16 fps# 性能统计信息,比如记录每个模型推理的耗时
Perf[] perfs# 感知目标集合
Target[] targets# 消失目标集合
Target[] disappeared_targets

其中的disappeared_targets是我们关注的重点,如果face出现在disappeared_targets中,就说明之前是有脸部的,但是现在没有了,这个时候就要向后端发出数据进一步处理。

我为了判断有无脸部,写了一个ROS2 Node,代码如下:

import rclpy
from rclpy.node import Node
from ai_msgs.msg import PerceptionTargets
from cv_bridge import CvBridge
import timefrom api_connection import APIConnectionBabyMonitorMapping = {# 这里的k-v要根据后端Django中的值来确定"face": "看不到脸","body": "不在摄像头范围内",
}class FaceDetectionListener(Node):"""检测宝宝的脸是不是在摄像头中"""def __init__(self):super().__init__("face_detection")self.bridge = CvBridge()self.subscription = self.create_subscription(PerceptionTargets, "hobot_mono2d_body_detection", self.listener_callback, 10)self.conn = APIConnection()self.timer = time.time()self.counter = 0def listener_callback(self, msg):targets = msg.targetsdisappeared_targets = msg.disappeared_targetstargets_list = []disappeared_targets_list = []if disappeared_targets:for item in disappeared_targets:disappeared_targets_list.append(item.rois[0].type)if targets:for item in targets:targets_list.append(item.rois[0].type)print(f"检测到的对象如下:{targets_list}")print(f"消失的对象如下:{disappeared_targets_list}")if disappeared_targets_list:self.sending_notification(disappeared_targets_list)def sending_notification(self, disappeared_targets_list):for item in disappeared_targets_list:if BabyMonitorMapping.get(item):event = BabyMonitorMapping.get(item)if self.counter == 0:# 这里baby的ID是模拟的,应该去数据库中查data = {"event": event, "baby": "6b56979a-b2b9-11ee-920d-f12e14f97477"} self.conn.post_data(item=data, api="api/monitor/face-detection/")self.counter += 1self.timer = time.time()else:if time.time() - self.timer >= 60.0:# 60秒不重复发消钉钉消息data = {"event": event, "baby": "6b56979a-b2b9-11ee-920d-f12e14f97477"}self.conn.post_data(item=data, api="api/monitor/face-detection/")self.timer = time.time()self.counter += 1def main(args=None):rclpy.init(args=args)try:face_detection_listener = FaceDetectionListener()rclpy.spin(face_detection_listener)except KeyboardInterrupt:print("终止运行")finally:face_detection_listener.destroy_node()rclpy.shutdown()if __name__ == "__main__":main()

代码整体不难,但还是做几点必要的说明:
1. BabyMonitorMapping
这个字典的作用是把TogetheROS.Bot里面的字段跟后端的字段做一个映射关系,方便后面使用

2. API调用
代码中有两行如下:

data = {"event": event, "baby": "6b56979a-b2b9-11ee-920d-f12e14f97477"} 
self.conn.post_data(item=data, api="api/monitor/face-detection/")

这里的uri和data的格式由后端决定,不必细究,想了解细节的可以看后端代码。

3.APIConnection
APIConnection是用于请求的API的一个封装类,代码如下:

"""
API CONNECTION FOR IMPORTING WRAPPER
"""
import json
import logging
import requests
import envslogging.basicConfig(format="%(asctime)s %(levelname)-8s %(message)s",level=logging.INFO,datefmt="%Y-%m-%d %H:%M:%S",
)class APIConnection:"""Api Connection"""def __init__(self):self.api_url = envs.API_URLself.token = Noneself.headers = {"Content-Type": "application/json","Cache-Control": "no-cache",}self.request_jwt()def request_jwt(self):"""Request JWT token."""logging.info("Requesting JWT..")api_url = f"{self.api_url}api/token/"data = {"username": envs.SCRIPT_USER,"password": envs.SCRIPT_PASSWORD,}res = requests.post(api_url, data=json.dumps(data), headers=self.headers)if res.status_code == 200:data = res.json()self.token = data["access"]self.headers["Authorization"] = f"Bearer {self.token}"else:logging.error(f"Failed to obtain JWT. Status code: {res.status_code}, Message: {res.text}")def upload_video(self, api, file):"""post data:param item: items to be posted in json format:param api: path of endpoint"""api_url = f"{self.api_url}{api}"try:res = requests.post(api_url, files=file, headers=self.headers, timeout=1)if res.status_code == 401:self.request_jwt()self.post_data(api, file)elif res.status_code in [200, 201]:logging.info(f"{res.status_code} - video uploaded successfully")return res.status_codeelse:logging.error(f"{res.status_code} - {res.json()}- unable to upload video")return res.status_codeexcept Exception as err:logging.error(err)return 500def post_data(self, item, api):"""新建一条数据"""api_url = f"{self.api_url}{api}"try:response = requests.post(api_url, data=json.dumps(item), headers=self.headers, timeout=1)if response.status_code == 403:self.request_jwt()self.post_data(item, api)elif response.status_code not in [200, 201]:logging.error(f"post data to backend failed, \status code is {response.status_code}, \error message is:\n \{response.text}")except Exception as err:logging.error(f"post data to backend failed, \error message is:\n \{err}")

后端操作

经过前面两个部分,如果发现了脸部数据从OriginBot的摄像头中消失的话,已经会把记录发送到后端了,现在来讲一下后端的部分。

后端的操作说起来其实也很简单,就是接收到数据后,现在数据库中存一份,然后再向钉钉发消息提醒家人。

在OriginBot家庭助理计划里面提到过,后端是基于Django + Django rest framework 开发的,下面的内容需要一点django的基础才能看懂。

首先是创建了两个django model来存储数据。

class Baby(models.Model):"""记录的宝宝的数据"""id = models.UUIDField(primary_key=True, default=uuid.uuid1, editable=False)name = models.CharField(max_length=256)birth_date = models.DateField()gender = models.CharField(max_length=1, choices=(("男", "男"), ("女", "女")))def __str__(self):return self.nameclass BabyMonitorData(models.Model):"""记录宝宝监控相关的数据"""event_choices = (("看不到脸", "看不到脸"),("哭", "哭"),("翻身", "翻身"),("不在摄像头范围内", "不在摄像头范围内"),)baby = models.ForeignKey(Baby, on_delete=models.PROTECT)event = models.CharField(max_length=128, choices=event_choices)timestamp = models.DateTimeField(auto_now_add=True)def __str__(self):return f"{self.baby.name} {self.event} {self.timestamp}"class Meta:ordering = ["-timestamp"]

需要注意的是,Baby和BabyMonitorData两个类之间存在外键关系。

在FaceDetectionListener中请求的api/monitor/face-detection/这个uri,最终在后端执行的代码是下面这一段:

class BabyMonitorView(viewsets.ModelViewSet):queryset = BabyMonitorData.objects.all().order_by("-timestamp")serializer_class = BabyMonitorSerializerdef create(self, request, *args, **kwargs):response = super().create(request, *args, **kwargs)message = request.datatry:event = message.get("event")baby = Baby.objects.filter(id=message.get("baby"))[0].nameexcept IndexError:print("找不到对应的婴儿数据")return responsesend_msg_to_dingtalk(f"{baby} {event} 啦!")# 返回原始的 responsereturn response

这里面做的事情就是向BabyMonitorData中存一条数据,然后通过send_msg_to_dingtalksend_msg_to_dingtalk给钉钉群组发消息。

send_msg_to_dingtalk的代码如下:

import json
import requests
from utils import envsfrom datetime import datetimedef send_msg_to_dingtalk(msg):webhook = envs.DING_TALK_URLheaders = {"Content-Type": "application/json;charset=utf-8"}data = {"msgtype": "text","text": {"content": msg+ f"[来自originbot,{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]"},}response = requests.post(webhook, headers=headers, data=json.dumps(data))return response.textif __name__ == "__main__":message = "Hello from my Python script!"send_msg_to_dingtalk(message)

代码中的webhook = envs.DING_TALK_URL其实是获取环境变量中钉钉群组机器人链接,至于如何创建钉钉群组机器人网上有很多教程,不再赘述。

到这里,如果一切顺利,应该就可以在钉钉里面看到消息啦,效果如下:

在这里插入图片描述


源代码

源码地址:https://github.com/yexia553/originbot_home_assistant


待优化

虽然一个最基础的demo完成了,但是还有很多需要优化的地方:

  1. 小车只能在地上跑,但是宝宝一般在床上,只有把小车放在特定的位置,才能实现上述功能,这限制了实际应用,需要解决。
  2. 续航问题,目前小车的电池只能使用2个小时左右
  3. 目前只是检测摄像头中有没有脸,而不是检测宝宝的脸,可以考虑优化
  4. 可以增加类似哭声检测、翻身等动作识别,并加上数据分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/630443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenLayers实战,OpenLayers点聚合有相同经纬度坐标时无法展开问题解决办法,当缩放级别达到一定等级后强行展开聚合为单个点

专栏目录: OpenLayers实战进阶专栏目录 前言 本章用于解决OpenLayers使用Cluster点聚合情况下,要素(Feature)出现有相同经纬度坐标时无法展开成单独图标的问题解决办法以及当缩放级别达到一定等级后强行展开聚合为单个点的功能。 本章展开后由于经纬度坐标还是同一个点,…

SAP 销售订单审批状态(查询/修改)

销售订单审批状态启用后,前端显示界面如下图 销售订单审批状态读取:STATUS_READ 销售订单审批状态修改:I_CHANGE_STATUS 销售订单审批状态读取 代码样例如下: DATA: lv_objnr TYPE vbak-objnr,lv_objnr_t TYPE jsto-objnr,l…

LeetCode:707. 设计链表

力扣题目链接 单链表 class ListNode {int val;ListNode next;public ListNode(int val) {this.val val;} } class MyLinkedList {int size;ListNode head;public MyLinkedList() {size 0;head new ListNode(0);}public int get (int index) {if ( index < 0 || index …

20/76-卷积,填充,步幅,多通道输入输出

19/76 卷积层总结 1、卷积层将输入矩阵和核矩阵进行交叉相关&#xff0c;加上偏移所得到输出。 2、核矩阵和偏移是可学习的参数。 3、核矩阵的大小是超参数。 import torch from torch import nndef corr2d(X, K): # 本函数已保存在d2lzh_pytorch包中方便以后使用,x是输入&a…

难道说 IT行业的下一个风口是鸿蒙开发吗?

按往年的习俗&#xff0c;在年底之季有很多HC都会缩减&#xff0c;尤其当下各种裁员的情况下&#xff0c;不管你是在哪个传统开发行业&#xff0c; 如&#xff1a;C/C、Java、前端、后端……等多少都会一股互联网寒流的影响。而今年却出现了一个怪现象&#xff0c;有个岗位在这…

微信小程序打包上线流程

微信公众平台 https://mp.weixin.qq.com/注册一个小程序账号&#xff08;小程序和公众号&#xff0c;服务号等&#xff0c;不互通&#xff09;按流程注册开发&#xff0c;开发设置&#xff0c;小程序ID 发布流程 4.微信开发者工具点击上传 5. 上传代码 6. 提交审核 7. 发布 在…

Ubuntu1804下如何切换python版本

Ubuntu1804下如何切换python版本 目录 1 如何查看当前python版本 2 如何配置默认python版本 2.1 配置python2为默认版本 2.2 配置python3为默认版本 3 小结 1 如何查看当前python版本 可以用过以下命令&#xff0c;查看当前python默认版本&#xff1a; python --version2 …

【欢迎您的到来】这里是开源库get_local_info作者的付费专栏

您好&#xff0c; 我是带剑书生&#xff0c;开源库get_local_info的作者&#xff0c;欢迎您的到来&#xff0c;这里是我的付费专栏&#xff0c;会用更简洁的语言&#xff0c;更通俗的话语&#xff0c;来帮助您更好的学习rust&#xff0c;这里不仅仅讲解Rust在某些应用功能实现上…

Java可视化物联网智慧工地综合云平台源码 私有化部署

智慧工地平台围绕建筑施工人、物、事的安全管理为核心&#xff0c;对应研发了劳务实名制、视频监控、扬尘监测、起重机械安全监测、安全帽监测等功能一体化管理的解决方案。 智慧工地是聚焦工程施工现场&#xff0c;紧紧围绕人、机、料、法、环等关键要素&#xff0c;综合运用…

智慧矿山知识点总结

目录 1.第一章数字矿山的定义、基本特征和功能数字矿山的框架结合专业&#xff0c;如何发挥专业优势参与数字矿山建设&#xff08;GPT&#xff09; 2.第二章矿区地质构造和生态环境信息都包括哪些内容地球探测信息技术分类矿山品味与储量矿量估算 3.第三章矿山空间信息获取的仪…

Linux grep命令教程:强大的文本搜索工具(附案例详解和注意事项)

Linux grep命令介绍 grep (Global Regular Expression Print)命令用来在文件中查找包含或者不包含某个字符串的行&#xff0c;它是强大的文本搜索工具&#xff0c;并可以使用正则表达式进行搜索。当你需要在文件或者多个文件中搜寻特定信息时&#xff0c;grep就显得无比重要啦…

RT-Thread Studio学习(十四)ADC

RT-Thread Studio学习&#xff08;十四&#xff09;ADC 一、简介二、新建RT-Thread项目并使用外部时钟三、启用ADC四、测试 一、简介 本文将基于STM32F407VET芯片介绍如何在RT-Thread Studio开发环境下使用ADC设备。硬件及开发环境如下&#xff1a; OS WIN10STM32F407VET6STM…

基于Springboot的摄影分享网站系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的摄影分享网站系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

Python文件自动化处理

os模块 Python标准库和操作系统有关的操作创建、移动、复制文件和文件夹文件路径和名称处理 路径的操作 获取当前Python程序运行路径不同操作系统之间路径的表示方式 windows中采用反斜杠(\)作为文件夹之间的分隔符 Mac和Linux中采用斜杠(/)作为文件夹之间的分隔符 把文件…

算法专题:差分数组

文章目录 前置知识构建一个差分数组操作一个连续区间 练习习题1094. 拼车1109. 航班预订统计1450. 在既定时间做作业的学生人数2406. 将区间分为最小组数2381. 字母移位II2772. 使数组中的所有元素都等于零 前置知识 构建一个差分数组 void func(int * lb1,int len) {int * l…

详解React与Vue的性能对比

React 和 Vue 是当前最流行的前端开发框架之一。它们都具有高度的灵活性和可扩展性&#xff0c;但在某些方面有所不同。在本篇文章中&#xff0c;我将详细介绍 React 和 Vue 这两个技术&#xff0c;并比较它们的优点和缺点。 目录 1. React&#xff1a; 1.1 优点&#xff1a; …

Qt之QxOrm

QxORM介绍 QxORM库是一种为了C/Qt开发者服务的关系对象映射型数据库的类库&#xff0c;每个类都有简单的C设置函数&#xff0c;你可以接触到如下特性&#xff1a; 持久性&#xff1a;支持最常见的数据库&#xff0c;如 SQLite、MySQL、PostgreSQL、Oracle、MS SQL Server、Mon…

2023年第十四届蓝桥杯软件赛省赛总评

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…

VCG 网格清洗之移除小组件

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 基于之前连通性的思路,我们可以很容易获取到许多连通的面片集合,这里我们根据指定的数量阈值来剔除较小的面片集合,以此达到网格清洗的目的。 二、实现代码 //VCG #include <vcg/complex/algorithms/create/…

[AI争霸] 普通人看ChatGPT和文心一言那个更好

文章目录 引言便利性准确性总结 引言 从2022/2/24号openAI正式发布chatgpt第一个版本以来&#xff0c;到2023年中旬openAI宣布chatgpt成为用户增长量最快的软件&#xff0c;随后掀起的大模型热。随后国内的各大厂商纷纷推出自己的大模型&#xff0c;首当其冲的就是百度的文心一…