Launch学习

news2024/9/30 15:31:42

参考博客:
(1) 史上最全的launch的解析来啦,木有之一欧

1 ROS工作空间简介

在这里插入图片描述

2 元功能包

src目录下可以包含多个功能包,假设需要使用机器人导航模块,但是这个模块中包含着地图、定位、路径规划等不同的功能包,它们的逻辑关系如下:

在这里插入图片描述
在Linux系统中为了更方便的组织工程项目(这里针对的是项目文件,即功能包),出现了“元功能包”的概念。这个是一个“虚包”,就是这个功能包的src目录下没有源文件,因此自身不会实现专属功能,其功能的实现完全依赖于其他的功能包,起到一个组织功能包的作用。

以导航模块中的元功能包为例:
在这里插入图片描述navigation功能包为元功能包(metapackage),元功能包中由于没有src目录因此无需添加任何依赖项,因为这个功能包没有自己的专属功能,它的功能是借助其他的功能包的功能来实现的。元功能包有两个文件即可:一个是package.xml文件:用于声明元功能包所依赖的其他功能包;另一个是CMakelist.txt文件:用于指定功能包之间的依赖关系。

CMakelist.txt

cmake_minimum_required(VERSION 3.0.2)  
project(navigation)  
find_package(catkin REQUIRED)  
catkin_metapackage() // 只需添加此条内容即可

package.xml

<exec_depend>amcl</exec_depend>  
<exec_depend>base_local_planner</exec_depend>  
<exec_depend>carrot_planner</exec_depend>  
<exec_depend>clear_costmap_recovery</exec_depend>  
<exec_depend>costmap_2d</exec_depend>  
<exec_depend>dwa_local_planner</exec_depend>  
<exec_depend>fake_localization</exec_depend>  
<exec_depend>global_planner</exec_depend>  
<exec_depend>map_server</exec_depend>  
<exec_depend>move_base</exec_depend>  
<exec_depend>move_base_msgs</exec_depend>  
<exec_depend>move_slow_and_clear</exec_depend>  
<exec_depend>navfn</exec_depend>  
<exec_depend>nav_core</exec_depend>  
<exec_depend>rotate_recovery</exec_depend>  
<exec_depend>voxel_grid</exec_depend>  
  
<export>  
    <metapackage/> // 表征:这个功能包为元功能包
</export>  

3 Launch文件

Launch文件:源文件的组织者
① 节点启动标签

<launch>  
    <node pkg = "turtlesim" type = "turtlesim_node" name = "my_node"/>  
    <node pkg = "turtlesim" type = "turtle_teleop_key" name = "my_key"/>  
</launch>  

Tip:因为ROS中采用多线程,因此节点的运行不会按照节点在launch中排列顺序进行。

pkg:功能包的名称
type:节点本来的名称,这个名称和节点所在.cpp源文件的文件名一致
name:节点重映射的名称,相当于在系统中给节点所在源文件改了个名字

launch标签有一个子级标签deprecated,用于文本说明:

<launch deprecated="this vision is out-of-date!">  
</launch> 

如果认为给很多节点取名太麻烦,可以使用name=”$(anon node_name)”标签在节点node_name名称之后加一些随机数,使得该节点名称在整个catkin编译项目中唯一:

<launch deprecated="this vision is out-of-date!">  
    <!-- the topic of turtlesim_node is /turtle1/cmd_vel -->  
    <node pkg="turtlesim" type="turtlesim_node" name="$(anon my_node)"/>  
    <!-- the topic of turtle_teleop_key is /turtle1/cmd_vel -->  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen"/>  
</launch>  

意外关闭后自动启动的子级标签
respawn = true|false 表示:如果节点意外关闭是否重新启动

<launch>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node" respawn="true"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" respawn="true"/>  
</launch>  

节点延迟启动的子级标签,一般结合节点重启动是使能标签respawn(如果节点异常退出运行,那么该节点会被重新启动)一起使用

<launch>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node" respawn="true" respawn_delay="10"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" respawn="true" respawn_delay="10"/>  
</launch>  

如果XXX节点结束运行(XXX节点被杀死),则所有节点都停止运行

<launch>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node" required="true"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" />  
</launch>  

给节点名称添加前缀(给节点添加命名空间)的子级标签

<launch>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node" ns="hello"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key"/>  
</launch> 

② 参数设置标签
设置global全局参数

<launch>
	<param name="var" type="int" value="10"/>
</launch>

结合标签设置带有命名空间的私有参数

