【ROS2】学习笔记(1)

ROS2dashing学习笔记(1)

ROS2增加serial头文件

介绍如何增加头文件来声明类或函数
安装ros-dashing-serial-driver无果后,只好通过添加第三方写好的文件,将话题订阅发布的数据通过串口发送出去
参照https://www.ncnynl.com/archives/202205/5197.html对文件进行修改增加串口通信功能

新建功能包

// An highlighted block
cd ~/dev_ws/src
ros2 pkg create --build-type ament_cmake cpp_header
cd ~/dev_ws/src/cpp_header/include/cpp_header
touch minimal_publisher.hpp

在minimal_publisher.hpp添加以下代码

#ifndef PUB_SERIALPORT_HPP
#define PUB_SERIALPORT_HPP

#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>
#include  <sys/types.h>
#include  <sys/signal.h>
#include  <sys/stat.h>
#include  <fcntl.h>
#include  <errno.h>
#include  <limits.h>
#include  <string.h>


enum sp_dev_e
{
    COM0 = 0,
    COM1,
    COM2,
    COM3,
    ttyUSB0,
    ttyUSB1,
    ttyUSB2
};
class Serial
{
    public:
    Serial();
    ~Serial();

    int OpenPort(int index);
    int SetPara(int serialfd,int speed=2,int databits=8,int stopbits=1,int parity=0);
    int WriteData(int fd,const char *data,int datalength);
    int ReadData(int fd,unsigned char *data,int datalength);
    void ClosePort(int fd);
    int BaudRate( int baudrate);
};


#endif // PUB_SERIALPORT_HPP

进入src目录,新建文件minimal_publisher.cpp

cd ~/dev_ws/src/cpp_header/src
touch minimal_publisher.cpp

在minimal_publisher.cpp添加

#include "cpp_header/minimal_publisher.hpp"

#include  <termios.h>

Serial::Serial()
{

}
Serial::~Serial()
{

}

int Serial::BaudRate( int baudrate)
{
    if(7 == baudrate)
        return B460800;
    else if(6 == baudrate)
        return B115200;
    else if(5 == baudrate)
        return B57600;
    else if(4 == baudrate)
        return B38400;
    else if(3 == baudrate)
        return B19200;
    else if(2 == baudrate)
        return B9600;
    else if(1 == baudrate)
        return B4800;
    else if(0 == baudrate)
        return B2400;
    else
        return B9600;

}

int Serial::SetPara(int serialfd,int speed,int databits , int stopbits ,int parity )
{
    struct termios termios_new;
    bzero( &termios_new, sizeof(termios_new));//等价于memset(&termios_new,sizeof(termios_new));
    cfmakeraw(&termios_new);//就是将终端设置为原始模式
    termios_new.c_cflag=BaudRate(speed);
    termios_new.c_cflag |= CLOCAL | CREAD;
  //  termios_new.c_iflag = IGNPAR | IGNBRK;

    termios_new.c_cflag &= ~CSIZE;
    switch (databits)
    {
    case 0:
        termios_new.c_cflag |= CS5;
        break;
    case 1:
        termios_new.c_cflag |= CS6;
        break;
    case 2:
        termios_new.c_cflag |= CS7;
        break;
    case 3:
        termios_new.c_cflag |= CS8;
        break;
    default:
        termios_new.c_cflag |= CS8;
        break;
    }

    switch (parity)
    {
    case 0:  				//as no parity
        termios_new.c_cflag &= ~PARENB;    //Clear parity enable
      //  termios_new.c_iflag &= ~INPCK; /* Enable parity checking */  //add by fu
        break;
    case 1:
        termios_new.c_cflag |= PARENB;     // Enable parity
        termios_new.c_cflag &= ~PARODD;
        break;
    case 2:
        termios_new.c_cflag |= PARENB;
        termios_new.c_cflag |= ~PARODD;
        break;
    default:
        termios_new.c_cflag &= ~PARENB;   // Clear parity enable
        break;
    }
    switch (stopbits)// set Stop Bit
    {
    case 1:
        termios_new.c_cflag &= ~CSTOPB;
        break;
    case 2:
        termios_new.c_cflag |= CSTOPB;
        break;
    default:
        termios_new.c_cflag &= ~CSTOPB;
        break;
    }
    tcflush(serialfd,TCIFLUSH); // 清除输入缓存
   tcflush(serialfd,TCOFLUSH); // 清除输出缓存
    termios_new.c_cc[VTIME] = 1;   // MIN与 TIME组合有以下四种:1.MIN = 0 , TIME =0  有READ立即回传 否则传回 0 ,不读取任何字元
    termios_new.c_cc[VMIN] = 1;  //    2、 MIN = 0 , TIME >0  READ 传回读到的字元,或在十分之一秒后传回TIME 若来不及读到任何字元,则传回0
    tcflush (serialfd, TCIFLUSH);  //    3、 MIN > 0 , TIME =0  READ 会等待,直到MIN字元可读
    return tcsetattr(serialfd,TCSANOW,&termios_new);  //    4、 MIN > 0 , TIME > 0 每一格字元之间计时器即会被启动 READ 会在读到MIN字元,传回值或
}

