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

河南省工程建设信息网一体化平台海口seo计费

河南省工程建设信息网一体化平台,海口seo计费,济南优化官网公司,企业网站最底下做的是什么文章目录 前言一、 ROS 文件系统级1). 工作空间 Ws2). 功能包3). 消息 msg4). 服务 srv 二、计算图级1). 动态加载节点 nodelet2). 主题 topic3). 服务 srv4). 消息 msg5). 试用练习5). 创建工作空间6). 创建 ROS 功能包和元功能包7). 编译ROS功能包8). 使用 ROS 节点9). 使用主…

文章目录

  • 前言
  • 一、 ROS 文件系统级
      • 1). 工作空间 Ws
      • 2). 功能包
      • 3). 消息 msg
      • 4). 服务 srv
  • 二、计算图级
      • 1). 动态加载节点 nodelet
      • 2). 主题 topic
      • 3). 服务 srv
      • 4). 消息 msg
      • 5). 试用练习
      • 5). 创建工作空间
      • 6). 创建 ROS 功能包和元功能包
      • 7). 编译ROS功能包
      • 8). 使用 ROS 节点
      • 9). 使用主题与节点交互 rostopic
      • 10). 使用服务 rosservice
      • 11). 使用参数服务器 rosparam
      • 12). 创建节点
      • 13). 编译节点
      • 14). 创建 msg 和 srv 文件
      • 15). 使用 srv 和 msg 文件
      • 16). launch
      • 17). 动态参数


前言

本文为 11 月 11 日 ROS 学习笔记——ROS 架构及概念,分为 ROS 文件系统级和计算图级两节。


一、 ROS 文件系统级

  • 功能包 package:用于创建ROS程序的最小结构和最少内容,包含ROS运行进程(节点)、配置文件等,
  • 功能包清单 package.xml:提供关于功能包、 许可证、依赖关系、编译标志等的信息,
  • 元功能包 Metapackage: 几个具有某些功能的包组织在一起,
  • 元功能包清单:
  • 消息类型 msg type: 进程发送到其他进程的信息,
  • 服务类型 srv type: 为 ROS 中由每个进程提供的服务定义请求和响应数据结构。

ROS 文件级

1). 工作空间 Ws

工作空间就是一个文件夹,包含功能包,功能包又包含源文件和环境或工作空间,提供编译这些功能包的一种方式.

请添加图片描述

  • 源文件空间 src:放置功能包、项目、复制的包等。最重要的一个文件是 CMakeLists.txt,
  • 编译空间 build:为功能包和项目保存缓存信息、配置和其他中间文件。
  • 开发空间 devel:用来保存编译后的程序,无须安装就能用来测试的程序。

2). 功能包

功能包是一种特定结构的文件和文件夹组合,结构如下:

  • include/package_name/:包含需要的库的头文件,
  • msg/: 存放非标准信息,
  • scripts/: 存放Bash、Python或任何其他脚本语言的可执行脚本,
  • src/: 存储程序源文件,
  • srv/: 服务类型,
  • package.xml: 功能包清单文件。

package.xml 必须在每个功能包中,用来说明此包相关的各类信息,包括包的名称、依赖关系等信息。两个典型标记 <build_depend> 和 <run_depend>:

  • <build_depend>: 显示当前功能包安装之前必须先安装哪些功能包
  • <run_depend>: 显示运行功能包中代码所需要的包.

3). 消息 msg

消息类型必须具有:字段 field 和常量 constant,如:

  • int32 id
  • float32 vel
  • string name

4). 服务 srv

用以实现节点之间的请求/响应通信。


二、计算图级

ROS 创建一个连接到所有进程的网络。在系统中的任何节点都可以访问此网络,并通过该网络与其他节点交互,获取其他节点发送的信息,并将自身数据发布到网络上。

