ROS--6 Writing ROS Nodes

6.0 add a script

step1. Adding the scripts directory

In order to create a new node in python, you must first create the scripts directory within the simple_arm package, as it does not yet exist.

$ cd ~/catkin_ws/src/simple_arm/
$ mkdir scripts

step2. Creating a new script

$ cd scripts
$ echo '#!/bin/bash' >> hello
$ echo 'echo Hello World' >> hello

Step3:run the script

After setting the appropriate execution permissions on the file, rebuilding the workspace, and sourcing the newly created environment, you will be able to run the script.

$ chmod u+x hello
$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ rosrun simple_arm hello

6.1 Add a Publisher

Step1:Creating the empty simple_mover node script

$ cd ~/catkin_ws/src/simple_arm
$ cd scripts
$ touch simple_mover
$ chmod u+x simple_mover

Step2: Edit simple_mover file

Publishers allow a node to send messages to a topic

pub1 = rospy.Publisher("/topic_name", message_type, queue_size=size)
#!/usr/bin/env python

import math
import rospy
from std_msgs.msg import Float64

def mover():
    pub_j1 = rospy.Publisher('/simple_arm/joint_1_position_controller/command',
                             Float64, queue_size=10)
    pub_j2 = rospy.Publisher('/simple_arm/joint_2_position_controller/command',
                             Float64, queue_size=10)
‘’’
Initializes a client node and registers it with the master. Here “arm_mover” is the name of the node. init_node() must be called before any other rospy package functions are called. The argument anonymous=True makes sure that you always have a unique name for your node
’’’
    rospy.init_node('arm_mover')
    rate = rospy.Rate(10)
    start_time = 0

    while not start_time:
        start_time = rospy.Time.now().to_sec()

    while not rospy.is_shutdown():
        elapsed = rospy.Time.now().to_sec() - start_time
        pub_j1.publish(math.sin(2*math.pi*0.1*elapsed)*(math.pi/2))
        pub_j2.publish(math.sin(2*math.pi*0.1*elapsed)*(math.pi/2))
        rate.sleep()

if __name__ == '__main__':
    try:
        mover()
    except rospy.ROSInterruptException:
        pass

 

Step3 Running simple_mover

Assuming that your workspace has recently been built, and it’s setup.bash has been sourced, you can launch simple_arm as follows:

$ cd ~/catkin_ws
$ roslaunch simple_arm robot_spawn.launch

Once ROS Master, Gazebo, and all of our relevant nodes are up and running, we can finally launch simple_mover. To do so, open a new terminal and type the following commands:

$ cd ~/catkin_ws
$ source devel/setup.bash
$ rosrun simple_arm simple_mover

6.2  ROS Services

6.2.1 Defining services

service = rospy.Service('service_name', serviceClassName, handler)

each service has a definition provided in an .srv file; this is a text file that provides the proper message type for both requests and responses.

The handler is the name of the function or method that handles the incoming service message. This function is called each time the service is called, and the message from the service call is passed to the handler as an argument. The handler should return an appropriate service response message.

6.2.2 Using Services

Services can be called directly from the command line, and you will see an example of this in the upcoming arm_mover classroom concepts.

On the other hand, to use a ROS service from within another node, you will define a ServiceProxy, which provides the interface for sending messages to the service:

service_proxy = rospy.ServiceProxy('service_name', serviceClassName)

One way the ServiceProxy can then be used to send requests is as follows:

msg = serviceClassNameRequest()
#update msg attributes here to have correct data
response = service_proxy(msg)

http://wiki.ros.org/rospy/Overview/Services

6.3: add a service

Namely, we still need to cover:

  • Custom message generation
  • Services
  • Parameters
  • Launch Files
  • Subscribers
  • Logging

step1. Creating a new service definition

As you learned earlier, an interaction with a service consists of two messages being passed. A request passed to the service, and a response received from the service. The definitions of the request and response message type are contained within .srv files living in the srv directory under the package’s root.

