ROS通信模式/动作编程(设置收发节点,使小海龟移动到指定位置)以及分布式通信

2023-05-16

文章目录

  • 一、话题、服务模式编程
    • 1.1 创建工作空间
      • 1.1.1 创建ROS工程包
      • 1.1.2 在工作区编译工程包
    • 1.2 话题模式编程
      • 1.2.1 创建通信的收、发节点
        • 1.2.1.1 在src目录中创建一个发布节点
        • 1.2.1.2 在src目录中创建一个订阅节点
      • 1.2.2 编辑Cmakelist.txt文件(注意:是comm项目包下的CMakelist文件)
      • 1.2.3 将目录切换到工作区目录,并执行catkin_make运行命令:
      • 1.2.4 测试程序正确性
    • 1.3 服务模式编程
      • 1.3.1 定义服务请求与应答的方式
      • 1.3.2 代码编写
  • 二、ROS编程,使小海龟移动到指定位置
    • 2.1 新建服务文件turtleMove.cpp
    • 2.2 创建小乌龟“发布目标位置文件”turtleMoveClient.cpp
    • 2.3 在功能包目录下创建action文件夹
    • 2.4 修改CMakeLists.txt文件内容
    • 2.5 修改package.xml文件内容
    • 2.6 运行程序进行测试
      • 2.6.1 进入工作空间
      • 2.6.2 新建一个终端,启动ros核心程序
      • 2.6.3 再新建一终端(终端3),运行小海龟
      • 2.6.4 再新建一个终端(终端4)运行目标位置程序代码:
      • 2.6.5 开始测试
  • 三、分布式通信
  • 四、总结
  • 五、参考资料

一、话题、服务模式编程

1.1 创建工作空间

输入以下三条命令:

mkdir -p ~/comm_ws/src
cd ~/comm_ws/src
catkin_init_workspace

在这里插入图片描述

编译工作空间,输入以下命令:

cd ..
catkin_make

在这里插入图片描述

在bash中注册工作区

source devel/setup.bash
echo $ROS_PACKAGE_PATH

在这里插入图片描述
输出工作空间路径代表成功

1.1.1 创建ROS工程包

在工作空间中使用catkin_create_pkg命令去创建一个叫comm(通信)的包,这个包依靠std_msgs、roscpp、rospy。

catkin_create_pkg comm std_msgs rospy roscpp

在这里插入图片描述

1.1.2 在工作区编译工程包

输入以下命令:

cd ..
catkin_make

在这里插入图片描述

1.2 话题模式编程

1.2.1 创建通信的收、发节点

进入工程包目录

cd ~/comm_ws/src/comm

因为我们已经编译过这个工程包了,所以会在comm文件夹下看到CmakeList.txt、package.xml文件和include、src这两个目录。

可以用ls命令查看:
在这里插入图片描述
进入src子目录

cd src

1.2.1.1 在src目录中创建一个发布节点

首先创建一个名为talker.cpp的文件

touch talker.cpp
gedit talker.cpp

并输入以下代码:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
  /**
   * The ros::init() function needs to see argc and argv so that it can perform
   * any ROS arguments and name remapping that were provided at the command line. For programmatic
   * remappings you can use a different version of init() which takes remappings
   * directly, but for most command-line programs, passing argc and argv is the easiest
   * way to do it.  The third argument to init() is the name of the node.
   *
   * You must call one of the versions of ros::init() before using any other
   * part of the ROS system.
   */
  ros::init(argc, argv, "talker");
 
  /**
   * NodeHandle is the main access point to communications with the ROS system.
   * The first NodeHandle constructed will fully initialize this node, and the last
   * NodeHandle destructed will close down the node.
   */
  ros::NodeHandle n;
 
  /**
   * The advertise() function is how you tell ROS that you want to
   * publish on a given topic name. This invokes a call to the ROS
   * master node, which keeps a registry of who is publishing and who
   * is subscribing. After this advertise() call is made, the master
   * node will notify anyone who is trying to subscribe to this topic name,
   * and they will in turn negotiate a peer-to-peer connection with this
   * node.  advertise() returns a Publisher object which allows you to
   * publish messages on that topic through a call to publish().  Once
   * all copies of the returned Publisher object are destroyed, the topic
   * will be automatically unadvertised.
   *
   * The second parameter to advertise() is the size of the message queue
   * used for publishing messages.  If messages are published more quickly
   * than we can send them, the number here specifies how many messages to
   * buffer up before throwing some away.
   */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
 
  ros::Rate loop_rate(10);
 
  /**
   * A count of how many messages we have sent. This is used to create
   * a unique string for each message.
   */
  int count = 0;
  while (ros::ok())
  {
    /**
     * This is a message object. You stuff it with data, and then publish it.
     */
    std_msgs::String msg;
 
    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
 
    ROS_INFO("%s", msg.data.c_str());
 
    /**
     * The publish() function is how you send messages. The parameter
     * is the message object. The type of this object must agree with the type
     * given as a template parameter to the advertise<>() call, as was done
     * in the constructor above.
     */
    chatter_pub.publish(msg);
 
    ros::spinOnce();
 
    loop_rate.sleep();
    ++count;
  }
  return 0;
}