请添加图片描述

  • 节点 node:计算执行进程,与其他节点进行交互。最好让众多节点都具有单一的功能,而不是在系统中创建一个包罗万象的大节点。
  • 节点管理器 master:用于节点的名称注册和查找等,也设置节点间的通信。如果在整个ROS中没有节点管理器,就无法与节点、服务、消息等通信。
  • 参数服务器: parameter server: 通过参数,就能够在运行时配置节点或改变节点的工作任务。
  • 消息 message: 节点通过消息完成彼此的沟通。消息包含一个节点发送到其他节点的信息数据。
  • 主题 topic:每个消息都必须有一个名称以便被 ROS 网络分发。节点可以通过订阅某个主题,接收来自其他节点的消息。一个节点可以订阅一个主题,而不需要任何其他节点同时发布该主题。
  • 服务 service:服务能够允许我们直接与某个节点进行交互。
  • 消息记录包 bag:用于保存和回放ROS消息 数据的文件格式。

1). 动态加载节点 nodelet

内部可通信的多个节点,可以在单个进程中运行多个节点,每个nodelet为一个线程。可以在不使用 ROS 网络的情况下与其他节点通信,节点通信效率更高。 nodelet 对于摄像头和3D传感器这类数据传输量非常大的设备特别有用。

2). 主题 topic

节点间用来传输数据的总线。通过主题进行消息传输不需要节点之间直接连接,发布者和订阅者之间不需要知道彼此是否存在。一个主题可以有多个订阅者,也可以有多个发布者。每个主题都是强类型的,发布到主题上的消息必须与主题的 ROS 消息类型相匹配,并且节点只能接收类型匹配的消息:

  • TCP/IP:基于 TCP 传输称为 TCPROS,使用 TCP/IP 长连接,是ROS默认的传输方式;
  • UDP:UDPROS,是一种低延迟高效率的传输方式, 但可能产生数据丢失,最适合远程操控之类的任务。

3). 服务 srv

当需要直接与节点通信并以 RPC 方式获得应答时,将无法通过主题实现,而需要使用服务。服务需要由用户开发,节点并不提供标准服务。包含消息源代码的 文件存储在 srv 文件夹中。

服务类型是包名和 .srv 文件名的组合。例如 chapter2_tutorials/srv/chapter2_srv1.srv 文件的服务类型是 chapter2_tutorials/chapter2_srv1

4). 消息 msg

一个节点通过向特定主题发布消息,将信息发送到另一个节点。消息的类型在遵循以下标准命名方式:包名/文件名.msg, 例 如,std_msgs/msg/String.msg 的消息类型是 std_msgs/String

5). 试用练习

  • 查找 turtlesim 包的路径
rospack find turtlesim
>>> /opt/ros/noetic/share/turtlesim
  • 查找在系统中安装过的某个元功能包
rosstack find ros_comm
>>> /opt/ros/noetic/share/ros_comm
  • 获得功能包或功能包集下面的文件列表
rosls turtlesim/
>>> cmake  images  msg  package.xml  srv
  • 更改当前工作目录
roscd turtlesim/
pwd
>>> /opt/ros/noetic/share/turtlesim

5). 创建工作空间

  • 查看 ROS 正在使用的工作空间
echo $ROS_PACKAGE_PATH 
>>> /home/li/Documents/Demo01_Ws/src:/opt/ros/noetic/share
  • 新建此文件夹
mkdir -p ~/dev/catkin_ws/src
cd ~/dev/catkin_ws/src/
catkin_init_workspace
  • 编译工作空间
cd ~/dev/catkin_ws/
catkin_make
  • 完成配置
source devel/setup.bash

6). 创建 ROS 功能包和元功能包

cd ~/dev/catkin_ws/src/
catkin_create_pkg chapter2_tut std_msgs roscpp

7). 编译ROS功能包

cd ~/dev/catkin_ws/
catkin_make

8). 使用 ROS 节点

  • 启动一个新的节点
rosrun turtlesim turtlesim_node
  • 查看用于程序调试的信息
rosnode info /turtlesim

9). 使用主题与节点交互 rostopic

  • 使用箭头键移动海龟
rosrun turtlesim turtle_teleop_key
  • 使用以下命令行查看主题清单
rostopic list
>>> /rosout/rosout_agg/turtle1/cmd_vel/turtle1/color_sensor/turtle1/pose
  • 运行以下命令行并使用箭头键查看消息产生时发送了哪些数据
