跳转至

总线伺服舵机SDK使用手册(ROS)

1.概述

本SDK内容有

  • ROS-Melodic安装
  • 基于总线伺服舵机通信协议的API函数,适用于所有总线伺服舵机型号。
  • ROS功能包,包括Message以及Python和C++的节点实例。

1.1.上位机软件

上位机软件可以调试总线伺服舵机,测试总线伺服舵机的功能。

1.2.SDK

本文例程、API下载。

1.4.图例

HP8-U45-M总线伺服舵机

总线伺服舵机转接板UC-01

2.接线说明

  1. 安装USB转TTL模块的驱动程序。
  2. 将TTL/USB调试转换板UC-01与控制器、总线伺服舵机以及电源连接。

3.ROS-Melodic安装手册

本文讲解了如何在Ubuntu18.04下安装ROS发行版Melodic。

使用以下命令来查询您的Ubuntu版本,如与本文不一致,可参考网上其他教程安装对应ROS版本。

当您能够成功打开小海龟窗口时,即代表ROS安装完成。

lsb_release -a

3.1.系统信息

Ubuntu: 18.04
ROS: Melodic

3.2.官方wiki

ROS Melodic官方安装教程

3.3.安装melodic

sources.list初始化

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

添加keys

sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

更新系统软件源

sudo apt update

安装ROS

sudo apt install ros-melodic-desktop-full

修改.bashrc

echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

3.4.安装依赖包

sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential

3.5.安装rosdep

rosdep 初始化

sudo apt install python-rosdep

修改IP地址映射

在初始化rosdep之前,需要重定向网址raw.githubusercontent.com的IP地址, 原因是被墙了,

sudo gedit /etc/hosts

使用网站 https://www.ipaddress.com/ 查询 raw.githubusercontent.com最新的IP地址,

追加一行(注:IP地址可以用你查到的地址) URL对应的IP解析地址有很多个,选取一个可用的。

185.199.109.133 raw.githubusercontent.com

编辑完成之后保存并退出,

sudo gedit /etc/resolv.conf 

注释原有的nameserver, 追加两行,

# nameserver 127.0.0.53
nameserver 8.8.8.8 #google域名服务器
nameserver 8.8.4.4 #google域名服务器

修改rosdep的源码

rosdep update的时候,一直提示timeout错误的原因是rosdep包里面的超时判断条件太短了,国内访问raw.githubusercontent.com的速度很慢,所以需要修改rosdep包的源码里面的超时判断条件。

设置全局变量DOWNLOAD_TIMEOUT =45

需要修改如下三个Python脚本里面的DOWNLOAD_TIMEOUT

sudo gedit /usr/lib/python2.7/dist-packages/rosdep2/sources_list.py
sudo gedit /usr/lib/python2.7/dist-packages/rosdep2/gbpdistro_support.py
sudo gedit /usr/lib/python2.7/dist-packages/rosdep2/rep3.py

修改之后,可能还是会遇到timeout的情况,多试几次。

rosdep初始化与更新

sudo rosdep init
rosdep update

输出日志

$ rosdep update
reading in sources list data from /etc/ros/rosdep/sources.list.d
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/base.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/python.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/ruby.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/releases/fuerte.yaml
Query rosdistro index https://raw.githubusercontent.com/ros/rosdistro/master/index-v4.yaml
Skip end-of-life distro "ardent"
Skip end-of-life distro "bouncy"
Skip end-of-life distro "crystal"
Skip end-of-life distro "dashing"
Skip end-of-life distro "eloquent"
Add distro "foxy"
Add distro "galactic"
Skip end-of-life distro "groovy"
Skip end-of-life distro "hydro"
Skip end-of-life distro "indigo"
Skip end-of-life distro "jade"
Skip end-of-life distro "kinetic"
Skip end-of-life distro "lunar"
Add distro "melodic"
Add distro "noetic"
Add distro "rolling"
updated cache in /home/kyle/.ros/rosdep/sources.cache

4.创建Workspace

创建Workspace文件夹,这一步可以自定义文件夹名称,但是后续操作需要对应修改。

cd ~
mkdir catkin_ws && cd catkin_ws
mkdir src
catkin_make

输出日志

