news 2026/10/4 1:48:16

【电机驱动】使用Jetson Orin NX实现与电机的通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【电机驱动】使用Jetson Orin NX实现与电机的通信

一开始计划打算直接使用Jetson ORIN NX上的CAN实现与电机的通信,但是在调试的过程中发现ORIN上的CAN使用会存在问题。为了加速开发,后面使用了一块STM32H7的板子实现电机数据的收发,再通过串口与ORIN实现通信。

CAN通讯实现(失败)

配置ORIN的CAN并使能CAN

参考:https://gitee.com/kit-miao/orin-board/blob/master/CAN%20%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95.md

  • 激活CAN
sudomodprobe mttcan
  • 配置CAN波特率
sudoiplinksetcan0typecan bitrate1000000
  • 开启CAN
sudoiplinksetcan0 up
  • 直接使用终端显示接收到的CAN消息帧
sudocandump can0

异常处理

有时候会因为CAN的不正常关闭,导致CAN会一直显示被占用:

RTNETLINK answers: Device or resource busy

这时候首先需要检查CAN的状态:

ifconfigcan0# 运行结果can0:flags=129<UP,NOARP>mtu16unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen10(UNSPEC)RX packets3bytes24(24.0B)RX errors0dropped0overruns0frame0TX packets0bytes0(0.0B)TX errors0dropped0overruns0carrier0collisions0device interrupt200

从运行结果中可以看到,这时候CAN仍处于UP的状态,就需要手动对CAN进行关闭:

sudoiplinksetcan0 down

这时候再次查询CAN的状态就会显示无占用了:

can0:flags=128<NOARP>mtu16unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen10(UNSPEC)RX packets3bytes24(24.0B)RX errors0dropped0overruns0frame0TX packets0bytes0(0.0B)TX errors0dropped1overruns0carrier1collisions0device interrupt200

电机控制命令发送及接收

最后

最后通过查阅类似开发经验得知,有不少开发者也同样遇到接收不稳定的问题,有博主通过更换CAN芯片解决了问题,链接:

https://blog.csdn.net/qq_22146161/article/details/132193036?spm=1001.2014.3001.5506

考虑到硬件开发,之后还是更换了硬件实现方案。先通过使用STM32对电机数据进行处理,再通过STM32的串口将数据发送到Jetson端。

串口通讯实现

为了不造成发送频率过高而导致的串口堵塞,我这里采取了半双工的通信方案。也就是Jetson端向STM32端发送命令,STM32端接收到命令后,对数据进行解包,然后再向Jetson端反馈当前的电机数据。

这里为了使得STM32发送数据不阻塞串口的接收中断,开启了串口的DMA。

由于STM32H7使用的是Cortex-M7内核,其包含多个存储区,包括TCM、SRAM等。为了保证DMA能够正常访问到数据,不存在Cache问题,这里将所需要发送的buffer显式地定义为dma_buffer中:

__attribute__((section(".dma_buffer")))uint8_tsend_jetson_buf[UARTS_TX_BUF_SZ]={0};

关于更多的介绍可以查看Cortex-M7的手册或者是《安富莱_STM32-V7开发板_用户手册》

在串口中断回调函数中增加数据解包以及状态发送的代码:

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart->Instance==USART10){staticuint8_tlast_byte=0;if(jetson_rx_index==0&&last_byte==0xAA&&jetson_rx_byte==0x55){jetson_rx_buffer[0]=0xAA;jetson_rx_buffer[1]=0x55;jetson_rx_index=2;}elseif(jetson_rx_index>=2){jetson_rx_buffer[jetson_rx_index++]=jetson_rx_byte;if(jetson_rx_index>=RX_FRAME_LEN){uint8_tchecksum=0;for(inti=2;i<RX_FRAME_LEN-2;i++)checksum+=jetson_rx_buffer[i];checksum&=0xFF;if(jetson_rx_buffer[RX_FRAME_LEN-2]==checksum&&jetson_rx_buffer[RX_FRAME_LEN-1]==0x0D){memcpy(&leftMotorCmdTorque,&jetson_rx_buffer[2],4);memcpy(&rightMotorCmdTorque,&jetson_rx_buffer[6],4);terrain_code=jetson_rx_buffer[10];}jetson_rx_index=0;send_state_check();}}last_byte=jetson_rx_byte;HAL_UART_Receive_IT(&huart10,&jetson_rx_byte,1);}}

send_state_check函数定义:

staticvoidsend_state_check(void){intp=0;send_jetson_buf[p++]=UARTS_SYNC0;send_jetson_buf[p++]=UARTS_SYNC1;send_jetson_buf[p++]=UARTS_MSG_STATE;intlen_pos=p;p+=2;uint32_tt_ms=HAL_GetTick();memcpy(&send_jetson_buf[p],&t_ms,4);p+=4;send_jetson_buf[p++]=g_motor_count;for(uint8_ti=0;i<g_motor_count;i++){send_jetson_buf[p++]=g_motors[i].id;memcpy(&send_jetson_buf[p],&g_motors[i].pos_rad,4);p+=4;memcpy(&send_jetson_buf[p],&g_motors[i].vel_rad_s,4);p+=4;memcpy(&send_jetson_buf[p],&g_motors[i].tau_nm,4);p+=4;}send_jetson_buf[p++]=g_imu9.valid_bits;memcpy(&send_jetson_buf[p],&g_imu9.ax,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.ay,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.az,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.gx,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.gy,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.gz,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.rollY,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.pitchX,4);p+=4;memcpy(&send_jetson_buf[p],&g_imu9.yawZ,4);p+=4;uint16_tlen=(uint16_t)(p-(len_pos+2));send_jetson_buf[len_pos+0]=(uint8_t)(len&0xFF);send_jetson_buf[len_pos+1]=(uint8_t)(len>>8);uint8_tchecksum=0;for(inti=2;i<p;i++)checksum+=send_jetson_buf[i];send_jetson_buf[p++]=checksum;send_jetson_buf[p++]=UARTS_END_BYTE;HAL_UART_Transmit_DMA(&UARTS_HUART,send_jetson_buf,(uint16_t)p);}

