Specific application of ROS Service Service communication mechanism in unmanned vehicles

1. Introduction

In the communication mechanism, we have introduced this Service service before: Application of ROS Communication Mechanism Service (Service)
It is generally suitable for scenarios where communication processing is relatively simple. Here we will actually operate some very common services on the unmanned vehicle: publishing voltage data, turning on and off LED lights, turning on and off the buzzer, and controlling the left and right motors. speed and other operations.

Server: The node that provides services defines a callback function to handle requests.
Client: The node that serves the request, calls the service through the local proxy

The creation process of workspace and package has been ignored. Those who are interested can check out: ROS new workspace (workspace) and package (package) compilation practice (C++ example)

2. Unmanned vehicle application

2.1, Directory

Let’s first understand what messages, services, and node files are:

cd ~/workspace/catkin_ws/src
ls jetbotmini_msgs/msg
ls jetbotmini_msgs/srv
ls jetbot_ros/scripts

Screenshot below:

2.2. Start node

Then we directly open ROSMASTER, start the node of the unmanned vehicle, and then post the main code to have a clearer understanding of the service communication mechanism of the unmanned vehicle.

roscore
rosrun jetbot_ros jetbotmini_driver.py

Okay, now we can communicate with the autonomous vehicle

2.3. View topics

Let’s first take a look at the topics: rostopic list

/rosout
/rosout_agg
/voltage

As you can see here, there is a voltage topic.

2.4. View services

Next, let’s focus on the list of services provided: rosservice list

/Buzzer
/LEDBLUE
/LEDGREE
/Motor
/Servo
/driver_node/get_loggers
/driver_node/set_logger_level
/rosout/get_loggers
/rosout/set_logger_level

The service list includes services such as buzzers, LED lights, motors, and steering gears. Next, we operate these services through the command line to communicate with the autonomous vehicle.
The definition of service is very simple, let’s take a closer look

2.5, buzzer

Enter the command: rossrv show Buzzer

The input type and output type are separated by three small horizontal lines —

[jetbotmini_msgs/Buzzer]:
int32 buzzer

bool result

Turn on the buzzer: rosservice call /Buzzer “buzzer: 1”
result: True #This is the return value
Turn off the buzzer: rosservice call /Buzzer “buzzer: 0”
result: True

2.6, steering gear

Enter the command: rossrv show Servo

[jetbotmini_msgs/Servo]:
int32 servoid
int32 angle

bool result

My unmanned vehicle has two servo interfaces S1 and S2, as shown below:

Servo 1 angle 90: rosservice call /Servo 1 90
result: True
Servo 2 angle 90: rosservice call /Servo 2 90
result: True

View servo parameters: rosservice args Servo
servoid angle

Servo 1 rotates 30 degrees: rosservice call /Servo 1 30

Of course, when there are multiple parameters, you can also specify the writing method of parameter names and use dictionary form to execute, which is more intuitive:
rosservice call /Servo “{‘servoid’: 1, ‘angle’: 30}”

2.7, LED light

Turn on the LEB blue light: rosservice call /LEDBLUE 1
result: True
As shown below:

Turn on the LED green light: rosservice call /LEDGREE 1
result: True
As shown below:

Light up together:

2.8. Motor (motor)

View the data type structure of Motor: rossrv show Motor

[jetbotmini_msgs/Motor]:
float32 rightspeed
float32 leftspeed

bool result

View the parameters of the Motor motor: rosservice args Motor

rightspeed leftspeed

[rightspeed]: [-1, 1], the right motor speed, less than 0 for reverse rotation, and greater than 0 for forward rotation.
[leftspeed]: [-1, 1], left motor speed, less than 0 for reverse rotation and greater than 0 for forward rotation.

Left motor (motor wheel) 50% speed, right motor 80% speed: rosservice call /Motor 0.5 0.8

See what topics are available: rostopic list

/rosout
/rosout_agg
/voltage

Visually view topic messages: rosrun rqt_topic rqt_topic
As shown below, click on the check box of the voltage topic to check it:

In this way, you can intuitively see that the voltage is 10.67V and the frequency is 0.03 Hz (every 30 seconds updated once)

3.jetbotmini_driver.py

Let’s focus on the code of this node, which contains the various services defined above, so we import the messages and services:

from jetbotmini_msgs.msg import *
from jetbotmini_msgs.srv import *

jetbotmini_msgs.msg and jetbotmini_msgs.srv are automatically generated. The definition of messages is also introduced earlier. Those who are interested can check out: ROS communication mechanism publishing and subscribing of topics (Topics) and the implementation of custom messages
It will be generated to the corresponding location according to your Python version. For example, I am in the following directory.
~/workspace/catkin_ws/devel/lib/python2.7/dist-packages/jetbotmini_msgs
Generated in the msg and srv folders respectively.
Screenshot below:


Then go back to the jetbotmini_driver.py file. The source code is as follows:

#!/usr/bin/env python3
# encoding: utf-8
import rospy
import random
import threading
from time import sleep
from jetbotmini_msgs.msg import *
from jetbotmini_msgs.srv import *
import RPi.GPIO as GPIO # GPIO library for Raspberry Pi
import smbus