kyle@turing:~/catkin_ws$ catkin_make
Base path: /home/kyle/catkin_ws
Source space: /home/kyle/catkin_ws/src
Build space: /home/kyle/catkin_ws/build
Devel space: /home/kyle/catkin_ws/devel
Install space: /home/kyle/catkin_ws/install
Creating symlink "/home/kyle/catkin_ws/src/CMakeLists.txt" pointing to "/opt/ros/melodic/share/catkin/cmake/toplevel.cmake"
####
#### Running command: "cmake /home/kyle/catkin_ws/src -DCATKIN_DEVEL_PREFIX=/home/kyle/catkin_ws/devel -DCMAKE_INSTALL_PREFIX=/home/kyle/catkin_ws/install -G Unix Makefiles" in "/home/kyle/catkin_ws/build"
####
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Using CATKIN_DEVEL_PREFIX: /home/kyle/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /opt/ros/melodic
-- This workspace overlays: /opt/ros/melodic
-- Found PythonInterp: /usr/bin/python2 (found suitable version "2.7.17", minimum required is "2") 
-- Using PYTHON_EXECUTABLE: /usr/bin/python2
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/kyle/catkin_ws/build/test_results
-- Found gtest sources under '/usr/src/googletest': gtests will be built
-- Found gmock sources under '/usr/src/googletest': gmock will be built
-- Found PythonInterp: /usr/bin/python2 (found version "2.7.17") 
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE  
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.29
-- BUILD_SHARED_LIBS is on
-- BUILD_SHARED_LIBS is on
-- Configuring done
-- Generating done
-- Build files have been written to: /home/kyle/catkin_ws/build
####
#### Running command: "make -j4 -l4" in "/home/kyle/catkin_ws/build"
####

将工作区的devel/setup.bash 添加到.bashrc

gedit ~/.bashrc 

追加一行

source /home/kyle/catkin_ws/devel/setup.bash

注: 这里的kyle改成你自己的用户名。

source ~/.bashrc 

5.C++功能包

5.1.安装依赖

需要在Ubuntu上安装总线伺服舵机 C++ SDK,参见教程:总线伺服舵机SDK使用手册(C++)

  • cserialport # 轻量级串口通信库
  • fsuartservo # FashionStar总线伺服舵机SDK (C++)

5.2.创建包

官方文档参考:ROS Wiki - Creating a ROS Package

进入文件夹

cd ~/catkin_ws/src

创建包

catkin_create_pkg fashionstar_uart_servo_ros1 std_msgs rospy roscpp

5.3.创建Message

官方文档参考:ROS wiki - msg

创建文件夹

fashionstar_uart_servo_ros1工程内新建msg 文件夹,放置Message

创建Message文件

标准信息格式支持如下

  • int8,int16,int32,int64
  • float32,float64
  • string
  • 其他自定义msg:package/MessageName
  • 不定长array[ ]以及定长array[N]

此处以setRawAngle API为例,需要舵机id和目标角度两个参数,因此设置msg为:

SetServoAngle.msg

uint8 id
float32 angle

配置pakcage.xml

添加如下两行:

构建的时候生成message的依赖

<build_depend>message_generation</build_depend>

运行的时候message依赖

<exec_depend>message_runtime</exec_depend>

image-20210629143422955

配置CMakeLists.txt

配置find_package

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation # Message生成依赖
)

注册.msg 文件

## Generate messages in the 'msg' folder
## 这里添加msg文件夹下的msg文件名称
add_message_files(
  FILES
  SetServoAngle.msg
)

添加生成message的时候需要的依赖

## Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)

配置catkin, CATKIN_DEPENDS 里面添加message_runtime

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
  INCLUDE_DIRS include
  LIBRARIES fashionstar_uart_servo_ros1
  CATKIN_DEPENDS message_runtime roscpp rospy std_msgs
  DEPENDS system_lib
)

构建Message

cd ~/catkin_ws
catkin_make

image-20210629144447923

使用rosmsg查看创建的msg

指令格式

rosmsg show <message文件的名称>

示例:

kyle@turing:~/catkin_ws$ rosmsg show SetServoAngle
[fashionstar_uart_servo_ros1/SetServoAngle]:
uint8 id
float32 angle

5.4.创建Service文件

创建文件夹

fashionstar_uart_servo_ros1工程内新建文件夹srv

创建Service文件

srv文件夹下创建query_servo_angle.srv

query_servo_angle.srv

uint8 id
---
float32 angle

ServiceMessage很相似,--- 符号上方是传入参数,下方是回传的参数。

配置package.xml

Message 配置方式一致。

配置CMakeLists.txt

注册service文件,

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

