第五次blog,实现宇树go2机器狗的消息桥接

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

本次blog实现订阅twist消息,并将之转换为go2所需的request消息,从而以ROS2自带键盘"cmd_vel"控制机器狗运动

1.实现思路:

要实现这个功能,重点在于数据的传递,需要订阅geometry_msgs/msg/Twist消息,然后解析geometry_msgs/msg/Twist消息并生成unitree_api/msg/Request消息,最后发布unitree_api/msg/Request消息即可。

2. 功能包创建

消息桥接可以使用c++或者python来实现,本文将逐个介绍。

现在打开终端,输入以下指令建立功能包:

  • c++功能包:
cd unitree_go2_ws/src/base/

ros2 pkg create go2_twist_bridge --build-type ament_cmake --dependencies rclcpp geometry_msgs unitree_api --node-name twist_bridge
  • python功能包:
cd unitree_go2_ws/src/base/

ros2 pkg create go2_twist_bridge_py --build-type ament_python --dependencies rclpy geometry_msgs unitree_api --node-name twist_bridge

创建完就可以开始编写代码了。

3. c++实现

1. 准备

实现之前要有json的文件,所以要去之前下载的unitree_ros2包里复制nlohmann包到go2_twist_bridge功能包中。所以需要到unitree_ros2/example/src/include目录下复制nlohmann包到unitree_go2_ws/src/base/go2_twist_bridge/include目录下。

随后在unitree_go2_ws/src/base/go2_twist_bridge/include目录下创建sport_model.hpp文件,用来存放定义常量和编号等。

sport_model.hpp文件编写代码如下:

#ifndef _SPORT_MODEL_
#define _SPORT_MODEL_
#include <iostream>

#pragma pack(1)

//1001:阻尼控制
const int32_t ROBOT_SPORT_API_ID_DAMP = 1001;

//1002:平衡站立,控制机器人进入平衡站立状态,保持直立和稳定
const int32_t ROBOT_SPORT_API_ID_BALANCESTAND = 1002;

//1003:停止运动,停止机器人的所有运动,使其立即静止
const int32_t ROBOT_SPORT_API_ID_STOPMOVE = 1003;

//1004:站立,控制机器人从其他姿势(坐下或躺下)恢复到站立姿势
const int32_t ROBOT_SPORT_API_ID_STANDUP = 1004;

//1005:站立下降,控制机器人从站立姿势转变为其他姿势(如坐下或躺下)
const int32_t ROBOT_SPORT_API_ID_STANDDOWN = 1005;

//1006:恢复站立,控制机器人在失去平衡后恢复到站立姿势
const int32_t ROBOT_SPORT_API_ID_RECOVERYSTAND = 1006;

//1007:欧拉角控制,用于调整机器人的姿态(如俯仰、横滚、偏航)
const int32_t ROBOT_SPORT_API_ID_EULER = 1007;

//1008:移动,控制机器人进行移动,可能是直线移动或转向
const int32_t ROBOT_SPORT_API_ID_MOVE = 1008;

//1009:坐下,控制机器人进入坐下姿势
const int32_t ROBOT_SPORT_API_ID_SIT = 1009;

//1010:从坐下恢复站立,控制机器人从坐下姿势恢复到站立姿势
const int32_t ROBOT_SPORT_API_ID_RISESIT = 1010;

//1011:切换步态,切换机器人的步态(如行走、跑步、爬行等)
const int32_t ROBOT_SPORT_API_ID_SWITCHGAIT = 1011;

//1012:触发,用于触发某个特定的动作或事件
const int32_t ROBOT_SPORT_API_ID_TRIGGER = 1012;

//1013:身体高度调整,调整机器人的身体高度
const int32_t ROBOT_SPORT_API_ID_BODYHEIGHT = 1013;

//1014:脚部抬起高度调整,调整机器人脚部的抬起高度
const int32_t ROBOT_SPORT_API_ID_FOOTTRAISEHEIGHT = 1014;

//1015:速度级别调整,调整机器人的运动速度级别
const int32_t ROBOT_SPORT_API_ID_SPEEDLEVEL = 1015;

//1016:打招呼,控制机器人执行"打招呼"的动作
const int32_t ROBOT_SPORT_API_ID_HELLO = 1016;

//1017:伸懒腰,控制机器人伸懒腰
const int32_t ROBOT_SPORT_API_ID_STRETCH = 1017;