在这里插入图片描述
在这里插入图片描述
点击右上角save保存

1.2.1.2 在src目录中创建一个订阅节点

首先创建一个listener.cpp文件

touch listener.cpp
gedit listener.cpp

打开文件并输入以下代码:

#include "ros/ros.h"
#include "std_msgs/String.h"
 
/**
 * This tutorial demonstrates simple receipt of messages over the ROS system.
 */
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}
 
int main(int argc, char **argv)
{
  /**
   * The ros::init() function needs to see argc and argv so that it can perform
   * any ROS arguments and name remapping that were provided at the command line. For programmatic
   * remappings you can use a different version of init() which takes remappings
   * directly, but for most command-line programs, passing argc and argv is the easiest
   * way to do it.  The third argument to init() is the name of the node.
   *
   * You must call one of the versions of ros::init() before using any other
   * part of the ROS system.
   */
  ros::init(argc, argv, "listener");
 
  /**
   * NodeHandle is the main access point to communications with the ROS system.
   * The first NodeHandle constructed will fully initialize this node, and the last
   * NodeHandle destructed will close down the node.
   */
  ros::NodeHandle n;
 
  /**
   * The subscribe() call is how you tell ROS that you want to receive messages
   * on a given topic.  This invokes a call to the ROS
   * master node, which keeps a registry of who is publishing and who
   * is subscribing.  Messages are passed to a callback function, here
   * called chatterCallback.  subscribe() returns a Subscriber object that you
   * must hold on to until you want to unsubscribe.  When all copies of the Subscriber
   * object go out of scope, this callback will automatically be unsubscribed from
   * this topic.
   *
   * The second parameter to the subscribe() function is the size of the message
   * queue.  If messages are arriving faster than they are being processed, this
   * is the number of messages that will be buffered up before beginning to throw
   * away the oldest ones.
   */
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
 
  /**
   * ros::spin() will enter a loop, pumping callbacks.  With this version, all
   * callbacks will be called from within this thread (the main one).  ros::spin()
   * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
   */
  ros::spin();
 
  return 0;
}

在这里插入图片描述
在这里插入图片描述
点击save保存

1.2.2 编辑Cmakelist.txt文件(注意:是comm项目包下的CMakelist文件)

gedit CMakeList.txt

在这里插入图片描述

在文件末尾输入以下代码:

include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker comm_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener comm_generate_messages_cpp)

在这里插入图片描述

1.2.3 将目录切换到工作区目录,并执行catkin_make运行命令:

cd ~/comm_ws
catkin_make

在这里插入图片描述

1.2.4 测试程序正确性

新开一个终端,启动ROS核心程序

roscore

在这里插入图片描述
在原终端注册程序

cd ~/comm_ws
source ./devel/setup.bash

在这里插入图片描述
(暂时不要运行这句命令)运行talker节点

rosrun comm talker

新建一个终端运行listener节点(暂时不要运行最后一句命令)

cd ~/comm_ws
source ./devel/setup.bash
rosrun comm listener

在这里插入图片描述

准备完成后,先在第一个终端运行talker程序,再在新终端运行listener程序,效果如下:
在这里插入图片描述
这表示订阅节点成功接收到了发送节点的信息
可通过CTRL+C结束程序

1.3 服务模式编程

1.3.1 定义服务请求与应答的方式

进入工作空间,定义srv文件

cd comm
mkdir srv
vim AddTwoInts.srv

在这里插入图片描述
在这里插入图片描述

