ros2-7.5 做一个自动巡检机器人
7.5.1 需求及设计
又到了小鱼老师带着做最佳实践项目了。需求:做一个在各个房间不断巡逻并记录图像的机器人。
到达目标点后首先通过语音播放到达目标点信息,
再通过摄像头拍摄一张图片保存到本地。
7.5.2 编写巡检控制节点
在chapt7_ws/src下新建功能包,添加rclpy和nav2_simple_commander依赖。
目录src/autopartol_robot/autopartol_robot/新建文件patrol_node.py
from geometry_msgs.msg import PoseStamped,Pose
from nav2_simple_commander.robot_navigator import BasicNavigator,TaskResult
import rclpy
from rclpy.node import Node
import rclpy.time
from tf2_ros import TransformListener, Buffer #坐标监听器
from tf_transformations import quaternion_from_euler,euler_from_quaternion# 欧拉角转换class PatrolNode(BasicNavigator):def __init__(self,node_name='patrol_node'):super().__init__(node_name)self.buffer_ = Buffer()self.listner_ = TransformListener(self.buffer_,self)#声明相关参数self.declare_parameter('initial_point',[0.0, 0.0, 0.0])self.declare_parameter('target_points',[0.0, 0.0, 0.0, 1.0, 1.0, 1.57])self.initial_point_ = self.get_parameter('initial_point').valueself.initial_points_= self.get_parameter('target_points').valuedef get_pose_by_xyyaw(self, x, y, yaw):# 通过x,y,yaw return PoseStamped对象 pose = PoseStamped()pose.header.frame_id = 'map'pose.pose.position.x = xpose.pose.position.y = yquat = quaternion_from_euler(0,0,yaw)pose.pose.orientation.x =quat[0]pose.pose.orientation.y =quat[1]pose.pose.orientation.z =quat[2]pose.pose.orientation.w =quat[3]return posedef init_robot_pose(self):#初始化机器人位姿self.initial_point_ = self.get_parameter('initial_point').valueinit_pose = self.get_pose_by_xyyaw(self.initial_point_[0],self.initial_point_[1],self.initial_point_[2]) self.setInitialPose(init_pose)self.waitUntilNav2Active()#等待导航可用def get_target_points(self):#通过参数值获取目标点集合points = []self.target_points_ = self.get_parameter('target_points').valuefor index in range(int(len(self.target_points_)/3)):x = self.target_points_[index*3]y = self.target_points_[index*3+1]yaw = self.target_points_[index*3+2]points.append([x,y,yaw])self.get_logger().info(f'获取到目标点:{index}->{x},{y},{yaw}')return points def nav_to_pose(self,target_pose):#导航到指定位姿self.waitUntilNav2Active()self.goToPose(target_pose)while not self.isTaskComplete():feedback = self.getFeedback()self.get_logger().info(f'剩余距离:{feedback.distance_remaining }')reslut = self.getResult()self.get_logger().info(f'导航结果:{reslut}') def get_cuurent_pose(self):#通过TF获取当前位姿while rclpy.ok():try:result = self.buffer_.lookup_transform('map','base_footprint',rclpy.time.Time(seconds=0),rclpy.time.Duration(seconds=1))transform = result.transformself.get_logger().info(f'平移:{transform.translation}')# self.get_logger().info(f'旋转:{transform.rotation}')# ratation_euler = euler_from_quaternion([# transform.rotation.x,# transform.rotation.y,# transform.rotation.z,# transform.rotation.w]# )# self.get_logger().info(f'平移rpy:{ratation_euler}')return transformexcept Exception as e:self.get_logger().warn(f'获取坐标转换异常{str(e)}')def main():rclpy.init()patrol = PatrolNode() #节点patrol.initial_pose()while rclpy.ok():points = patrol.get_target_points()for point in points:x,y,yaw = point[0],point[1],point[2]target_pose = patrol.get_pose_by_xyyaw(x,y,yaw)patrol.nav_to_pose(target_pose)rclpy.shutdown()
基本上吧之前的单接口调用综合起来。
为方便参数配置,在src/autopartol_robot/新建目录config,新增配置文件
patrol_config.yaml
patrol_node:ros__parameters:initial_point: [0.0, 0.0, 0.0]target_points: [0.0,0.0,0.0,1.0,2.0,3.14,-4.5,1.5,1.57,-8.0,-5.0,1.57,1.0,-5.0,3.14,]
注意目标点的选取是机器人可达的位置,不能选到障碍物。
启动gazebo模拟器,启动nav2.
再启动patrol_node
ros2 run autopartol_robot patrol_node --ros-args --params-file /home/bohu/chapt7/chapt7_ws/install/autopartol_robot/share/autopartol_robot/config/patrol_config.yaml
会发现等一会开始初始化地图后,开始沿着设定目标点运动。效果如下
也有异常情况,机器人靠墙太近,gazebo看出距离墙还有段距离,但是在rviz里面局部代价地图有一点变形,导致机器以为有障碍物卡住的现象。
7.5.3 添加语音播报功能
在chatpt7_ws/src下新建功能包autopatrol_interfaces,功能包下新建目录srv,srv新新建接口文件
speachText.srv
string text # 合成文字
---
bool result # 合成结果
在CMakeLists.txt注册,并在package.xml添加标签
<member_of_group>rosidl_interface_packages</member_of_group>
重新构建
src/autopartol_robot/autopartol_robot/目录下新建文件speaker.py
import rclpy
from rclpy.node import Node
from autopatrol_interfaces.srv import SpeachText
import espeakngclass Speaker(Node):def __init__(self):super().__init__('speaker')self.speech_service_ = self.create_service(SpeachText,'speech_text',self.speech_text_callback)self.speaker_ = espeakng.Speaker()self.speaker_.voice ='zh'def speech_text_callback(self,request,response):self.get_logger().info(f'正在朗读 {request.text}')self.speaker_.say(request.text)self.speaker_.wait()response.result = Truereturn responsedef main():rclpy.init()node = Speaker()rclpy.spin(node)rclpy.shutdown()
修改patrol_node.py,增加语音合成调用
from geometry_msgs.msg import PoseStamped,Pose
from nav2_simple_commander.robot_navigator import BasicNavigator,TaskResult
import rclpy
from rclpy.node import Node
import rclpy.time
from tf2_ros import TransformListener, Buffer #坐标监听器
from tf_transformations import quaternion_from_euler,euler_from_quaternion# 欧拉角转换
from autopatrol_interfaces.srv import SpeachTextclass PatrolNode(BasicNavigator):def __init__(self,node_name='patrol_node'):super().__init__(node_name)self.buffer_ = Buffer()self.listner_ = TransformListener(self.buffer_,self)#声明相关参数self.declare_parameter('initial_point',[0.0, 0.0, 0.0])self.declare_parameter('target_points',[0.0, 0.0, 0.0, 1.0, 1.0, 1.57])self.initial_point_ = self.get_parameter('initial_point').valueself.initial_points_= self.get_parameter('target_points').valueself.speech_client_ = self.create_client(SpeachText,'speech_text')def get_pose_by_xyyaw(self, x, y, yaw):# 通过x,y,yaw return PoseStamped对象 pose = PoseStamped()pose.header.frame_id = 'map'pose.pose.position.x = xpose.pose.position.y = yquat = quaternion_from_euler(0,0,yaw)pose.pose.orientation.x =quat[0]pose.pose.orientation.y =quat[1]pose.pose.orientation.z =quat[2]pose.pose.orientation.w =quat[3]return posedef init_robot_pose(self):#初始化机器人位姿self.initial_point_ = self.get_parameter('initial_point').valueinit_pose = self.get_pose_by_xyyaw(self.initial_point_[0],self.initial_point_[1],self.initial_point_[2]) self.setInitialPose(init_pose)self.waitUntilNav2Active()#等待导航可用def get_target_points(self):#通过参数值获取目标点集合points = []self.target_points_ = self.get_parameter('target_points').valuefor index in range(int(len(self.target_points_)/3)):x = self.target_points_[index*3]y = self.target_points_[index*3+1]yaw = self.target_points_[index*3+2]points.append([x,y,yaw])self.get_logger().info(f'获取到目标点:{index}->{x},{y},{yaw}')return points def nav_to_pose(self,target_pose):#导航到指定位姿self.waitUntilNav2Active()self.goToPose(target_pose)while not self.isTaskComplete():feedback = self.getFeedback()self.get_logger().info(f'剩余距离:{feedback.distance_remaining }')reslut = self.getResult()self.get_logger().info(f'导航结果:{reslut}') def get_cuurent_pose(self):#通过TF获取当前位姿while rclpy.ok():try:result = self.buffer_.lookup_transform('map','base_footprint',rclpy.time.Time(seconds=0),rclpy.time.Duration(seconds=1))transform = result.transformself.get_logger().info(f'平移:{transform.translation}')# self.get_logger().info(f'旋转:{transform.rotation}')# ratation_euler = euler_from_quaternion([# transform.rotation.x,# transform.rotation.y,# transform.rotation.z,# transform.rotation.w]# )# self.get_logger().info(f'平移rpy:{ratation_euler}')return transformexcept Exception as e:self.get_logger().warn(f'获取坐标转换异常{str(e)}')def speech_text(self,text):#调用服务合成语音while not self.speech_client_.wait_for_service(timeout_sec=1):self.get_logger().info("wait for speech service")request = SpeachText.Request()request.text = textfuture = self.speech_client_.call_async(request)rclpy.spin_until_future_complete(self,future)if future.result() is not None:result = future.result().resultif result:self.get_logger().info(f'语音合成成功:{text}')else:self.get_logger().warn(f'语音合成失败:{text}')else:self.get_logger().warn('语音合成服务请求失败')def main():rclpy.init()patrol = PatrolNode() #节点patrol.speech_text('正在准备初始化位置')patrol.init_robot_pose()patrol.speech_text('初始化位置完成')while rclpy.ok():points = patrol.get_target_points()for point in points:x,y,yaw = point[0],point[1],point[2]target_pose = patrol.get_pose_by_xyyaw(x,y,yaw)patrol.speech_text(text=f'准备前往目标点{x},{y}')patrol.nav_to_pose(target_pose)rclpy.shutdown()
效果跟上面类似,日志输出多了语音的
[speaker-2] [INFO] [1737017187.818829250] [speaker]: 正在朗读 准备前往目标点1.0,2.0
[patrol_node-1] [INFO] [1737017191.194245817] [patrol_node]: 语音合成成功:准备前往目标点1.0,2.0
[patrol_node-1] [INFO] [1737017195.311579068] [patrol_node]: Nav2 is ready for use!
[patrol_node-1] [INFO] [1737017195.314096555] [patrol_node]: Navigating to goal: 1.0 2.0...
[patrol_node-1] [INFO] [1737017195.509438991] [patrol_node]: 剩余距离:0.2445448786020279
[patrol_node-1] [INFO] [1737017195.612344544] [patrol_node]: 剩余距离:2.230710744857788
[patrol_node-1] [INFO] [1737017195.716231929] [patrol_node]: 剩余距离:2.230710744857788
[patrol_node-1] [INFO] [1737017195.819218225] [patrol_node]: 剩余距离:2.230710744857788
[patrol_node-1] [INFO] [1737017195.923079415] [patrol_node]: 剩余距离:2.230710744857788
7.5.4订阅图像并记录
from geometry_msgs.msg import PoseStamped,Pose
from nav2_simple_commander.robot_navigator import BasicNavigator,TaskResult
import rclpy
from rclpy.node import Node
import rclpy.time
from tf2_ros import TransformListener, Buffer #坐标监听器
from tf_transformations import quaternion_from_euler,euler_from_quaternion# 欧拉角转换
from autopatrol_interfaces.srv import SpeachText
from sensor_msgs.msg import Image #消息接口
from cv_bridge import CvBridge #图像转换
import cv2 #保存图像class PatrolNode(BasicNavigator):def __init__(self,node_name='patrol_node'):super().__init__(node_name)self.buffer_ = Buffer()self.listner_ = TransformListener(self.buffer_,self)#声明相关参数self.declare_parameter('initial_point',[0.0, 0.0, 0.0])self.declare_parameter('target_points',[0.0, 0.0, 0.0, 1.0, 1.0, 1.57])self.initial_point_ = self.get_parameter('initial_point').valueself.initial_points_= self.get_parameter('target_points').valueself.speech_client_ = self.create_client(SpeachText,'speech_text')# 订阅与保存图像相关定义self.declare_parameter('image_save_path', '')self.image_save_path = self.get_parameter('image_save_path').valueself.bridge = CvBridge()self.latest_img_ = Noneself.image_sub_ = self.create_subscription(Image,'/camera_sensor/image_raw',self.img_callback,1)def img_callback(self,msg):self.latest_img_ = msgdef record_img(self):if self.latest_img_ is not Node:pose = self.get_cuurent_pose()cv_image = self.bridge.imgmsg_to_cv2(self.latest_img_)cv2.imwrite(f'{self.image_save_path}img_{pose.translation.x:3.2f}_{pose.translation.y:3.2f}.png',cv_image) def get_pose_by_xyyaw(self, x, y, yaw):# 通过x,y,yaw return PoseStamped对象 pose = PoseStamped()pose.header.frame_id = 'map'pose.pose.position.x = xpose.pose.position.y = yquat = quaternion_from_euler(0,0,yaw)pose.pose.orientation.x =quat[0]pose.pose.orientation.y =quat[1]pose.pose.orientation.z =quat[2]pose.pose.orientation.w =quat[3]return posedef init_robot_pose(self):#初始化机器人位姿self.initial_point_ = self.get_parameter('initial_point').valueinit_pose = self.get_pose_by_xyyaw(self.initial_point_[0],self.initial_point_[1],self.initial_point_[2]) self.setInitialPose(init_pose)self.waitUntilNav2Active()#等待导航可用def get_target_points(self):#通过参数值获取目标点集合points = []self.target_points_ = self.get_parameter('target_points').valuefor index in range(int(len(self.target_points_)/3)):x = self.target_points_[index*3]y = self.target_points_[index*3+1]yaw = self.target_points_[index*3+2]points.append([x,y,yaw])self.get_logger().info(f'获取到目标点:{index}->{x},{y},{yaw}')return points def nav_to_pose(self,target_pose):#导航到指定位姿self.waitUntilNav2Active()self.goToPose(target_pose)while not self.isTaskComplete():feedback = self.getFeedback()self.get_logger().info(f'剩余距离:{feedback.distance_remaining }')reslut = self.getResult()self.get_logger().info(f'导航结果:{reslut}') def get_cuurent_pose(self):#通过TF获取当前位姿while rclpy.ok():try:result = self.buffer_.lookup_transform('map','base_footprint',rclpy.time.Time(seconds=0),rclpy.time.Duration(seconds=1))transform = result.transformself.get_logger().info(f'平移:{transform.translation}')# self.get_logger().info(f'旋转:{transform.rotation}')# ratation_euler = euler_from_quaternion([# transform.rotation.x,# transform.rotation.y,# transform.rotation.z,# transform.rotation.w]# )# self.get_logger().info(f'平移rpy:{ratation_euler}')return transformexcept Exception as e:self.get_logger().warn(f'获取坐标转换异常{str(e)}')def speech_text(self,text):#调用服务合成语音while not self.speech_client_.wait_for_service(timeout_sec=1):self.get_logger().info("wait for speech service")request = SpeachText.Request()request.text = textfuture = self.speech_client_.call_async(request)rclpy.spin_until_future_complete(self,future)if future.result() is not None:result = future.result().resultif result:self.get_logger().info(f'语音合成成功:{text}')else:self.get_logger().warn(f'语音合成失败:{text}')else:self.get_logger().warn('语音合成服务请求失败')def main():rclpy.init()patrol = PatrolNode() #节点patrol.speech_text('正在准备初始化位置')patrol.init_robot_pose()patrol.speech_text('初始化位置完成')while rclpy.ok():points = patrol.get_target_points()for point in points:x,y,yaw = point[0],point[1],point[2]target_pose = patrol.get_pose_by_xyyaw(x,y,yaw)patrol.speech_text(text=f'准备前往目标点{x},{y}')patrol.nav_to_pose(target_pose)patrol.speech_text(text=f'已到达目标点{x},{y},准备记录图像')patrol.record_img()patrol.speech_text(text=f'图像记录完成')rclpy.shutdown()
重新构建后运行,拍照效果如下
相关文章:

ros2-7.5 做一个自动巡检机器人
7.5.1 需求及设计 又到了小鱼老师带着做最佳实践项目了。需求:做一个在各个房间不断巡逻并记录图像的机器人。 到达目标点后首先通过语音播放到达目标点信息, 再通过摄像头拍摄一张图片保存到本地。 7.5.2 编写巡检控制节点 在chapt7_ws/src下新建功…...

服务器下发任务镭速利用变量实现高效的大文件传输效率
在分布式系统和自动化部署场景中,任务下发往往伴随着大量的文件传输需求。为了提高文件传输的效率,本文将介绍如何巧妙地利用变量来优化任务下发过程中的文件传输。我们将介绍几种方法,通过合理利用变量来减少传输负担、提升传输速度…...

本地用docker装mysql
目录 拉取镜像查看镜像 启动容器查看运行中的容器连接到 MySQL 容器其他一些操作 装WorkBench链接mysql——————————————允许远程登录MySql 拉取镜像 docker pull mysql查看镜像 docker image lsREPOSITORY TAG IMAGE ID CREATED SIZE mysq…...

前端canvas对象转成file对象
import html2canvas from html2canvasexport default {methods: {//canvas对象转成file对象canvasToFile() {html2canvas(this.$parent.$refs[mapPanel].$el,{width: this.$parent.$refs[mapPanel].$el.clientWidth - 600// height:}).then(canvas > {const base64Data can…...

mermaid大全(语法、流程图、时序图、甘特图、饼图、用户旅行图、类图)
⚠️ 有些网站的mermaid可能不完整,因此下面教程中可能有些语法是无效的。 😊亲测Typora软件均可以显示。 1. 介绍 Mermaid是一个基于JavaScript的图表绘制工具,它使用类似Markdown的语法来创建和修改各种类型的图表。以下是关于Mermaid的详…...

运行fastGPT 第四步 配置ONE API 添加模型
上次已经装好了所有的依赖和程序。 下面在网页中配置One API ,这个是大模型的接口。配置好了之后,就可以配置fastGPT了。 打开 OneAPI 页面 添加模型 这里要添加具体的付费模型的API接口填进来。 可以通过ip:3001访问OneAPI后台,**默认账号…...

Spring Initializr创建springboot项目 “java: 错误: 无效的源发行版:19”
我用的1.8的jdk,排查发现这是jdk和springboot版本冲突导致的。 1、File->Project Structure->Project Settings->Project,把language level改成相应的版本 2、File->Project Structure->Module,source和dependancies改成相应的版本 3、F…...

Java IDEA中Gutter Icons图标的含义
前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 前言: 很多人刚开始用IDEA来学习编程,会发现下面这些图标。 但是…...

如何进行域名跳转与域名重定向的综合指南
文章摘取于 Dynadot官方博客内容。 在访问一些商业网站时,我们通常会发现这些平台会将多个域名都指向到同一个内容界面。当然,也存在网站迁移到新域名,旧域名则指向新域名以及其内容页面的情况。 这两者实际上都属于域名跳转的范畴ÿ…...

YOLOv10-1.1部分代码阅读笔记-build.py
build.py ultralytics\data\build.py 目录 build.py 1.所需的库和模块 2.class InfiniteDataLoader(dataloader.DataLoader): 3.class _RepeatSampler: 4.def seed_worker(worker_id): 5.def build_yolo_dataset(cfg, img_path, batch, data, mode"train"…...

redux 结合 @reduxjs/toolkit 的使用
1,使用步骤 使用React Toolkit 创建 counterStore(store目录下) --> 为React注入store(src下面的index) --> React组件使用store中的数据(组件) 2,例如下面有一个简单加减的…...

tui-editor报错
原因: 原先的tui-editor插件(富文本编辑器插件)换了个名称,现在已经更名为toast-ui/editor因此安装不了,从而报错! 解决: 1.首先将package.json中的tui-editor那一行修改为 "toast-ui/…...

运行fastGPT 第二步 安装宝塔面板 用于管理安装docker和其文件
if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec 上面运行以下,安装宝塔。如果不行,系…...

常见好用的PHP CMS开源系统有哪些?
开源的系统,网站大家估计也见过很多,尤其是用PHP写的开源系统也很受用户们欢迎,这类系统通常以简单、使用、开源为优势,为用户提供更好的服务。以下就为大家介绍几个常见且好用的PHP CMS开源系统。欢迎补充! 1、WordP…...

【排错记录】免密、nginx、cgroup、sshd
1、免密登录回显很慢。 现象: 免密登录超级慢,而且巡检脚本跑不起来 解决: vi /etc/ssh/sshd_configGSSAPIAuthentication no UseDNS nosystemctl restart sshd2、nginx服务起不来 现象: Redirecting to /bin/systemctl rest…...

浅谈云计算19 | OpenStack管理模块 (上)
OpenStack管理模块(上) 一、操作界面管理架构二、认证管理2.1 定义与作用2.2 认证原理与流程2.2.1 认证机制原理2.2.2 用户认证流程 三、镜像管理3.1 定义与功能3.2 镜像服务架构3.3 工作原理与流程3.3.1 镜像存储原理3.3.2 镜像检索流程 四、计算管理4.…...

LabVIEW 程序中的 R6025 错误
R6025错误 通常是 运行时库 错误,特别是与 C 运行时库 相关。这种错误通常会在程序运行时出现,尤其是在使用 C 编译的程序或依赖 C 运行时库的程序时。 可能的原因: 内存访问冲突: R6025 错误通常是由于程序在运行时访问无效内…...

【认识油管头部频道】ep5 “5-Minute Crafts”——DIY 和生活技巧
5-Minute Crafts 是一个非常受欢迎的 DIY 和生活技巧频道,它的火爆有多方面的原因: 1. 简单实用的内容 视频主要以解决日常生活中遇到的小问题为主,提供简单易学的技巧,吸引了想快速获取实用知识的观众。 2. 短视频形式 每个视…...

HarmonyOS应用开发者初级认证最新版– 2025/1/13号题库新版
1.欢迎各位读者,本文档来自鸿蒙开发学员亲测,最新版。(考试时直接Ctrlf进行搜索,一定要认真比对答案,有的答案相似度很高)!!!!!! 欢迎…...
improve-gantt-elastic(vue2中甘特图实现与引入)
1.前言 项目开发中需要使用甘特图展示项目实施进度,左侧为表格计划,右侧为图表进度展示。wl-gantt-mater,dhtmlx尝试使用过可拓展性受到限制。gantt-elastic相对简单,可操作性强,基础版本免费。 甘特图(Gan…...

【k8s面试题2025】1、练气期
主要通过呼吸吐纳等方法,将外界的天地灵气吸入体内,初步改造身体,使身体素质远超常人。 文章目录 docker 和虚拟机的不同Kubernetes 和 docker 的关系Kube-proxy IPVS 和 iptables 的异同蓝绿发布Kubernetes中常见的数据持久化方式关于 Docke…...

SpringBoot源码解析(七):应用上下文结构体系
SpringBoot源码系列文章 SpringBoot源码解析(一):SpringApplication构造方法 SpringBoot源码解析(二):引导上下文DefaultBootstrapContext SpringBoot源码解析(三):启动开始阶段 SpringBoot源码解析(四):解析应用参数args Sp…...

SpringSecurity-前后端分离
在前后端分离的架构中,Spring Security 的配置与传统的单体应用有所不同。为了确保安全性和灵活性,我们需要对 Spring Security 进行适当的调整以适应这种架构。下面将详细介绍如何在前后端分离的应用程序中实现 Spring Security。 1. 理解前后端分离的…...

sparkRDD教程之基本命令
作者:nchu可乐百香果 指导者:nchu-YoungDragon 1.前期准备 (1)从迅雷网盘上面下载这个项目,并且把scala,maven和java环境配置好 网盘链接: 分享文件:SparkRDD.zip 链接…...

Linux:SystemV通信
目录 一、System V通信 二、共享内存 代码板块 总结 三、信号量 信号量理论 信号量接口 一、System V通信 System V IPC(inter-process communication),是一种进程间通信方式。其实现的方法有共享内存、消息队列、信号量这三种机制。 …...

C#上位机通过CAN总线发送bin文件
让gpt生成一段代码用来把bin文件通过can总线发出去 c#代码还是比较强大的,各种功能基本都是一两行代码就实现了,这里记录一下对这个代码的理解和解读 主要代码如下,传入bin文件的地址即可将其从指定的can通道发送出去: public …...

CV 图像处理基础笔记大全(超全版哦~)!!!
一、图像的数字化表示 像素 数字图像由众多像素组成,是图像的基本构成单位。在灰度图像中,一个像素用一个数值表示其亮度,通常 8 位存储,取值范围 0 - 255,0 为纯黑,255 为纯白。例如,一幅简单的…...

2-Kbengine+Unity3D多人在线游戏DEMO源码架构分析
2-Kbengine+Unity3D多人在线游戏DEMO源码架构分析 目录 一、服务器端 1、编写并生成我们的服务器端和客户端通用的游戏协议 2、 认识Entity实体 3、 官方DEMO-kbengine_demos_assets分析 二、 客户端...

Vue.js组件开发-如何实现表头搜索
在Vue.js组件开发中,实现表头搜索通常涉及在表格组件的表头添加输入框,并让用户能够输入搜索关键字来过滤表格数据。 以下是一个使用Element UI的el-table组件实现表头搜索的示例: 一、准备阶段 确保Element UI已安装: 确保…...

lerna使用指南
lerna版本 以下所有配置命令都是基于v8.1.9,lerna v5 v7版本差别较大,在使用时,注意自身的lerna版本。 lerna开启缓存及缓存配置 nx缓存是v5版本以后才有的,小于该版本的无法使用该功能。 初始化配置 缓存配置文件nx.json&am…...