其他部分跟Message的配置方式一致。

构建Service

cd ~/catkin_ws
catkin_make

5.5.C++节点-实际控制节点

src下创建uservo_demo_node.cpp

/* 
 * 舵机控制节点(Demo) 
 */
// 导入ROS依赖
#include "ros/ros.h"
#include "fashionstar_uart_servo_ros1/SetServoAngle.h"
#include "fashionstar_uart_servo_ros1/SetServoDamping.h"
#include "fashionstar_uart_servo_ros1/QueryServoAngle.h"

#include "CSerialPort/SerialPort.h"
#include "FashionStar/UServo/FashionStar_UartServoProtocol.h"
#include "FashionStar/UServo/FashionStar_UartServo.h"

using namespace fsuservo;
using namespace fashionstar_uart_servo_ros1;

// 参数定义
#define SERVO_PORT_NAME "/dev/ttyUSB0"  // Linux下端口号名称 /dev/ttyUSB{}
#define SERVO_ID 0                      // 舵机ID号

// 创建协议对象
FSUS_Protocol protocol(SERVO_PORT_NAME, FSUS_DEFAULT_BAUDRATE);
// 创建一个舵机对象
FSUS_Servo servo0(SERVO_ID, &protocol);

/* 舵机角度设置回调函数 */
void set_servo_angle_callback(const SetServoAngle& data){
    ROS_INFO("[RECV] Servo ID = %d Set Angle = %.1f", data.id, data.angle);
    // 设置舵机角度
    servo0.setRawAngle(data.angle, 0);
}

/* 舵机阻尼模式回调函数 */
void set_servo_damping_callback(const SetServoDamping& data){
    ROS_INFO("[RECV] Servo ID = %d, Set Damping Power = %d", data.id, data.power);
    servo0.setDamping(data.power);
}

/* 舵机角度查询 */
bool query_servo_angle_callback(QueryServoAngle::Request& req, QueryServoAngle::Response& res){
    float angle;
    // 通过SDK查询舵机角度 
    angle = servo0.queryRawAngle();
    // 填写返回数据包
    res.angle = angle;
    // 打印日志
    ROS_INFO("[RECV] Servo ID = %d  , Query Servo Angle = %.1f", req.id,  angle);
    // 注: C++版本的 服务回调函数,最后一定要返回一个bool值
    return true;
}

int main(int argc, char **argv)
{
    // 创建节点名称
    ros::init(argc, argv, "uservo_demo_node");
    // 创建NodeHandle
    ros::NodeHandle node_handle;

    // 创建接收者
    ros::Subscriber set_servo_angle_sub = node_handle.subscribe("set_servo_angle", 2, set_servo_angle_callback);
    ros::Subscriber set_servo_damping_sub = node_handle.subscribe("set_servo_damping", 2, set_servo_damping_callback);
    // 创建服务
    ros::ServiceServer query_servo_angle_srv = node_handle.advertiseService("query_servo_angle", query_servo_angle_callback);
    // 进入循环等待
    ros::spin();
}

5.6.C++节点-Message发送者

以使用简易角度控制API,控制舵机在-90°~90°之间来回运动为例。

src下创建test_set_angle_node.cpp

/* 
 * 测试舵机角度控制节点 
 */

#include "ros/ros.h"
// 自定义消息
#include "fashionstar_uart_servo_ros1/SetServoAngle.h"

using namespace fashionstar_uart_servo_ros1;

#define SERVO_ID 0                      // 舵机ID号

int main(int argc, char **argv)
{
    // 创建节点名称
    ros::init(argc, argv, "test_set_angle_node");
    // 创建NodeHandle
    ros::NodeHandle node_handle;
    // 创建发布者
    ros::Publisher set_servo_angle_pub = node_handle.advertise<SetServoAngle>("set_servo_angle", 2);
    // 循环频率0.2HZ
    ros::Rate loop_rate(0.2);
    // 创建Message
    SetServoAngle msg;

    float angle = 90.0;
    while(ros::ok()){
        // 修改目标角度
        angle *= -1.0;
        // 构建Message
        msg.id = SERVO_ID;
        msg.angle = angle;
        // 发布消息
        set_servo_angle_pub.publish(msg);
        // 输出日志
        ROS_INFO("Set Servo %d Angle = %.1f", msg.id, msg.angle);
        // 延时等待
        loop_rate.sleep();
    }
}

5.7.C++节点-Service客户端

