从零开始 使用OMNET++结合VEINS,INET和SUMO的联合仿真

背景知识

当我们探索未来的交通系统和智能交通解决方案时,车辆到一切(Vehicle-to-Everything, V2X)通信技术显得尤为重要。V2X是指在车辆与车辆(V2V)、车辆与基础设施(V2I)、车辆与行人(V2P)以及车辆与网络(V2N)之间进行的通信。这种技术能够提高道路安全,优化交通流量,减少拥堵,提升驾驶体验,并为自动驾驶汽车的实现打下基础。

为了准确模拟和分析V2X通信中的复杂交互,需要使用一些专用的仿真工具:

  1. OMNeT++ 是一个公开源码的网络仿真框架,提供了广泛的工具集和功能,用于构建复杂的网络和其他分布式系统。OMNeT++的灵活性和模块化使其成为研究和模拟通信网络,特别是V2X通信网络的理想选择。

  2. INET 是OMNeT++的一个扩展模型库,专注于互联网协议和网络技术的仿真。INET提供了大量的网络协议模型,如TCP/IP、路由协议等,允许研究者构建和测试各种网络架构和服务。

  3. SUMO (Simulation of Urban MObility) 是一个开源的交通仿真软件,用于模拟城市的车辆流动。通过SUMO,研究人员可以创建详细的城市交通场景,包括道路网络、交通信号灯、车辆行为等,来分析不同交通策略和管理措施的效果。

  4. VEINS 是一个允许OMNeT++和SUMO之间进行联合仿真的框架,专门用于车辆通信系统的研究。它使得OMNeT++模拟的通信网络和SUMO模拟的移动车辆能够实时交互,从而实现对V2X通信场景的全面仿真。

使用OMNeT++结合VEINS、INET和SUMO进行联合仿真,能够在复杂的城市交通环境中准确模拟V2X通信。这种仿真可以帮助研究者评估V2X技术在实际应用中的表现,如通信延迟、系统可靠性和安全性等。此外,仿真结果还可以指导政策制定者和工程师设计更有效的交通管理策略和智能交通系统,推动智能交通技术的发展,为实现无缝、安全和高效的未来交通网络提供支持。

在这篇博文中,我将尝试:

  • 搭建仿真的环境,导入VEINS和INET开源库
  • 使用INET库中提供的网络协议和功能模型来模拟通信协议和网络功能
  • 使用SUMO创建道路网络和车辆轨迹,并导出到OMNeT++中
  • 在OMNeT++中使用VEINS通过socket和SUMO连接,借助VEINS实现的TRACI接口来设置车辆相关的行为和场景
  • 在OMNET++中结合VEINS,SUMO和INET运行联合仿真实验,并分析车辆在遇到紧急情况下的通信状况和具体行为

环境搭建

OMNet++ / Veins / INET / SUMO下载

关于这四个软件的下载安装,以下两篇博文是很好的参考:

https://www.cnblogs.com/Xylona/p/17779621.html

(veins5.0+sumo1.2.0+OMNeT++5.5.1)车载自组织网络仿真环境安装配置教程(一步一步)_sumo1.3.0+veins5.2+omnet++5.5.1-CSDN博客

 我最终使用的版本:

  • OMNeT++ 5.6.2
  • Veins 5.2
  • SUMO 1.10.0
  • INET 4.2.5

环境配置

  • 修改configure.user:

在分别下载好这四个版本的软件后,进入OMNet++文件夹,打开configure.user,修改PREFER_CLANG的值为no

  • sumo环境变量配置:

共配置四条:

  • 解压&编译OMNET++:

打开OMNet++中的“mingwenv.cmd”,并按下任意键开始解压;

解压完成后分别输入“./configure”和“make”进行编译,make的时间大概要一个小时

  • 打开OMNETT++:

在“mingwenv.cmd”中输入“omnetpp”打开IDE界面

  • 导入VEINS & INET:

点击左上角File -> Import:

导入inet和veins:

注意!对于veins要勾选search for nested projects!!

  • 点击左上角Project -> Build All
  • 运行INET的示例仿真:

点击inet -> examples -> aodv ->  omnetpp.ini,然后右键Run as Omnet ++ simulation就可以看到示例仿真:

  •  运行VEINS的示例仿真:

