当前位置: 首页 > news >正文

10.FreeRTOS_互斥量

互斥量概述

在博文“ FreeRTOS_信号量 ”中,使用了二进制信号量实现了互斥,保护了串口资源。博文链接如下:

FreeRTOS_信号量-CSDN博客

但还是要引入互斥量的概念。互斥量与二进制信号量相比,能够多实现如下两个功能:

  • 防止优先级反转问题
  • 解决递归上锁/解锁问题

互斥量有两种:

  • 普通互斥量:具有优先级继承功能,解决优先级反转问题
  • 递归锁:具有优先级继承功能、能够解决递归上锁/解锁问题、解决谁上锁谁才能解锁的问题

优先级反转

优先级反转问题

假设任务A、B、C的优先级为1、2、3。在程序开始时,A进行上锁,之后B运行抢断的A,之后C运行抢断了B。在C中,想获得锁,但A已经上锁,所以C进入了阻塞,释放了CPU。这时B继续运行,但不解锁,从而导致C被B抢占,即高优先级的任务是否能继续执行由低优先级的任务决定。

这种现象称为优先级反转。

优先级继承

使用优先级继承的方法解决优先级反转的问题。

在C获得锁之后进入阻塞状态,同时执行上锁的任务A会继承C的优先级3,从而进行执行。当A执行完解锁后,A优先级的优先级变回原来的优先级1。之后C获得锁,不再阻塞,C继续执行,执行完成之后,B运行。

在低优先级任务上锁,高优先级任务获得锁阻塞之后,低优先级任务继承高优先级任务的优先级的操作叫做优先级继承。

递归上锁/解锁

递归上锁原因

递归上锁的示例代码如下:

void fun(){xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);/* 上锁 *//* 一些功能 */xSemaphoreGive(SemaphoreHandleTest);/* 解锁 */
}
void Task1(void *param){while(1){xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);/* 上锁 *//* 一些功能 */fun();xSemaphoreGive(SemaphoreHandleTest);/* 解锁 */}
}

在上述代码中,任务1首先进行了上锁,之后调用了fun函数。在fun函数中又进行了上锁,但这时已经上锁,所以就阻塞在了fun函数中。最终导致Task1无法执行完成fun函数,也就无法执行解锁函数,形成死锁。

递归锁

使用递归锁可以解决上述问题。递归锁的作用是,如果开始时为A来上锁,那么在上锁之后,A依旧可以进行调用上锁函数进行上锁,而不会进入阻塞状态。但不管怎样,上锁之后都要解锁,即:上锁多少次,就要解锁多少次,上锁与解锁一 一配对。

除此之外,递归锁还可以满足谁上锁,谁才有权力解锁的问题。而信号量和普通互斥锁并不能实现这个功能。

相关配置

在使用互斥量之前,需要打开宏开关,具体的步骤如下:

在使用递归锁之前,需要打开宏开关,具体的步骤如下:

互斥量相关函数

创建普通互斥量

函数声明如下:

/* 这是一个宏,创建普通互斥量 */
xSemaphoreCreateMutex()/* 宏定义 */
#define xSemaphoreCreateMutex()    xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )/* 实际调用函数 */
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )

返回值:互斥量的句柄

互斥量在创建后与二进制信号量不同,互斥量初始值为1,二进制信号量初始值为0

获取/释放互斥量

获取和释放互斥量所用的函数与二值信号量的函数一致。

函数声明如下:

/* 这是一个宏,存入(释放)信号量/互斥量 */
xSemaphoreGive( xSemaphore )/* 这是一个宏,获取信号量/互斥量 */
xSemaphoreTake( xSemaphore, xBlockTime )

存入函数返回值:成功返回pdPASS,在当前计数值=最大计数值时,会存入失败

xSemaphore :信号量句柄

xBlockTime :阻塞等待时间,portMAX_DELAY为死等

创建递归锁

函数声明如下:

/* 这是一个宏,创建递归锁 */
xSemaphoreCreateRecursiveMutex()/* 宏定义 */
#define xSemaphoreCreateRecursiveMutex()  \xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )/* 实际调用函数 */
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )

返回值:互斥量的句柄

创建后初始值为1

获取/释放递归锁

获取和释放递归锁的函数用法与互斥量一致,只是名字有所差别