以使用当前角度查询API,查询角度为例。

src/test_query_angle_node.cpp

/* 
 * 测试舵机角度查询节点 
 */
#include "ros/ros.h"
// 自定义消息
#include "fashionstar_uart_servo_ros1/SetServoDamping.h"
// 自定义服务
#include "fashionstar_uart_servo_ros1/QueryServoAngle.h"


using namespace fashionstar_uart_servo_ros1;

#define SERVO_ID 0                      // 舵机ID号

int main(int argc, char **argv)
{
    // 创建节点名称
    ros::init(argc, argv, "test_query_angle_node");
    // 创建NodeHandle
    ros::NodeHandle node_handle;
    // 创建发布者
    ros::Publisher set_servo_damping_pub = node_handle.advertise<SetServoDamping>("set_servo_damping", 2);
    // 创建阻尼模式Message
    SetServoDamping damping_msg;

    // 角度查询服务客户端
    ros::ServiceClient query_angle_client = node_handle.serviceClient<QueryServoAngle>("query_servo_angle");
    // 创建服务对象
    QueryServoAngle query_angle_srv;

    // 等待角度查询服务开启
    query_angle_client.waitForExistence();

    // 设置舵机为阻尼模式
    damping_msg.id = SERVO_ID;
    damping_msg.power = 400;
    set_servo_damping_pub.publish(damping_msg);

    // 循环频率1HZ
    ros::Rate loop_rate(1);
    float angle = 90.0;
    while(ros::ok()){
        // 构造服务请求头
        query_angle_srv.request.id = SERVO_ID;
        // 发送服务请求
        if(query_angle_client.call(query_angle_srv)){
            // 服务成功
            ROS_INFO("[Send]Query Servo %d Angle %.1f", SERVO_ID, query_angle_srv.response.angle);
        }else{
            // 发送失败
            ROS_INFO("[Send]Query Servo %d Angle Failed", SERVO_ID);
        }
        // 延时等待
        loop_rate.sleep();
    }
}

5.8.配置CMakeLists.txt

配置CMakeLists.txt

添加include路径,这样才可以找到依赖库的.h文件所在的路径

set(CMAKE_INSTALL_PREFIX /usr/local)
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  ${CMAKE_INSTALL_PREFIX}/include
)

设置link文件夹, 动态链接库所在的位置

# 设置总线伺服舵机链接库的位置
link_directories(
  ${CMAKE_INSTALL_PREFIX}/lib
)

添加可执行程序

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
## 定义C++可执行文件
# - 总线伺服舵机服务示例节点
add_executable(uservo_demo_node src/uservo_demo_node.cpp)

add_executable(test_set_angle_node src/test_set_angle_node.cpp)

add_executable(test_query_angle_node src/test_query_angle_node.cpp)

动态链接

## Specify libraries to link a library or executable target against
target_link_libraries(uservo_demo_node
  ${catkin_LIBRARIES}
  cserialport # 轻量级串口通信库
  fsuartservo # FashionStar总线伺服舵机SDK (C++)
)

target_link_libraries(test_set_angle_node
  ${catkin_LIBRARIES}
)

修改完成的CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)
project(fashionstar_uart_servo_ros1)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation # Message生成依赖
)


################################################
## Declare ROS messages, services and actions ##
################################################

## Generate messages in the 'msg' folder
## 这里添加msg文件夹下的msg文件名称
add_message_files(
  FILES
  SetServoAngle.msg     # 设置舵机角度
  SetServoDamping.msg   # 设置舵机为阻尼模式
)

## Generate services in the 'srv' folder
add_service_files(
  FILES
  QueryServoAngle.srv  # 舵机角度查询服务

)

## Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)


###################################
## catkin specific configuration ##
###################################

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

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
set(CMAKE_INSTALL_PREFIX /usr/local)
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  ${CMAKE_INSTALL_PREFIX}/include
)

# 设置总线伺服舵机链接库的位置
link_directories(
  ${CMAKE_INSTALL_PREFIX}/lib
)

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
## 定义C++可执行文件
# - 总线伺服舵机服务示例节点
add_executable(uservo_demo_node src/uservo_demo_node.cpp)

add_executable(test_set_angle_node src/test_set_angle_node.cpp)