//1018:轨迹跟随,控制机器人按照预定的轨迹进行移动
const int32_t ROBOT_SPORT_API_ID_TRAJECTORYEOLLOW = 1018;

//1019:连续步态,控制机器人进行连续的步态运动
const int32_t ROBOT_SPORT_API_ID_CONTINUOUSGAIT = 1019;

//1020:内容,可能用于控制机器人显示或执行某种内容(如播放视频或音频)
const int32_t ROBOT_SPORT_API_ID_CONTENT = 1020;

//1021:打滚,控制机器人执行"打滚"或"翻滚"动作
const int32_t ROBOT_SPORT_API_ID_WALLOW = 1021;

//1022:舞蹈1,控制机器人执行第一种舞蹈动作
const int32_t ROBOT_SPORT_API_ID_DANCE1 = 1022;

//1023:舞蹈2,控制机器人执行第二种舞蹈动作
const int32_t ROBOT_SPORT_API_ID_DANCE2 = 1023;

//1024:获取身体高度,获取机器人当前的身体高度
const int32_t ROBOT_SPORT_API_ID_GETBODYHEIGHT = 1024;

//1025:获取脚部抬起高度,获取机器人当前抬起脚部的高度
const int32_t ROBOT_SPORT_API_ID_GETFOOTRAISEHEIGHT = 1025;

//1026:获取速度级别,获取机器人当前的运动速度级别
const int32_t ROBOT_SPORT_API_ID_GETSPEEDLEVEL = 1026;

//1027:切换操纵杆,切换操纵杆的控制模式或功能
const int32_t ROBOT_SPORT_API_ID_SWITCHJOYSTICK = 1027;

//1028:姿态,控制机器人进入特定的姿势或姿态
const int32_t ROBOT_SPORT_API_ID_POSE = 1028;

//1029:刮擦,控制机器人进行“刮擦”动作
const int32_t ROBOT_SPORT_API_ID_SCRAPE = 1029;

//1030:前空翻,控制机器人执行“前空翻”动作
const int32_t ROBOT_SPORT_API_ID_FRONTFLIP = 1030;

//1031:前跳,控制机器人执行前跳动作
const int32_t ROBOT_SPORT_API_ID_FRONTJUMP = 1031;

//1032:前扑,控制机器人执行前扑动作
const int32_t ROBOT_SPORT_API_ID_FRONTPOUNCE = 1032;

#endif

定义了机器狗的各种动作及编号。

2.编写思路

随后在unitree_go2_ws/src/base/go2_twist_bridge/src目录下打开twist_bridge.cpp文件进行主程序的编写。

要实现消息桥接,就是要创建订阅方和发布方,这要用到ROS2的两个函数:create_publisher和create_subscription。

  // 1.创建一个Request的发布对象
    request_pub_ = this->create_publisher<unitree_api::msg::Request>("/api/sport/request",10);
  // 2.创建Twist的订阅对象
   twist_sub_ = this->create_subscription<geometry_msgs::msg::Twist>("cmd_vel",10,std::bind(&Twist_Bridge::twist_cb,this,_1));

在回调函数twist_cb中进行消息的桥接:

   void twist_cb(const geometry_msgs::msg::Twist::SharedPtr twist){
    //  3.在回调函数中实现消息的转换及发布    
    unitree_api::msg::Request request;
    //转换
    //获取 twist 消息的线速度和角速度
    double x = twist->linear.x;
    double y = twist->linear.y;   
    double th = twist->angular.z;
    //默认平衡状态为平衡站立
    auto api_id =  ROBOT_SPORT_API_ID_BALANCESTAND;   
    if(x !=0 ||y !=0 || th!=0){
      api_id = ROBOT_SPORT_API_ID_MOVE;
      //设置参数----组织一个字符串样式速度指令
      nlohmann::json js;
      js["x"] = x;
      js["y"] = y;
      js["z"] = th;
      request.parameter = js.dump();
      RCLCPP_INFO(this->get_logger(),"current speed:%s",request.parameter.c_str());     

    }
    request.header.identity.api_id = api_id;
    request_pub_->publish(request);

   }

然后再完成剩余程序的编写,是ros2的知识点了,不多概述。

最终的twist_bridge.cpp文件:

// 包含头文件
#include "rclcpp/rclcpp.hpp"
#include "unitree_api/msg/request.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "sport_model.hpp"
#include "nlohmann/json.hpp"