输入以下代码:

int64 a
int64 b
---
int64 sum

在这里插入图片描述
在package.xml中添加功能包依赖

cd ~/comm_ws/src/comm
gedit package.xml

输入以下代码:

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

修改CMakeLists.txt

gedit CMakeLists.txt

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3.2 代码编写

进入comm的src子目录

touch server.cpp
touch client.cpp
gedit server.cpp

输入以下代码:

#include<ros/ros.h>
#include"comm/AddTwoInts.h"
//service回调函数,输入参数req,输出参数res
bool add(comm::AddTwoInts::Request &req,comm::AddTwoInts::Response &res)
{
	//将输入的参数中的请求数据相加,结果放到应答变量中
	res.sum=req.a+req.b;
	ROS_INFO("request: x=%1d,y=%1d",(long int)req.a,(long int)req.b);
	ROS_INFO("sending back response:[%1d]",(long int)res.sum);
	return true;
}
int main(int argc,char **argv)
{
	//ROS节点初始化
	ros::init(argc,argv,"add_two_ints_server");
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个名为add_two_ints的server,注册回调函数add()
	ros::ServiceServer service=n.advertiseService("add_two_ints",add);
	//循环等待回调函数
	ROS_INFO("Ready to add two ints.");
	ros::spin();
	return 0;
}

在这里插入图片描述

gedit client.cpp

输入以下代码:

#include<cstdlib>
#include<ros/ros.h>
#include"comm/AddTwoInts.h"
int main(int argc,char **argv)
{
	//ROS节点初始化
	ros::init(argc,argv,"add_two_ints_client");
	//从终端命令行获取两个加数
	if(argc!=3)
	{
		ROS_INFO("usage:add_two_ints_client X Y");
		return 1;
	}
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个client,请求add_two_ints_service
	//service消息类型是learning_communication::AddTwoInts
	ros::ServiceClient client=n.serviceClient<comm::AddTwoInts>("add_two_ints");
	//创建learning_communication::AddTwoInts类型的service消息
	comm::AddTwoInts srv;
	srv.request.a=atoll(argv[1]);
	srv.request.b=atoll(argv[2]);
	//发布service请求,等待加法运算的应答请求
	if(client.call(srv))
	{
		ROS_INFO("sum: %1d",(long int)srv.response.sum);
	}
	else
	{
		ROS_INFO("Failed to call service add_two_ints");
		return 1;
	}
	return 0;
}

在这里插入图片描述
设置CMakeLists.txt文件
在这里插入图片描述

add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp)

在这里插入图片描述
编译

catkin_make

在这里插入图片描述
运行程序:
新开一个终端:

roscore

原终端:

source devel/setup.bash
rosrun comm server

再新开一个终端:

source devel/setup.bash
rosrun client server

在这里插入图片描述
在客户端输入:

rosrun client server 整数 整数

在这里插入图片描述

运行成功!

二、ROS编程,使小海龟移动到指定位置

进入到工程包的src子目录下

cd ~/comm_ws/src/comm/src

2.1 新建服务文件turtleMove.cpp

touch turtleMove.cpp

打开turtleMove.cpp并输入以下代码:

gedit turtleMove.cpp
/*  
   此程序通过通过动作编程实现由client发布一个目标位置
   然后控制Turtle运动到目标位置的过程
 */
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "comm/turtleMoveAction.h"
#include <turtlesim/Pose.h> 
#include <turtlesim/Spawn.h>
#include <geometry_msgs/Twist.h>
 
typedef actionlib::SimpleActionServer<comm::turtleMoveAction> Server;
 
struct Myturtle
{
    float x;
    float y;
    float theta;
}turtle_original_pose,turtle_target_pose;
 
ros::Publisher turtle_vel;
 
void posecallback(const turtlesim::PoseConstPtr& msg) 
{ 
  ROS_INFO("turtle1_position:(%f,%f,%f)",msg->x,msg->y,msg->theta);
  turtle_original_pose.x=msg->x; 
  turtle_original_pose.y=msg->y;
  turtle_original_pose.theta=msg->theta;
 }
 
