安防监控项目---web点灯(网页发送命令控制A9的led)

news2024/9/23 17:19:53

文章目录

  • 前言
  • 一、web点亮LED流程
  • 二、静态网页设计(html界面)
  • 三、 CGI和BOA在本项目中的使用
  • 总结


前言

书接上期,和大家分享的是web点灯,哈哈哈,谈论起点灯这个词,这么久以来我已然已经成长为一名合格的点灯大师了;点灯是一个很好的测试办法,不仅要去测试开发板是否正常,也要去测试网页是否能够顺利下发数据,接下俩让我们仔细来看一下这个过程!!!


一、web点亮LED流程

先来理一下流程,之后呢我们按照这个流程来一步步实现相应的功能;

html的数据发送到A9期间经历了这么几个过程:
在这里插入图片描述
接下来咱们就根据上面流程图一步步来看一下每一步的具体实现!

二、静态网页设计(html界面)

以下呢就是默认index.html网页的具体设计;代码还是比较简单的,没啥技术含量,大家只要能读懂,在这个基础上可以根据自己的需求去修改就可以了;

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>登录界面</title>
	</head>

	<body>

		<body  background="./images/ckp00.png">
			<div align="center">
				<table width="900" border="0" background="./images/ckp3.png">
					<tr>
						<th width="385" height="320" scope="col">&nbsp;</th>
						<th width="385" scope="col">&nbsp;</th>
						<th width="70" scope="col">&nbsp;</th>
					</tr>
					<tr>
						<td height="50">&nbsp;</td>
						<td>
							<div align="center"></div>
						</td>
						<td>&nbsp;</td>
					</tr>
				</table>
			</div>
			<div></div>
			<div align="center">
				<table width="900" border="0">
					<tr>
						<td>
							<form onsubmit="return isValidate(myform)" action="cgi-bin/login.cgi" method="post">
								用户名: <input type="text" name="username" id="username"> 密码: <input type="password" name="userpass" id="userpass">
								<input type="submit" value="登录" id="button">
							</form>

						</td>
					</tr>
				</table>
			</div>

			<!--<p>&nbsp;</p>-->

			<div align="center">
				<table width="900" height="467" border="0" background="./images/ckp4.jpg">
					<tr>
						<td width="126" height="248">&nbsp;</td>
						<td width="351"></td>
						<td width="101">&nbsp;</td>
					</tr>
					<tr>
						<td></td>
						<td>&nbsp;</td>
					</tr>
					<tr>
						<td>
							<form name="myform" method="get" action="cgi-bin/login.cgi" onsubmit="return isValidate(myform)"></form>
						</td>
					</tr>
			</div>
		</body>

</html>

网页打开如下:
在这里插入图片描述
在这里呢需要用户名和密码来进行登录;那接下来肯定是看一下怎样进行登录了呀!

三、 CGI和BOA在本项目中的使用

用户名和密码由网页端发送BOA服务器,接收到数据后CGI进行数据的解析,那接下来看下CGI的程序到底是如何编写的!

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   

char name[64];   
char pass[64];  
  
char* getcgidata(FILE* fp, char* requestmethod)   
{   
	char* input;   
	int len;   
	int size = 1024;   
	int i = 0;   
	  
	if (!strcmp(requestmethod, "GET"))   {   
		 input = getenv("QUERY_STRING");   
		 return input;   
	}   
	else if (!strcmp(requestmethod, "POST"))   {   
		 len = atoi(getenv("CONTENT_LENGTH"));   
		 input = (char*)malloc(sizeof(char)*(size + 1));   
		   
		 if (len == 0)   {   
			input[0] = '\0';   
			return input;   
		 }   
		   
		 while(1)   
		 {   
			input[i] = (char)fgetc(fp);   
			if (i == size)   {   
				 input[i+1] = '\0';   
				 return input;   
			}   										  
			--len;   
			if (feof(fp) || (!(len)))   {   
				 i++;   
				 input[i] = '\0';   
				 return input;   
			}   
			i++;   
						  
		 }   
	}   
	return NULL;  
}	