在“mingwenv.cmd”中输入以下指令使veins连接上sumo:

C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/veins-veins-5.2/bin/veins_launchd -vv -c C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/bin/sumo.exe

点击左侧veins -> examples -> veins -> omnetpp.ini,然后右键Run as Omnet ++ simulation就可以看到示例仿真:

点击Run后:


现在,成功完成了环境搭建,接下来就要开始创建一个属于我的项目

关于接下来的大体步骤,我参考了以下油管的视频,但是如果仿真软件的版本和我相差不大,建议参考我的版本,因为我按照我下载的这一套VEINS,SUMO,INET,OMNET++版本完全按照视频操作会出现一些问题,所以建议有什么不会再去参考油管的视频:

How to Create a New OMNET++ Project That Works with INET and Veins (youtube.com)

How to Simulate a V2V Network using OMNET++, INET, and Veins - YouTube

How to Add A Custom Sumo Simulation to OMNET++ (youtube.com)


创建工程

  • 左上角file -> new -> OMNET++ project创建project:

  • 右击新建的project -> Properties -> Project References,链接INET和VEINS和veins_inet

  • 别关掉上面的窗口,按照以下步骤完善make规则:

 


现在,这个新的project已经成功的refer到了veins 和 inet,但是,问题来了,如何才能将二者结合起来?答案是:veins中提供了一个sub-project,名字是veins_inet,其中提供了大量现成的cc代码和一个简单的示例,这个sub-project提供了一个将二者结合的很好例子,这也是为什么刚刚import veins的时候要同时Import veins_inet这个子项目!


 

VEINS & INET 的结合

  • 将veins_inet的仿真例程迁移到我新创建的project:

找到刚刚import的veins_inet子项目, 找到 examples -> veins_inet 拷贝到新项目的simulation下:

出现了大量的红色叉叉,这是因为.ned文件中的package的名字不正确

② 修改.ned文件中的package name:

//在所有.ned文件中:(其实就两个)
//将开头的 
package XXXXXXX.veins_inet;
//修改为 
package _9038_project.simulations.veins_inet;

可以通过运行当前路径下的omnetpp.ini来测试是否成功:

由于此处包含veins,别忘了在OMNETT++根目录下的mingwenv.cmd中输入以下代码连接SUMO:

C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/veins-veins-5.2/bin/veins_launchd -vv -c C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/bin/sumo.exe

 

可见,成功在自己创建的项目中运行了veins_inet的例程仿真! 


现在,已经可以成功的在自己创建的project里运行结合veins_inet例子下的仿真,但是例子中的sumo地图并不是我想要的,下一步是根据自己的地图,实现veins,inet,sumo的联合仿真


VEINS & INET & SUMO 的联合仿真

  • 创建自己的仿真路径

在新建项目的simulations下再创建一个“my_veins_inet”的文件夹,把刚刚“veins_inet”的内容全部复制进来:

       (同样,记得修改两个.ned文件中的package名,此处不再展开!)

然后,将square.net.xml; square.poly.xml; square.rou.xml; obstacles.xml删除,剩下的留着待会修改

  • 自定义sumo地图导入:

此处我选择了留学所在城市的一家costco附近的地图

网站:https://www.openstreetmap.org/export#map=16/42.9852/-81.2900

选定区域后点击左侧的“Export”导出.osm文件

通过python脚本分别生成.net.xml;.rou.xml;.poly.xml:

请根据文件位置修改文件路径!

