ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

松灵底盘ROS下CAN通讯调试实战:从接线到轮子转动的全流程

松灵底盘ROS下CAN通讯调试实战:从接线到轮子转动的全流程 拿到松灵底盘的第一天我对着那根CAN线愣了十分钟。网口、串口都好理解CAN是什么为什么不上网线更麻烦的是ROS下的CAN通讯调试跟普通Linux开发完全是两个路子命令多、概念杂网上资料还东一块西一块。这篇就是把我从“接上线”到“轮子转起来”的全过程捋一遍重点是两条线一是松灵机器人驱动所需的调用包去哪找、怎么编译二是ROS下CAN通讯从物理链路到话题收发的操作步骤。适合手上有松灵底盘、或者刚入坑移动机器人底盘开发的兄弟看完能少踩我踩过的那堆坑。标题说“ROS下的CAN通讯调试”很多人以为这俩是一回事其实得拆成两个独立问题来解。ROS管的是上层节点和话题CAN管的是底层数据搬运中间靠松灵官方提供的驱动调用包搭桥。整个调试的本质就是先确认CAN总线上能收到正确报文再确认驱动包能把报文转成ROS话题最后确认ROS话题能反过来驱动底盘运动。任何一层出问题表象都是“车不动”或者“数据不对”这时候如果不懂分层排查就会陷入瞎改代码的泥潭。这篇文章我会按调试顺序来写先讲为什么松灵选CAN、整体调试怎么分层再讲CAN通讯的核心原理和底盘的报文模型接着是环境准备和调用包编译然后是完整的实操步骤最后把我遇到的典型问题整理成排查手册。整篇偏实战命令我都会给全但你拿到手上时务必对着自己那款底盘的官方手册核对波特率和帧ID别盲抄。1. 调试前的整体规划与方案选型为什么先打通CAN1.1 松灵底盘为什么用CAN总线先解决一个最基础的疑问为什么这些移动机器人底盘不用网线或者串口非要用CAN原因其实不复杂。底盘内部有电机驱动器、转向控制器、电池管理单元有的还挂着IMU和急停模块这些部件分布在车体各处工作环境是强震动、强电磁干扰的。CAN总线是差分信号传输抗干扰能力强而且采用多主发送的仲裁机制多个控制器之间可以直接通信不需要主机轮询。对比一下就清楚了通讯方式抗干扰性实时性布线复杂度典型应用串口UART较差一般简单传感器点对点RS485中中中工业总线以太网中中复杂大数据量传输CAN强高简单车载/机器人松灵这种AGV底盘轮子电机、转向电机、刹车模块都在底盘内部用CAN把整车控制器和驱动板串成一个网络一根双绞线就能搞定比走网线简单可靠得多。所以说这不是“坚持用老技术”而是CAN在车载这个场景下确实是最合适的方案。1.2 调试全程分层拆解从物理链路到上层节点我在实际调试中把整个CAN通讯问题分成四层这个分层帮我省了大量时间也推荐给你第一层是物理链路包括CAN线有没有接对、CAN_H和CAN_L有没有接反、波特率是否一致、有没有接终端电阻、是否共地。这一层的典型表现是“完全没有数据”或者偶发丢帧。第二层是数据链路包括帧ID、DLC、数据字节顺序和校验方式典型表现是“能收到数据但是解析出来是乱的”。第三层是应用层也就是松灵官方驱动调用包这个黑盒它负责把CAN原始报文转成ROS话题或者把ROS话题转回CAN报文。第四层是整机验证验证cmd_vel话题能不能真的让轮子按预期转。分层有一个直接好处每一层都有对应的独立工具去验证。物理链路用万用表和can-utils里的candump数据链路用cansend发固定报文再检查回包应用层用rostopic echo查看话题数据整机验证就靠人眼盯着轮子了。把这四层搞清楚遇到问题你不会慌因为你至少知道问题不在自己的代码里。1.3 驱动调用包选型官方包还是通用包松灵底盘在ROS下的驱动方案主流有两条路。一条是直接用松灵官方维护的调用包ROS1对应agilex_ros、ROS2对应agilex_ros2里面有各个系列底盘的驱动节点。另一条是通用的ros_canopen/canopen_chain这类包自己写协议映射文件。两条路的取舍很简单。如果你是想快速跑通底盘、马上做上层导航或感知算法那直接用官方包这是最稳的方案。官方包已经把底盘协议封装好了launch文件一拉起来就能用还自带了URDF模型和遥控示例。但封装的代价是黑盒一旦协议对不上你很难从代码层面定位问题。我自己第一次调的时候就是官方包跑起来但轮子不动官方包里找不到原因。用通用ros_canopen包的好处是整套协议映射都是你自己写的每一帧数据你都门儿清调试时能更快定位是协议解析还是硬件问题。但代价是你得把底盘协议表吃透起步成本高不少。我的建议是两条腿走路先用官方包把底盘跑起来证明链路没问题再去读官方包的源码结合协议表理解它的报文处理逻辑这样出现问题你能绕开黑盒直接查根本原因。2. CAN通讯原理与松灵底盘报文模型先搞懂对讲机规则2.1 CAN总线的工作机制与人话版类比CAN总线是个什么玩意我教你一个很好记的类比它就像一个单位里大家共用的对讲机频道谁都能说话但同一时刻只允许一个人的声音发出去。如果两个人同时按住PTT按键系统会让优先级高的人先说另一个人自动退让等频道空闲了再重发。这个“优先级”就是CAN报文头里的帧IDID数值越小优先级越高。上报健康档位的节点比如电池电压检测优先级就低而控制类和急停类报文优先级必须最高。否则一旦总线上流量大了控制指令被挤到后面机器人反应延迟那可是会出事的。从协议格式上CAN报文分标准帧和扩展帧标准帧是11位ID扩展帧是29位ID。松灵底盘大多用的是标准帧。一帧标准CAN报文由ID、DLC数据长度、最多8字节数据和校验机制组成。调试时用candump看到的每一行就是一个CAN帧你看到的比如can0 141 [8] 00 00 00 00 00 00 00 00意思是在can0总线上收到一个ID为0x141的帧数据段长度是8字节后面的十六进制数就是数据。理解了这一行你就理解了CAN数据链路层的全部精髓。2.2 控制帧与状态帧一收一发两个方向松灵底盘的CAN通讯模型其实特别简单核心就两类报文上层发下去的“控制指令帧”和底盘回上来的“状态反馈帧”。控制指令帧一般包含目标线速度、目标角速度或转向角、使能位和控制模式等字段状态反馈帧一般包含当前轮速、转向角、电池电压、整车电流、运行模式和故障码等字段。底盘的中控板或电机驱动板按固定周期解析这些报文同时按固定周期上报状态。这里必须敲黑板不同系列底盘的控制帧ID、数据字段排列、字节序、缩放比例很可能都不一样。比如我调试的这款底盘控制指令帧ID是0x141线速度占前两个字节角速度占后两个字节带符号位小端排列还有一个单独的使能控制帧。但你手头的Hunter或者Bunker可能就是完全另一套定义。我见过不少兄弟拿到底盘不查协议表直接拿网上别人的报文去试结果底盘纹丝不动转头骂厂家。这真不该。正确的做法是把手头底盘的《CAN协议说明书》打开对照里面的字段定义表逐一确认ID、偏移、缩放因子再上手写代码或拼报文。协议表就是你的地图不看地图就开车不迷路才怪。2.3 波特率、终端电阻和总线质量物理层决定成败物理层的三个关键词波特率、终端电阻、共地。波特率是CAN通讯的“语速”通信双方必须完全一致才能对话。松灵底盘默认波特率常见的是500kbps也就是每秒500000位。波特率不一致的典型表现是candump监听半天什么都收不到或者收到的全是一堆错帧。判断波特率的方法是用candump加上-t参数如果完全没有数据就用ip -details link show can0查看当前设置逐一尝试常见波特率。终端电阻同样关键。CAN总线规范要求在总线两端各接一个120欧姆的终端电阻作用是在总线两端形成阻抗匹配吸收信号反射。缺了终端电阻总线上的信号会出现反射短距离可能看不出问题线一长就大量丢帧。很多USB转CAN卡内部自带120欧姆电阻有的还带跳线开关使用前务必确认。另外松灵底盘本身通常在内部已经处理好了电阻你只需要确保调试工具那一端匹配就行。共地问题是最隐蔽的。CAN物理层虽然是差分信号但两个节点之间如果地电位差太大依然会导致通讯异常。USB转CAN卡接电脑、底盘电池供电两边电源系统完全独立时地电位不定就容易出现偶发性无响应。最简单的处理是确认转CAN卡与底盘之间在电气上是共地的很多转接模块自带隔离就不会有这个问题。3. 调试环境准备从Ubuntu到松灵调用包3.1 系统环境与ROS版本匹配ROS的版本跟Ubuntu版本严格绑定这一步错了后面全白搭。先给你一张对应表做参考Ubuntu版本ROS1版本ROS2版本Ubuntu 18.04MelodicDashingUbuntu 20.04NoeticFoxy / GalacticUbuntu 22.04官方无ROS1Humble注意松灵官方调用包分ROS1和ROS2两套仓库名也不同动手前先确认你装的是哪套。如果你的工控机是Ubuntu 22.04那ROS1只能在Docker或容器里跑直接用ROS2的agilex_ros2更省心。如果还是18.04或20.04用ROS1的Melodic/Noetic完全没问题生态更成熟网上案例也多。3.2 鱼香ROS一键安装与基础工具ROS本身的安装流程比较长依赖多国内网络环境下全量安装也很考验耐心。如果你不想被这些步骤折磨可以直接用“鱼香ROS一键安装”这个社区方案我个人在虚拟机、实体机和若干工控机上实测过很多次稳定可靠。一条命令就能拉起来wget http://fishros.com/install -O fishros . fishros执行之后跟着交互菜单走选择安装ROS版本和桌面版或基础版它会自动配置软件源、装依赖、初始化rosdep。这里有个小提醒安装过程中会提示是否需要修改源、是否安装rosdep建议都选是。装完记得source一下环境变量echo source /opt/ros/noetic/setup.bash ~/.bashrc source ~/.bashrc手上用的版本小尾巴不同把noetic换成你实际的ROS版本名就行。另外还要用到can-utils这个工具集它包含cansend、candump、cansniffer等一系列CAN调试命令后面测试链路全靠它。安装命令一行搞定sudo apt install can-utils还有一个特别容易踩的坑权限。USB转CAN卡通常会被识别为串口设备或网络设备操作它们一般需要root权限或者把自己加进dialout组。不加权限的话后面一执行ip link set can0 up就会报“Operation not permitted”之类的错误。执行下面命令加组然后注销重新登录一次sudo usermod -aG dialout $USER3.3 获取并编译松灵底盘驱动调用包环境基本就绪后下一步就是拉取松灵官方调用包。ROS1一般用agilex_rosROS2用agilex_ros2。用git克隆到你的工作空间src目录下mkdir -p ~/agilex_ws/src cd ~/agilex_ws/src git clone https://github.com/agilexrobotics/agilex_ros.git克隆完成后先别急着编译先看一眼仓库结构和README。通常里面会按底盘系列分几个子包比如Scout系列对应scout_baseHunter系列对应hunter_base还有公共的权限和消息定义。确认你手里底盘的型号在支持列表里再开始编译。编译前建议先安装必要的依赖包否则catkin_make中途报找不到头文件会让人心态爆炸sudo apt install ros-noetic-teleop-twist-keyboard ros-noetic-ros-control ros-noetic-ros-controllers ros-noetic-gazebo-ros ros-noetic-rviz这里的ros-noetic-前缀对应ROS1 Noetic版本如果你用的是Melodic或者ROS2就替换成对应的包名。依赖装好后编译cd ~/agilex_ws catkin_make source devel/setup.bashROS2环境下的编译则一般用colcon build命令换成cd ~/agilex_ws colcon build source install/setup.bash编译过程中如果出现找不到某个包的错误八成就是依赖没装全根据错误提示搜索包名装依赖就行。我当时第一次编译就卡在一个地盘消息依赖上搜索之后一条apt install命令解决整个过程并不复杂。4. 实操全流程把CAN卡变成can0把can0变成能跑的车4.1 挂载CAN卡让系统识别总线的第一步先把USB转CAN卡插到工控机的USB口然后执行以下命令加载SocketCAN相关内核模块sudo modprobe can sudo modprobe can_raw sudo modprobe can_dev加载完模块后一般就能在系统里看到can0这个网络接口了。有时候CAN卡需要厂家提供的linux驱动和工具确认一下产品说明书里是否需要额外安装。用以下命令检查接口是否存在ip link show can0如果看到了can0接下来配置波特率并启动接口。以500kbps为例sudo ip link set can0 up type can bitrate 500000注意这里的500000是bitrate数值单位bps。有些板卡的驱动支持canfd指令略有不同。配置成功后再看一次状态ip -details link show can0输出中会包含state UP和bitrate 500000之类的信息说明物理接口已经就绪了。此时最好再确认一下当前工作正常dmesg | grep -i can如果输出中有can0相关的注册信息说明内核和驱动层面都认了这块卡。我踩过的坑是USB转CAN卡插在前置USB口供电不稳偶尔掉线后来换到后置USB口并禁用USB自动休眠才彻底解决。4.2 裸命令验证cansend与candump打通链路接口拉起来后第一个要做的事不是启动ROS节点而是用裸命令验证CAN链路通不通。打开两个终端一个监控总线数据candump can0另一个终端发送一帧测试报文cansend can0 001#00如果链路是通的并且底盘那边有节点在回复监控窗口会立刻出现来自底盘状态反馈的报文。这类报文的ID通常是底盘协议表里定义好的状态帧ID。如果监控窗口什么都没打出来先别怀疑代码回到第2.3节的问题清单去排查物理层波特率、接线、终端电阻、共地逐一确认。如果只想看看总线上有哪些ID在活动用cansniffer更直观它会自动按照ID分组显示报文cansniffer can0这个命令在调试阶段特别好用。底盘上电后你能在cansniffer里看到一组一组周期出现的ID和对应的数据对照协议表就能快速确认哪条是状态帧、哪条是轮速反馈。我调试时习惯先整机断电只开CAN卡自检确认接收端没数据也没报错然后上电底盘这能帮我确认报文的来源到底是底盘发出的还是总线上其它设备制造的噪音。4.3 启动底盘驱动节点话题在哪里反馈在哪里链路通了、报文能看到接下来才轮到调用包登场。先看看包内提供的launch文件以Scout底盘为例roslaunch scout_base scout_base.launch启动后建议立刻用一组命令确认节点状态rosnode list rostopic list正常情况下你会看到驱动节点和一组以底盘型号名开头的话题例如/scout_status、/cmd_vel、/scout_odom等。注意不同系列话题名会有差异。验证状态反馈是否在更新用rostopic hz看频率是最直接的rostopic hz /scout_status如果能看到“average rate”在跳动比如10Hz或者50Hz说明驱动节点已经从CAN总线上正确读取了底盘状态并发布到ROS话题了。接着再用echo看一眼具体内容rostopic echo /scout_status你至少应该能看到电池电压、轮速等数值在变化。如果一直是重复的一样的值甚至没有话题输出基本可以判断驱动节点没有从CAN收到有效报文这时候不要疑神疑鬼回到第4.2步重新检查CAN链路和协议ID映射。4.4 端到端联调从cmd_vel到轮子转动最后一步也是最激动人心的一步让车动起来。松灵底盘驱动包一般会订阅标准的/cmd_vel话题并把它转换成CAN控制帧发到底盘。为了保证安全第一次测试请把底盘架起来让轮子悬空或者用箱子垫起来。先发一个很小的速度指令rostopic pub -r 10 /cmd_vel geometry_msgs/Twist {linear: {x: 0.1, y: 0.0, z: 0.0}, angular: {z: 0.0}}这里-r 10表示10Hz周期发送持续发送而不是只发一次因为许多底盘的控制实现是“超过一定时间没有收到新指令就自动停车”这是非常安全的设计。如果轮子开始缓慢转动恭喜你整条链路已经打通了。如果不转先检查两件事急停开关有没有拉起来以及底盘是否处于使能状态。底盘使能这块不同系列差别很大。有些系列上电默认就能通过/cmd_vel控制有些需要通过一个独立的服务或话题发送使能指令还有些则是把使能位放在CAN控制指令帧的某个字节里。你一定要去官方协议表里找到“使能”这个字段的定义单独验证它。我当时的坑就在这儿官方包跑起来了/cmd_vel也在发轮子就是不动查了半天才想起来急停按钮没复位拉起急停之后底盘立刻响应。整车动起来之后最后再用rviz验证一下底盘的里程计数据rosrun rviz rviz -d $(rospack find scout_base)/rviz/scout_base.rviz如果驱动包自带了URDF模型你会看到底盘模型在地图上移动里程计话题在更新。底盘“会跑了”CAN通讯这个环节才算真正过关。5. 常见问题与排障实录我踩过的那些坑5.1 插上CAN卡但没有can0设备现象CAN卡插上后ip link看不到can0。排查思路先看硬件枚举dmesg | grep -i usb和lsusb确认电脑是否识别到了CAN卡设备。如果dmesg里一点USB插入事件都没有检查USB线、换一个USB口、看设备指示灯。如果识别到了但没生成can0大概率是内核模块没加载回到第4.1节执行modprobe三条命令或者手动加载厂家提供的驱动模块。还有一种可能是设备被识别成了别的类型比如ttyUSB的串口设备那就需要安装厂家原厂驱动并按其说明创建CAN接口。5.2 can0起来了但candump什么都收不到这是物理层问题的大本营。优先级最高的是波特率是不是跟底盘一致用ip -details link show can0看当前bitrate然后跟协议手册核对。其次是CAN_H和CAN_L有没有接反我见过不止一次用颜色区分结果厂家线序跟你想的不一样的情况用万用表蜂鸣档对照针脚定义测一下最稳妥。再就是终端电阻有些USB转CAN卡侧面有个小开关出厂默认断开拨到ON位置才能启用内部电阻。最后是共地问题在电脑和底盘之间接一根共地线试试。5.3 驱动节点启动后底盘没有反应启动驱动节点后rosnode和rostopic都正常但发cmd_vel时轮子不动。我的排查顺序固定是这样的先看急停开关状态很多底盘急停拉下后一切指令都会被忽略而且急停开关本身在外观上不一定特别显眼再看使能状态在协议表找到使能位用cansend手动把使能位置位然后用cansend直接发一条控制指令帧看轮子动不动这样能区分问题是CAN层还是驱动包层最后看驱动节点的日志输出里有没有异常报错比如收到的帧ID不匹配之类。5.4 状态反馈数据乱跳或长时间不更新反馈数据乱跳最常见的原因是字节序搞反了。CAN协议里多字节整数的大小端定义各不相同同样两个字节01 02小端解析是0x0201大端解析是0x0102数值完全不一样。看协议表时务必注意“字节序”这一栏很多官方驱动包源码里的解析也最容易在这种地方出错。另一个常见原因是缩放因子没算对比如协议表说电压原始值要乘以0.1才是实际电压你直接拿原始值显示数据当然跟万用表对不上。反馈数据长时间不更新则多半是底盘的周期上报机制没触发或者上了急停、进入异常保护模式后底盘暂停了状态上报。5.5 排障速查表一套命令定位问题现象可能原因排查命令/方法没有can0接口内核模块未加载/驱动未装/USB未识别lsusb、dmesg、sudo modprobe can can_raw can_dev接口有但candump静默波特率不一致/接线反/缺终端电阻/未共地ip -details link show can0、万用表测通断有报文但全是不认识的ID协议不匹配/收到噪声cansniffer can0对照协议表驱动节点起不来依赖缺失/串口权限不足catkin_make报错信息、sudo usermod -aG dialout $USER底盘不动急停未解除/未使能/控制ID不对检查急停、协议表使能位、cansend手动控制帧反馈数据乱字节序错/缩放因子错对照协议表逐字段核对反馈频率为0驱动卡死/底层报文无回复rostopic hz /scout_status、candump看CAN层排查过程中最忌讳同时改多个参数。一次只改一个变量改完再测这是我调试所有硬件现场的基本纪律。比如我发现candump收不到数据就先从波特率开始试试完波特率再动接线接线确认没问题再查电阻一层一层来不会越调越乱。写在最后整个松灵底盘CAN通讯调试走下来我的最大感受是80%的时间花在了物理层和协议核对上真正写代码的时间反而很少。很多人一上来就钻进驱动源码里去查为什么底盘不动结果发现只是急停没拉起来或者波特率差了那么一个数字。CAN调试的本质是在跟一条总线对话工具就那些——candump、cansend、万用表、协议说明书而耐心和分层排查的思路才是最重要的。最后分享一个小技巧调试前在纸上画一张链路图把CAN卡型号、波特率、底盘型号、控制帧ID、状态帧ID、使能位的字节位置全标出来贴在工位上。我几次凌晨调底盘卡住都是靠着这张纸冷静下来从物理层开始逐项复核最后找到原因。这比任何花哨的调试工具都管用。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表