rostopic echo /turtle1/cmd_vel
>>> linear: x: 2.0y: 0.0z: 0.0angular: x: 0.0y: 0.0z: 0.0
---
  • 使用以下命令行查看由主题发送的消息类型
rostopic type /turtle1/cmd_vel
>>> geometry_msgs/Twist
  • 使用以下命令查看消息字段
rosmsg show geometry_msgs/Twist
>>> geometry_msgs/Vector3 linearfloat64 xfloat64 yfloat64 zgeometry_msgs/Vector3 angularfloat64 xfloat64 yfloat64 z
  • 直接发布主题使海龟做圆周运动
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist "linear:x: 1.0y: 0.0z: 0.0
angular:x: 0.0y: 0.0z: 1.0" 

10). 使用服务 rosservice

服务是能够使节点之间相互通信的另一种方法。服务允许节点发送请求和接收响应

  • 列出活动服务
rosservice list
>>> /clear/kill/reset/rosout/get_loggers/rosout/set_logger_level/spawn/turtle1/set_pen/turtle1/teleport_absolute/turtle1/teleport_relative/turtlesim/get_loggers/turtlesim/set_logger_level
  • 查看某个服务的类型
rosservice type /clear
>>> std_srvs/Empty
  • 调用服务
rosservice call /clear 
  • 以不同的方向在另一个位置创建另一只海龟
rosservice type /spawn | rossrv show // 查看该服务的类型
>>> float32 xfloat32 yfloat32 thetastring name---string namerosservice call /spawn 3 3 0.2 "new_turtle" // 调用服务

11). 使用参数服务器 rosparam

参数服务器用于存储所有节点均可访问的数据。ROS中用来管理参数服务器的工具称为 rosparam.

  • 查看所有节点使用的服务器参数
rosparam list
>>> /rosdistro/roslaunch/uris/host_li_alienware__41685/rosversion/run_id/turtlesim/background_b/turtlesim/background_g/turtlesim/background_r
  • 读取参数的某个值
rosparam get /turtlesim/background_b
>>> 255
  • 设定一个新的值
rosparam set /turtlesim/background_b 100
  • 使用 dump 参数保存或加载参数服务器的内容
rosparam dump save.yaml
  • 使用 load 向参数服务器加载新的数据文件
rosparam load load.yaml namespace

12). 创建节点

创建两个节点:一个发布数据,另一个接收数据.

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>/*发布方
*/int main(int argc, char **argv)
{// 初始化节点ros::init(argc, argv, "example_a");// 进程的处理程序,它允许我们与环境交互ros::NodeHandle n;// 将节点实例化成发布者,将发布的主题和类型的名称告知节点管理器ros::Publisher chatter_pub = n.advertise<std_msgs::String>("message", 1000);// 设置发送数据的频率ros::Rate loop_rate(10);while (ros::ok()) {// 创建消息变量std_msgs::String msg;std::stringstream ss;ss << "I am the example_a_node";msg.data = ss.str();// 继续发布消息chatter_pub.publish(msg);// spinOnce 在主循环中执行一次迭代允许用户执行操作ros::spinOnce();// 将程序挂起loop_rate.sleep();}return 0;
}
#include "ros/ros.h"
#include "std_msgs/String.h"/*订阅方
*/// 回调函数
// 每次节点收到一条消息时,调用该函数处理数据
void chatterCallback(const std_msgs::String::ConstPtr& msg) {ROS_INFO("I heard: [%s]", msg->data.c_str());}int main(int argc, char  **argv)
{ros::init(argc, argv, "example_b");ros::NodeHandle n;// 创建一个订阅者,并从主题获取以message为名称的消息数据ros::Subscriber sub = n.subscribe("message", 1000,chatterCallback);// 运行到这里时调用 chatterCallback 回调函数ros::spin();return 0;
}

13). 编译节点

  • 修改 CMakeLists.txt
add_executable(example1_a src/example1_a.cpp)
add_executable(example1_b src/example1_b.cpp)add_dependencies(example1_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example1_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})target_link_libraries(example1_a${catkin_LIBRARIES}
)
target_link_libraries(example1_b${catkin_LIBRARIES}
)
  • 启动 roscore,并在不同的命令行窗口下分别运行两个节点