import numpy as np
import subprocess#生成map.net.xml# 设置你的.osm文件路径
osm_file_path = 'costco.osm' #根据https://www.openstreetmap.org 网站导出的# 设置输出文件路径
output_file_path = 'costco.net.xml'# 构建netconvert命令
netconvert_command = f'netconvert --osm-files {osm_file_path} -o {output_file_path}'# 调用命令
subprocess.run(netconvert_command, shell=True)
print(".net.xml成功生成")#生成map.poly.xmlosm_file_path = 'costco.osm' #根据https://www.openstreetmap.org 网站导出的
type_file_path = 'typemap.xml' #内容根据CSDN收藏# 设置输出文件路径
output_file_path = 'costco.poly.xml'# 构建netconvert命令
netconvert_command = f'polyconvert --net-file costco.net.xml --osm-files {osm_file_path} --type-file {type_file_path} -o {output_file_path}'# 调用命令
subprocess.run(netconvert_command, shell=True)
print(".poly.xml成功生成")# 生成map.rou.xml# 设置SUMO环境变量(请根据你的SUMO安装路径进行调整)
sumo_tools_dir = "C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/tools"
sumo_network_file = "costco.net.xml"
output_route_file = "costco.rou.xml"# 构建randomTrips.py脚本的完整命令
command = ["python",f"{sumo_tools_dir}/randomTrips.py","-n", sumo_network_file,"-e", "100","-l",
]# 使用subprocess运行命令
result = subprocess.run(command, capture_output=True, text=True)# 检查命令输出(可选)
if result.returncode == 0:print("执行成功1")print(result.stdout)  # 打印标准输出
else:print("执行出错1")print(result.stderr)  # 打印错误输出##################################################### 构建randomTrips.py脚本的完整命令
command = ["python",f"{sumo_tools_dir}/randomTrips.py","-n", sumo_network_file,"-r", output_route_file,"-e", "50","-l",
]# 使用subprocess运行命令
result = subprocess.run(command, capture_output=True, text=True)# 检查命令输出(可选)
if result.returncode == 0:print("执行成功2")print(".rou.xml成功生成")print(result.stdout)  # 打印标准输出
else:print("执行出错2")print(result.stderr)  # 打印错误输出

生成后,将这三个文件放到“my_veins_inet”下备用

  • 修改SUMO相关文件:

my_veins_inet”下已经有了sumo相关的各种文件,现在需要根据具体要求修改它们

将square.sumocfg和square.launchd.xml改名为costco.sumocfgcostco.launchd.xml,并根据新的rou, net和poly文件修改内容:

 

修改完成后,可以用sumo gui打开costco.sumocfg查看效果:

修改.rou.xml:

为了让仿真效果尽可能的清晰,将随机生成的50辆车的代码注释掉,并替换成如下的代码:

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/routes_file.xsd"><vType id="vtype0" accel="2.6" decel="4.5" sigma="0.5" length="4.5" minGap="2.5" maxSpeed="14" color="1,1,0"/><route id="route0" edges="-62088703#4 -62088703#3 -62088703#2 -62088703#1 474843229#0 474843229#1 474843229#2 474843229#3 474843229#4 474843229#5 474843229#6 466961753#1 466961753#2 466961753#3 466961753#4 466961753#5 466961753#6 466961753#7 466961753#8 -466961753#8"/><flow id="flow0" type="vtype0" route="route0" begin="0" period="3" number="5" arrivalPos="0" />
</routes>
  • <vType>元素:
  1. 定义了一种车辆类型vtype0
  2. accel(加速度): 车辆的最大加速度,这里是2.6米/秒²
  3. decel(减速度): 车辆的最大减速度,这里是4.5米/秒²
  4. sigma(驾驶员不确定性): 描述了驾驶员行为的不确定性,这里是0.5,范围从0(完全确定行为)到1(非常不确定的行为)
  5. length(长度): 车辆的长度,这里是4.5米
  6. minGap(最小间隙): 车辆之间的最小间隙,这里是2.5米
  7. maxSpeed(最大速度): 车辆的最大速度,这里是14米/秒
  8. color(颜色): 车辆的颜色,这里定义为黄色(RGB颜色模式中的1,1,0)
  • <route>元素:
  1. 定义了一个路线route0
  2. id属性指定了路线的唯一标识符
  3. edges属性列出了构成该路线的边的序列。边是道路网中的基本元素,代表单向的道路。这里列出的边通过它们的唯一标识符来指定,负号表示逆向行驶
  • <flow>元素:
  1. 定义了一个流量flow0,表示一定数量的车辆将沿指定的路线移动
  2. id属性为流量提供了一个唯一标识
  3. type属性指定了车辆类型,这里引用了之前定义的vtype0
  4. route属性指定了车辆将遵循的路线,这里是route0
  5. begin属性定义了流量开始的时间(秒),这里是从仿真开始的0秒
  6. period属性定义了车辆生成的时间间隔(秒),这里是每3秒生成一次
  7. number属性定义了将被生成的车辆总数,这里是5辆
  8. arrivalPos属性定义了车辆到达目的地的位置,这里是0,通常表示车辆将尝试行驶整个路线

  • 修改.ned & .ini文件:

修改Scenario.ned

修改灰色区域范围大小,以适应新的sumo地图

修改omnetpp.ini

  • 运行仿真文件

右击omnetpp.ini,右击Runs as -> Run Configuration:

设置完成后点击右下角:Apply -> run:

进入页面后选择genreal:

结果展示

最终仿真效果:

  • 根据.rou.xml的设置,node[0]至node[4]五辆车预先定义要走的路线完全一致。
  • 然而,node[0]变红代表了模拟突发事故,事故发生后,车辆之间开始相互通信,得到消息的车辆会变成绿色。
  • node[3]和node[4]由于具备调头条件,直接掉头,更改了原有的路线并规划了新的路线。
  • node[1]和node[2]由于不具备调头条件只能按照原有路线继续往前,在被node[0]挡住后,根据sumo内部的算法换道并继续前进。

仿真结果简单分析

吞吐量(overall throughput value)

上图显示了4个节点随着时间的吞吐量变化,可以明显的看到,对于这4个节点,吞吐量都在事故发送时开始增加,在0.1秒左右达到最高,并在0.2秒后快速下降,可见通讯速度很快。

 

包发送量(packet sent count)

可见,node0为2次,其他三个节点都为1次,这也很好理解,因为node0是事故发送的节点,所以需要发送一次事故包一次转发包;而其他节点只需要发送一次转发包即可。

思路和代码解读

现在,已经实现OMNET++,SUMO,VEINS和INET的联合仿真,但是背后的原理是什么其实依然一知半解,于是我开始解读代码,尝试理解为什么仿真会这样进行。

注意,以下所有内容都属于自问自答,完全可能有错误,欢迎大家指正,本人纯小白

Q1在最后的仿真路径中,Scenario.ned中的manager,,radio medium,node和physical environmnet分别到底代表什么?在图形界面中拖动他们的位置或改变他们的大小有什么实际的意义?

A

  • manager通常用于仿真管理任务,如控制仿真流程、管理节点间的通信、协调事件等
  • radioMedium定义了无线电传播的模型,如何模拟信号的传播和衰减、如何处理节点间的无线通信等
  • node代表仿真网络中的一个参与者或设备,在车辆网络仿真中,这些节点通常是车辆
  • physicalEnvironment模块定义了仿真的物理环境,包括地形、建筑物等,这些因素可能会影响信号的传播和车辆的移动

在OMNeT++ IDE的图形界面中,拖动这些组件的位置或改变它们的大小主要是为了改善视觉布局,帮助仿真设计者更好地理解和组织仿真场景。这些操作不直接影响仿真逻辑或结果,仿真的逻辑和行为完全由NED文件中的参数定义和配置文件(.ini文件)中的设置决定

Q2根据网络原理,网络协议应该分为5层,应用层,传输层,网络层,链路层,物理层,这些体现在项目的哪里?

A

  • 在这个仿真中,每一个节点(汽车)都被注册为一个名为VeinsInetCar的对象:

  • 而通过跳转VeinsInetCar.nedf会发现,它继承自一个叫AdhocHost的对象:

  • 使用同样的方法不停的跳转,会得出这样一个继承的关系:

  • 而通过分析这些.ned文件,可以总结出以下内容:

  • 所以,每个汽车所代表的VeinsInetCar都是从NodeBase一路继承过来的,而观察继承的路线就可以发现,在一路的继承中,就分别实现了网络协议中5大层的接口定义!

 

Q3根据Q2,有个随之而来的问题:我理解了为什么VeinsInetCar具备了完整的协议栈,网络协议的5大层实现在了哪里。但是我依然不理解:从NodeBase一直继承到了VeinsInetCar,现在已经有了无数的接口用来配置完整的协议,但是使用哪些接口具体配置哪些值,比如在链路层具体配置为无线还是VLAN,网络层具体配置为IPV4还是IPV6,传输层具体配置为TCP还是UDP,这些具体的配置在哪里?

A

答案是:大部分在仿真文件omnetpp.ini中定义:

  • 链路层具体配置:

  • 网络层具体配置:

  •  应用层具体配置:

物理层没有定义很好理解,这都是最底层的设定。