<launch>
	<node pkg="turtlesim" type="turtlesim_node" name="my_node"/>
	<node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen">
		<param name="var1" type="int" value="20"/>
	</node>
</launch>

③ 参数打包输入输出删除的标签
从.yaml文件中读取参数:

<launch>
	<rosparam command="load" file="$(find test01)/launch/params.yaml"/>
	<node pkg="turtlesim" type="turtlesim_node" name="my_node"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen"/> 
	<param name="var" type="int" value="10"/>  
</launch> 

将.yaml参数文件中的参数导入参数服务器时,我们还可以给这些参数添加namespace命名空间:

<launch>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen"/>  
    <param name="var" type="int" value="10"/>  
    <rosparam command="load" file="$(find test01)/launch/params.yaml" ns="hello"/>  
</launch> 

将参数打包输入进.yaml文件中,这样做啥变量都没导进去:
在这里插入图片描述

<launch>  
    <rosparam command="dump" file="$(find test01)/launch/input.yaml"/>  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node"/>  
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen"/>  
    <param name="var" type="int" value="10"/>  
</launch>  

我们要想导入参数必须另建一个.launch文件,在使用上述launch文件启动完所有节点之后,在另一个launch文件中执行该功能包参数的导出操作:

<launch>
	<rosparam command="dump" file="$(find test01)/launch/input.yaml" />
</launch>
<launch>  
    <rosparam command="dump" file="$(find test01)/launch/input.yaml"/>  
    <rosparam command="delete" param="/hello/n1"/>  
</launch>  

④ 参数统一管理的标签

<launch>  
    <arg name="car_width" default="[1,2,3,4]" doc="the width of car"/>  
    <rosparam param="a_list">$(arg car_width)</rosparam>  
    <rosparam>  
        Name:  
            a: 9  
            b: "hello"  
            c: $(arg car_width)  
    </rosparam>  
</launch>

⑤ 改topic名称的标签

<launch>  
    <!-- the topic of turtlesim_node is /turtle1/cmd_vel -->  
    <node pkg="turtlesim" type="turtlesim_node" name="my_node"/>  
    <remap from="/turtle1/cmd_vel" to="new_topic"/>  
</launch>  

⑥ 节点组织标签
就是给被<group>…</group>包含的所有参数、节点的属性加上了namespace

<launch deprecated="this vision is out-of-date!">  
    <group ns="family">  
        <!-- the topic of turtlesim_node is /turtle1/cmd_vel -->  
        <node pkg="turtlesim" type="turtlesim_node" name="my_node"/>  
        <!-- the topic of turtle_teleop_key is /turtle1/cmd_vel -->  
        <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen"/>  
        <rosparam command="load" file="$(find test01)/launch/params.yaml" ns="hello"/>  
        <arg name="car_width" default="[1,2,3,4]" doc="the width of car"/>  
        <rosparam param="a_list" value="$(arg car_width)"/>  
        <rosparam>  
            Name:  
                a: 9  
                b: "hello"  
                c: [1,2,3,4]  
        </rosparam>  
        <param name="var" type="int" value="$(arg car_width)"/>  
    </group>  
</launch> 

⑦ 启动其他launch文件的标签

<launch>  
    <include file="$(find test01)/launch/test01_launch.launch">  
        <arg name="car_width" default="10"/>  
    </include>  
</launch>  

4 功能包/源文件/launch文件组织工具

在这里插入图片描述文件组织形式如下所示:
在这里插入图片描述

5 功能包绝对路径替换标签

标签格式:$(find package_name)
使用示例:

<launch>  
     <include file="$(find tf2_turtle)/launch/setupGUI.launch"/>  
</launch>  

6 工作空间下绝对路径替换标签

下面是test1.launch调用setupGUI.launch文件的代码,并且两个launch文件在一个文件夹之中:

<launch>  
     <include file="$(dirname)/setupGUI.launch"/>  
</launch> 

$(dirname)代表“test1.launch文件所在工作空间的绝对地址

7 Launch文件

列出几个Launch文件自测一下学习成果

<?xml version="1.0"?>
<launch>
    <!--  加载车模型 -->
    <include file="$(find vehicle_description)/launch/estima_black.launch" />
    <!--  -->
    <node pkg="car_simulation" type="car_model_node" name="car_simulation" output="screen" />
</launch>
<launch>
    <include file="$(find global_routing)/launch/global_routing.launch"/>
    <!-- 车辆仿真 -->
    <include file="$(find car_simulation)/launch/car_simulation.launch" />
    <!-- rviz -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find global_routing)/config/planning_demo.rviz"/>  