// 收到action的goal后调用该回调函数
void execute(const comm::turtleMoveGoalConstPtr& goal, Server* as)
{
    comm::turtleMoveFeedback feedback;
 
    ROS_INFO("TurtleMove is working.");
    turtle_target_pose.x=goal->turtle_target_x;
    turtle_target_pose.y=goal->turtle_target_y; 
    turtle_target_pose.theta=goal->turtle_target_theta;
    
    geometry_msgs::Twist vel_msgs;
    float break_flag;
    
    while(1)
    {  
        ros::Rate r(10);
        
        vel_msgs.angular.z = 4.0 * (atan2(turtle_target_pose.y-turtle_original_pose.y,
                                   turtle_target_pose.x-turtle_original_pose.x)-turtle_original_pose.theta);
        vel_msgs.linear.x = 0.5 * sqrt(pow(turtle_target_pose.x-turtle_original_pose.x, 2) +
                                      pow(turtle_target_pose.y-turtle_original_pose.y, 2)); 
        break_flag=sqrt(pow(turtle_target_pose.x-turtle_original_pose.x, 2) +
                                        pow(turtle_target_pose.y-turtle_original_pose.y, 2));
        turtle_vel.publish(vel_msgs);
 
        feedback.present_turtle_x=turtle_original_pose.x;
        feedback.present_turtle_y=turtle_original_pose.y;
        feedback.present_turtle_theta=turtle_original_pose.theta;
        as->publishFeedback(feedback);
        ROS_INFO("break_flag=%f",break_flag);
        if(break_flag<0.1) break;
        r.sleep();
    }
        // 当action完成后,向客户端返回结果
        ROS_INFO("TurtleMove is finished.");
        as->setSucceeded();
}
 
int main(int argc, char** argv)
{
    ros::init(argc, argv, "turtleMove");
    ros::NodeHandle n,turtle_node;
    ros::Subscriber sub = turtle_node.subscribe("turtle1/pose",10,&posecallback); //订阅小乌龟的位置信息
    turtle_vel = turtle_node.advertise<geometry_msgs::Twist>("turtle1/cmd_vel",10);//发布控制小乌龟运动的速度
    // 定义一个服务器
        Server server(n, "turtleMove", boost::bind(&execute, _1, &server), false);
        // 服务器开始运行
        server.start();
        ROS_INFO("server has started.");
    ros::spin();
 
    return 0;
}

在这里插入图片描述
在这里插入图片描述

2.2 创建小乌龟“发布目标位置文件”turtleMoveClient.cpp

输入以下命令:

touch turtleMoveClient.cpp

打开此文件并输入以下代码:

gedit turtleMoveClient.cpp
#include <actionlib/client/simple_action_client.h>
#include "comm/turtleMoveAction.h"
#include <turtlesim/Pose.h> 
#include <turtlesim/Spawn.h>
#include <geometry_msgs/Twist.h> 
typedef actionlib::SimpleActionClient<comm::turtleMoveAction> Client;
struct Myturtle
{
    float x;
    float y;
    float theta;
}turtle_present_pose;
 
// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
        const comm::turtleMoveResultConstPtr& result)
{
    ROS_INFO("Yay! The turtleMove is finished!");
    ros::shutdown();
}
 
// 当action激活后会调用该回调函数一次
void activeCb()
{
    ROS_INFO("Goal just went active");
}
 
// 收到feedback后调用该回调函数
void feedbackCb(const comm::turtleMoveFeedbackConstPtr& feedback)
{
    ROS_INFO(" present_pose : %f  %f  %f", feedback->present_turtle_x,
                   feedback->present_turtle_y,feedback->present_turtle_theta);
}
 
int main(int argc, char** argv)
{
    ros::init(argc, argv, "turtleMoveClient");
 
    // 定义一个客户端
    Client client("turtleMove", true);
 
    // 等待服务器端
    ROS_INFO("Waiting for action server to start.");
    client.waitForServer();
    ROS_INFO("Action server started, sending goal.");
 
    // 创建一个action的goal
    comm::turtleMoveGoal goal;
    goal.turtle_target_x = 1;
    goal.turtle_target_y = 1;
    goal.turtle_target_theta = 0;
 
    // 发送action的goal给服务器端,并且设置回调函数
    client.sendGoal(goal,  &doneCb, &activeCb, &feedbackCb);
 
    ros::spin();
 
    return 0;
}

在这里插入图片描述
在这里插入图片描述

2.3 在功能包目录下创建action文件夹

输入以下命令:

cd ..
mkdir action

在这里插入图片描述
进入action文件夹并创建turtleMove.action文件