但是,显然会有一个问题,为什么没有传输层的相关定义?到底使用TCP还是UDP还是什么?

chatgpt给出了三种回答:

  1. 默认配置:许多OMNeT++和INET模块,包括传输层协议模块,具有默认配置。例如,如果一个应用需要使用UDP或TCP,并且在其模块定义中正确指定了,那么即使在.ini文件中没有明确配置,这些传输层协议也会被自动实例化并使用默认设置。

  2. 应用层决定:在许多情况下,特别是在使用简单应用(如UdpBasicApp)或其他特定的应用模型时,所使用的传输协议(UDP、TCP等)可能已在应用层模块的实现中明确定义。例如,一些应用模型默认使用UDP进行通信,而无需在.ini文件中进行额外配置。

  3. 模块继承:由于VeinsInetCar继承自AdhocHost,进而继承自StandardHost,一直到NodeBase,这些基础模块可能已经包含了对传输层协议的支持。特别是在AdhocHostStandardHost级别,通常会包括对主要传输层协议(如TCP和UDP)的支持,而无需在每个仿真场景的.ini文件中进行单独配置。

我认为都有一定的道理,根据上面的截图可以知道,应用层是由VeinsInetSampleApplication来实现的,跳转到其对应的.ned文件,可以发现它继承自VeinsInetApplicationBase:

再次跳转,找到这个VeinsInetApplicationBase:

这个代码并没有明确的规定UDP还是TCP,但是根据socket门的注释,应该是使用的UDP。

  • 最后,为了验证,运行仿真并点击一个节点:

可见,虽然链路层出现了循环回路lo,传输层出现了tcp,但是真正实现从上到下沟通的协议还分别是:UDP,IPV4和WLAN

我的推测是:由于没有明确的规定lo和tcp不能使用,所以他们也可能存在,并在某种方面帮助消息传播?

Q4在最后的仿真结果中,汽车的行为是在哪里被定义的?比如,为什么node[0]会在20秒坏掉又在50秒重新启动?

A

这个问题的答案其实已经在仿真结果下的文字中回答一部分了,但是其行为的核心,其实还是在于应用层所定义的这个“VeinsInetSampleApplication”,核心的代码就定义在VeinsInetSampleApplication.cc中(veins_inet->src->veins_inet),以下是代码中最重要的两个函数:

//设置哪个节点什么时候速度为0,什么时候恢复的函数
bool VeinsInetSampleApplication::startApplication()
{// host[0] should stop at t=20sif (getParentModule()->getIndex() == 0) { //如果节点为0auto callback = [this]() { //这个函数在20秒倒计时结束后被运行getParentModule()->getDisplayString().setTagArg("i", 1, "red"); //车子变红traciVehicle->setSpeed(0); //车子不会立刻停下来,因为仿真软件中定义的物理限制的影响,例如车辆的减速能力,但车子肯定会在20秒后很短时间内停下//定义要发送的包的内容auto payload = makeShared<VeinsInetSampleMessage>();payload->setChunkLength(B(100));payload->setRoadId(traciVehicle->getRoadId().c_str());timestampPayload(payload);//定义包的名字并发送包auto packet = createPacket("accident");packet->insertAtBack(payload);sendPacket(std::move(packet));// host should continue after 30sauto callback = [this]() { //这个函数在30秒倒计时结束后被运行traciVehicle->setSpeed(-1); //车子恢复正常};timerManager.create(veins::TimerSpecification(callback).oneshotIn(SimTime(30, SIMTIME_S)));//30秒倒计时};timerManager.create(veins::TimerSpecification(callback).oneshotAt(SimTime(20, SIMTIME_S)));//20秒倒计时}return true;
}//规定节点在收到消息之后如何反应的函数
void VeinsInetSampleApplication::processPacket(std::shared_ptr<inet::Packet> pk)
{auto payload = pk->peekAtFront<VeinsInetSampleMessage>();EV_INFO << "Received packet: " << payload << endl;getParentModule()->getDisplayString().setTagArg("i", 1, "green"); //接收到包,车子变绿traciVehicle->changeRoute(payload->getRoadId(), 999.9); //重新规划路径if (haveForwarded) return; //用于避免重复处理或发送相同的消息//如果是第一次收到消息auto packet = createPacket("relay"); //创建用于转发的包packet->insertAtBack(payload);sendPacket(std::move(packet)); //转发haveForwarded = true;
}