void unencode_for_name_pass(char *input)
{
	int i = 0;   
	int j = 0;   
	
		// 我们获取的input字符串可能像如下的形式   
		// Username="admin"&Password="aaaaa"   
		// 其中"Username="和"&Password="都是固定的   
		// 而"admin"和"aaaaa"都是变化的,也是我们要获取的   
		  
		// 前面9个字符是UserName=   
		// 在"UserName="和"&"之间的是我们要取出来的用户名   	
	for ( i = 9; i < (int)strlen(input); i++ )  {   
		 if (input[i] == '&')   {   
			name[j] = '\0';   
			break;   
		 }                                       
		 name[j++] = input[i];   
	}   
   
	// 前面9个字符 + "&Password="10个字符 + Username的字符数   
	// 是我们不要的,故省略掉,不拷贝   
	for ( i = 19 + strlen(name), j = 0; i < (int)strlen(input); i++ ){   
		 pass[j++] = input[i];   
	}   
	pass[j] = '\0';   	
	
	//printf("Your Username is %s<br> Your Password is %s<br> \n", name, pass);   
	
	printf("Content-type: text/html\n\n");   //告诉编译器,用html语法来解析
	if((strcmp(name,"Romeo") == 0)&&(strcmp(pass,"123") == 0))		//html登陆的用户名和密码
	{
		printf("<script language='javascript'>document.location = 'http://192.168.1.100/choose.html'</script>"); //自动跳转到这个页面	
	}
	else{
		printf("用户名或密码错误<br><br>");  
		//exit(-1);
	}
}

int main()   
{   
	char *input;   
	char *req_method;   
 
	printf("Content-type: text/html\n\n");   //告诉编译器,用html语法来解析
	printf("The following is query reuslt:<br><br>");   

	req_method = getenv("REQUEST_METHOD");   
	input = getcgidata(stdin, req_method);   //获取URL 编码的数据
	
	unencode_for_name_pass(input);   //解码,并判断用户名,密码,如果正确,跳转至选择界面,否则提示错误	
	return 0;   
}   

上述代码就能够实现自动解析用户名和密码并且实现在登陆成功后跳转至二级页面的操作;
二级页面如下:
在这里插入图片描述
三级页面如下:
在这里插入图片描述
四级页面如下:
在这里插入图片描述
到这里呢我们就可以开始点灯了,但是我们必须清楚,网页下发指令后BOA服务器和CGI究竟是如何进行相应的;下来看一下cgi的工程框架:
在这里插入图片描述

下面看一下这个是a9_led.c的CGI代码:

#include <stdio.h> 
#include "cgic.h" 
#include <string.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define N 8

struct msg
{
	long type;
	long msgtype;
	unsigned char text[N];
};


int cgiMain() 
{ 
	key_t key;
	char buf[N];
	char sto_no[2];
	int msgid;
	struct msg msg_buf;
	memset(&msg_buf,0,sizeof(msg_buf));
	
	cgiFormString("led",buf,N);				//bled网页键值进行接收,接收8个字节
	cgiFormString("store",sto_no,2);

	if((key = ftok("/tmp", 'g')) < 0)		//创建IPC对象键值(生成一个IPC对象),用于消息队列,参数1是指定的文件名,参数2是子序号
	{
		perror("ftok");
		exit(1);
	}

	if((msgid = msgget(key, 0666)) < 0)		//创建一个消息队列
	{
		perror("msgget");
		exit(1);
	}

	bzero (msg_buf.text, sizeof (msg_buf.text));

	//如果是打开的按钮
	if (buf[0] == '1')
	{
		//开灯
		msg_buf.text[0] = ((sto_no[0] - 48)) << 6 | (0x0 << 4) | (1 << 0);
	}
	else
	{
		msg_buf.text[0] = ((sto_no[0] - 48)) << 6 | (0x0 << 4) | (0 << 0);
	}

	msg_buf.type = 1L;		//1L表示home1,也表示消息队列的消息类型
	msg_buf.msgtype = 1L;	//表示led设备的消息类型
	
	//向消息队列中写入消息,将消息发出,在A9端进行接收
	//这里发送的字节数为sizeof(msg_buf)-sizeof(long)的原因是由于不需要传送消息类型,只需要传送具体的消息大小即可(也就是msg结构体的第一个变量不需要作为发送的有效字节传送)
	msgsnd(msgid, &msg_buf,sizeof(msg_buf)-sizeof(long),0);

	sto_no[0] -= 48;	//为了后面生成是哪一个操作引起网页反馈(生成二级网页名序号)

	cgiHeaderContentType("text/html\n\n"); 
	fprintf(cgiOut, "<HTML><HEAD>\n"); 
	fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n"); 
	fprintf(cgiOut, "<BODY>"); 

	fprintf(cgiOut, "<H2>send sucess</H2>");

	//fprintf(cgiOut, "<a href='.html'>返回</a>"); 
	fprintf(cgiOut, "<meta http-equiv=\"refresh\" content=\"1;url=../a9_zigbee%d.html\">", sto_no[0]);
	fprintf(cgiOut, "</BODY>\n"); 
	fprintf(cgiOut, "</HTML>\n"); 


	return 0; 
} 