cd action
touch turtleMove.action

打开此文件并输入以下代码:
在这里插入图片描述
在这里插入图片描述

action是动作编程里的处理代码,相当于servlet,一个用户的请求发过来,交个action处理,处理完了返回需要返回的界面,其实就是一个自己写的类,让这个类处理,这个类里有很多方法,交给你指定的方法处理

2.4 修改CMakeLists.txt文件内容

进入工程包目录

cd ..

打开CMakeLists.txt文件

sudo gedit CMakeLists.txt

在文件末尾输入以下代码:

add_executable(turtleMoveClient src/turtleMoveClient.cpp)
target_link_libraries(turtleMoveClient ${catkin_LIBRARIES})
add_dependencies(turtleMoveClient ${PROJECT_NAME}_gencpp)
add_executable(turtleMove src/turtleMove.cpp)
target_link_libraries(turtleMove ${catkin_LIBRARIES})
add_dependencies(turtleMove ${PROJECT_NAME}_gencpp)

在这里插入图片描述
在这里插入图片描述

在该文件中找到find_package函数方法,修改为如下:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
  actionlib_msgs
  actionlib
)

在这里插入图片描述

继续在该文件中找到add_action_files函数一项,默认是用#注释掉了的,这里我们找到后修改为如下代码:

 add_action_files(
   FILES
   turtleMove.action
 )

在这里插入图片描述

注意:以上turtleMove.action为我们在action文件夹下面创建的文件名字turtleMove.action

继续在该文件中找到generate_messages函数一项,默认也是#注释,这里修改为如下代码:

generate_messages(
   DEPENDENCIES
   std_msgs
   actionlib_msgs
 )

在这里插入图片描述

找到catkin_package函数一项,修改为如下代码:

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES comm
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
 CATKIN_DEPENDS roscpp rospy std_msgs
  message_runtime
)

在这里插入图片描述

保存关闭文件

2.5 修改package.xml文件内容

gedit package.xml

将文件中build_depend一栏、替换为如下代码:

<buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>actionlib</build_depend>
  <build_depend>actionlib_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>

在这里插入图片描述

将文件中exec_depend一栏、替换为如下代码:

<exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>message_runtime</exec_depend>
  <exec_depend>actionlib</exec_depend>
  <exec_depend>actionlib_msgs</exec_depend> 

在这里插入图片描述
保存关闭

2.6 运行程序进行测试

2.6.1 进入工作空间

cd ~/comm_ws
catkin_make

在这里插入图片描述

编译成功
在这里插入图片描述

注册程序

source ./devel/setup.bash

在这里插入图片描述
不要关闭此终端(终端1),输入以下命令但先不要运行

rosrun comm turtleMove

2.6.2 新建一个终端,启动ros核心程序

roscore

在这里插入图片描述

2.6.3 再新建一终端(终端3),运行小海龟

rosrun turtlesim turtlesim_node

2.6.4 再新建一个终端(终端4)运行目标位置程序代码:

cd ~/comm_ws
source ./devel/setup.bash

输入以下命令,但先不要运行

rosrun comm turtleMoveClient

2.6.5 开始测试

终端1回车、终端4回车,出现如下画面,则我们动作编程的实验完美成功:

在这里插入图片描述

终端4在实时更新小乌龟的位置,而运行窗口则显示小乌龟的移动路径

三、分布式通信

主机:

ifconfig
export ROS_IP=XXXX.XXXX.XXXX.XXXX

在这里插入图片描述
运行ROS

roscore

新开终端,输入以下命令:

export ROS_IP=XXXX.XXXX.XXXX.XXXX(主机的IP)
export ROS_MASTER_URI=http://XXXX.XXXX.XXXX.XXXX:11311/
rosrun turtlesim turtlesim_node __name:=my_turtle

在这里插入图片描述
在这里插入图片描述
从机:
输入以下命令:

export ROS_IP=X.X.X.X (该电脑ip)
export ROS_MASTER_URI=http://XXXX.XXXX.XXXX/(主机地址)

而后输入即可在从机上操控小海龟

rosrun turtlesim turtle_teleop_key

可通过以下命令查看是否能ping通主从机

rosnode ping my_turtle

遗憾的是我与室友的Ubuntu即使在连接到同一个路由器下始终无法ping通,在此期间关闭了防火墙,尝试绑定ip与主机名均无法解决,此问题待日后解决!!