add_executable(test_query_angle_node src/test_query_angle_node.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(uservo_demo_node
  ${catkin_LIBRARIES}
  cserialport # 轻量级串口通信库
  fsuartservo # FashionStar总线伺服舵机SDK (C++)
)

target_link_libraries(test_set_angle_node
  ${catkin_LIBRARIES}
)
target_link_libraries(test_query_angle_node
  ${catkin_LIBRARIES}
)

5.9.运行例程

运行Master节点

roscore

运行实际控制节点

rosrun fashionstar_uart_servo_ros1 uservo_demo_node

运行舵机角度控制节点(Message发送者)

rosrun fashionstar_uart_servo_ros1 test_set_angle_node

运行当前角度查询节点(运行Service客户端节点)

rosrun fashionstar_uart_servo_ros1 test_query_angle_node

6.Python功能包

6.1.创建包

官方文档参考:ROS Wiki - Creating a ROS Package

进入文件夹

cd ~/catkin_ws/src

创建包

catkin_create_pkg fashionstar_uart_servo_ros1 std_msgs rospy roscpp

6.2.创建Message

官方文档参考: ROS wiki - msg

创建文件夹

fashionstar_uart_servo_ros1工程内新建msg 文件夹,放置Message

创建Message文件

标准信息格式支持如下

  • int8,int16,int32,int64
  • float32,float64
  • string
  • 其他自定义msg:package/MessageName
  • 不定长array[ ]以及定长array[N]

此处以set_servo_angle API为例,需要舵机id和目标角度两个参数,因此设置msg为:

SetServoAngle.msg

uint8 id
float32 angle

配置pakcage.xml

添加如下两行:

构建的时候生成message的依赖,

<build_depend>message_generation</build_depend>

运行的时候message依赖,

<exec_depend>message_runtime</exec_depend>

image-20210629143422955

配置CMakeLists.txt

配置find_package

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation # Message生成依赖
)

注册.msg 文件

## Generate messages in the 'msg' folder
## 这里添加msg文件夹下的msg文件名称
add_message_files(
  FILES
  SetServoAngle.msg
)

添加生成message的时候需要的依赖

## Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)

配置catkin,CATKIN_DEPENDS 里面添加message_runtime

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
  INCLUDE_DIRS include
  LIBRARIES fashionstar_uart_servo_ros1
  CATKIN_DEPENDS message_runtime roscpp rospy std_msgs
  DEPENDS system_lib
)

构建Message

cd ~/catkin_ws
catkin_make

image-20210629144447923

使用rosmsg查看创建的msg

指令格式

rosmsg show <message文件的名称>

示例:

kyle@turing:~/catkin_ws$ rosmsg show SetServoAngle
[fashionstar_uart_servo_ros1/SetServoAngle]:
uint8 id
float32 angle

6.3.创建Service文件

创建文件夹

fashionstar_uart_servo_ros1工程内创建文件夹srv

创建Service文件

srv文件夹下创建QueryServoAngle.srv

QueryServoAngle.srv

uint8 id
---
float32 angle

ServiceMessage很相似,--- 符号上方是传入参数,下方是回传的参数

配置package.xml

添加如下两行:

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

image-20210629143422955

配置CMakeLists.txt

注册service文件,

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

其他部分跟Message的配置方式一致。

构建Service

cd ~/catkin_ws
catkin_make

Python节点-服务提供者

新添加一个设置舵机为阻尼模式的Message,具体设置参考**创建message**

SetServoDamping.msg

uint8 id
uint16 power

服务提供者的完整源码如下,

uservo_demo_node.py

#!/usr/bin/env python
#coding:utf-8
'''
舵机控制节点(Demo)
'''
import rospy
import serial
from uservo import UartServoManager
from fashionstar_uart_servo_ros1.msg import SetServoAngle, SetServoDamping
from fashionstar_uart_servo_ros1.srv import QueryServoAngle, QueryServoAngleResponse

# 参数配置
# 角度定义
SERVO_PORT_NAME =  u'/dev/ttyUSB0'      # 舵机串口号 <<< 修改为实际串口号
SERVO_BAUDRATE = 115200                 # 舵机的波特率
SERVO_ID = 0                            # 舵机的ID号
SERVO_HAS_MTURN_FUNC = False            # 舵机是否拥有多圈模式

# 初始化串口
uart = serial.Serial(port=SERVO_PORT_NAME, baudrate=SERVO_BAUDRATE,\
                     parity=serial.PARITY_NONE, stopbits=1,\
                     bytesize=8,timeout=0)
# 初始化舵机管理器
uservo = UartServoManager(uart)