$ cd ~/catkin_ws/src/simple_arm/
$ mkdir srv
$ cd srv
$ touch GoToPosition.srv

GoToPosition是服务类型名字

You should now edit GoToPosition.srv, so it contains the following:

float64 joint_1
float64 joint_2
---
duration time_elapsed

Service definitions always contain two sections, separated by a ‘---’ line. The first section is the definition of the request message. Here, a request consists of two float64 fields, one for each of simple_arm’s joints. The second section contains is the service response. The response contains only a single field, time_elapsed. The time_elapsed field is of type duration, and is responsible for indicating how long it took the arm to perform the movement.

 

Step2: Modifying CMakeLists.txt

In order for catkin to generate the python modules or C++ libraries which allow you to utilize messages in your code you must first modify simple_arm’s CMakeLists.txt (~/catkin_ws/src/simple_arm/CMakeLists.txt).

First, ensure that the find_package() macro lists std_msgs and message_generation as required packages. The find_package() macro should look as follows:

find_package(catkin REQUIRED COMPONENTS
        std_msgs
        message_generation
)

As the names might imply, the std_msgs package contains all of the basic message types, and message_generation is required to generate message libraries for all the supported languages (cpp, lisp, python, javascript).

Note: In your CMakeLists.txt, you may also see controller_manager listed as a required package. In actuality this package is not required. It was simply added as a means to demonstrate a build failure in the previous lesson. You may remove it from the list of REQUIRED COMPONENTS if you choose.

Next, uncomment the commented-out add_service_files() macro so it looks like this:

## Generate services in the 'srv' folder
add_service_files(
   FILES
   GoToPosition.srv
)

This tells catkin which files to generate code for.

Lastly, make sure that the generate_messages() macro is uncommented, as follows:

generate_messages(
   DEPENDENCIES
   std_msgs  # Or other packages containing msgs
)

Step3: Modifying package.xml

package.xml is responsible for defining many of the package’s properties, such as the name of the package, version numbers, authors, maintainers, and dependencies.

When rosdep is searching for these dependencies, it’s the package.xml file that is being parsed. Let’s add the message_generation and message_runtimedependencies.

Step3: Modifying package.xml
package.xml is responsible for defining many of the package’s properties, such as the name of the package, version numbers, authors, maintainers, and dependencies.
When rosdep is searching for these dependencies, it’s the package.xml file that is being parsed. Let’s add the message_generation and message_runtimedependencies.
<buildtool_depend>catkin</buildtool_depend>
  <build_depend>message_generation</build_depend>

  <run_depend>controller_manager</run_depend>
  <run_depend>effort_controllers</run_depend>
  <run_depend>gazebo_plugins</run_depend>
  <run_depend>gazebo_ros</run_depend>
  <run_depend>gazebo_ros_control</run_depend>
  <run_depend>joint_state_controller</run_depend>
  <run_depend>joint_state_publisher</run_depend>
  <run_depend>robot_state_publisher</run_depend>
  <run_depend>message_runtime</run_depend>
  <run_depend>xacro</run_depend>

You are now ready to build the package! For more information about package.xml, check out the ROS Wiki.

http://wiki.ros.org/catkin/package.xml

step4: Building the package

If you build the workspace successfully, you should now find that a python package containing a module for the new service GoToPosition has been created deep down in the devel directory.

$ cd ~/catkin_ws
$ catkin_make
$ cd devel/lib/python2.7/dist-packages
$ ls


$ env | grep PYTHONPATH

Step5: Creating the empty arm_mover node script

The steps you take to create the arm_mover node are exactly the same as the steps you took to create the simple_mover script, excepting the actual name of the script itself.

$ cd ~/catkin_ws
$ cd src/simple_arm/scripts
$ touch arm_mover
$ chmod u+x arm_mover

You can now edit the empty arm_mover script with your favorite text editor.