四、总结

本次实验学习了ROS系统的通信模式编程和动作编程,完成了一次简单的话题、服务器模式程序设计并学习了cmakelists 的使用方法。但遗憾的是由于无法将两台Ubuntu主机ping通,未能完成主从机间的通信从而通过从机控制小海龟。

五、参考资料

https://blog.csdn.net/qq_42451251/article/details/104650192
https://blog.csdn.net/qq_42451251/article/details/104747355
https://blog.csdn.net/xiongmingkang/article/details/81203329

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ROS通信模式/动作编程(设置收发节点,使小海龟移动到指定位置)以及分布式通信 的相关文章

  • 深入探讨Linux驱动开发:驱动介绍与hello驱动实例

    文章目录 前言一 Linux驱动介绍1 用户态和内核态2 内核功能介绍3 驱动程序介绍 二 驱动程序分类与注意事项1 驱动程序分类2 内核驱动开发注意事项 三 hello驱动开发1 驱动模块2 模块加载和卸载函数3 编写hello模块4 M
  • ROS中使用乐视 奥比中光(Astra Pro)深度相机显示彩色和深度图像

    环境 UbuntuROS Kinect or Melodic 奥比中光ROS驱动包安装地址 xff1a https github com orbbec ros astra camera 1 安装ROS 2 安装依赖 span class t
  • 深入探讨Linux驱动开发:字符设备驱动开发与测试

    文章目录 一 字符设备驱动介绍1 设备驱动介绍 二 设备号1 设备号介绍2 分配与释放设备编号 dev t类型 静态分配设备号 动态分配设备号 释放主次设备号 手动创建设备节点 自动创建设备节点 删除设备节点 三 字符设备注册1 cdev结
  • ZED-深度感知使用

    文章目录 1 深度感知配置2 得到深度数据2 1 得到深度值 3 展示深度图4 获取点云数据4 1 从点云数据中计算距离 5 得到法线图像6 调整深度分辨率 1 深度感知配置 可以在初始化时使用InitParameters xff0c 在运
  • Linux系统之 开机自启动程序脚本 编写

    Linux系统启动加载程序 最近完成了项目 xff0c 来个开机自启运行 找到已编译好的程序 xff08 以下是我编译的house xff09 span class token function ls span l span class t
  • 算法时间复杂度、空间复杂度分析

    算法时间复杂度 在计算机程序编写前 xff0c 依据统计方法对算法进行估算 xff0c 经过总结 xff0c 我们发现一个高级语言编写的程序程序在计算机上运行所消耗的时间取决于下列因素 1 算法采用的策略和方案 编译产生的代码质量 3 问题
  • Linux 选择题一百道

    cron 后台常驻程序 daemon 用于 xff1a A 负责文件在网络中的共享 B 管理打印子系统 C 跟踪管理系统信息和错误 D 管理系统日常任务的调度 在大多数Linux发行版本中 xff0c 以下哪个属于块设备 block dev
  • 程序返回return与系统退出exit

    程序返回return与系统退出exit return是语言级别的 xff0c 它表示了调用堆栈的返回 xff1b exit则是系统调用级别的 xff0c 它表示了一个进程的结束 return是返回函数调用 xff0c 如果返回的是main函
  • 结构体、结构体变量、结构体指针、字符串

    结构体 数组是用于保存一组相同类型数据的 而结构体是用于保存一组不同类型数组的 在使用结构体之前必须先定义结构体类型 因为C语言不知道你的结构体中需要存储哪些类型数据 我们必须通过定义结构体类型来告诉C语言 我们的结构体中需要存储哪些类型的
  • linux下脚本实现 切换root用户并执行

    借助一个工具 expect sudo apt span class token operator span get install expect 编写脚本 xff1a vim root sh span class token operato
  • csdn极客江南

    零基础学会 C 语言课程学习突破 1500 人 xff1a 学习地址 xff1a https edu csdn net course detail 31452 spm 61 1001 2014 3001 5507TS TS 专栏文章更新至第
  • 音视频编码格式认知

    编码格式对应的就是音频编码和视频编码 xff0c 音频编码标准和视频编码标准 xff0c 每种编码标准都对应的编码算法 xff0c 其目的是通过一定编码算法实现数据的压缩 减少数据的冗余 视频编码指的是通过特定的压缩技术 xff0c 将某个
  • TensorFlow、PyTorch各版本对应的CUDA、cuDNN关系

    TensorFlow PyTorch各版本对应的CUDA cuDNN关系 xff08 截止2021年4月7日 xff09 TensorFowLinuxCPUGPU macOSCPUGPU WindowsCPUGPU PyTorchCPUGP
  • Android源码新大陆

    vold amp av http aospxref com android 13 0 0 r3 xref system vold model http aospxref com android 13 0 0 r3 xref framewor
  • QT读取GPS模块时,显示不完全,一条指令只能ReadAll32个字节,数据被分开

    在用QT读取GPS模块的时候 xff0c 发现读取到的数据总是显示不出来经度 xff0c 用debug调试 xff0c 发现数据被分开 xff0c 一条指令只能读到N那里 xff0c GNRMC 064401 65 A 3110 47069
  • STM32CubeMX配置串口DMA传输实现不定长数据收发

    串口简介 串口是全双工的串行通信协议 串口通信指串口按位 xff08 bit xff09 发送和接收字节 xff08 一个字节有8位 xff09 尽管比特字节 xff08 byte xff09 的串行通信慢 xff0c 但是串口可以在使用一
  • 单片机串口不够用怎么办?

    扩展串口 一 为什么要扩展串口 xff1f 一块单片机的串口是有限的 xff0c 一般2 4个 当我们做一个项目时需要连接多个外设时跟单片机通讯时 xff0c 且通讯都是以串口形式 那么我们只能去扩展串口来满足我们的应用需求 二 解决方法
  • C语言:自定义实现strcat函数

    include lt stdio h gt include lt assert h gt char My Strcat char str1 const char str2 assert str1 amp amp str2 指针不为空 cha
  • 了解串口协议,及完成STM32的USART串口通讯程序,并用keil观察波形

    文章目录 前言一 串口协议1 RS 2322 485标准 二 RS 232 485和TTL电平1 RS 232电平2 4853 TTL电平4 区别 三 USB TTL转2321 CH3402 发送接收3 USB转RS 232 四 完成一个S
  • UART RS232 RS485协议原理及应用

    一 URAT UART Universal Asynchronous Receiver Transmitter 通用异步收发传输器 xff0c 简称串口 xff0c 是设备间进行异步通信的模块 UART负责处理数据总线和串行口之间的串 并