bus = smbus.SMBus(1) # Use bus number 1 for I2C communication
ADDRESS = 0x1B
Led_G_pin = 24 # Pin number
Led_B_pin = 23
Key1_pin = 8
GPIO.setmode(GPIO.BCM) # BCM mode refers to the method of using Broadcom SOC channel numbering
GPIO.setup(Led_G_pin, GPIO.OUT, initial=GPIO.HIGH) # Set the pin to output mode and initialize it to high level
GPIO.setup(Led_B_pin, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(Key1_pin, GPIO.IN) #Input mode, you can read the status of the pin

print("Starting now!")

class transbot_driver:
    def __init__(self):
        rospy.on_shutdown(self.cancel)
        self.srv_Buzzer = rospy.Service("/Buzzer", Buzzer, self.Buzzercallback)
        self.srv_Servo = rospy.Service("/Servo", Servo, self.Servocallback)
        self.srv_LEDBLUE = rospy.Service("/LEDBLUE", LEDBLUE, self.LEDBLUEcallback)
        self.srv_LEDGREE = rospy.Service("/LEDGREE", LEDGREE, self.LEDGREEcallback)
        self.srv_Motor = rospy.Service("/Motor", Motor, self.Motorcallback)
        self.volPublisher = rospy.Publisher("/voltage", Battery, queue_size=10)

    def cancel(self):
        self.srv_Buzzer.shutdown()
        self.srv_Servo.shutdown()
        self.srv_LEDBLUE.shutdown()
        self.srv_LEDGREE.shutdown()
        self.srv_Motor.shutdown()
        self.volPublisher.unregister()
        GPIO.cleanup()
        rospy.loginfo("Close the robot...")
        rospy.sleep(1)

    def pub_data(self):
        # Publish battery voltage
        while not rospy.is_shutdown():
            sleep(30)
            AD_value = bus.read_i2c_block_data(ADDRESS,0x00,2)
            voltage = ((AD_value[0] << 8) + AD_value[1]) * 13.3 / 1023.0
            battery = Battery()
            battery.Voltage = voltage
            self.volPublisher.publish(battery)

    def Buzzercallback(self, request):
        #Buzzer control, server callback function
        if not isinstance(request, BuzzerRequest): return
        bus.write_byte_data(ADDRESS,0x06,request.buzzer)
        sleep(0.01)
        bus.write_byte_data(ADDRESS,0x06,request.buzzer)
        response = BuzzerResponse()
        response.result = True
        return response

    def Servocallback(self, request):
        #Control servo
        if not isinstance(request, ServoRequest): return
        bus.write_i2c_block_data(ADDRESS,3,[request.servoid,request.angle])
        sleep(0.01)
        bus.write_i2c_block_data(ADDRESS,3,[request.servoid,request.angle])
        response = ServoResponse()
        response.result = True
        return response

    def LEDBLUEcallback(self, request):
        #Control blue LED
        if not isinstance(request, LEDBLUERequest): return
        if request.ledblue == 1:
            GPIO.output(Led_B_pin, GPIO.LOW)
        elif request.ledblue == 0:
            GPIO.output(Led_B_pin, GPIO.HIGH)
        response = LEDBLUEResponse()
        response.result = True
        return response

    def LEDGREEcallback(self, request):
        #Control green LED
        if not isinstance(request, LEDGREERequest): return
        if request.ledgree == 1:
            GPIO.output(Led_G_pin, GPIO.LOW)
        elif request.ledgree == 0:
            GPIO.output(Led_G_pin, GPIO.HIGH)
        response = LEDGREEResponse()
        response.result = True
        return response

    def Motorcallback(self, request):
        # Control the motor, if the speed is less than 0, it will reverse the direction.
        if not isinstance(request, MotorRequest): return
        if request.leftspeed < 0:
            request.leftspeed = -request.leftspeed
            leftdir = 0
        else:
            leftdir = 1
        if request.rightspeed < 0:
            request.rightspeed = -request.rightspeed
            rightdir = 0
        else:
            rightdir=1
        bus.write_i2c_block_data(ADDRESS,0x01,[leftdir,int(request.leftspeed*255),rightdir,int(request.rightspeed*255)])
        sleep(0.01)
        bus.write_i2c_block_data(ADDRESS,0x01,[leftdir,int(request.leftspeed*255),rightdir,int(request.rightspeed*255)])
        response = MotorResponse()
        response.result = True
        return response

if __name__ == '__main__':
    rospy.init_node("driver_node", anonymous=False)
    try:
        driver = transbot_driver()
        driver.pub_data()
        rospy.spin()
    except:
        rospy.loginfo("Final!!!")

First initialize some related operations, set GPIO to BCM mode, and then initialize it to high level according to the pin number, the mode is Output mode, so that the LED light can be controlled according to the high and low levels. If you want to control the brightness, you need to use the PWM pulse width modulation method. It should be noted that the GPIO.cleanup() in the cancel function returns the pins to their original state and releases all pins so that they no longer have output, thus avoiding some electrical damage and fires.
The rest are some callback functions, mainly some writing and judgment operations. Through these callback functions, the corresponding components can be operated.

This section is mainly about doing a practical operation of the Service service, consolidating its usage as one of the communication mechanisms, and its specific application in practice.
The code contains some knowledge points about GPIO general input and output. Those who are interested can check it out:
Physical diagram and function introduction of the RPi.GPIO interface of the JetsonNano board