using namespace std::placeholders;

// 自定义节点类:
class Twist_Bridge: public rclcpp::Node {
public:
  Twist_Bridge():Node("twist_bridge_node_cpp"){
   RCLCPP_INFO(this->get_logger(),"TwistBridge创建!,可将geometry_msgs/msg/twist消息转换成unitree_api/msg/request消息");
  // 1.创建一个Request的发布对象
    request_pub_ = this->create_publisher<unitree_api::msg::Request>("/api/sport/request",10);
  // 2.创建Twist的订阅对象
   twist_sub_ = this->create_subscription<geometry_msgs::msg::Twist>("cmd_vel",10,std::bind(&Twist_Bridge::twist_cb,this,_1));
   }
  
private:
   rclcpp::Publisher<unitree_api::msg::Request>::SharedPtr request_pub_;
   rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr twist_sub_;
   void twist_cb(const geometry_msgs::msg::Twist::SharedPtr twist){
    //  3.在回调函数中实现消息的转换及发布    
    unitree_api::msg::Request request;
    //转换
    //获取 twist 消息的线速度和角速度
    double x = twist->linear.x;
    double y = twist->linear.y;   
    double th = twist->angular.z;
    //默认平衡状态为平衡站立
    auto api_id =  ROBOT_SPORT_API_ID_BALANCESTAND;   
    if(x !=0 ||y !=0 || th!=0){
      api_id = ROBOT_SPORT_API_ID_MOVE;
      //设置参数----组织一个字符串样式速度指令
      nlohmann::json js;
      js["x"] = x;
      js["y"] = y;
      js["z"] = th;
      request.parameter = js.dump();
      RCLCPP_INFO(this->get_logger(),"current speed:%s",request.parameter.c_str());     

    }
    request.header.identity.api_id = api_id;
    request_pub_->publish(request);

   }
};

int main(int argc,char const *argv[])
{
   // 2.初始化ros2客户端
   rclcpp::init(argc,argv);
   // 4.调用spin函数,传入自定义类对象指针
   rclcpp::spin(std::make_shared<Twist_Bridge>());
   // 5.释放资源
   rclcpp::shutdown();
   return 0;
}

3. 编译运行

输入以下指令编译运行桥接程序:

cd unitree_go2_ws
colcon build --packages-select go2_twist_bridge
. install/setup.bash
ros2 run go2_twist_bridge twist_bridge

然后Ctrl+Alt+T新建一个终端,运行

ros2 run teleop_twist_keyboard teleop_twist_keyboard

就可以实现用人ROS2内置的键盘来操控机器🐶运动了,效果最后展示。

3. python实现

1. 准备

先在~/unitree_go2_ws/src/base/go2_twist_bridge_py/go2_twist_bridge_py目录下创建sport_model.py文件,用来存放机器🐶对应的编号和动作。

sport_model.py文件编写代码如下:

#定义常量字典
ROBOT_SPORT_API_IDS = {
    "DAMP":1001,                                #阻尼控制
    "BALANCESTAND":1002,                        #平衡站立
    "STOPMOVE":1003,                            #停止运动
    "STANDUP":1004,                             #站立
    "STANDDOWN":1005,                           #站立下降
    "RECOVERYSTAND":1006,                       #恢复站立
    "EULER":1007,                               #欧拉角控制
    "MOVE":1008,                                #移动
    "SIT":1009,                                 #坐下
    "RISESIT":1010,                             #从坐下恢复站立
    "SWITCHGAIT":1011,                          #切换步态
    "TRIGGER":1012,                             #触发
    "BODYHEIGHT":1013,                          #身体高度调整
    "FOOTTRAISEHEIGHT":1014,                    #脚步抬起高度调整
    "SPEEDLEVEL":1015,                          #速度级别调整
    "HELLO":1016,                               #打招呼
    "STRETCH":1017,                             #伸懒腰
    "TRAJECTORYEOLLOW":1018,                    #轨迹跟随
    "CONTINUOUSGAIT":1019,                      #连续步态
    "CONTENT":1020,                             #内容
    "WALLOW":1021,                              #打滚
    "DANCE1":1022,                              #舞蹈1
    "DANCE2":1023,                              #舞蹈2
    "GETBODYHEIGHT":1024,                       #获取身体高度
    "GETFOOTRAISEHEIGHT":1025,                  #获取脚部抬起高度
    "GETSPEEDLEVEL":1026,                       #获取速度级别
    "SWITCHJOYSTICK":1027,                      #切换操纵杆
    "POSE":1028,                                #姿态
    "SCRAPE":1029,                              #刮擦
    "FRONTFLIP":1030,                           #前空翻
    "FRONTJUMP":1031,                           #前跳
    "FRONTPOUNCE":1032,                         #前扑
}