roscore
rosrun demo01_pub_cli example1_a
rosrun demo01_pub_cli example1_b>>> [ INFO] [1700051508.077304984]: I heard: [I am the example_a_node][ INFO] [1700051508.177173843]: I heard: [I am the example_a_node][ INFO] [1700051508.277239966]: I heard: [I am the example_a_node]...
  • 使用 rosnoderostopic 命令来调试和查看当前节点的运行状况
rosnode info /example_b
>> Node [/example_b]Publications: * /rosout [rosgraph_msgs/Log]Subscriptions: * /message [std_msgs/String]Services: * /example_b/get_loggers* /example_b/set_logger_level
rostopic info /message 
>>> Type: std_msgs/StringPublishers: * /example_aSubscribers: * /example_b
rostopic type /message
>>> std_msgs/String
rostopic bw /message
>>> subscribed to [/message]average: 296.19B/smean: 27.00B min: 27.00B max: 27.00B window: 10

14). 创建 msg 和 srv 文件

  • 首先创建一个新的 .msg 文件,添加:
int32 A
int32 B
int32 C
  • 编辑 package.xml ,取消注释:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 编辑 CMakeList.txt,并编译
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)generate_messages(DEPENDENCIESstd_msgs
)# Generate messages in the 'msg' folder
add_message_files(FILESchapter2_msg1.msg
)# Generate added messages and services with any dependencies listed here
generate_messages(DEPENDENCIESstd_msgs
)
  • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
rosmsg show demo01_pub_cli/chapter2_msg1
>>> int32 Aint32 Bint32 C
  • 创建一个新的 .srv 文件:
int32 A
int32 B
int32 C
---
int32 sum
  • 编辑 package.xml ,取消注释:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 编辑 CMakeList.txt,并编译
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo01_pub_cliCATKIN_DEPENDS message_runtime
#  DEPENDS system_lib
)# Generate messages in the 'msg' folder
add_message_files(FILESchapter2_msg1.msg
)# Generate services in the 'srv' folder
add_service_files(FILESchapter2_srv1.srv
)# Generate added messages and services with any dependencies listed here
generate_messages(DEPENDENCIESstd_msgs
)
  • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
rossrv show demo01_pub_cli/chapter2_srv1
>>> int32 Aint32 Bint32 C---int32 sum

15). 使用 srv 和 msg 文件

该服务将对三个整数求和,需要两个节点:一个 服务器 和一个 客户端

#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_srv1.h"/*服务端
*/// 回调函数
bool add(demo01_pub_cli::chapter2_srv1::Request &req,demo01_pub_cli::chapter2_srv1::Response &res) {res.sum = req.A + req.B + req.C;ROS_INFO("request: A=%d, B=%d, C=%d", (int)req.A, (int)req.B,(int)req.C);ROS_INFO("sending back response: [%d]", (int)res.sum);return true;}int main(int argc, char **argv)
{ros::init(argc, argv, "add_3_ints_server");ros::NodeHandle n;// 创建服务并在 ROS 中发布广播ros::ServiceServer service = n.advertiseService("add_3_ints", add);ROS_INFO("Ready to add 3 ints.");ros::spin();return 0;
}
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_srv1.h"
#include <cstdlib>/*客户端
*/int main(int argc, char **argv)
{ros::init(argc, argv, "add_3_ints_client");if (argc != 4) {ROS_INFO("usage: add_3_ints_client A B C");return 1;}ros::NodeHandle n;// 创建客户端,名为 add_3_intsros::ServiceClient client = n.serviceClient<demo01_pub_cli::chapter2_srv1>("add_3_ints");// 创建 srv 请求类型的实例// 加入需要发送的数据值demo01_pub_cli::chapter2_srv1 srv;srv.request.A = atoll(argv[1]);srv.request.B = atoll(argv[2]);srv.request.C = atoll(argv[3]);// 调用服务并发送数据if (client.call(srv)) {ROS_INFO("Sum: %ld", (long int)srv.response.sum);} else {ROS_ERROR("Failed to call service add_3_ints");return 1;}return 0;
}
  • 编译 CMakeList.txt