</launch>
<?xml version="1.0"?>
<launch>
    <!-- 其他launch文件传入的参数 -->
    <arg name="is_planner"/>
    <arg name="is_lateral_optimization"/>
    <arg name="is_change_lane"/>
    <arg name="is_carla_simulation"/>
    <arg name="ego_vehicle_name"/>
    <arg name="is_parking"/>
    <arg name="is_goal"/>

    <!-- 模拟动态障碍物的加载文件,这些都是录好的轨迹点,播放这个文件就可以实现障碍物移动 -->
    <param name="obstacle_test_path" value="$(find dynamic_routing)/obstacle_files"/>
    <!-- 加载存储的其他参考线数据 -->
    <param name="referenceline_path" value="$(find dynamic_routing)/other_referenceline_files"/>
    <!-- yaml文件 -->
    <param name="yaml_path" value="$(find dynamic_routing)/config"/>

    <!-- 规划算法选择 -->
    <param name="use_what_planner"  value="$(arg is_planner)"/>
    <!-- 变道决策是否开启 -->
    <param name="change_lane"  value="$(arg is_change_lane)"/>
    <!-- 是否使用二次规划,选择了lattice规划,选择这个才有效果 -->
    <param name="use_lateral_optimization"  value="$(arg is_lateral_optimization)"/>
    <!-- 是否选择carla联合仿真 -->
    <param name="carla_simulation"  value="$(arg is_carla_simulation)"/>
    <!-- role_name -->
    <param name="role_name"  value="$(arg ego_vehicle_name)"/>
    <!-- carla 停车场景 -->
    <param name="parking_mode" value="$(arg is_parking)"/>

    <!-- 在frenet规划下的参数设置,lattice规划不用这些 -->
    <!-- COLLISION_CHECK_THRESHOLD 距离障碍物的最短距离 -->
    <param name="COLLISION_CHECK_THRESHOLD" type="double" value="2" />
    <!-- 调整轨迹的长度 -->
    <param name="MaxT" type="double" value="11" />
    <param name="MinT" type="double" value="9" />

    <!--  判断与终点的停车距离阈值 -->
     <param name="goal_distanse"  type="double" value="$(arg is_goal)"/>
    
    <!-- 打开 Hybrid_a_star 的测试图 -->
    <!-- mapserver提供了一个ROS节点,该节点通过一个ROS Service来提供地图数据 -->
	<node name="map_server" pkg="map_server" type="map_server" args="$(find dynamic_routing)/maps/map.yaml" >
		<param name="frame_id" value="map" />
	</node>
 
    <!--Open palnner的launch参数,顺便加载dynamic节点 -->
    <include file="$(find dynamic_routing)/launch/op_common_params.launch" />

    <!-- DWA -->
    <arg name="dwa_params" default="$(find dynamic_routing)/config/dwa_params.yaml"/>
    <rosparam command="load" file="$(arg dwa_params)"/>