定义了机器狗的各种动作及编号。

2.编写思路

随后在~/unitree_go2_ws/src/base/go2_twist_bridge_py/go2_twist_bridge_py目录下打开twist_bridge.py文件进行主程序的编写。

要实现消息桥接,就是要创建订阅方和发布方,这要用到ROS2的两个函数:create_publisher和create_subscription。

        #    1.创建一个Request发布对象;
        self.request_pub_ = self.create_publisher(Request,"/api/sport/request",10)
        #    2.创建一个twist订阅对象;
        self.twsit_sub_ = self.create_subscription(Twist,"cmd_vel",self.twist_to_request,10)

在回调函数twist_to_request中进行消息的桥接:

    def twist_to_request(self,twist:Twist):
        request = Request()
        api_id  = ROBOT_SPORT_API_IDS["BALANCESTAND"]
        #转换,只要x,y线速度和z的角速度
        x = twist.linear.x
        y = twist.linear.y
        z = twist.angular.z   
        #设置api_id
        if x!=0 or y!=0 or z!=0:
            api_id = ROBOT_SPORT_API_IDS["MOVE"]
            #设置参数
            js = {"x":x,"y":y,"z":z}
            request.parameter = json.dumps(js)
        request.header.identity.api_id = api_id
        self.request_pub_.publish(request)

然后再完成剩余程序的编写。

最终的twist_bridge.py文件:

'''
需求:
流程:
   需求:订阅twist消息,并将之转换为go2所需的request消息以控制机器狗运动
   流程:
   1.创建一个Request发布对象;
   2.创建一个twist订阅对象;
   3.在回调函数中实现消息的转换及发布
'''

#1.导包
import rclpy
from rclpy.node import Node
from .sport_model import ROBOT_SPORT_API_IDS
from geometry_msgs.msg import Twist
from unitree_api.msg import Request
import json

# 3.自定义节点类:
class Twist_Bridge(Node):
    def __init__(self):
        super().__init__("twist_bridge_node_py")
        self.get_logger().info("TwistBridge创建!,可将twist消息转换成urequest消息")
        #    1.创建一个Request发布对象;
        self.request_pub_ = self.create_publisher(Request,"/api/sport/request",10)
        #    2.创建一个twist订阅对象;
        self.twsit_sub_ = self.create_subscription(Twist,"cmd_vel",self.twist_to_request,10)

    def twist_to_request(self,twist:Twist):
        request = Request()
        api_id  = ROBOT_SPORT_API_IDS["BALANCESTAND"]
        #转换,只要x,y线速度和z的角速度
        x = twist.linear.x
        y = twist.linear.y
        z = twist.angular.z   
        #设置api_id
        if x!=0 or y!=0 or z!=0:
            api_id = ROBOT_SPORT_API_IDS["MOVE"]
            #设置参数
            js = {"x":x,"y":y,"z":z}
            request.parameter = json.dumps(js)
        request.header.identity.api_id = api_id
        self.request_pub_.publish(request)

def main():
# 2.初始化ros2
    rclpy.init()

# 4.调用spin函数,传入自定义类对象指针
    rclpy.spin(Twist_Bridge())
# 5.释放资源
    rclpy.shutdown()

if __name__ == '__main__':
    main()

对比不难发现,python语法比c++简洁。

3. 编译运行

输入以下指令编译运行桥接程序:

cd unitree_go2_ws
colcon build --packages-select go2_twist_bridge_py
. install/setup.bash
ros2 run go2_twist_bridge_py twist_bridge

然后Ctrl+Alt+T新建一个终端,运行

ros2 run teleop_twist_keyboard teleop_twist_keyboard

就可以实现用ROS2内置的键盘来操控机器🐶运动了,效果也最后展示。

4. 结果展示

桥接消息的机器狗

下午阳光明媚,机器🐶身上缕缕金丝,用ROS2内置键盘操控,比较放荡形骸哈哈哈哈,这和第2次blog用自制键盘操控实现的效果其实差不多。

这篇blog就这样了,下次再见!!!

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值