根据代码可以得出以下的逻辑:

当node[0]停止,即模拟事故发生时,会立刻装载一个大小为100Byte的包,并在其中添加当前所在路的ID,然后向通讯范围内的所有节点(汽车)发送这个包。每个收到这个包的节点(汽车)会立刻从包中获取事故路段的ID,并假设事故路段的通过时间为最大。这就导致所有会经过事故路段且拥有切换路径条件的汽车在收到这个包后会立刻切换路径。同时,向其通信范围的所有节点(汽车)转发一次包的内容。

 

Q5

A

 

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

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

相关文章

0 决策树基础

目录 1 绪论 2 模型 3 决策树面试总结 1 绪论 决策树算法包括ID3、C4.5以及C5.0等&#xff0c;这些算法容易理解&#xff0c;适用各种数据&#xff0c;在解决各种问题时都有良好表现&#xff0c;尤其是以树模型为核心的各种集成算法&#xff0c;在各个行业和领域都有广泛的…

优化页面加载时间:改善用户体验的关键

✨✨ 祝屏幕前的您天天开心&#xff0c;每天都有好运相伴。我们一起加油&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、为什么页面加载时间重要&#xff1f; 二、如何减少页面加载时间&#xff1f; …

干部任免管理系统开发(二) 数据库表的建设

前言: 字段照搬Lrmx文件内容 数据库表字段的设计基本上就是照搬Lrmx文件内容,没有什么过多的技术含量,也可以根据自己的需要对照Lrmx文件的格式自己去定义字段了。 软件的功能截图如下&#xff1a;核心就是能够任免审批表内容读取到数据库&#xff0c;生成lrmx和word格式方便做…

SiteSucker Pro mac 5.3.2激活版 网站扒站神器

SiteSucker是一个Macintosh应用程序&#xff0c;可以从互联网自动下载网站。它通过将站点的网页、图像、PDF、样式表和其他文件异步复制到本地硬盘驱动器&#xff0c;复制站点的目录结构来实现此目的。只需输入一个URL&#xff08;统一资源定位器&#xff09;&#xff0c;按回车…

JavaScript 入门指南(三)BOM 对象和 DOM 对象

BOM 对象 BOM 简介 BOM&#xff08;browser Object Model&#xff09;即浏览器对象模型BOM 由一系列对象组成&#xff0c;是访问、控制、修改浏览器的属性的方法BOM 没有统一的标准&#xff08;每种客户端都可以自定标准&#xff09;。BOM 的顶层是 window 对象 window 对象 …

习题2-5 求平方根序列前N项和

本题要求编写程序&#xff0c;计算平方根序列 的前N项之和。可包含头文件math.h&#xff0c;并调用sqrt函数求平方根。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中按照“sum S”的格式输出部分和的值S&#xff0c;精确到小数点后两位。题目保证计算结果不…

1.10 类、方法、封装、继承、多态、装饰器