arm_mover

#!/usr/bin/env python

import math
import rospy
from std_msgs.msg import Float64
from sensor_msgs.msg import JointState
from simple_arm.srv import *

def at_goal(pos_j1, goal_j1, pos_j2, goal_j2):
    tolerance = .05
    result = abs(pos_j1 - goal_j1) <= abs(tolerance)
    result = result and abs(pos_j2 - goal_j2) <= abs(tolerance)
    return result

def clamp_at_boundaries(requested_j1, requested_j2):
    clamped_j1 = requested_j1
    clamped_j2 = requested_j2

    min_j1 = rospy.get_param('~min_joint_1_angle', 0)
    max_j1 = rospy.get_param('~max_joint_1_angle', 2*math.pi)
    min_j2 = rospy.get_param('~min_joint_2_angle', 0)
    max_j2 = rospy.get_param('~max_joint_2_angle', 2*math.pi)

    if not min_j1 <= requested_j1 <= max_j1:
        clamped_j1 = min(max(requested_j1, min_j1), max_j1)
        rospy.logwarn('j1 is out of bounds, valid range (%s,%s), clamping to: %s',
                      min_j1, max_j1, clamped_j1)

    if not min_j2 <= requested_j2 <= max_j2:
        clamped_j2 = min(max(requested_j2, min_j2), max_j2)
        rospy.logwarn('j2 is out of bounds, valid range (%s,%s), clamping to: %s',
                      min_j2, max_j2, clamped_j2)

    return clamped_j1, clamped_j2

def move_arm(pos_j1, pos_j2):
    time_elapsed = rospy.Time.now()
    j1_publisher.publish(pos_j1)
    j2_publisher.publish(pos_j2)

    while True:
        joint_state = rospy.wait_for_message('/simple_arm/joint_states', JointState)
        if at_goal(joint_state.position[0], pos_j1, joint_state.position[1], pos_j2):
            time_elapsed = joint_state.header.stamp - time_elapsed
            break

    return time_elapsed

def handle_safe_move_request(req):
    rospy.loginfo('GoToPositionRequest Received - j1:%s, j2:%s',
                   req.joint_1, req.joint_2)
    clamp_j1, clamp_j2 = clamp_at_boundaries(req.joint_1, req.joint_2)
    time_elapsed = move_arm(clamp_j1, clamp_j2)

    return GoToPositionResponse(time_elapsed)

def mover_service():
    rospy.init_node('arm_mover')
    service = rospy.Service('~safe_move', GoToPosition, handle_safe_move_request)
    rospy.spin()

if __name__ == '__main__':
    j1_publisher = rospy.Publisher('/simple_arm/joint_1_position_controller/command',
                                   Float64, queue_size=10)
    j2_publisher = rospy.Publisher('/simple_arm/joint_2_position_controller/command',
                                   Float64, queue_size=10)

    try:
        mover_service()
    except rospy.ROSInterruptException:
        pass

step6:  Launch and Interact

First, Launching the project with the new service

To get the arm_mover node, and accompanying safe_move service to launch along with all of the other nodes, you will modify robot_spawn.launch.

Launch files, when they exist, are located within the launch directory in the root of a catkin package. simple_arm’s launch file is located in ~/catkin_ws/src/simple_arm/launch

To get the arm_mover node to launch, simply add the following:

<!-- The arm mover node -->
  <node name="arm_mover" type="arm_mover" pkg="simple_arm">
    <rosparam>
      min_joint_1_angle: 0
      max_joint_1_angle: 1.57
      min_joint_2_angle: 0
      max_joint_2_angle: 1.0
    </rosparam>
  </node>

More information on the format of the launch file can be found

http://wiki.ros.org/roslaunch/XML

step7 Testing the new service

Now that you've modified the launch file, you are ready to test everything out.

First. To do so, launch the simple_arm, verify that the arm_mover node is running, and that the safe_move service is listed:

Note: You will need to make sure that you've exited out of your previous roslaunch session before re-launching.

$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ roslaunch simple_arm robot_spawn.launch

Then, in a new terminal, verify that the node and service have indeed launched.

$ rosnode list
$ rosservice list

To view the camera image stream, you can use the command rqt_image_view (you can learn more about rqt and the associated tools here):

http://wiki.ros.org/rqt

$ rqt_image_view /rgb_camera/image_raw

Adjusting the view

The camera is displaying a gray image. This is as to be expected, given that it is straight up, towards the gray sky of our gazebo world.

To point the camera towards the numbered blocks on the counter top, we would need to rotate both joint 1 and joint 2 by approximately pi/2 radians. Let’s give it a try:

$ cd ~/catkin_ws/
$ source devel/setup.bash
$ rosservice call /arm_mover/safe_move "joint_1: 1.57
joint_2: 1.57"

Note: rosservice call can tab-complete the request message, so that you don’t have to worry about writing it out by hand. Also, be sure to include a line break between the two joint parameters.

What was not expected is the resulting position of the arm. Looking at the roscore console, we can very clearly see what the problem was. The requested angle for joint 2 was out of the safe bounds. We requested 1.57 radians, but the maximum joint angle was set to 1.0 radians.

By setting the max_joint_2_angle on the parameter server, we should be able to bring the blocks into view the next time a service call is made. To increase joint 2’s maximum angle, you can use the command rosparam

$ rosparam set /arm_mover/max_joint_2_angle 1.57

Now we should be able to move the arm such that all of the blocks are within the field of view of the camera:

rosservice call /arm_mover/safe_move "joint_1: 1.57
joint_2: 1.57"

And there you have it. All of the blocks are within the field of view!

6.4 add Subscribers

添加一个订阅者节点,根据从主题获取的数据,做相应处理,当出现机械臂指向天空时,发送一个service消息,把机械臂移动到指定位置。

也包括一个发送服务消息

A Subscriber enables your node to read messages from a topic, allowing useful data to be streamed into the node. 

sub1 = rospy.Subscriber("/topic_name", message_type, callback_function)

The "/topic_name" indicates which topic the Subscriber should listen to.

The message_type is the type of message being published on "/topic_name".

The callback_function is the name of the function that should be called with each incoming message. Each time a message is received, it is passed as an argument to callback_function. Typically, this function is defined in your node to perform a useful action with the incoming data. Note that unlike service handler functions, the callback_function is not required to return anything.

More in http://docs.ros.org/api/rospy/html/rospy.topics.Subscriber-class.html

创建一个look_away节点, look_away 节点订阅/rgb_camera/image_raw 主题,该主题可以从robotic arm获取摄像头数据。

Step1: Creating the empty look_away node scrip

$ cd ~/catkin_ws
$ cd src/simple_arm/scripts
$ touch look_away
$ chmod u+x look_away

Note: If look_away starts before the system has fully initialized, then look_away hangs in the call to safe_move

解决办法:wait_for_message

def __init__(self):
    rospy.init_node('look_away')
    self.last_position = None
    self.arm_moving = False

    rospy.wait_for_message('/simple_arm/joint_states', JointState)
    rospy.wait_for_message('/rgb_camera/image_raw', Image)

    self.sub1 = rospy.Subscriber('/simple_arm/joint_states', 
                                 JointState, self.joint_states_callback)
    self.sub2 = rospy.Subscriber('/rgb_camera/image_raw', 
                                 Image, self.look_away_callback)
    self.safe_move = rospy.ServiceProxy('/arm_mover/safe_move', 
                                 GoToPosition)
    rospy.spin()

step2: Updating the launch file

<!-- The look away node -->
  <node name="look_away" type="look_away" pkg="simple_arm"/>

修改默认值