函数声明如下:

/* 这是一个宏,存入(释放)递归锁 */
xSemaphoreGiveRecursive( xMutex )/* 这是一个宏,获取递归锁 */
xSemaphoreTakeRecursive( xMutex, xBlockTime )

存入函数返回值:成功返回pdPASS,在当前计数值=最大计数值时,会存入失败

xSemaphore :信号量句柄

xBlockTime :阻塞等待时间,portMAX_DELAY为死等

验证实验

1、基本互斥实验

使用互斥量来确保串口的输出不被打断。

与二进制信号量实现该功能相比,代码方面的差异只在初始化不同,其他代码都没有进行修改

二进制信号量实现该功能的初始化代码如下:

/* 创建二进制信号量,初始值自动设置为 0 */
SemaphoreHandleTest = xSemaphoreCreateBinary();
/* 让信号量为1,代表串口资源可用 */
xSemaphoreGive(SemaphoreHandleTest);

互斥量实现该功能的初始化代码如下:

/* 创建互斥量,初始值自动设置为 1 */
SemaphoreHandleTest = xSemaphoreCreateMutex();

实验现象和其他代码实现与博文“ FreeRTOS_信号量 ”中 “ 验证实验 ” 均一致。博文链接如下:

FreeRTOS_信号量-CSDN博客

2、优先级继承实验

根据本文 “互斥量概述” 中 “ 优先级反转 ”内容的描述,来进行编写代码。这里使用二进制信号量与互斥量进行运行结果的对比。

具体的代码实现如下:

QueueHandle_t SemaphoreHandleTest;
char taskA_flag = 0;
char taskB_flag = 0;
char taskC_flag = 0;
void TaskAFunction(void *param){int i=0;while(1){/* 上锁 */xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);/* 执行一个很长的时间的程序 */taskA_flag = 1;taskB_flag = 0;taskC_flag = 0;for(i=0;i<20;i++){printf("%d,%d,%d",taskA_flag,taskB_flag,taskC_flag);taskA_flag = 1;taskB_flag = 0;taskC_flag = 0;}/* 解锁 */xSemaphoreGive(SemaphoreHandleTest);}
}
void TaskBFunction(void *param){vTaskDelay(5);/* B先休眠,让A任务执行 */while(1){/* B一直在执行,这会导致如果A优先级低于B,则A一直不执行 */taskA_flag = 0;taskB_flag = 1;taskC_flag = 0;}
}
void TaskCFunction(void *param){int i=0;vTaskDelay(10);/* C先休眠,让AB任务执行 */while(1){taskA_flag = 0;taskB_flag = 0;taskC_flag = 1;/* 上锁 */xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);/* C任务执行一段时间 */for(i=0;i<10;i++){printf("this is taskC\r\n");taskA_flag = 0;taskB_flag = 0;taskC_flag = 1;}/* 解锁 */xSemaphoreGive(SemaphoreHandleTest);}
}
int main( void )
{TaskHandle_t xHandleTask1;TaskHandle_t xHandleTask2;TaskHandle_t xHandleTask3;prvSetupHardware();SerialPortInit();printf("UART TEST\r\n");/* 创建互斥量,初始值自动设置为 1 *///SemaphoreHandleTest = xSemaphoreCreateMutex();/* 创建二进制信号量,初始值自动设置为 0 */SemaphoreHandleTest = xSemaphoreCreateBinary();/* 让信号量为1,代表资源可用 */xSemaphoreGive(SemaphoreHandleTest);xTaskCreate(TaskAFunction,"TaskA",100,(void*)NULL,1,&xHandleTask1);xTaskCreate(TaskBFunction,"TaskB",100,(void*)NULL,2,&xHandleTask2);xTaskCreate(TaskCFunction,"TaskC",100,(void*)NULL,3,&xHandleTask3);vTaskStartScheduler();return 0;
}

该代码的主要功能就是利用taskA_flag 、taskB_flag、taskC_flag 这三个标志位来判断当前任务是谁在运行。通过逻辑分析仪显示出这三个变量的电平来分析抢占关系。

二值信号量的运行结果如下:

可以看到,在1 -> 2阶段中,BC调用延时处于阻塞态,因此A运行。在2->3阶段中,A运行了一段时间后,B的延时结束,B开始抢占A进行运行。在2->3阶段中,B运行了一段时间后,C的延时结束,C开始抢占B进行运行。以上是正常的抢占流程。