随机推荐

  • 2022年电赛E题声源定位跟踪系统

    我们组本来是奔着视觉题去的 xff0c 可是到比赛的时候突然发现好像就无人机比较合适 xff0c 但是我们都没玩过无人机 xff0c 本想转战小车 xff0c 可是材料突然发现要两辆小车 xff0c 材料也不够来不及买 xff0c 于是我们
  • Python中requests库使用方法详解

    python中requests库使用方法详解 前言 xff1a 一 什么是Requests二 安装Requests库三 各种请求方式1 基本的GET请求2 带参数的GET请求3 解析json4 获取二进制数据5 添加headers6 基本P
  • File Browser的安装(适用于Kali/Ubuntu/Debian)

    filebrowser是一个基于Go开发的开源轻量级免费的文件浏览器 xff0c 可以把电脑上的文件快速传到其他设备上 1 下载 下载地址 xff1a https github com filebrowser filebrowser rel
  • Ubuntu 挂载硬盘到 /home 目录下扩容

    实验室提供的 NVIDIA Jetson NX 内存容量太小 xff0c 只有16G xff0c 刷完机装上系统和 CUDA 等安装包后 xff0c 系统只剩下一点多个G xff0c 后面完全是不够用的 xff0c 因此需要加硬盘并挂载才能
  • vue-element-admin 二次开发 报错修改

    安装 GitHub git clone https github com PanJiaChen vue element admin git Gitee https gitee com panjiachen vue element admin
  • 常用字符串函数

    1 strlen 函数 strlen 函数用来计算字符串长度 用法 xff1a 结果 xff1a 2 strcpy 函数 用来将一个数组的内容拷贝到另一个数组 用法 xff1a 结果 xff1a 注意 xff1a 如果a1中的比a2中的长
  • C++ 学习日志——STL中的容器 Vector

    本文简单的介绍下STL下的容器 vector的简单的存放和遍历方法 目录 一 vector存放和访问方式 二 三种遍历方式 三 vector存放对象 四 vector存放指针 难点 一 vector存放和访问方式 迭代器 xff1a vec
  • ARM汇编程序入门实践

    一 stm32程序 1 新建工程 1 xff09 双击打开keil xff0c 点击菜单栏Project gt New Vision Project xff0c 新建项目 xff0c 在弹窗中设置工程项目的名称和路径 xff0c 在这里 x
  • 【STM32学习笔记】(5)—— STM32工程添加源文件和头文件

    向工程目录里创建头文件和源文件 STM32的工程文件构成较为复杂 xff0c 同时为STM32工程文件添加源文件和头文件也是较为复杂的 xff0c 下面就由此文章来介绍怎么给STM32工程添加源文件 xff08 c xff09 与头文件 x
  • 在STM32中使用printf函数

    一 目的 利用printf函数使得输出打印到串口中 二 工作原理 我们在C语言中为什么能够调用printf函数打印数据到控制台中 xff0c 那是因为printf函数又调用了fputc 函数 那么我们我们可不可以说printf函数之所以能够
  • 电赛备赛日记(一):K210与STM32串口通信

    拖更了n久的备赛日记终于来啦 xff0c 最近实现了关于K210图像识别并将所需数据 xff08 即目标类别 xff0c 目标在图像中的加权坐标 xff09 其中 xff0c 加权坐标指K210识别到的目标并框出的框的宽和高与框左上顶点的坐
  • 实验作业2(数组)

    64 TOC实验题目2 xff1a 某公司生产5种产品 xff0c 每周记录生产的每种产品数量和销售数量 每个月月末 xff0c 公司将对其生产规划进行评估 该评估需要以下一个或多个信息 每周生成和销售的数量 xff1b 所有生产产品的总量
  • 函数作业

    仅供交流 xff0c 禁止抄袭 实验一 求序列的和 问题描述 求Y 61 1 1 2 43 1 3 1 4 43 1 2 n 的前n项之和 输入形式 从键盘输入n 输出形式 输出n项和 按四舍五入的方式精确到小数点后第二位 样例输入 30
  • Windows下MySQL安装及配置

    目录 1 MySQL下载2 MySQL配置 mysql版本 xff1a 5 7 注 xff1a 1 现在最新的mysql版本是8 0 xff0c 但是一般安装软件都不推荐安装最新版本的 xff0c 因为可能会有一些错误无法预见 xff0c
  • SLAM遇到的问题及技巧

    文章目录 1 rosbag太大了 xff0c 打不开2 机器人的三维变换roslaunch的node强制关闭了 xff0c exit code 11 1 rosbag太大了 xff0c 打不开 以LZ4格式压缩 rosbag compres
  • 关于螺旋数组的讨论

    下面收集了几种可能的螺旋数组形式以及相应的主代码 供大家学习参考 给定N的值 xff0c 从矩阵的左上角输出顺时针螺旋矩阵 例如N 61 4时 xff0c 输出 xff1a 1 2 3 4 12 13 14 5 11 16 15 6 10
  • 动态链接库dll详解

    动态链接库概述 DLL就是整个Windows操作系统的基础 动态链接库不能直接运行 xff0c 也不能接收消息他们是一些独立的文件 Windows API中的所有函数都包含在DLL中 其中有三个最重要的DLL kernel32 dll xf
  • stm32中printf重定向

    先上代码 加入以下代码 支持printf函数 而不需要选择use MicroLIB if 1 pragma import use no semihosting 标准库需要的支持函数 struct FILE int handle FILE s
  • STM32F103RCTX 串口USART 不定长接收

    串口不定长接收的方法有多种 xff0c 这里我所介绍的是通过设置IDLE中断来检测串口是否接收完毕 xff0c 从而结束串口接受中断 1 首先设置串口 xff0c 如下图所示 xff1a 2 使用IDLE中断检测 xff0c 所以需要开启对
  • ROS通信模式/动作编程(设置收发节点,使小海龟移动到指定位置)以及分布式通信

    文章目录 一 话题 服务模式编程1 1 创建工作空间1 1 1 创建ROS工程包1 1 2 在工作区编译工程包 1 2 话题模式编程1 2 1 创建通信的收 发节点1 2 1 1 在src目录中创建一个发布节点1 2 1 2 在src目录中