int Serial::WriteData(int fd,const char *data, int datalength )//index 代表串口号 0 串口/dev/ttyAMA1 ......
{
    if(fd <0){ return -1;}
    int len = 0, total_len = 0;//modify8.
    for (total_len = 0 ; total_len < datalength;)
    {
        len = 0;
        len = write(fd, &data[total_len], datalength - total_len);
        printf("WriteData fd = %d ,len =%d,data = %s\n",fd,len,data);
        if (len > 0)
        {
            total_len += len;
        }
        else if(len <= 0)
        {
            len = -1;
            break;
        }
     }
       return len;
}

int Serial::ReadData(int fd,unsigned char *data, int datalength)
{
       if(fd <0){ return -1;}
       int len = 0;
       memset(data,0,datalength);

       int max_fd = 0;
       fd_set readset ={0};
       struct timeval tv ={0};

       FD_ZERO(&readset);
       FD_SET((unsigned int)fd, &readset);
       max_fd = fd +1;
       tv.tv_sec=0;
       tv.tv_usec=1000;
       if (select(max_fd, &readset, NULL, NULL,&tv ) < 0)
       {
               printf("ReadData: select error\n");
       }
       int nRet = FD_ISSET(fd, &readset);
      if (nRet)
       {
              len = read(fd, data, datalength);
       }
       return len;
}

void Serial::ClosePort(int fd)
{
    struct termios termios_old;
    if(fd > 0)
    {
        tcsetattr (fd, TCSADRAIN, &termios_old);
        ::close (fd);
    }
}

int  Serial::OpenPort(int index)
{
    char *device;
    struct termios termios_old;
    int fd;
    switch(index)
    {
    case COM0:  device="/dev/ttyS0";  break;
    case COM1:  device="/dev/ttyS1";  break;
    case COM2:  device="/dev/ttyS2";  break;
    case COM3:  device="/dev/pts/2";  break;
    case ttyUSB0:  device="/dev/ttyUSB0";  break;
    case ttyUSB1:  device="/dev/ttyUSB1";  break;
    case ttyUSB2:  device="/dev/ttyUSB2";  break;
    default: device="/dev/ttyAMA2"; break;
    }

    fd = open( device, O_RDWR | O_NOCTTY |O_NONBLOCK);//O_RDWR | O_NOCTTY | O_NDELAY   //O_NONBLOCK
    if (fd < 0)
    { return -1;}
    tcgetattr(fd , &termios_old);
    return fd;
}

进入src目录,新建文件minimal_publisher_node.cpp

cd ~/dev_ws/src/cpp_header/src
touch minimal_publisher_node.cpp

minimal_publisher_node.cpp中添加以下代码

#include "cpp_header/minimal_publisher.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "stdio.h"
#include "arraylist.h"
using namespace std;

int fp;