add_executable(example2_a src/example2_a.cpp)
add_executable(example2_b src/example2_b.cpp)add_dependencies(example2_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example2_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})target_link_libraries(example2_a${catkin_LIBRARIES}
)
target_link_libraries(example2_b${catkin_LIBRARIES}
)
  • 启动节点
rosrun demo01_pub_cli example2_a
>>> [ INFO] [1700059229.228592862]: Ready to add 3 ints.[ INFO] [1700059253.580802115]: request: A=1, B=2, C=3[ INFO] [1700059253.580825139]: sending back response: [6]
rosrun demo01_pub_cli example2_b 1 2 3
>>> [ INFO] [1700059253.580914856]: Sum: 6
  • 创建发布和订阅节点
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_msg1.h"
#include <sstream>int main(int argc, char **argv)
{ros::init(argc, argv, "example3_a");ros::NodeHandle n;ros::Publisher pub = n.advertise<demo01_pub_cli::chapter2_msg1>("message", 1000);ros::Rate loop_rate(10);while (ros::ok()) {demo01_pub_cli::chapter2_msg1 msg;msg.A = 1;msg.B = 2;msg.C = 3;pub.publish(msg);ros::spinOnce();loop_rate.sleep();}return 0;
}
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_msg1.h"void messageCallback(const demo01_pub_cli::chapter2_msg1::ConstPtr& msg) {ROS_INFO("I heard: [%d] [%d] [%d]", msg->A, msg->B, msg->C);}int main(int argc, char **argv)
{ros::init(argc, argv, "example3_b");ros::NodeHandle n;ros::Subscriber sub = n.subscribe("message", 1000, messageCallback);ros::spin();return 0;
}
  • 编译 CMakeList.txt
add_executable(example3_a src/example3_a.cpp)
add_executable(example3_b src/example3_b.cpp)add_dependencies(example3_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example3_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})target_link_libraries(example3_a${catkin_LIBRARIES}
)
target_link_libraries(example3_b${catkin_LIBRARIES}
)
  • 启动节点
rosrun demo01_pub_cli example3_a
rosrun demo01_pub_cli example3_b
>>> [ INFO] [1700061493.409080424]: I heard: [1] [2] [3][ INFO] [1700061493.509095136]: I heard: [1] [2] [3][ INFO] [1700061493.609056521]: I heard: [1] [2] [3]...

16). launch

用于启动多个节点。 当执行启动文件时,并不需要在 roscore 命令前启动,roslaunch 会启动它。当在 shell中只运行一个节点时,可以看到 ROS_INFO 输出的消息。但是当运行启动文件时,则看不到。

  • 创建 .launch 文件
<launch><node name="example1_a" pkg="demo01_pub_cli" type="example1_a" /><node name="example1_b" pkg="demo01_pub_cli" type="example1_b" />
</launch>
  • 启动 .launch 文件
roslaunch demo01_pub_cli chapter2.launch
  • 查看运行的节点:
rosnode list
>>> /example1_a/example1_b/rosout
  • 运行 rqt_console 程序看到信息
rqt_console

rqt

17). 动态参数

配置一个包含动态重配置实用程序功能的基本节点。

  • 创建配置文件 .cfg
#! /usr/bin/env python
PACKAGE = "demo01_pub_cli"# 初始化参数生成器
from dynamic_reconfigure.parameter_generator_catkin import *gen = ParameterGenerator()# 加入不同的参数类型并设置默认值、描述、范围等
"""gen.add()- name: 参数的名称- type: 参数值的类型- level: 一个传递给回调的位掩码- description: 描述- default: 节点启动时的默认值- min: 参数最小值- max: 参数最大值
"""
gen.add("double_param", double_t, 0, "A double parameter",.1, 0, 1)
gen.add("str_param", str_t, 0, "A string parameter", "Chapter2_dynamic_reconfigure")
gen.add("int_param", int_t, 0, "An Integer parameter", 1, 0, 100)
gen.add("bool_param", bool_t, 0, "A Boolean parameter", True)size_enum = gen.enum([gen.const("Low", int_t, 0, "Low is 0"),gen.const("Medium", int_t, 1, "Medium is 1"),gen.const("High", int_t, 2, "High is 2")],"Select from the list")gen.add("size", int_t, 0, "Select from the list", 1, 0, 3, edit_method=size_enum)# 生成必要的文件并退出程序
exit(gen.generate(PACKAGE, "demo01_pub_cli", "chapter2_"))
  • 修改执行文件的权限