代码中注释的消息类型如下,这个是在通信结构体设计的分享中对应用层进程间通信使用消息队列机制的具体实现;
在这里插入图片描述
那么当cgi发把消息写入到消息队列后,那应用层究竟是如何进行接收的呢,继续来看吧:
下图是应用层的框架:
在这里插入图片描述
在pthread_client_request.c中,对消息队列进行了接收操作:

#include "data_global.h"
#include "linuxuart.h"

//消息队列id
extern int msgid;
//ipc对象键值
extern key_t key;
//锁资源
extern pthread_mutex_t mutex_client_request,
        		mutex_refresh,
        		mutex_sqlite,
	        	mutex_transfer,
	        	mutex_analysis,
	        	mutex_sms,
	        	mutex_buzzer,
	         	mutex_led,
	         	mutex_camera;
//条件变量
extern pthread_cond_t  cond_client_request,
        		cond_refresh,
        		cond_sqlite,
	        	cond_transfer,
	        	cond_analysis,
	        	cond_sms,
	        	cond_buzzer,
	         	cond_led,
	         	cond_camera;
//模块的控制命令字
extern unsigned char cmd_led;
extern unsigned char  cmd_buzzer;
extern unsigned char  cmd_fan;

//GPRS模块的电话号
extern char recive_phone[12] ;
extern char center_phone[12] ;

//消息队列通信结构体
struct msg msgbuf;


void *pthread_client_request(void *arg)
{
	if((key = ftok("/tmp",'g')) < 0){
		perror("ftok failed .\n");
		exit(-1);
	}

	msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666); 		//检测消息队列中是否有这个键值,如果有则返回对应的-1,没有则创建并返回创建消息队列的id
	if(msgid == -1)	{
		if(errno == EEXIST){ 							//如果已经存在
			msgid = msgget(key,0777); 					//设置权限为0777
		}else{
			perror("fail to msgget");
			exit(1);
		}
	}
	printf("pthread_client_request\n");
	
	while(1){
		bzero(&msgbuf,sizeof(msgbuf)); 					//清理操作,但一般使用memset,功能更加强大一点
		printf("wait form client request...\n"); 
		msgrcv (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 1L, 0); //从消息队列中读取消息
		printf ("Get %ldL msg\n", msgbuf.msgtype); 		//打印消息类型
		printf ("text[0] = %#x\n", msgbuf.text[0]); 	//打印消息内容

		//判断消息类型,从而确定是哪一个设备
		switch(msgbuf.msgtype){
			case 1L:
				//1L的类型是led的消息类型,此时上锁,等待消息内容也就是控制命令字复制完成后解锁,通过pthread_cond_signal唤醒pthread_led.c这个led线程,进行led的具体硬件操作
				pthread_mutex_lock(&mutex_led);
				printf("hello led\n");
				cmd_led = msgbuf.text[0];
				pthread_mutex_unlock(&mutex_led);
				pthread_cond_signal(&cond_led);
				break;
		}
	}

}
#endif 

下面来看一下led线程里都干了什么事情呢,代码如下:

#include "data_global.h"
#include "chrdev.h"
#include <unistd.h>

//led的锁资源和条件变量,用来进行同步和互斥操作
extern pthread_mutex_t  	mutex_led;
extern pthread_cond_t      	cond_led;

extern unsigned char cmd_seg;

//流水灯
int fswaterled_control(int led_fd, int times);
//四位二进制表示十六进制
int fsled_control(int led_fd, unsigned char led_control_cmd);  //发送的数字
//关闭所有的灯
int fsled_close_all(int led_fd);



//:A9LED模块线程.
void *pthread_led(void *arg)
{
	printf("pthread_led\n");
	int i, j;
	int led_fd;
	led_desc_t led;

	led_fd = open(LED_DEV, O_RDWR);
	if(led_fd == -1){
		printf("open failed.\n");
	}
	printf("led_fd ;%d.\n",led_fd);

	while(1){
		pthread_mutex_lock(&mutex_led); 				//锁和条件变量的操作都是原子操作,不会被cpu的任务调度机制打断
		printf("led ioctl:***********\n");
		pthread_cond_wait(&cond_led,&mutex_led); 		//会主动释放上面的锁并且判断是否有唤醒信号,在被唤醒后主动又继续加上锁(wait前必须加锁)
		printf("led ioctl:***********\n");
		if(cmd_led == 0x41){
			fswaterled_control(led_fd, 2);
			cmd_led = 0;
		}

		int tmp = cmd_seg & 0xf0;

		if(!(tmp ^ 0x70)) {

			fsled_control(led_fd, cmd_seg);
		}
		pthread_mutex_unlock(&mutex_led);
	}
	
	close(led_fd);
	
}