一、介绍类 类(class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例 实例化&#xff1a;创建一个类的实例&#xff0c;类的具体对象。 对象&#xff1a;通过类定义的数据结构实例。对象包括两个数据成员&#x…

物联网实战--入门篇之(六)嵌入式-WIFI驱动(ESP8266)

目录 一、WIFI简介 二、基础网络知识 三、思路讲解 四、代码分析 4.1 状态机制 4.2 客户端连接 4.3 应用数据接收处理 4.4 数据发送 4.5 主函数调用 4.6 网络连接ID分配 五、总结 一、WIFI简介 WIFI在我们生活中太常见了&#xff0c;手机电脑都可以用WiFi连接路由器进行上…

2024品牌私域运营:「去中心化」正在成为企业决胜关键

越来越多的品牌选择以DTC模式与消费者互动和销售。通过与消费者建立紧密联系&#xff0c;不仅可提供更具成本效益的规模扩张方式&#xff0c;还能控制品牌体验、获取宝贵的第一方数据并提升盈利能力。许多企业采取的DTC私域策略以交易为中心的方法往往导致了成本上升和运营复杂…

【Blockchain】区块链浏览器 | 以太坊Etherscan比特币Blockchain门罗币Monero

区块链浏览器概述 区块链浏览器是一种软件,它使用API(应用程序编程接口)和区块链节点从区块链中提取各种数据&#xff0c;然后使用数据库来排列搜索到的数据&#xff0c;并以可搜索的格式将数据呈现给用户。 用户的输入是资源管理器上的可搜索项&#xff0c;然后通过数据库上…

市场复盘总结 20240329

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 60% 最常用…

STM32系统结构及总线介绍

1、学习路径 STM32中文参考手册中的第二章存储器和总线构架 2、系统架构&#xff08;中等容量芯片stm32f103c8&#xff09; 在小容量、中容量和大容量产品中,主系统由以下部分构成: 四个驱动单元&#xff1a; CortexTM-M3内核DCode总线&#xff08;D-bus&#xff09;&#…

霍尼韦尔大路灯怎么样?书客、雷士、霍尼韦尔护眼落地灯实测PK!

生活在快节奏的时代中&#xff0c;当代青少年都顶着很大的压力&#xff0c;熬夜学习是家常便饭&#xff0c;有时还需要借助电子产品来辅助学习&#xff0c;再加上许多家长都不太注重孩子的视力健康问题&#xff0c;孩子长时间处于一个不良的环境光下学习&#xff0c;眼睛极易疲…

PyLMKit(9):ChatTable与你的表格聊天,表格问答

功能介绍 与你的结构化数据聊天&#xff1a;支持主流数据库、表格型excel等数据&#xff01; ChatDB&#xff1a;支持数据库问答ChatTable&#xff1a;支持txt,excel,csv等pandas dataframe表格的问答 1.下载安装 pip install pylmkit -U pip install pandasql2.ChatTable实…

【AI+儿童绘本】从0到1制作儿童绘本故事操作思路

今天刷了会小H书&#xff0c;无意刷到一些 睡前儿童绘本故事&#xff0c; 下面一堆评论说 求软件什么的&#xff0c;博主只是引流没做任何回复。 这里写一篇文章科普下吧&#xff0c;免得有人被割韭菜。 制作儿童绘本&#xff0c; 大概这几个步骤。1、写生动有趣的故事&#x…

隐私计算实训营学习六:隐语PIR介绍及开发指南

文章目录 一、隐语实现的PIR总体介绍1.1 PIR的定义和种类1.2 隐语PIR功能分层 二、Index PIR-SealPIR介绍三、Keyword PIR- Labeled PSI介绍四、隐语PIR后续计划 一、隐语实现的PIR总体介绍 1.1 PIR的定义和种类 PIR(Private Information Retrieval PIR)隐匿查询&#xff1a;…

使用VNP时 本地服务/Dbeaver 无法连接数据库

在家使用VPN连接&#xff0c;启动本地Eclipse 的springboot 服务时&#xff0c;无法正常连接数据库。 解决方法: 在启动项配置中增加 -Djava.net.preferIPv4Stacktrue 之后&#xff0c;使用 Dbeaver时&#xff0c;也出现如下连接异常&#xff1a; 解决方法&#xff1a; 在dbe…

C语言:文件操作(2)

4.2 fputc的使用 这里写自定义目录标题 fputc的定义&#xff1a; 主要功能&#xff1a;一个字符一个字符的写进文件&#xff0c;将int类型的字符character写进文件流&#xff08;FILE* stream&#xff09;中&#xff0c;返回一个整形。如果成功fputc会返回写进文件的字符&…

城市雨量水位监测站

TH-SW2在繁华的现代都市中&#xff0c;有这样一个默默奉献的守护者&#xff0c;它时刻监测着城市的雨量和水位&#xff0c;为城市的安全提供着重要的保障。它就是我们今天要介绍的——城市雨量水位监测站。 一、雨量水位监测站的重要性 城市雨量水位监测站是城市防洪排涝体系的…

全局UI方法-弹窗三-文本滑动选择器弹窗(TextPickDialog)

1、描述 根据指定的选择范围创建文本选择器&#xff0c;展示在弹窗上。 2、接口 TextPickDialog(options?: TextPickDialogOptions) 3、TextPickDialogOptions 参数名称 参数类型 必填 参数描述 rang string[] | Resource 是 设置文本选择器的选择范围。 selected nu…