在C抢占B之后,尝试获取信号量,但信号量已经被A所获取,还未进行释放,因此C进入了阻塞状态,之后B进行抢占到CPU继续执行。但B的优先级高于A,A不能够释放信号量,这样就导致了C永远处于阻塞态,只有当B释放CPU,A才能释放信号量,从而C才可以运行,这样就产生了优先级反转的问题。

互斥量的运行结果如下:

可以看到,在1 -> 2阶段中,BC调用延时处于阻塞态,因此A运行。在2->3阶段中,A运行了一段时间后,B的延时结束,B开始抢占A进行运行。在2->3阶段中,B运行了一段时间后,C的延时结束,C开始抢占B进行运行。以上是正常的抢占流程。

在C抢占B之后,尝试获取互斥量,但互斥量已经被A所获取,还未进行释放,因此A进行优先级的继承,此时A的优先级变成了3,即:4 -> 5阶段。在5->6阶段中,A的优先级为3,执行完成打印工作后,对互斥量进行了解锁,这时任务A的优先级变回原来的优先级1,C任务抢占A开始执行。

3、谁上锁,谁才能解锁实验

普通互斥量和二进制信号量并未实现谁上锁,谁才能解锁,这里使用普通互斥量与递归锁来进行对比,来验证递归锁能够实现谁上锁,谁才能解锁的功能。

具体代码实现如下:

QueueHandle_t SemaphoreHandleTest;void PrintFunction(void *param){while(1){//		/* 以下为互斥量测试 */
//		xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);
//		printf("%s",(char*)param);
//		xSemaphoreGive(SemaphoreHandleTest);/* 以下为递归锁测试 */xSemaphoreTakeRecursive(SemaphoreHandleTest,portMAX_DELAY);printf("%s",(char*)param);xSemaphoreGiveRecursive(SemaphoreHandleTest);vTaskDelay(1);}
}
void BreakSemaphoreTask(void *param){vTaskDelay(1);while(1){//		/* 以下为互斥量测试 */
//		while(1){
//			
//			/* 如果获得不到锁,就直接往里面放一把锁 */
//			if(xSemaphoreTake(SemaphoreHandleTest,0) == pdFALSE){
//				xSemaphoreGive(SemaphoreHandleTest);
//			}else{
//				break;
//			}
//		}
//		printf("this is break\r\n");
//		xSemaphoreGive(SemaphoreHandleTest);/* 以下为递归锁测试 */while(1){/* 如果获得不到锁,就直接往里面放一把锁 */if(xSemaphoreTakeRecursive(SemaphoreHandleTest,0) == pdFALSE){xSemaphoreGiveRecursive(SemaphoreHandleTest);}else{break;}}printf("this is break\r\n");xSemaphoreGiveRecursive(SemaphoreHandleTest);vTaskDelay(1);}
}int main( void )
{TaskHandle_t xHandleTask1;TaskHandle_t xHandleTask2;TaskHandle_t xHandleTask3;prvSetupHardware();SerialPortInit();printf("UART TEST\r\n");//	/* 创建互斥量,初始值自动设置为 1 */
//	SemaphoreHandleTest = xSemaphoreCreateMutex();/* 创建递归锁,初始值自动设置为 1 */SemaphoreHandleTest = xSemaphoreCreateRecursiveMutex();xTaskCreate(PrintFunction,"TaskA",100,(void*)"this is TaskA\r\n",1,&xHandleTask1);xTaskCreate(PrintFunction,"TaskB",100,(void*)"this is TaskB\r\n",1,&xHandleTask2);xTaskCreate(BreakSemaphoreTask,"TaskC",100,(void*)NULL,1,&xHandleTask3);vTaskStartScheduler();return 0;
}

互斥量的运行结果如下:

可以看到,打印结果乱套了。这是因为在A上锁之后,C任务去放进去了一把锁,导致B任务原来在阻塞,突然获得了锁,执行了B任务中的printf。因此互斥量并没有实现谁上锁,谁才能够解锁的功能。

递归锁的运行结果如下:

可以看到,打印变得非常正常。这是因为递归锁可以实现谁上锁,谁才能够解锁,因此不能够再C中解开A的锁,从而执行结果变得正常。

4、递归上锁实验

在“ 谁上锁,谁才能解锁实验 ”的代码基础上进行修改,将打印函数修改为如下情况:

void PrintFunction(void *param){int i=0;while(1){//		/* 以下为互斥量测试 */
//		xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);
//		printf("%s",(char*)param);
//		xSemaphoreGive(SemaphoreHandleTest);/* 以下为递归锁测试 */xSemaphoreTakeRecursive(SemaphoreHandleTest,portMAX_DELAY);for(i=0;i<5;i++){/* 递归上锁,上锁之后再上一次锁 */xSemaphoreTakeRecursive(SemaphoreHandleTest,portMAX_DELAY);printf("lock:%i\r\n",i);xSemaphoreGiveRecursive(SemaphoreHandleTest);}printf("%s",(char*)param);xSemaphoreGiveRecursive(SemaphoreHandleTest);vTaskDelay(1);}
}

运行结果如下:

可以看到lock0~4正常打印,说明并未产生阻塞的情况,递归锁可以递归上锁。

相关文章:

10.FreeRTOS_互斥量

互斥量概述 在博文“ FreeRTOS_信号量 ”中&#xff0c;使用了二进制信号量实现了互斥&#xff0c;保护了串口资源。博文链接如下&#xff1a; FreeRTOS_信号量-CSDN博客 但还是要引入互斥量的概念。互斥量与二进制信号量相比&#xff0c;能够多实现如下两个功能&#xff1a…...

EtherCAT总线冗余让制造更安全更可靠更智能

冗余定义 什么是总线冗余功能&#xff1f;我们都知道&#xff0c;EtherCAT现场总线具有灵活的拓扑结构&#xff0c;设备间支持线型、星型、树型的连接方式&#xff0c;其中线型结构简单、传输效率高&#xff0c;大多数的现场应用中也是使用这种连接方式&#xff0c;如下图所示…...

Android IdleHandler源码分析

文章目录 Android IdleHandler源码分析概述前提基本用法源码分析添加和删除任务执行任务 应用场景 Android IdleHandler源码分析 概述 IdleHandler是一个接口&#xff0c;它定义在MessageQueue类中&#xff0c;用于在主线程的消息队列空闲时执行一些轻量级的任务。IdleHandle…...

Mac安装stable diffusion 工具

文章目录 1.安装 Homebrew2.安装 stable diffusion webui 的依赖3.下载 stable diffusion webui 代码4.启动 stable diffusion webui 本体5.下载模型6.这里可能会遇到一个clip-vit-large-patch14报错 参考&#xff1a;https://brew.idayer.com/install/stable-diffusion-webui/…...

CVE-2024-6387Open SSH漏洞彻底解决举措(含踩坑内容)

一、漏洞名称 OpenSSH 远程代码执行漏洞(CVE-2024-6387) 二、漏洞概述 Open SSH是基于SSH协议的安全网络通信工具&#xff0c;广泛应用于远程服务器管理、加密文件传输、端口转发、远程控制等多个领域。近日被爆出存在一个远程代码执行漏洞&#xff0c;由于Open SSH服务器端…...

python的简单爬取

需要的第三方模块 requests winr打开命令行输入cmd 简单爬取的基本格式&#xff08;爬取百度logo为例&#xff09; import requests url"http://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" resprequests.get(url)#回应 #保存到本地 with open(&…...

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第60集-agent训练资讯APP重点推荐AI资讯内容(含视频)

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第60集-agent训练资讯APP重点推荐AI资讯内容&#xff08;含视频&#xff09; 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。d…...

【学术会议征稿】第三届智能电网与能源系统国际学术会议

第三届智能电网与能源系统国际学术会议 2024 3rd International Conference on Smart Grid and Energy Systems 第三届智能电网与能源系统国际学术会议&#xff08;SGES 2024&#xff09;将于2024年10月25日-27日在郑州召开。 智能电网可以优化能源布局&#xff0c;让现有能源…...

01. 课程简介

1. 课程简介 本课程的核心内容可以分为三个部分&#xff0c;分别是需要理解记忆的计算机底层基础&#xff0c;后端通用组件以及需要不断编码练习的数据结构和算法。 计算机底层基础可以包含计算机网络、操作系统、编译原理、计算机组成原理&#xff0c;后两者在面试中出现的频…...

iOS热门面试题(三)

面试题1&#xff1a;在iOS开发中&#xff0c;什么是MVC设计模式&#xff1f;请详细解释其各个组成部分&#xff0c;并给出一个实际应用场景&#xff0c;包括具体的代码实现。 答案&#xff1a; MVC设计模式是一种在软件开发中广泛使用的架构模式&#xff0c;特别是在iOS开发中…...

ECS中postTransform.Value = float4x4.Scale(1, math.sin(elapsedTime), 1)

在Unity的ECS&#xff08;Entity Component System&#xff09;架构中&#xff0c;postTransform.Value float4x4.Scale(1, math.sin(elapsedTime), 1); 用于设置一个变换矩阵的缩放部分。下面是对这行代码的详细解释&#xff1a; postTransform: 这是一个表示变换的组件或结构…...

VLM技术介绍

1、背景 视觉语言模型&#xff08;Visual Language Models&#xff09;是可以同时从图像和文本中学习以处理许多任务的模型&#xff0c;从视觉问答到图像字幕。 视觉识别&#xff08;如图像分类、物体保护和语义分割&#xff09;是计算机视觉研究中一个长期存在的难题&#xff…...

x264 编码器 AArch64 汇编函数模块关系分析

x264 编码器 AArch64 汇编介绍 x264 是一个流行的开源视频编码器,它实现了 H.264/MPEG-4 AVC 标准。x264 项目致力于提供一个高性能、高质量的编码器,支持多种平台和架构。对于 AArch64(即 64 位 ARM 架构),x264 编码器利用该架构的特性来优化编码过程。在 x264 编码器中,…...

windows10开启防火墙,增加入站规则后不生效,还是不能访问后端程序

一、背景&#xff1a; 公司护网要求开启防火墙&#xff0c;开启防火墙后&#xff0c;前后端分离的项目调试受影响&#xff0c;于是增加入站规则开放固定的后台服务端口&#xff0c;增加的mysql端口3306和redis端口6379&#xff0c;别人都可以访问&#xff0c;但是程序的端口808…...

academic-homepage:快速搭建个人学术主页,页面内容包括个人简介、教育经历、发布过的学术列表等,同时页面布局兼容移动端。

今天给大家分享GitHub 上一个开源的 GitHub Pages 模板 academic-homepage。 可帮助你快速搭建个人学术主页&#xff0c;页面内容包括个人简介、教育经历、发布过的学术列表等最基本内容&#xff0c;同时页面布局兼容移动端。 相关链接 github.com/luost26/academic-homepage …...

.env.development、.env.production、.env.staging

环境变量文件&#xff08;如 .env.development、.env.production、.env.staging&#xff09;用于根据不同的环境&#xff08;开发、生产、测试等&#xff09;配置应用程序的行为。 作用 .env.development&#xff1a;用于开发环境的配置。开发人员在本地开发时会使用这个文件…...

国密证书(gmssl)在Kylin Server V10下安装

1.查看操作系统信息 [root@localhost ~]# cat /etc/.kyinfo [dist] name=Kylin milestone=Server-V10-GFB-Release-ZF9_01-2204-Build03 arch=arm64 beta=False time=2023-01-09 11:04:36 dist_id=Kylin-Server-V10-GFB-Release-ZF9_01-2204-Build03-arm64-2023-01-09 11:04:…...

【数据服务篇】法律快车问答数据:为法律智能化铺就道路

数据来源 法律快车汇集了广泛的法律问题和专业律师的回答&#xff0c;这些来自用户和律师的数据构成了丰富的问答资源。用户通过平台提交各类法律疑问&#xff0c;得到资深律师的详尽解答&#xff0c;形成了一系列真实、多样化的法律案例和讨论。 数据获取见文末。 数据内容…...

各向异性含水层中地下水三维流基本微分方程的推导(二)

各向异性含水层中地下水三维流基本微分方程的推导 参考文献&#xff1a; [1] 刘欣怡,付小莉.论连续性方程的推导及几种形式转换的方法[J].力学与实践,2023,45(02):469-474. 书接上回&#xff1a; 我们能得到三个方向的流入流出平衡方程&#xff1a; ∂ ρ u x ∂ x d x d y d…...

2024 微信小程序 学习笔记 第一天

微信公众平台 (qq.com) 小程序代码的构成 项目结构 JSON 配置文件 WXML 模板 WXSS 样式 JS 逻辑交互 小程序的宿主环境 宿主 通信模型 运行机制 组件 视图组件 view scrioll-view swiper swiper-item swiper属性 text button image image mode属性 小程序API 协…...

PCIe驱动开发(3)— 驱动设备文件的创建与操作

PCIe驱动开发&#xff08;3&#xff09;— 驱动设备文件的创建与操作 一、前言 在 Linux 中一切皆为文件&#xff0c;驱动加载成功以后会在“/dev”目录下生成一个相应的文件&#xff0c;应用程序通过对这个名为“/dev/xxx” (xxx 是具体的驱动文件名字)的文件进行相应的操作即…...

【Redis】简单了解Redis中常用的命令与数据结构

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境 二、Redis的特点和适用场景三、Redis的数据类型和使用3.1字符串&#xff08;String&…...

IDEA启动Web项目总是提示端口占用

文章目录 IDEA启动Web项目总是提示端口占用一、前言1.场景2.环境 二、正文1.场景一:真端口占用2. 场景二:假端口占用 IDEA启动Web项目总是提示端口占用 一、前言 1.场景 IDEA启动Web项目总是提示端口占用&#xff1a; 确实是端口被占用&#xff0c;比如&#xff1a;没有正常…...

JRT打印鉴定记录单

良好的基础会使上层实现越做越简单&#xff0c;jrt在开始写业务之前就把运用场景需要的基础实验和设计完毕了。基于jrt的基础可以很轻松的实现强大的打印效果。jrt的打印和lodop比较像&#xff0c;是高度为满足建议系统打印定制的打印实现&#xff0c;设计器可能没lodop通用&am…...

数据处理-Matplotlib 绘图展示

文章目录 1. Matplotlib 简介2. 安装3. Matplotlib Pyplot4. 绘制图表1. 折线图2. 散点图3. 柱状图4. 饼图5. 直方图 5. 中文显示 1. Matplotlib 简介 Matplotlib 是 Python 的绘图库&#xff0c;它能让使用者很轻松地将数据图形化&#xff0c;并且提供多样化的输出格式。 Ma…...

Nginx -Web服务器/反向代理/负载均衡

文章目录 一、web服务1.1 nginx安装1.2 配置文件1.3 Nginx处理Web机制 二、反向代理三、负载均衡3.1 分类3.2 负载相关配置文件3.3 keepalive 提高吞吐量3.4 配置浏览器缓存 附、JMeter性能测试工具 以赛促学内容,大概率感觉会使用nginx做web服务,特对nginx做总结归纳. Nginx是…...

机器人三定律及伦理分析

全世界的机器人定律并没有一个统一的标准或体系&#xff0c;但是在科学文献中&#xff0c;最广为人知的是由科幻小说家阿西莫夫提出的“机器人三定律”。本文将以这些定律为基础&#xff0c;分析现有的机器人伦理和实际应用中的问题&#xff0c;给出若干实例&#xff0c;并对相…...

自动驾驶算法———车道检测(一)

“ 在本章中&#xff0c;我将指导您构建一个简单但有效的车道检测管道&#xff0c;并将其应用于Carla 模拟器中捕获的图像。管道将图像作为输入&#xff0c;并产生车道边界的数学模型作为输出。图像由行车记录仪&#xff08;固定在车辆挡风玻璃后面的摄像头&#xff09;捕获。…...

小程序自学教程

从0开始搭建微信小程序前后台 0、准备 如何安装&#xff1f;去CSDN搜索“xxx安装教程”即可。 &#xff08;1&#xff09;工具 IntelliJ IDEA&#xff08;必选&#xff09;——Java开发集成环境&#xff0c;可以前后端同时使用 Web Storm——web开发集成环境&#xff0c;主要…...

How do I format markdown chatgpt response in tkinter frame python?

题意&#xff1a;怎样在Tkinter框架中使用Python来格式化Markdown格式的ChatGPT响应&#xff1f; 问题背景&#xff1a; Chatgpt sometimes responds in markdown language. Sometimes the respond contains ** ** which means the text in between should be bold and ### te…...