const char* constc = nullptr;
// char Msg[] = { '20' ,'22', '23' ,'4', '2', '12', '6', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '11', '4', '13', '10' };
int test1[] = {'0','3','-2'};
string tempStr1;

Serial sp;
class TopicSuscribe01 : public rclcpp::Node
{
public:
    TopicSuscribe01(std::string name) : Node(name)
    {
        RCLCPP_INFO(this->get_logger(),"我是%s,订阅话题为:/command.",name.c_str());
        command_subscribe_ = this->create_subscription<std_msgs::msg::String>("command",10,std::bind(&TopicSuscribe01::command_callback,this,std::placeholders::_1));
    }
private:
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr command_subscribe_;
    void command_callback(const std_msgs::msg::String::SharedPtr msg)
    {
        double speed = 0.0f;
        if(msg->data == "9999")
        {
            speed = 0.2f;
            RCLCPP_INFO(this->get_logger(),"收到的[%s]指令,发送速度%f",msg->data.c_str(),speed);
        
            constc = msg->data.c_str();
        }
        // constc = Msg;
        // cout<<constc<<endl;
       // char buf[10];
        //for(int i = 0;i<6;i++)
        //{
            
            //sprintf(buf,"%s",test1[i]);//linux没有itoa用sprintf代替
            //tempStr1+=buf;
        //}
        // tempStr1=test1;
        // cout << tempStr1;
        //constc = tempStr1.c_str();
        //cout << constc;
        sp.WriteData(fp,constc,10);
    }

};


int main(int argc, char **argv)
{
    /* code */
    rclcpp::init(argc, argv);
    /*产生一个Wang2的节点*/
    // auto node = std::make_shared<rclcpp::Node>("pub_serial");
    auto node = std::make_shared<TopicSuscribe01>("topic_subscribe_01");
    // 打印一句自我介绍
    // int fp;
    // Serial sp;
    sp.BaudRate(115200);
    fp = sp.OpenPort(COM3);
    if(fp>0){
      RCLCPP_INFO(node->get_logger(), "serial success!.");
    }
    
    // sp.WriteData(fp,"1024",10); RCLCPP_INFO(node->get_logger(), "serial success!.");
    }
    
    // cout<<fp<<endl;

    RCLCPP_INFO(node->get_logger(), "大家好,我是topic_subscribe_01.");
    /* 运行节点,并检测退出信号*/
    rclcpp::spin(node);
    rclcpp::shutdown();
    sp.ClosePort(fp);
    return 0;
    
    return 0;
}

编辑package.xml
在<buildtool_depend>ament_cmake</buildtool_depend>后增加

<depend>rclcpp</depend>
<depend>std_msgs</depend>

编辑 CMakelist.txt
在find_package(ament_cmake REQUIRED)后增加

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

再增加可执行文件,ros2 run能够调用的名称

add_executable(minimal_publisher_node src/minimal_publisher_node.cpp src/minimal_publisher.cpp) 
ament_target_dependencies(minimal_publisher_node rclcpp std_msgs)

增加可执行文件位置,ros2 run可以找到这个可执行文件

install(TARGETS
  minimal_publisher_node
  DESTINATION lib/${PROJECT_NAME})
ament_package()

编译运行

cd ~/dev_ws/
rosdep install -i --from-path src --rosdistro dashing -y
colcon build --symlink-install --packages-select cpp_header
source install/setup.bash
ros2 run cpp_header minimal_publisher_node 

话题名为/command,新建个话题发布数据,minimal_publisher_node节点就能订阅到数据并且通过串口发送出去了。

运行结果

请添加图片描述使用了虚拟串口pts/3和pts/6,由于每次新建虚拟虚拟串口对都不一样,因此根据实际情况修改minimal_publisher.cpp中最底部的COM口

参考:

ROS2与C++入门教程-增加头文件 :
https://www.ncnynl.com/archives/202205/5197.html
虚拟串口:
https://blog.csdn.net/qq_41742043/article/details/124931406?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166184976616781647594090%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166184976616781647594090&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v33_ecpm-15-124931406-null-null.142

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值