int fsled_control(int led_fd, unsigned char led_control_cmd)
{
	int i = 0;
	led_desc_t led;
	led_control_cmd &= 0x0f;
	int shift_count = 1; //第0位,第1 - 3位 

	printf("led_control_cmd = %d.\n",led_control_cmd);
	fsled_close_all(led_fd);
	sleep(3);
	while(led_control_cmd){
		if(shift_count >= 5)
			break;
		if((led_control_cmd & 0x1) == 1){ //第0位开始 = LED2
			shift_count ++;  // = 2  LED2 
			printf("if shift_count :%d.\n",shift_count);
			led.which = shift_count; //led2 3 4 5 灯
			ioctl(led_fd,FSLEDON,&led);
			usleep(50000);  //让驱动响应的时间
		}else {
			shift_count ++;
			printf("else shift_count :%d.\n",shift_count);
			led.which = shift_count; //led2 3 4 5 灯
			ioctl(led_fd,FSLEDOFF,&led);
			usleep(50000);
		}
		led_control_cmd >>= 1;
	}

	return 0;
}


int fsled_close_all(int led_fd)
{
	int i = 0;
	led_desc_t led;	

	for(i = 2;i < 6;i ++){
		led.which = i;
		ioctl(led_fd,FSLEDOFF,&led);
		usleep(50000);
	}

	return 0;
}



int fswaterled_control(int led_fd, int times)
{
	int i = 0,j = 0;
	led_desc_t led;	

	for(j = 0;j < times;j ++){
		for(i = 2;i < 6;i ++){
			led.which = i;
			ioctl(led_fd,FSLEDON,&led);
			usleep(500000);

			led.which = i;
			ioctl(led_fd,FSLEDOFF,&led);
			usleep(500000);
		}
	}

	return 0;
}

当然这里不要忘记在跑主框架之前先要加载led的驱动模块哦,要不然我们前面写的再牛,这里的灯也是亮不起来的,哈哈哈哈!


总结

好啦,本期的分享大概就到这里结束了,是不是点个灯这个操作还是具有一定的难度的呢,虽然步骤过程很多,但是每一步都需要我们谨小慎微,把每一步做好,最后自然就能把灯点亮;不要着急,我饿能够按照步骤整理出来是因为我整个项目已经做完了,所以大家不要着急,还是需要一步步来,后面做完后大家肯定比我理解的更深刻!把灯点亮后,那后面蜂鸣器就不是什么问题了,都是一样的原理,是不是跃跃欲试了!加油哦!最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

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

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

相关文章

C#,数值计算——分类与推理Svmpolykernel的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Svmpolykernel : Svmgenkernel { public int n { get; set; } public double a { get; set; } public double b { get; set; } public double d { get; set; …

gRPC源码剖析-Builder模式

一、Builder模式 1、定义 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的的表示。 2、适用场景 当创建复杂对象的算法应独立于该对象的组成部分以及它们的装配方式时。 当构造过程必须允许被构造的对象有不同的表示时。 说人话&#xff1a…

java之数组的定义以及使用

文章目录 定义数组1. 定义数组并分配内存空间&#xff1a;2. 定义并初始化数组的值&#xff1a;3. 动态初始化数组&#xff1a;4. 使用数组长度属性&#xff1a;5. 多维数组的定义&#xff1a; 数组的应用数组赋值&#xff1a;1. 直接赋值&#xff1a;直接初始化数组&#xff1…

多线程---synchronized特性+原理

文章目录 synchronized特性synchronized原理锁升级/锁膨胀锁消除锁粗化 synchronized特性 互斥 当某个线程执行到某个对象的synchronized中时&#xff0c;其他线程如果也执行到同一个对象的synchronized就会阻塞等待。 进入synchronized修饰的代码块相当于加锁 退出synchronize…

【UE 模型描边】UE5中给模型描边 数字孪生 智慧城市领域 提供资源下载

目录 0 引言1 Soft Outlines1.1 虚幻商城1.2 使用步骤 2 Auto Mesh Outlines2.1 虚幻商城2.2 使用步骤 3 Survivor Vision3.1 虚幻商城3.2 使用步骤 结尾 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xf…

设计模式_状态模式

状态模式 介绍 设计模式定义案例问题堆积在哪里解决办法状态模式一个对象 状态可以发生改变 不同的状态又有不同的行为逻辑游戏角色 加载不同的技能 每个技能有不同的&#xff1a;攻击逻辑 攻击范围 动作等等1 状态很多 2 每个状态有自己的属性和逻辑每种状态单独写一个类 角色…