</launch> 
<?xml version="1.0"?>
<launch>
    <!-- 是否使用carla联合仿真 -->
    <arg name="carla"  default="false"/>   
    <arg name="ego_vehicle_name"  default="ego_vehicle"/>  
    <param name="carla_simulation" value="$(arg carla)"/>
    <param name="role_name" value="$(arg ego_vehicle_name)"/>

    <!-- 不能改这里的参数 -->
    <arg name="parking" default="false"/>
    <param name="parking_mode" value="$(arg parking)"/>
    
    <!-- ros单独仿真下的controller,carla不适用: 
        1 stanley  
        2 lqr 
        3 pure_pursuit
        4 pid 
        5 mpc  
    -->
    <arg name="control"  value="2"/>  
    <param name="use_what_controller" value="$(arg control)"/>

    <!-- planner: 
        1是纯frenet规划
        2是lattice规划
        3是em_palnner规划
        4是混合A*规划
        5是op_planner规划
        6是DWA规划
        7是Teb规划
        8是simple_em(EM的简化版本,待更新)
    -->
    <arg name="planner"  value="7"/>    
    <param name="use_what_planner" value="$(arg planner)"/>

    <!-- 是否使用二次规划,选择了lattice规划,选择这个才有效果 -->
    <!-- false:lattice 采样规划,true:lattice 二次规划 -->
    <arg name="use_lateral_optimization" default="false"/>    

    <!-- 是否开启变道决策,变道选择的是Lattce采样规划,其他方法不使用 -->
    <arg name="change_lane"  default="false"/>    

    <!-- 参考线平滑的方式选择: true:CosThetaSmoother  false:FemPosSmooth-->
    <arg name="which_smoother" default="false"/>    
    <param name="which_smoothers" value="$(arg which_smoother)"/>

    <!--  判断与终点的停车距离阈值 -->
    <arg name="goal_dis"  value="0.5"/>  
    <param name="goal_distanse"  type="double" value="$(arg goal_dis)"/>

    <!-- 局部规划 -->
    <include file="$(find dynamic_routing)/launch/dynamic_routing.launch" >
        <arg name="is_planner" value="$(arg planner)" />
        <arg name="is_lateral_optimization" value="$(arg use_lateral_optimization)" />
        <arg name="is_change_lane" value="$(arg change_lane)" />
        <arg name="is_carla_simulation" value="$(arg carla)" />
        <arg name="ego_vehicle_name" value="$(arg ego_vehicle_name)" />
        <arg name="is_parking" value="$(arg parking)"/>
        <arg name="is_goal" value="$(arg goal_dis)"/>
    </include>

    <!-- carla联合仿真下的控制方法和参数  -->
    <!-- LQR_dynamics  LQR_kinematics Stanley PurePursuit -->
    <param name="control_method" value='LQR_kinematics'/>  
    <!-- "PurePursuit"增益系数 -->
    <param name="k_pure" type="double" value="0.3" /> 
    <!-- "Stanley"增益系数  -->
    <param name="k_cte" type="double" value="100" /> 
    <param name="kp"  value="0.5" />
    <param name="ki" type="double" value="0.02" />
    <param name="kd" type="double" value="0.05" />
    <!-- LQR Q R矩阵参数 -->
    <param name="Q_ed" type="double" value="20.0" />
    <param name="Q_ed_dot" type="double" value="1.0" />
    <param name="Q_ephi" type="double" value="10.0" />
    <param name="Q_ephi_dot" type="double" value="1.0" />
    <param name="R_value" type="double" value="40.0" />
    <!--  -->
    <param name="Q_ex_k" type="double" value="3.0" />
    <param name="Q_ed_k" type="double" value="3.0" />
    <param name="Q_ephi_k" type="double" value="1.5" />
    <param name="R_value_k" type="double" value="4.0" />

    <!-- 全局规划 -->
    <node pkg="global_routing" type="global_routing_node" name="global_routing" output="screen" />

</launch>

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

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

相关文章

【Python】1. 背景知识

认识 Python 计算机基础概念 什么是计算机? 很多老一辈的人, 管下面这个叫做计算机. 然鹅, 它只是 “计算器”, 和计算机是有很大区别的. 现在我们所说的计算机, 不光能进行算术运算, 还能进行逻辑判断, 数据存储, 网络通信等等功能,。 以至于可以自动的完成非常复杂的工作…

SLAM基础知识-卡尔曼滤波

前言&#xff1a; 在SLAM系统中&#xff0c;后端优化部分有两大流派。一派是基于马尔科夫性假设的滤波器方法&#xff0c;认为当前时刻的状态只与上一时刻的状态有关。另一派是非线性优化方法&#xff0c;认为当前时刻状态应该结合之前所有时刻的状态一起考虑。 卡尔曼滤波是…

java垃圾回收

垃圾回收 一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现内存泄漏。我们称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收。 内存泄漏指的是不再使用的对象在系统中未被回收&#xff0c;内存泄漏的积累可能…

LCR 124. 推理二叉树

解题思路&#xff1a; 分治 class Solution {// 一个哈希表用于存储中序遍历中每个值对应的索引&#xff0c;用于快速查找HashMap<Integer,Integer> map new HashMap<>();// 保存前序遍历的结果数组int[] preorder;// 主函数&#xff0c;传入前序和中序遍历的结果…

Vue中的计算属性和方法有什么区别?

Vue.js是一款流行的JavaScript前端框架&#xff0c;提供了丰富的功能和便捷的开发方式。在Vue中&#xff0c;计算属性和方法是常用的两种方式来处理数据和逻辑。但它们之间存在一些区别&#xff0c;本文将详细介绍Vue中计算属性和方法的区别&#xff0c;并通过示例代码加深理解…

UE4c++ ConvertActorsToStaticMesh ConvertProceduralMeshToStaticMesh

UE4c ConvertActorsToStaticMesh 创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建蓝图函数UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量生成模型&#xff0c;我们把虚幻带有的方法迁移成函…

一周学会Django5 Python Web开发-Django5详细视图DetailView

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计28条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的停车位检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发停车位检测系统对于优化停车资源管理和提升用户体验至关重要。本篇博客详细介绍了如何利用深度学习构建一个停车位检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并结合了YOLOv7、YOLOv6、YOLOv5的性能对比&#xf…