def set_servo_angle_callback(data):
    '''回调函数-设置舵机角度'''
    rospy.loginfo("[Recv] Servo ID ={} Set Angle={}".format(data.id, data.angle))
    # 调用舵机SDK - 设置舵机的角度
    uservo.set_servo_angle(data.id, data.angle, interval=0)

def set_servo_damping_callback(data):
    '''回调函数-设置舵机为阻尼模式'''
    rospy.loginfo("[Recv] Servo ID ={} Set Damping Power={}".format(data.id, data.power))
    # 调用舵机SDK - 设置舵机为阻尼模式
    uservo.set_damping(data.id, data.power)

def query_servo_angle_callback(req):
    '''回调函数-查询舵机角度'''
    # 创建回传对象
    response = QueryServoAngleResponse()
    # 查询舵机的角度
    angle = uservo.query_servo_angle(req.id)
    # 填充回传对象的内容
    response.angle = angle
    rospy.loginfo("[Recv] Servo ID ={} Query Angle={}".format(req.id, angle))

    return response

def uart_manager():    
    # 初始化节点
    rospy.init_node('uservo_node', anonymous=True)
    # 舵机角度设置订阅者
    rospy.Subscriber("set_servo_angle", SetServoAngle, set_servo_angle_callback)
    # 舵机阻尼模式订阅者
    rospy.Subscriber("set_servo_damping", SetServoDamping, set_servo_damping_callback)
    # 舵机角度查询服务
    rospy.Service('query_servo_angle', QueryServoAngle, query_servo_angle_callback)
    # 持续等待, 直到节点结束
    rospy.spin()

if __name__ == '__main__':
    uart_manager()

6.4.Python节点-实际控制节点

fashionstar_uart_servo_ros1工程文件里面,创建Python脚本的文件夹scripts

将Python2版本的uservo.py拷贝到scripts 文件夹里面

并创建一个串口总线舵机的Python节点 uservo_demo_node.py

赋予其执行权限

sudo chmod +x uservo_demo_node.py 

uservo_demo_node.py

#!/usr/bin/env python
#coding:utf-8
'''
舵机控制节点(Demo)
'''
import rospy
import serial
from uservo import UartServoManager
from fashionstar_uart_servo_ros1.msg import SetServoAngle, SetServoDamping
from fashionstar_uart_servo_ros1.srv import QueryServoAngle, QueryServoAngleResponse

# 参数配置
# 角度定义
SERVO_PORT_NAME =  u'/dev/ttyUSB0'      # 舵机串口号 <<< 修改为实际串口号
SERVO_BAUDRATE = 115200                 # 舵机的波特率
SERVO_ID = 0                            # 舵机的ID号
SERVO_HAS_MTURN_FUNC = False            # 舵机是否拥有多圈模式

# 初始化串口
uart = serial.Serial(port=SERVO_PORT_NAME, baudrate=SERVO_BAUDRATE,\
                     parity=serial.PARITY_NONE, stopbits=1,\
                     bytesize=8,timeout=0)
# 初始化舵机管理器
uservo = UartServoManager(uart)

def set_servo_angle_callback(data):
    '''回调函数-设置舵机角度'''
    rospy.loginfo("[Recv] Servo ID ={} Set Angle={}".format(data.id, data.angle))
    # 调用舵机SDK - 设置舵机的角度
    uservo.set_servo_angle(data.id, data.angle, interval=0)

def set_servo_damping_callback(data):
    '''回调函数-设置舵机为阻尼模式'''
    rospy.loginfo("[Recv] Servo ID ={} Set Damping Power={}".format(data.id, data.power))
    # 调用舵机SDK - 设置舵机为阻尼模式
    uservo.set_damping(data.id, data.power)

def query_servo_angle_callback(req):
    '''回调函数-查询舵机角度'''
    # 创建回传对象
    response = QueryServoAngleResponse()
    # 查询舵机的角度
    angle = uservo.query_servo_angle(req.id)
    # 填充回传对象的内容
    response.angle = angle
    rospy.loginfo("[Recv] Servo ID ={} Query Angle={}".format(req.id, angle))

    return response

def uart_manager():    
    # 初始化节点
    rospy.init_node('uservo_node', anonymous=True)
    # 舵机角度设置订阅者
    rospy.Subscriber("set_servo_angle", SetServoAngle, set_servo_angle_callback)
    # 舵机阻尼模式订阅者
    rospy.Subscriber("set_servo_damping", SetServoDamping, set_servo_damping_callback)
    # 舵机角度查询服务
    rospy.Service('query_servo_angle', QueryServoAngle, query_servo_angle_callback)
    # 持续等待, 直到节点结束
    rospy.spin()