一个注解,实现数据脱敏-plus版

shigen坚持日更的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。坚持记录和分享从业两年以来的技术积累和思考&#xff0c;不断沉淀和成长。 当看到这个文章名的时候&#xff0c;是不是很熟悉&#xff0c;是的shigen之前发表了一个这…

[C++]命名空间等——喵喵要吃C嘎嘎

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

CSRF 篇

一、CSRF 漏洞&#xff1a; 1、漏洞概述&#xff1a; &#xff08;1&#xff09;一般情景&#xff1a; 利用已认证用户的身份执行未经用户授权的操作。攻击者试图欺骗用户在其不知情的情况下执行某些操作&#xff0c;通常是在受害者已经登录到特定网站的情况下。 &#xff0…

《动手深度学习》线性回归简洁实现实例

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

百度富文本上传图片后样式崩塌

&#x1f525;博客主页&#xff1a; 破浪前进 &#x1f516;系列专栏&#xff1a; Vue、React、PHP ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 问题描述&#xff1a;上传图片后&#xff0c;图片会变得很大&#xff0c;当点击的时候更是会顶开整个的容器的高跟宽 原因&#…

【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割7(数据预处理)

在上一节&#xff1a;【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割6&#xff08;数据预处理&#xff09; 中&#xff0c;我们已经得到了与mhd图像同seriesUID名称的mask nrrd数据文件了&#xff0c;可以说是一一对应了。 并且&#xff0c;mask的文件&#xff0c;还根据结…

【错误解决方案】ModuleNotFoundError: No module named ‘ngboost‘

1. 错误提示 在python程序&#xff0c;尝试导入一个名为ngboost的模块&#xff0c;但Python提示找不到这个模块。 错误提示&#xff1a;ModuleNotFoundError: No module named ‘ngboost‘ 2. 解决方案 出现上述问题&#xff0c;可能是因为你还没有安装这个模块&#xff0c;…

CXL技术交流群问题讨论记录(1)

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c;转载或引用请注明出处【https://mangopapa.blog.csdn.net/article/details/134131924】。…

Python 学习1 基础

文章目录 基础字符串字面量常用的值类型注释变量print语句数据类型数据类型转换标识符运算符 字符串拓展小结 2023.10.28 周六 最近打算学一下Python&#xff0c;毕竟确实简单方便&#xff0c;而且那个编程语言排名还是在第一。不过不打算靠它吃饭&#xff0c;深不深入暂且不说…

防数据泄密的解决方案

防数据泄密的解决方案 安企神数据防泄密系统下载使用 现代化企业离不开信息数据&#xff0c;数据对企业的经营至关重要&#xff0c;也是企业发展的命脉。为了保护公司数据不被泄露&#xff0c;尤其是在防止数据泄密方面&#xff0c;公司面临着巨大的挑战&#xff0c;需要采取…

Python爬虫实战(六)——使用代理IP批量下载高清小姐姐图片(附上完整源码)

文章目录 一、爬取目标二、实现效果三、准备工作四、代理IP4.1 代理IP是什么&#xff1f;4.2 代理IP的好处&#xff1f;4.3 获取代理IP4.4 Python获取代理IP 五、代理实战5.1 导入模块5.2 设置翻页5.3 获取图片链接5.4 下载图片5.5 调用主函数5.6 完整源码5.7 免费代理不够用怎…

EasyFlash移植使用- 关于单片机 BootLoader和APP均使用的情况

目前&#xff0c;我的STM32单片机&#xff0c;需要在BootLoader和APP均移植使用EasyFlash&#xff0c;用于参数管理和IAP升级使用。 但是由于Flash和RAM限制&#xff0c;减少Flash占用&#xff0c;我规划如下&#xff1a; BootLoader中移植EasyFlash使用旧版本&#xff0c;因为…

机器学习-基本知识

 任务类型 ◼ 有监督学习(Supervised Learning) 每个训练样本x有人为标注的目标t&#xff0c;学习的目标是发现x到t的映射&#xff0c;如分类、回归。 ◼ 无监督学习(Unsupervised Learning) 学习样本没有人为标注&#xff0c;学习的目的是发现数据x本身的分布规律&#xf…

ROS自学笔记二十: Gazebo里面仿真环境搭建

Gazebo 中创建仿真实现方式有两种:1直接添加内置组件创建仿真环境2: 手动绘制仿真环境 1.添加内置组件创建仿真环境 1.1启动 Gazebo 并添加组件 1.2保存仿真环境 添加完毕后&#xff0c;选择 file ---> Save World as 选择保存路径(功能包下: worlds 目录)&#xff0c;文…