python-分享篇-控制摄像头

文章目录 准备代码效果 准备 安装cv2 pip install opencv-python &#xff08;如果只用主模块&#xff0c;使用这个命令安装&#xff09; pip install opencv-contrib-python &#xff08;如果需要用主模块和contrib模块&#xff0c;使用这个命令安装&#xff09; 我的代码l里…

Vue+SpringBoot打造知识图谱构建系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 知识图谱模块2.2 知识点模块2.3 学生测评模块2.4 学生成绩模块 三、系统展示四、核心代码4.1 查询知识点4.2 新增知识点4.3 查询知识图谱4.4 查询学生成绩4.5 查询学生成绩 五、免责说明 一、摘要 1.1 项目介绍 基于J…

AJAX 学习笔记(Day3)

「写在前面」 本文为黑马程序员 AJAX 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. AJAX 学习笔记&#xff08;Day1&#xff09; 目录 3 AJAX 原理 3.1 XMLHttpRequest 3…

Stable Video文本生成视频公测地址——Scaling Latent Video Diffusion Models to Large Datasets

近期&#xff0c;Stability AI发布了首个开放视频模型——"Stable Video"&#xff0c;该创新工具能够将文本和图像输入转化为生动的场景&#xff0c;将概念转换成动态影像&#xff0c;生成出电影级别的作品&#xff0c;旨在满足广泛的视频应用需求&#xff0c;包括媒…

为什么要在业务系统中引入大宽表?

在高度系统化驱动的业务中&#xff0c;查看业务报表已经是一个很常见的需求了。在分工非常明确的大型企业里&#xff0c;往往有专门的数据分析团队 BI 或者数据开发团队&#xff0c;他们能够胜任此类需求&#xff08;但也未必是轻松的&#xff0c;或者说高效的&#xff09;。 …

编码器原理图

操作 旋转编码器提供两种交互方式&#xff1a; 每次用户旋转旋钮时&#xff0c;都会在 DT 和 CLK 引脚上产生低电平信号&#xff1a; 顺时针旋转会先使CLK引脚变为低电平&#xff0c;然后DT引脚也变为低电平。 逆时针旋转会使 DT 引脚首先变为低电平&#xff0c;然后 CLK 引脚…

深度学习PyTorch 之 RNN-中文多分类

关于RNN的理论部分我们已经在前面介绍过&#xff0c;所以这里直接上代码 1、 数据部分 1.1 读取数据 # 加载数据 data_path ./data/news.csv data pd.read_csv(data_path)# 预览数据的前几行 data.head()数据是csv格式&#xff0c;只有两列&#xff0c;第一列是标签&#…

2024最新外贸建站:WordPress搭建外贸独立站零基础教程

想与外国人做生意有多种方式&#xff0c;一些朋友选择在跨境电商平台上开店如&#xff08;亚马逊&#xff09;&#xff0c;而另一些朋友则决定建立自己的外贸独立站点。本篇教程主要说的是第二种方式如何快速建立自己的外贸独立站&#xff01;通过学习这篇外贸建站教程&#xf…

【AI绘画】免费GPU Tesla A100 32G算力部署Stable Diffusion

免责声明 在阅读和实践本文提供的内容之前&#xff0c;请注意以下免责声明&#xff1a; 侵权问题: 本文提供的信息仅供学习参考&#xff0c;不用做任何商业用途&#xff0c;如造成侵权&#xff0c;请私信我&#xff0c;我会立即删除&#xff0c;作者不对读者因使用本文所述方法…

Beans模块之工厂模块BeanClassLoaderAware

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

DataGrip(IDEA 内置)连接 SQL Server

原文&#xff1a;https://blog.iyatt.com/?p14265 测试环境&#xff1a; IDEA 2023.1SQL Server 2022 首先打开 SQL Server 配置管理工具 启用 TCP/IP 打开 Windows 服务管理 在服务列表中找到 SQL Server&#xff08;MSSQLSERVER&#xff09;&#xff0c;右键重新启…

【计算机网络通信】计算机之间的局域网通信和互联网通信方法(附Python和C#代码)

文章目录 前言一、局域网通信1.1 基本原理和方法1.1.1 获取本地ip1.1.2 实现局域网内的广播1.1.3 进行局域网通信 1.2 实现多客户端连接1.3 Python源码1.4 C#源码1.5 可能存在的问题 二、互联网通信2.1 实现原理2.1.1 内网穿透软件2.1.2 实现互联网通信 2.2 Python源码2.3 C#源…