if __name__ == '__main__':
    uart_manager()

注意: 文件头必须有如下声明

#!/usr/bin/env python
#coding:utf-8

否则运行的时候会遇到报错

kyle@turing:~/catkin_ws$ rosrun fashionstar_uart_servo_ros1 uservo_demo_node.py
import-im6.q16: not authorized `rospy' @ error/constitute.c/WriteImage/1037.
from: can't read /var/mail/fashionstar_uart_servo_ros1.msg
/home/kyle/catkin_ws/src/fashionstar_uart_servo_ros1/scripts/uservo_node.py: line 6: syntax error near unexpected token `('
/home/kyle/catkin_ws/src/fashionstar_uart_servo_ros1/scripts/uservo_node.py: line 6: `def set_servo_angle_callback(data):'

6.5.Python节点-Message发送者

创建另外一个文件test_set_angle_node.py 并赋予其可执行权限

 chmod +x test_set_angle_node.py

test_set_angle_node.py

#!/usr/bin/env python
#coding:utf-8
'''
电机角度控制测试节点
'''
import rospy
from fashionstar_uart_servo_ros1.msg import SetServoAngle

SERVO_ID = 0 # 测试的电机ID

def test_set_servo_angle():
    '''回调函数'''
    # 创建节点
    rospy.init_node('test_set_angle_node', anonymous=True) 
    # 创建发布者 
    # - Topic名称为:  'set_servo_angle'
    # - Message格式类型: set_servo_angle
    # - queue_size 消息队列尺寸,自定义
    pub = rospy.Publisher('set_servo_angle', SetServoAngle, queue_size=2)
    # 设置发送节拍 0.2HZ 
    rate = rospy.Rate(0.2)
    # 电机角度
    angle = 90.0
    # 创建信息对象 
    msg = SetServoAngle()

    while not rospy.is_shutdown():
        # 修改电机角度
        angle *= -1.0
        # 填充Message
        msg.id = SERVO_ID
        msg.angle = angle
        rospy.loginfo('Set Servo {} = {}'.format(msg.id, msg.angle))
        pub.publish(msg)
        rate.sleep()

if __name__ == '__main__':
    test_set_servo_angle()

6.6.Python节点-Service客户端

test_query_angle_node.py

#!/usr/bin/env python
#coding:utf-8
'''
电机角度查询节点
'''
import rospy
from fashionstar_uart_servo_ros1.msg import SetServoDamping
from fashionstar_uart_servo_ros1.srv import QueryServoAngle

SERVO_ID = 0 # 测试的电机ID

def test_query_servo_angle():
    '''回调函数'''
    # 创建节点
    rospy.init_node('test_query_angle_node', anonymous=True) 
    # 等待角度查询服务开启
    srv_query_servo_angle = rospy.ServiceProxy('query_servo_angle', QueryServoAngle)
    # 创建发布者 - 舵机阻尼模式发布器
    set_servo_damping_pub = rospy.Publisher('set_servo_damping', SetServoDamping, queue_size=1)
    # 构造Damping控制指令
    damping_msg = SetServoDamping()
    damping_msg.id = SERVO_ID   # 舵机ID
    damping_msg.power = 400    # 功率
    rospy.sleep(2)
    set_servo_damping_pub.publish(damping_msg) # 设置为Damping模式
    rospy.loginfo("Servo #{} Set Damping Mode, Power={} mW".format(SERVO_ID, damping_msg.power))

    # 设置发送节拍 0.2HZ 
    rate = rospy.Rate(1)

    while not rospy.is_shutdown():
        # 设置舵机为阻尼模式
        # 理论上来讲,发一次就可以了, 但是实际上因为message通信模式
        # 不能保证发出的一定被接受到,所以保险还是多发几次
        # set_servo_damping_pub.publish(damping_msg)

        # 请求query_servo_angle的服务
        try:
            response = srv_query_servo_angle(SERVO_ID)
            # 提取查询的角度
            angle = response.angle
            # 输出角度查询日志
            rospy.loginfo("Servo #{} Angle={}".format(SERVO_ID, angle))
        except rospy.ServiceException as e:
            # 服务并不总是可以被处理,会存在一定概率的服务请求失败的问题
            print("Service Call Failed : {}".format(e))

        # 等待
        rate.sleep()

if __name__ == '__main__':
    test_query_servo_angle()

6.7.运行例程

运行Master节点

roscore

运行实际控制节点

rosrun fashionstar_uart_servo_ros1 uservo_demo_node.py

运行舵机角度控制节点(Message发送者)

rosrun fashionstar_uart_servo_ros1 test_set_angle_node.py

运行当前角度查询节点(运行Service客户端节点)

rosrun fashionstar_uart_servo_ros1 test_query_angle_node.py 

注:若提示/dev/ttyUSB0 permission denied,请运行以下指令:

sudo chmod 777 /dev/ttyUSB0

6.8.控制舵机

修改uservo_demo_node.py文件,导入FashionStar 舵机Python3的SDK,驱动舵机转动起来。

可以根据自己的需求定制角度控制的具体的参数,例如指定时间/功率/转速等等。

具体的API请参阅总线伺服舵机SDK使用手册(Python )。

#!/usr/bin/env python
#coding:utf-8
'''
舵机控制节点
'''
import rospy
import serial
from uservo import UartServoManager
from fashionstar_uart_servo_ros1.msg import SetServoAngle

# 参数配置
# 角度定义
SERVO_PORT_NAME =  u'/dev/ttyUSB0'      # 舵机串口号 <<< 修改为实际串口号
SERVO_BAUDRATE = 115200                 # 舵机的波特率
SERVO_ID = 0                            # 舵机的ID号
SERVO_HAS_MTURN_FUNC = False            # 舵机是否拥有多圈模式

# 初始化串口
uart = serial.Serial(port=SERVO_PORT_NAME, baudrate=SERVO_BAUDRATE,\
                     parity=serial.PARITY_NONE, stopbits=1,\
                     bytesize=8,timeout=0)
# 初始化舵机管理器
uservo = UartServoManager(uart)

def set_servo_angle_callback(data):
    '''回调函数-设置舵机角度'''
    rospy.loginfo("[Recv] Servo ID ={} Set Angle={}".format(data.id, data.angle))
    uservo.set_servo_angle(data.id, data.angle, interval=0)

def uart_manager():    
    # 初始化节点
    rospy.init_node('uservo_node', anonymous=True)
    # 舵机角度订阅者
    rospy.Subscriber("set_servo_angle", SetServoAngle, set_servo_angle_callback)
    # 持续等待, 直到节点结束
    rospy.spin()

if __name__ == '__main__':
    uart_manager()

7.API使用

7.1.描述

ROS功能包源码依语言分为两类,可以在Fashion Star Wiki页面获取到对应的SDK使用手册。

  • C++

  • Python

另外,根据通信协议中指令在默认情况下是否带舵机响应包,建议在设置命令节点时根据原始指令是否带响应包,使用不同的通信机制。

  • Message:指令不带响应包,发送后无需等待响应数据,例如set_servo_angle等控制指令。
  • Service:指令带响应包,发送后需要得到响应数据,例如Pingquery_servo_angle等指令。

判断API是否带响应包

查看总线伺服舵机通信协议中原始指令是否带响应包,其对应的API也即有无响应包。

7.2.例程结构

节点 描述
实际控制节点 控制硬件串口收发的节点,根据其他节点的Message或者Service请求进行动作
Message发送者节点 用户根据使用条件,编写的Message发送者节点,使用Message间接控制舵机
Service客户端节点 用户根据使用条件,编写的Service客户端节点,使用Service间接控制舵机

7.3.举例

因ROS环境下使用舵机基于 C++_SDKPython_SDK ,本SDK使用手册仅举例说明API的使用,关于API的更多信息,可以参考对应SDK使用手册。

setRawAngle

1.根据总线伺服舵机通信协议,在原始指令中,设置舵机角度的响应包默认不开启,因此舵机不会在收到请求包后反馈响应包,使用Message即可。

1724116519343

2.根据C++_SDK中函数原型如下,编程时使用该接口。

1724116154829

3.在实际控制节点中,设置回调函数,其他部分,只需对照例程配置即可。

/* 舵机角度设置回调函数 */
void set_servo_angle_callback(const SetServoAngle& data){
    ROS_INFO("[RECV] Servo ID = %d Set Angle = %.1f", data.id, data.angle);
    // 设置舵机角度
    servo0.setRawAngle(data.angle, 0);
}
回到页面顶部