一开始计划打算直接使用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激活CANsudomodprobe mttcan配置CAN波特率sudoiplinksetcan0typecan bitrate1000000开启CANsudoiplinksetcan0 up直接使用终端显示接收到的CAN消息帧sudocandump can0异常处理有时候会因为CAN的不正常关闭,导致CAN会一直显示被占用:RTNETLINK answers: Device or resource busy这时候首先需要检查CAN的状态:ifconfigcan0# 运行结果can0:flags129UP,NOARPmtu16unspec 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:flags128NOARPmtu16unspec 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?spm1001.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-InstanceUSART10){staticuint8_tlast_byte0;if(jetson_rx_index0last_byte0xAAjetson_rx_byte0x55){jetson_rx_buffer[0]0xAA;jetson_rx_buffer[1]0x55;jetson_rx_index2;}elseif(jetson_rx_index2){jetson_rx_buffer[jetson_rx_index]jetson_rx_byte;if(jetson_rx_indexRX_FRAME_LEN){uint8_tchecksum0;for(inti2;iRX_FRAME_LEN-2;i)checksumjetson_rx_buffer[i];checksum0xFF;if(jetson_rx_buffer[RX_FRAME_LEN-2]checksumjetson_rx_buffer[RX_FRAME_LEN-1]0x0D){memcpy(leftMotorCmdTorque,jetson_rx_buffer[2],4);memcpy(rightMotorCmdTorque,jetson_rx_buffer[6],4);terrain_codejetson_rx_buffer[10];}jetson_rx_index0;send_state_check();}}last_bytejetson_rx_byte;HAL_UART_Receive_IT(huart10,jetson_rx_byte,1);}}send_state_check函数定义staticvoidsend_state_check(void){intp0;send_jetson_buf[p]UARTS_SYNC0;send_jetson_buf[p]UARTS_SYNC1;send_jetson_buf[p]UARTS_MSG_STATE;intlen_posp;p2;uint32_tt_msHAL_GetTick();memcpy(send_jetson_buf[p],t_ms,4);p4;send_jetson_buf[p]g_motor_count;for(uint8_ti0;ig_motor_count;i){send_jetson_buf[p]g_motors[i].id;memcpy(send_jetson_buf[p],g_motors[i].pos_rad,4);p4;memcpy(send_jetson_buf[p],g_motors[i].vel_rad_s,4);p4;memcpy(send_jetson_buf[p],g_motors[i].tau_nm,4);p4;}send_jetson_buf[p]g_imu9.valid_bits;memcpy(send_jetson_buf[p],g_imu9.ax,4);p4;memcpy(send_jetson_buf[p],g_imu9.ay,4);p4;memcpy(send_jetson_buf[p],g_imu9.az,4);p4;memcpy(send_jetson_buf[p],g_imu9.gx,4);p4;memcpy(send_jetson_buf[p],g_imu9.gy,4);p4;memcpy(send_jetson_buf[p],g_imu9.gz,4);p4;memcpy(send_jetson_buf[p],g_imu9.rollY,4);p4;memcpy(send_jetson_buf[p],g_imu9.pitchX,4);p4;memcpy(send_jetson_buf[p],g_imu9.yawZ,4);p4;uint16_tlen(uint16_t)(p-(len_pos2));send_jetson_buf[len_pos0](uint8_t)(len0xFF);send_jetson_buf[len_pos1](uint8_t)(len8);uint8_tchecksum0;for(inti2;ip;i)checksumsend_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_modeLG10): Perform synchronous exchange: send torque command then read sensor data ifnotself.connected:returnNone# Step 1: Send torque commandtx_successself.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_dataself.read_sensor_data()returnsensor_datadefsend_torque_command(self,left_torque,right_torque,terrain_modeLG10): Synchronous send of torque command ifnotself.connected:returnFalsetry:# Update state trackingself.last_left_torqueleft_torque self.last_right_torqueright_torque self.last_terrain_modeterrain_mode# Convert terrain mode to codeterrain_codeself.TERRAIN_CODES.get(terrain_mode,3)# Frame header and tailframe_headb\xAA\x55frame_tailb\x0D# Pack data: 2 floats (left, right torque) 1 uint8 (terrain code)datastruct.pack(ffB,left_torque,right_torque,terrain_code)# Calculate checksum (sum of all data bytes, keep low 8 bits)checksumsum(data)0xFF# Construct full frameframeframe_headdatabytes([checksum])frame_tail# Send frameself.ser.write(frame)self.ser.flush()# Ensure data is sent immediatelyself.tx_count1returnTrueexceptException:returnFalsedefread_sensor_data(self): Synchronous read of sensor data Returns parsed frame or None if no complete frame available ifnotself.connected:returnNonetry:# Read available datadataself.ser.read(self.ser.in_waitingor1)forbindata:bbifisinstance(b,int)elseord(b)ifself.rx_state0:ifbSYNC0:self.rx_state1self.rx_framebytearray([b])elifself.rx_state1:ifbSYNC1:self.rx_state2self.rx_frame.append(b)else:self.rx_state0elifself.rx_state2:self.rx_frame.append(b)iflen(self.rx_frame)5:# Read length field earlylengthstruct.unpack(H,self.rx_frame[3:5])[0]expected_len7length# SYNC0SYNC1MSGLEN(2)payloadCHECKENDiflen(self.rx_frame)expected_len:resultself.parse_rx_frame(self.rx_frame)self.rx_state0returnresultexceptException:passreturnNone最终能够实现200Hz较为稳定的通讯。