<!-- The arm mover node -->
  <node name="arm_mover" type="arm_mover" pkg="simple_arm">
    <rosparam>
      min_joint_1_angle: 0
      max_joint_1_angle: 1.57
      min_joint_2_angle: 0
      max_joint_2_angle: 1.57
    </rosparam>
  </node>

step3: Look_up

#!/usr/bin/env python

import math
import rospy
from sensor_msgs.msg import Image, JointState
from simple_arm.srv import *


class LookAway(object):
    def __init__(self):
        rospy.init_node('look_away')

        self.sub1 = rospy.Subscriber('/simple_arm/joint_states', 
                                     JointState, self.joint_states_callback)
        self.sub2 = rospy.Subscriber("rgb_camera/image_raw", 
                                     Image, self.look_away_callback)
        self.safe_move = rospy.ServiceProxy('/arm_mover/safe_move', 
                                     GoToPosition)

        self.last_position = None
        self.arm_moving = False

        rospy.spin()

    def uniform_image(self, image):
        return all(value == image[0] for value in image)

    def coord_equal(self, coord_1, coord_2):
        if coord_1 is None or coord_2 is None:
            return False
        tolerance = .0005
        result = abs(coord_1[0] - coord_2[0]) <= abs(tolerance)
        result = result and abs(coord_1[1] - coord_2[1]) <= abs(tolerance)
        return result

    def joint_states_callback(self, data):
        if self.coord_equal(data.position, self.last_position):
            self.arm_moving = False
        else:
            self.last_position = data.position
            self.arm_moving = True

    def look_away_callback(self, data):
        if not self.arm_moving and self.uniform_image(data.data):
            try:
                rospy.wait_for_service('/arm_mover/safe_move')
                msg = GoToPositionRequest()
                msg.joint_1 = 1.57
                msg.joint_2 = 1.57
                response = self.safe_move(msg)

                rospy.logwarn("Camera detecting uniform image. \
                               Elapsed time to look at something nicer:\n%s", 
                               response)

            except rospy.ServiceException, e:
                rospy.logwarn("Service call failed: %s", e)



if __name__ == '__main__':
    try: 
        LookAway()
    except rospy.ROSInterruptException:
        pass

step4: Launch and Interact

$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ roslaunch simple_arm robot_spawn.launch

After launching, the arm should move away from the grey sky and look towards the blocks. To view the camera image stream, you can use the same command as before:

观察图片数据

$ rqt_image_view /rgb_camera/image_raw

发送消息操作simple_arm

rosservice call /arm_mover/safe_move "joint_1: 0
joint_2: 0"

Note:

1.对safe_move发送一个service消息,移动simple_arm,移动的相关信息会发布在主题“joint_states”和“rgb_camera/image_raw”;

2.look_away节点作为订阅者节点,从这两个主题中获取位置和图像信息,当发现摄像头朝向天空时,发送一个service消息,将simple_arm移动到指定位置。

