STM32 ROS控制器底层代码讲解

news2024/11/27 8:23:00

本文主要对控制器底层代码的整天架构进行讲解。

控制器由两部分组成一部分是BootLoader,另一部分是APP;BootLoader主要用于固件升级,APP则作为应用程序。

BootLoader的地址为:0x8000000~0x8008000

App的地址为:0x8010000~0x8FFFFFF

参数保存地址为:0x8008000~:0x8010000

BootLoader使用的是裸机,主要是对升级标志位进行判断然后对运行区进行跳转和通过UART1、UART2把APP固件写入,目前只支持通过UART1(PA9,PA10)、UART2(PD5,PD6),对于首次更新则只能使用UART1或者UART2对APP进行写入,BootLoader代码目录如下图所示。BootLoader代码目前是不开源的,由博主进行维护,大家在使用中遇到问题博主会及时进行测试修改和同步更新烧入文件。

APP是由FreeRTOS作为嵌入式系统,使用rosserial(roslib库)和ROS1进行交互,使用标准的接口协议,让ROS的开发者不需要去了解底层的通行层,只需要关注底层控制器发布和订阅的节点名称即可,使用rosserial还可以使用ros的参数服务器,在ROS层即可对底层的一些开放参数进行修改(使用方法参考博主的另一篇博客 通过ROS修改控制器参数:https://blog.csdn.net/qq_36349536/article/details/126515022?spm=1001.2014.3001.5501);除了通信部分控制器编写了D2(差速两轮)、D4(差速四轮)、T2、T4、O3、O4、M4、A1、A2等车型的运动学解算,PS2手柄驱动代码,MPU6050、MPU9250、MPU6500、GY85等IMU驱动代码、位置式PID代码,代码使用函数指针对应用层和驱动层进行了标准接口分离解耦每个文件的相互依赖,让用户可以更好的进行二次开发。下面主要对用户在二次开发过程中可能遇到的一些疑惑进行说明。

1、初始化入口讲解,初始化入口是用户任务代码和驱动代码的初始化位置,因为激活校准函数是授权使用的所以对main函数进行了封装,留出了任务代码入口和驱动代码入口,如下图所示:

用户在开发过程中任务初始化需要放到InitTaskArr[]中,驱动代码需要放到InitSetupArr[]中,InitLoopArr[]是作为运行逻机的预留接口,没用,用户无需对该数组进行操作。

2、roslib库的使用讲解,roslib库是rosserial生成过来用于嵌入式设备的一个库,具体的打包过程和怎么使用自定义msgs可以参考另一篇博客;roslib库主要是提供了3个函数接口,read(),wirte(),time(),我们只需要去实现这三条函数即可,接口函数是在stm32Hardware.h中。

我们需要先确定我们使用的STM32串口或者USB_VCP的收发函数是否正常,然后把实现好的函数在stm32Hardware.h文件中对应的位置加入即可。读写函数实现完成,我们还需要对这个库进行初始化

第一步创建一个nh句柄

ros::NodeHandle nh;

第二步对句柄进行初始化

nh.initNode();

第三步创建发布的msgs,订阅的回调函数,发布的节点名称和订阅的节点名称

starrobot_msgs::Imu        raw_imu_msg;
starrobot_msgs::Velocities raw_vel_msg;
starrobot_msgs::analog     raw_analog_msg;
starrobot_msgs::info_show  raw_info_show_msg;
starrobot_msgs::Sonar      raw_sonar_msg;
starrobot_msgs::Relaid     raw_Relaid_msg;
starrobot_msgs::Upgrader   raw_Up_msg;

void pid_callback( const starrobot_msgs::PID& pid);
void command_callback(const geometry_msgs::Twist& cmd_msg);
void swerve_callback(const std_msgs::Int16& swerve_servo);
void servo_callback(const starrobot_msgs::Servo& servo_data);
void relaid_callback(const starrobot_msgs::Relaid& data_t);
void upgrader_callback(const starrobot_msgs::Upgrader& data_t);
void handleParam_callback(const std_msgs::Int8& data_t);

ros::Subscriber<geometry_msgs::Twist> cmd_sub("cmd_vel", command_callback);
ros::Subscriber<starrobot_msgs::PID>  pid_sub("pid", pid_callback);
ros::Subscriber<std_msgs::Int16> swerve_sub("initial_angle",swerve_callback); 
ros::Subscriber<starrobot_msgs::Servo>servo_sub("servo",servo_callback);
ros::Subscriber<starrobot_msgs::Relaid>relaid_sub("setRelaid",relaid_callback); 
ros::Subscriber<std_msgs::Int8>handleParam_sub("handleParam",handleParam_callback); 
ros::Subscriber<starrobot_msgs::Upgrader>upgrader_sub("PubUpgrade",upgrader_callback); 

ros::Publisher raw_vel_pub("raw_vel", &raw_vel_msg);
ros::Publisher raw_imu_pub("raw_imu", &raw_imu_msg);
ros::Publisher raw_battery_pub("raw_analog", &raw_analog_msg);
ros::Publisher raw_info_show_pub("info_show", &raw_info_show_msg);
ros::Publisher raw_sonar_pub("sonar", &raw_sonar_msg);
ros::Publisher raw_relaid_pub("getRelaid", &raw_Relaid_msg);
ros::Publisher raw_upgrader_pub("SubUpgrade", &raw_Up_msg);

第四步对发布和订阅的节点名称进行注册

    nh.subscribe(cmd_sub);
    nh.subscribe(pid_sub);
    nh.subscribe(servo_sub);
    nh.subscribe(swerve_sub);
    nh.subscribe(relaid_sub);
    nh.subscribe(handleParam_sub);
    nh.subscribe(upgrader_sub);
    
    nh.advertise(raw_vel_pub);
    nh.advertise(raw_imu_pub);
    nh.advertise(raw_battery_pub);
    nh.advertise(raw_info_show_pub);
    nh.advertise(raw_sonar_pub);
    nh.advertise(raw_relaid_pub);
    nh.advertise(raw_upgrader_pub);

第五步在循环中进行等待即可完成roslib库的使用,大家可以根据自己的实际情况对节点进行修改增加和删除

for(;;){
        while (!nh.connected()){    
            nh.spinOnce();
            hStr_t = 1;
            vTaskDelay(300);
        }
        if(hStr_t){
            watchdogInit(600);
            initParam(&nh);
            ShowHardwareStr();
            vTaskDelay(100);
            showParamSet(&nh);
            setBaseClassInit();
            setBaseClassPid();
            imuDriveInit();
            configParam.IsRosNodePub = 1;
            setMotorDebugMaxRpm();
            hStr_t = 0;
            watchdogInit(WATCHDOG_RESET_MS);
        }
        if(IsParamShow){
            IsParamShow = 0;
            ShowHardwareStr();
            showParamSet(&nh);
        }
        nh.spinOnce();
        vTaskDelay(100);
    }

3、如何编写一个订阅节点,订阅节点第一步是需要编写一个回调函数,用于roslib库当接收到订阅的话题时进行一个响应;第二步需要编写订阅的节点名称和消息类型;第三步则是需要对订阅的节点进行注册。

void command_callback(const geometry_msgs::Twist& cmd_msg);//函数声明
ros::Subscriber<geometry_msgs::Twist> cmd_sub("cmd_vel", command_callback);//订阅的话题名称和消息类型
//回调函数的编写
void command_callback(const geometry_msgs::Twist& cmd_msg){
    stcTwist str_p;
    str_p.linearVelX = cmd_msg.linear.x;
    str_p.linearVelY = cmd_msg.linear.y;
    str_p.angularVelZ = cmd_msg.angular.z;
    if(false ==twistlinkSendPacket( &str_p))
        nh.loginfo("twistlinkSendPacket error");
}
nh.subscribe(cmd_sub);//回调函数的注册

4、如何编写一个发布节点,发布节点第一步需要创建发布的消息类型变量;第二步需要编写发布的节点名称和消息类型;第三步是编写一个函数对发布节点的消息类型数据进行处理和发布;第四步是需要对发布的节点进行注册。

starrobot_msgs::Imu        raw_imu_msg; //创建消息类型变量
ros::Publisher raw_imu_pub("raw_imu", &raw_imu_msg); //发布的节点的名称和消息类型
//对发布节点的消息类型数据处理和发布
void PublishImu(){
    stcATBuff sBatBuffr_p;
    if((configParam.IsRosNodePub != 0) && (upStatus==0)){
        switch(configParam.IMUType){   
            case 1:
                raw_imu_msg.linear_acceleration = imu_gy85.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_gy85.readGyroscope();
                raw_imu_msg.magnetic_field = imu_gy85.readMagnetometer();
            break;
            case 2:
            case 3:
                raw_imu_msg.linear_acceleration = imu_mpu9250.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu9250.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu9250.readMagnetometer();
            break;
            case 4:
            case 5:
                raw_imu_msg.linear_acceleration = imu_mpu6050.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu6050.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu6050.readMagnetometer();
            break;
            case 6:
            case 7:
                raw_imu_msg.linear_acceleration = imu_mpu6500.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu6500.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu6500.readMagnetometer();
            break;
            default:break;
        }
        raw_imu_pub.publish(&raw_imu_msg);
    }    
} 

4、如何定义自己的硬件,底层代码是做了接口解耦合的,硬件定义的代码在HwConfig/hw_xxx.c文中,端口定义则在HwConfig/hw_xxx.h中大家可以根据自己的需要对HwConfig/hw_xxx.h文件里的端口进行修改,HwConfig/hw_xxx.c里面的函数名称最好不要修改,否则需要自己同步把调用该函数的地方全部进行修改才不会报错

5、如何编写一个任务,编写任务第一步是需要写一个任务函数;第二步是创建任务;第三步是把创建的任务初始化函数放到初始化函数入口数组中。

static void SysLedBeep_HTask(void *pvParameters){          //任务执行函数  
    for( ;; ){
        STARBOT_LED_RUN_Toggle();      
        vTaskDelay(300);
    }
}
void SysLedBeep_TaskInit(void){    
    LED_Init();
// 创建任务
    xTaskCreate(SysLedBeep_HTask,(const char *)"SysLedBeepHTask",128, NULL,LedBeep_Pri, NULL);
}
PrivateFun InitTaskArr[]={
    //函数名称
    SysLedBeep_TaskInit,//把任务初始化添加到任务初始化函数入口数组中
//    #ifndef STM32F10X_HD
    ros_task_create,
    OledShow_TaskInit,
    MoveBase_TaskInit,
    Terminal_TaskInit,
    usbLink_TaskInit,
    uart1_TaskInit,
    hcBle_TaskInit,
    uart3Link_TaskInit,
    uart4Link_TaskInit,
    uart5Link_TaskInit,
    ps2Manage_TaskInit,
    timeout_TaskInit,
    canLink_TaskInit,
    ErrorManage_TaskInit,
    Ws2812Run_TaskInit,
//    #endif
    NULL//这个NULL不能删除
};

欢迎大家进群一起交流学习:129923584

相关视频讲解链接:https://www.bilibili.com/video/BV1bD4y1g7V5/

博文使用的开发板购买链接:https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-21142386790.2.c0086177uv5IlA&id=606445592295

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/372976.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基本密码技术

AESAES取代DES&#xff0c;是一种对称加密技术&#xff0c;分为AES-128/192/256, 其分组长度固定为128b&#xff0c;若最后一个分组长度不够&#xff0c;需要补全至128b长度。所支持的秘钥长度分别为128b/192b/256b.分组密码模式AES是对明文进行分组之后逐块进行加密&#xff0…

2023年软考高级网络规划设计师

网络规划设计师是软考高级考试科目之一&#xff0c;也是比较难的科目&#xff0c;据官方数据统计网规每年的通过率很低&#xff0c;而且每年只有下半年11月份考一次&#xff0c;如果是直接裸考&#xff0c;估计很悬哦~ 但是你参加考试获得证书的过程就是一个学习网络规划系统知…

【python学习笔记】:SQL常用脚本(一)

1、行转列的用法PIVOT CREATE table test (id int,name nvarchar(20),quarter int,number int) insert into test values(1,N苹果,1,1000) insert into test values(1,N苹果,2,2000) insert into test values(1,N苹果,3,4000) insert into test values(1,N苹果,4,5000) insert…

本周大新闻|索尼PS VR2立项近7年;传腾讯将引进Quest 2

本周大新闻&#xff0c;AR方面&#xff0c;传立讯精密开发苹果初代AR头显&#xff0c;第二代低成本版将交给富士康&#xff1b;iOS 16.4代码曝光新的“计算设备”&#xff1b;EM3推出AR眼镜Stellar Pro&#xff1b;努比亚将在MWC2023推首款AR眼镜。VR方面&#xff0c;传闻腾讯引…

编辑器、论坛、评论列表图文混排的一些思路

好久没写帖子了&#xff0c;今天写一个吧 众所众知从用户那里拿到的数据直接innerHtml插入 有被xss攻击的风险&#xff0c;所以一般会转义,拿csdn编辑文章的来举个例子 通过前端转义 ‘>’后,传给后台&#xff1b;这里title没有用innerHtml而是文本所以不需要转义。 前端请…

SerenityOS 操作系统类 Unix 操作系统

创建于2018年的SerenityOS是一个类似Unix的操作系统&#xff0c;但是带有图形化界面&#xff0c;适合X86台式计算机&#xff0c;&#xff0c;其界面类似90 年代的Win98/NT。几乎由一个人完成额操作系统。这几天其Web浏览器通过了 Acid3 浏览器。 Kernel features 具有抢占式多…

计算机网络笔记、面试八股(四)—— TCP连接

本章目录4. TCP连接4.1 TCP报文段的首部格式4.2 TCP连接如何保证可靠4.3 ARQ协议4.3.1 停止等待ARQ协议4.3.1.1 无差错情况4.3.1.2 出现差错情况4.3.1.3 确认丢失和确认迟到4.3.2 连续ARQ协议4.3.2.1 流水线传输4.3.2.2 累积确认4.3.2.3 滑动窗口协议4.3.3 停止等待ARQ和连续AR…

java面试题-JVM内存结构

整体结构&#xff1a;1.说说JVM内存整体的结构&#xff1f;线程私有还是共享的&#xff1f;JVM&#xff08;Java Virtual Machine&#xff09;内存可以分为以下几个部分&#xff1a;程序计数器&#xff08;Program Counter Register&#xff09;&#xff1a;是线程私有的&#…

JUC并发编程(二)

一、过时方法 一些不推荐使用的方法已经过时&#xff0c;容易破坏同步代码块&#xff0c;使对象的锁得不到释放&#xff0c;进而造成线程死锁 二、守护线程 默认情况下&#xff0c;Java 进程需要等待所有线程都运行结束&#xff0c;才会结束。有一种特殊的线程叫做守护线程…

spring中BeanFactory 和ApplicationContext

在学习spring的高阶内容时&#xff0c;我们有必要先回顾一下spring回顾spring1.什么是springspring是轻量级的&#xff0c;指核心jar包时很小的&#xff1b;非侵入式的一站式框架(数据持久层&#xff0c;web层&#xff0c;核心aop)&#xff0c;为了简化企业级开发。核心是IOC&a…

Python自动化测试实战篇(6)用PO分层模式及思想,优化unittest+ddt+yaml+request登录接口自动化测试

这些是之前的文章&#xff0c;里面有一些基础的知识点在前面由于前面已经有写过&#xff0c;所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇&#xff08;1&#xff09;读取xlsx中账户密码&#xff0c;unittest框架实现通过requests接口post登录网站请求&…

计算及网络第一章

计算机网络-第一章 概述 文章目录计算机网络-第一章 概述计算机网络在信息时代中的作用互联网概述网络的网络互联网的组成网络的边缘部分互联网的核心部分电路交换的主要特点分组交换的主要特点计算机网络的类别几种不同类别的计算机网络计算及网络的性能计算机网络的性能指标计…

面向对象之-接口鉴权

1 需求 1.1 需求背景 为了保证接口调用的安全性&#xff0c;我们希望设计实现一个接口调用鉴权功能&#xff0c;只有经过认证之后的系统才能调用我们的接口&#xff0c;没有认证过的系统调用我们的接口会被拒绝。 2 需求分析 2.1 基础分析 对于如何做鉴权这样一个问题&…

配置 Haproxy 负载均衡群集

配置 haproxy 负载均衡群集 &#x1f3c6;荣誉认证&#xff1a;51CTO博客专家博主、TOP红人、明日之星&#xff1b;阿里云开发者社区专家博主、技术博主、星级博主。 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#…

Android 基础知识4-3.3 Button(按钮)与ImageButton(图像按钮)详解

一、引言 今天给大家介绍的Android基本控件中的两个按钮控件&#xff0c;Button普通按钮和ImageButton图像按钮&#xff1b; 其实ImageButton和Button的用法基本类似&#xff0c;至于与图片相关的则和后面ImageView相同&#xff0c;所以本节 只对Button进行讲解&#xff0c;另外…

MySQL进阶之锁

锁是计算机中协调多个进程或线程并发访问资源的一种机制。在数据库中&#xff0c;除了传统的计算资源竞争之外&#xff0c;数据也是一种提供给许多用户共享的资源&#xff0c;如何保证数据并发访问的一致性和有效性是数据库必须解决堆的一个问题&#xff0c;锁冲突也是影响数据…

Neo4j列表函数

使用列表 标量列表函数 size() 函数返回列表中的元素的数量 MATCH (p:Person)-[:ACTED_IN]->(m:Movie) WITH p, collect (m.title) AS MovieTitles WITH p, MovieTitles, size(MovieTitles) AS NumMovies WHERE NumMovies > 20 RETURN p.name AS Actor, NumMovies, Movie…

浙大PTA拼题A读者验证码刷题页面、PTA免费刷题页面(不需要读者验证码)

有一个B站号李桥桉&#xff0c;很多年前讲过PTA里的一些题目的解法。近两年有好多同学反馈&#xff0c;需要读者码才能进行答题&#xff0c;不然只能免费注册、看题、编写代码&#xff0c;就是不能提交代码(大受震撼)。 咱就是说&#xff0c;会不会是同学们找错页面了&#xff…

【数据库】第十二章 数据库管理

第12章 数据库管理 数据库的物理存储 关于内存、外存、磁盘、硬盘、软盘、光盘的区别_Allenzyg的博客-CSDN博客_磁盘和硬盘的区别 数据库记录在磁盘上的存储 定长&#xff0c;变长跨块&#xff0c;非跨快 文件的组织方方法&#xff1a; 无序记录文件(堆文件heap或pile file…

eddsa 算法

信息安全课程设计&#xff1a;eddsa 算法 一、项目要求 使用 C 语言开发&#xff1b;可以实现公私钥生成、签名、认证&#xff1b;只需要手动输入明文&#xff0c;代码会自动生成公私钥、签名、认证&#xff1b;记录公私钥生成、签名、认证的时间&#xff1b;在 VS 上运行&am…