【电机驱动】使用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
sudo modprobe mttcan
- 配置CAN波特率
sudo ip link set can0 type can bitrate 1000000
- 开启CAN
sudo ip link set can0 up
- 直接使用终端显示接收到的CAN消息帧
sudo candump can0
异常处理
有时候会因为CAN的不正常关闭,导致CAN会一直显示被占用:
RTNETLINK answers: Device or resource busy
这时候首先需要检查CAN的状态:
ifconfig can0
# 运行结果
can0: flags=129<UP,NOARP> mtu 16
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 3 bytes 24 (24.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 200
从运行结果中可以看到,这时候CAN仍处于UP的状态,就需要手动对CAN进行关闭:
sudo ip link set can0 down
这时候再次查询CAN的状态就会显示无占用了:
can0: flags=128<NOARP> mtu 16
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 3 bytes 24 (24.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 1 overruns 0 carrier 1 collisions 0
device interrupt 200
电机控制命令发送及接收
最后
最后通过查阅类似开发经验得知,有不少开发者也同样遇到接收不稳定的问题,有博主通过更换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_t send_jetson_buf[UARTS_TX_BUF_SZ] = {0};
关于更多的介绍可以查看Cortex-M7的手册或者是《安富莱_STM32-V7开发板_用户手册》
在串口中断回调函数中增加数据解包以及状态发送的代码:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART10)
{
static uint8_t last_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;
}
else if (jetson_rx_index >= 2)
{
jetson_rx_buffer[jetson_rx_index++] = jetson_rx_byte;
if (jetson_rx_index >= RX_FRAME_LEN)
{
uint8_t checksum = 0;
for (int i = 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函数定义:
static void send_state_check(void)
{
int p = 0;
send_jetson_buf[p++] = UARTS_SYNC0;
send_jetson_buf[p++] = UARTS_SYNC1;
send_jetson_buf[p++] = UARTS_MSG_STATE;
int len_pos = p; p += 2;
uint32_t t_ms = HAL_GetTick();
memcpy(&send_jetson_buf[p], &t_ms, 4); p += 4;
send_jetson_buf[p++] = g_motor_count;
for(uint8_t i=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_t len = (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_t checksum = 0;
for(int i=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端发送命令并进行数据接收:
def synchronous_exchange(self, left_torque, right_torque, terrain_mode='LG10'):
"""
Perform synchronous exchange: send torque command then read sensor data
"""
if not self.connected:
return None
# Step 1: Send torque command
tx_success = self.send_torque_command(left_torque, right_torque, terrain_mode)
# Step 2: Small delay to allow hardware processing
time.sleep(0.0001) # 0.1ms delay
# Step 3: Read sensor response
sensor_data = self.read_sensor_data()
return sensor_data
def send_torque_command(self, left_torque, right_torque, terrain_mode='LG10'):
"""
Synchronous send of torque command
"""
if not self.connected:
return False
try:
# Update state tracking
self.last_left_torque = left_torque
self.last_right_torque = right_torque
self.last_terrain_mode = terrain_mode
# Convert terrain mode to code
terrain_code = self.TERRAIN_CODES.get(terrain_mode, 3)
# Frame header and tail
frame_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 frame
frame = frame_head + data + bytes([checksum]) + frame_tail
# Send frame
self.ser.write(frame)
self.ser.flush() # Ensure data is sent immediately
self.tx_count += 1
return True
except Exception:
return False
def read_sensor_data(self):
"""
Synchronous read of sensor data
Returns parsed frame or None if no complete frame available
"""
if not self.connected:
return None
try:
# Read available data
data = self.ser.read(self.ser.in_waiting or 1)
for b in data:
b = b if isinstance(b, int) else ord(b)
if self.rx_state == 0:
if b == SYNC0:
self.rx_state = 1
self.rx_frame = bytearray([b])
elif self.rx_state == 1:
if b == SYNC1:
self.rx_state = 2
self.rx_frame.append(b)
else:
self.rx_state = 0
elif self.rx_state >= 2:
self.rx_frame.append(b)
if len(self.rx_frame) >= 5:
# Read length field early
length = struct.unpack('<H', self.rx_frame[3:5])[0]
expected_len = 7 + length # SYNC0+SYNC1+MSG+LEN(2)+payload+CHECK+END
if len(self.rx_frame) == expected_len:
result = self.parse_rx_frame(self.rx_frame)
self.rx_state = 0
return result
except Exception:
pass
return None
最终能够实现200Hz较为稳定的通讯。
更多推荐

所有评论(0)