资源下载链接为: https://pan.quark.cn/s/5c50e6120579 在Android移动应用开发中,定位功能扮演着极为关键的角色,尤其是在提供导航、本地搜索等服务时,它能够帮助应用获取用户的位置信息。以“baiduGPS.rar”为例,这是一个基于百度地图API实现定位功能的示例项目,旨在展示如何在Android应用中集成百度地图的GPS定位服务。以下是对该技术的详细阐述。 百度地图API简介 百度地图API是由百度提供的一系列开放接口,开发者可以利用这些接口将百度地图的功能集成到自己的应用中,涵盖地图展示、定位、路径规划等多个方面。借助它,开发者能够开发出满足不同业务需求的定制化地图应用。 Android定位方式 Android系统支持多种定位方式,包括GPS(全球定位系统)和网络定位(通过Wi-Fi及移动网络)。开发者可以根据应用的具体需求选择合适的定位方法。在本示例中,主要采用GPS实现高精度定位。 权限声明 在Android应用中使用定位功能前,必须在Manifest.xml文件中声明相关权限。例如,添加<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />,以获取用户的精确位置信息。 百度地图SDK初始化 集成百度地图API时,需要在应用启动时初始化地图SDK。通常在Application类或Activity的onCreate()方法中调用BMapManager.init(),并设置回调监听器以处理初始化结果。 MapView的创建 在布局文件中添加MapView组件,它是地图显示的基础。通过设置其属性(如mapType、zoomLevel等),可以控制地图的显示效果。 定位服务的管理 使用百度地图API的LocationClient类来管理定位服务
资源下载链接为: https://pan.quark.cn/s/dab15056c6a5 Oracle Instant Client是一款轻量级的Oracle数据库连接工具,能够在不安装完整Oracle客户端软件的情况下,为用户提供访问Oracle数据库的能力。以“instantclient-basic-nt-12.1.0.1.0.zip”为例,它是针对Windows(NT)平台的Instant Client基本版本,版本号为12.1.0.1.0,包含连接Oracle数据库所需的基本组件。 Oracle Instant Client主要面向开发人员和系统管理员,适用于数据库查询、应用程序调试、数据迁移等工作。它支持运行SQL*Plus、PL/SQL Developer等管理工具,还能作为ODBC和JDBC驱动的基础,让非Oracle应用连接到Oracle数据库。 安装并解压“instantclient_12_1”后,为了使PL/SQL Developer等应用程序能够使用该客户端,需要进行环境变量配置。设置ORACLE_HOME指向Instant Client的安装目录,如“C:\instantclient_12_1”。添加TNS_ADMIN环境变量,用于存放网络配置文件(如tnsnames.ora)。将Instant Client的bin目录添加到PATH环境变量中,以便系统能够找到oci.dll等关键动态链接库。 oci.dll是OCI(Oracle Call Interface)库的重要组成部分。OCI是Oracle提供的C语言接口,允许开发者直接与数据库交互,执行SQL语句、处理结果集和管理事务等功能。确保系统能够找到oci.dll是连接数据库的关键。 tnsnames.ora是Oracle的网络配置文件,用于定义数据库服务名与网络连接参数的映射关系,包括服务器地址
## 1. 概述 `SpineManager` 是用于管理 Spine 动画实例的核心单例类,主要负责 Spine 动画的对象池管理、分组轮转更新、LOD(细节层次)控制,确保性能与资源使用最优化。 `SpineManagerExtend` 作为其业务逻辑扩展,封装常用的实例生成和回收方法,避免主管理类与游戏业务逻辑耦合。 `SpineManagerLODConfig` 是通过 ScriptableObject 配置的参数文件,方便设计师在编辑器中调节 Spine 动画的 LOD 距离阈值、更新频率和分区数量。 --- ## 2. SpineManager 核心功能 ### 2.1 单例设计 - 真单例实现,避免静态构造顺序带来的隐患。 - 全局唯一 Spine 管理实例,支持任意时机调用。 ### 2.2 对象池管理 - 每个 `SkeletonDataAsset` 资源路径对应一个 Spine 实例对象池。 - 实例租赁时优先复用,避免频繁销毁创建。 - 实例回收后自动隐藏并挂入管理隐藏节点,停止更新。 ### 2.3 分组轮转更新机制 - 所有激活 Spine 实例被划分为 `groupCount` 个分区。 - 每帧仅更新当前轮转分区,分散性能压力。 - 支持动态注册与注销 Spine 代理。 ### 2.4 LOD 细节层次控制 - 自动计算摄像机与实例距离,选择适当更新频率: - 高精度(近距离):高频更新。 - 中精度(中距离):中频更新。 - 低精度(远距离):低频更新。 - 更新频率及距离阈值由 `SpineManagerLODConfig` 决定。 ### 2.5 注册与注销机制 - 实例激活时自动加入负载最少的分区。 - 回收时从对应分区中移除并归还对象池。 ### 2.6 每帧更新流程 - `SpineM
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值