Jetson端发送命令并进行数据接收:

defsynchronous_exchange(self,left_torque,right_torque,terrain_mode='LG10'):""" Perform synchronous exchange: send torque command then read sensor data """ifnotself.connected:returnNone# Step 1: Send torque commandtx_success=self.send_torque_command(left_torque,right_torque,terrain_mode)# Step 2: Small delay to allow hardware processingtime.sleep(0.0001)# 0.1ms delay# Step 3: Read sensor responsesensor_data=self.read_sensor_data()returnsensor_data
defsend_torque_command(self,left_torque,right_torque,terrain_mode='LG10'):""" Synchronous send of torque command """ifnotself.connected:returnFalsetry:# Update state trackingself.last_left_torque=left_torque self.last_right_torque=right_torque self.last_terrain_mode=terrain_mode# Convert terrain mode to codeterrain_code=self.TERRAIN_CODES.get(terrain_mode,3)# Frame header and tailframe_head=b'\xAA\x55'frame_tail=b'\x0D'# Pack data: 2 floats (left, right torque) + 1 uint8 (terrain code)data=struct.pack('<ffB',left_torque,right_torque,terrain_code)# Calculate checksum (sum of all data bytes, keep low 8 bits)checksum=sum(data)&0xFF# Construct full frameframe=frame_head+data+bytes([checksum])+frame_tail# Send frameself.ser.write(frame)self.ser.flush()# Ensure data is sent immediatelyself.tx_count+=1returnTrueexceptException:returnFalse
defread_sensor_data(self):""" Synchronous read of sensor data Returns parsed frame or None if no complete frame available """ifnotself.connected:returnNonetry:# Read available datadata=self.ser.read(self.ser.in_waitingor1)forbindata:b=bifisinstance(b,int)elseord(b)ifself.rx_state==0:ifb==SYNC0:self.rx_state=1self.rx_frame=bytearray([b])elifself.rx_state==1:ifb==SYNC1:self.rx_state=2self.rx_frame.append(b)else:self.rx_state=0elifself.rx_state>=2:self.rx_frame.append(b)iflen(self.rx_frame)>=5:# Read length field earlylength=struct.unpack('<H',self.rx_frame[3:5])[0]expected_len=7+length# SYNC0+SYNC1+MSG+LEN(2)+payload+CHECK+ENDiflen(self.rx_frame)==expected_len:result=self.parse_rx_frame(self.rx_frame)self.rx_state=0returnresultexceptException:passreturnNone

最终能够实现200Hz较为稳定的通讯。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 1:47:31

U9客开中UI插件开发过程中碰到有一个坑

U9系统太大了&#xff0c;不可避免会发生一些隐含的重大BUG。今天碰到一个这样的问题&#xff0c;在请购单列表上做了一个【模拟提交】的按钮&#xff0c;目的是取代原来的【提交】按钮&#xff0c;目的是可以加上个性化需要的防呆措施&#xff0c;实现客制化的目的。效果如下图…

作者头像 李华
网站建设 2026/10/4 1:47:14

《计算机网络第5版》课后答案怎么用?严伟潘爱民版复习避坑指南

简介&#xff1a;《计算机网络》第5版严伟、潘爱民译本的课后答案&#xff0c;定位为计算机专业学生、考研者及自学者的习题辅导资料&#xff0c;覆盖教材第一章至第二章课后习题&#xff0c;帮助核对解题过程、理解网络核心原理。内含1个doc文档&#xff0c;压缩包仅733KB&…

作者头像 李华
网站建设 2026/10/4 1:46:51

奇诺多面体在虚拟电厂聚合调控中的应用与Python实现

简介&#xff1a;一份基于奇诺多面体的虚拟电厂分布式资源广域聚合调控方法复现资源&#xff0c;面向智能电网、能源管理与优化控制研究人员&#xff0c;以及关注分布式资源聚合的学者。内容以Python完整可运行代码及逐段解释为主线&#xff0c;涵盖奇诺多面体类定义与顶点计算…

作者头像 李华
网站建设 2026/10/4 1:46:14

TM4C123 SPI驱动MR25H40CDF MRAM:嵌入式非易失存储的实践与避坑指南

我最早接触MR25H40CDF这颗芯片&#xff0c;是因为一个工业数据采集项目里&#xff0c;需要频繁记录传感器校准值和运行日志。当时用的MCU是TM4C123GH6PZ&#xff0c;内部Flash虽然够存代码&#xff0c;但拿来做数据存储太憋屈了——写一次要按页擦除&#xff0c;日志写多了还担…

作者头像 李华