chmod +x *.cfg
  • 修改 CMakeList.txt 并编译
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generationdynamic_reconfigure
)generate_dynamic_reconfigure_options(cfg/chapter2.cfg
)add_dependencies(example4 demo01_pub_cli_gencfg)
  • 写具有动态重配置支持的新节点
#include <ros/ros.h>
#include <dynamic_reconfigure/server.h>
#include <demo01_pub_cli/chapter2_Config.h>// 参数访问的方式, 将输出参数的新值
void callback(demo01_pub_cli::chapter2_Config &config, uint32_t level) {ROS_INFO("Reconfigure Request: %d, %f, %s %s %d",config.int_param,config.double_param,config.str_param.c_str(),config.bool_param?"True":"False",config.size);}int main(int argc, char **argv)
{ros::init(argc, argv, "example4_dynamic_reconfigure");// 初始化服务器dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config> server;dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config>::CallbackType f;f = boost::bind(&callback, _1, _2);// 向服务器发送callback函数。当服务器得到重新配置请求时调用 callback 函数server.setCallback(f);ros::spin();return 0;
}
  • 修改 CMakeLists.txt , 编译并运行节点和动态重配置 GUI
add_executable(example4 src/example4.cpp)
add_dependencies(example4 demo01_pub_cli_gencfg)
rosrun demo01_pub_cli example4
>>> [ INFO] [1700312052.497444818]: Reconfigure Request: 1, 0.100000, Chapter2_dynamic_reconfigure True 1
rosrun rqt_reconfigure rqt_reconfigure
>>> [ INFO] [1700312097.307494990]: Reconfigure Request: 42, 0.100000, Chapter2_dynamic_reconfigure True 1[ INFO] [1700312099.355379827]: Reconfigure Request: 42, 0.470000, Chapter2_dynamic_reconfigure True 1[ INFO] [1700312101.253127436]: Reconfigure Request: 73, 0.470000, Chapter2_dynamic_reconfigure True 1[ INFO] [1700312103.210104907]: Reconfigure Request: 73, 0.710000, Chapter2_dynamic_reconfigure True 1

rqt_reconfigure


http://www.yidumall.com/news/62248.html

相关文章:

  • 辽宁省阜蒙县建设局网站建站宝盒
  • 宁波做网站的企业杭州百度推广优化排名
  • 北京网站建设设计公司关键词林俊杰免费听
  • 这么做网站原型图推广网址
  • 动漫制作专业学习方法成都seo招聘信息
  • 网站建设负责那内容上传吗谷歌seo和百度seo
  • 网站视频转码软件广州企业网站seo
  • 镇平做网站小程序商城
  • 网页程序开发学什么语言进一步优化
  • 联通 网站备案专业网络推广公司
  • 无锡专业网站建设网站推广基本方法是
  • 绵阳的网站制作公司百度seo教程网
  • 安陆网站设计品牌营销理论
  • 做网站需要的图片微信公众号怎么推广
  • 财务网站建设手机优化专家
  • 微信网站制作系统深圳网站建设优化
  • 做网站推广员需要seo优化包括哪些
  • 手机网站推广怎么做关键词排名优化软件价格
  • 网页设计基础教程第七章课后习题济南seo关键词优化方案
  • ps怎么做网站界面设计建个人网站的详细步骤
  • 如何备份网站数据库sem托管公司
  • 企业网站源码英文网站设计与实现毕业设计
  • 网站建设西安网站搭建免费
  • magento官方网站百度竞价点击神器下载安装
  • apache部署多个网站网上教育培训机构
  • 网站建设教程浩森宇特竞价推广怎样管理
  • 对高校网站建设的期待免费b站在线观看人数在哪
  • 成都网站设计制作价格刷赞网站推广免费链接
  • 公安网站建设自查报告公司的公关
  • 江门网站建设公司哪家好广西壮族自治区