The simulation environment can be used, but the ground control station data has a large delay after connection

Signed-off-by: STT
This commit is contained in:
STT 2021-12-10 16:51:24 +08:00
parent 88e1479fd1
commit dbcf696f52
9 changed files with 255 additions and 16 deletions

Binary file not shown.

Binary file not shown.

View File

@ -17,7 +17,9 @@ python版船控也可控两轮无人车作者初学python练手。
AppData中csv格式文件为日志信息
Airsim仿真功能待完善暂不对外分享。
仿真环境使用Airsim车模型代码在Airsim文件夹相关配置教程请自行百度navigation_type设置为airsim
@ -65,6 +67,7 @@ python版船控也可控两轮无人车作者初学python练手。
1. Add ini feature
2. Change the mode of communication with the airsim to non-blocking
2. The simulation environment can be used, but the ground control station data has a large delay after connection

123
USVAirsim/main.py Normal file
View File

@ -0,0 +1,123 @@
# PythonClient/car/hello_car.py
import airsim
import time
from udpCommunication import Udp
udp = Udp()
# 连接到Airsim仿真器
client = airsim.CarClient()
client.confirmConnection() # 每1秒检查一次连接状态并在控制台中报告以便用户可以查看连接的进度实际操作只显示一次奇怪
# Connected!
# Client Ver:1 (Min Req: 1), Server Ver:1 (Min Req: 1)
client.enableApiControl(True) # 默认是false有的车辆不允许用API控制所以用isApiControlEnabled可以查看是否可以用API控制
# 仿真器左上角会显示ControlMode: API
while True:
states = client.getCarState()
car_state = states.__dict__['kinematics_estimated'].__dict__
states = [car_state['position'].__dict__['x_val'], car_state['position'].__dict__['y_val'],
car_state['position'].__dict__['z_val'],
car_state['orientation'].__dict__['x_val'], car_state['orientation'].__dict__['y_val'],
car_state['orientation'].__dict__['z_val'],
car_state['linear_velocity'].__dict__['x_val'], car_state['linear_velocity'].__dict__['y_val'],
car_state['linear_velocity'].__dict__['z_val'], car_state['angular_velocity'].__dict__['x_val'],
car_state['angular_velocity'].__dict__['y_val'], car_state['angular_velocity'].__dict__['z_val'],
car_state['linear_acceleration'].__dict__['x_val'], car_state['linear_acceleration'].__dict__['y_val'],
car_state['linear_acceleration'].__dict__['z_val']]
udp.states = states
print(states)
Udp.send(udp)
Udp.receive(udp)
car_controls = airsim.CarControls() # 获得车辆控制数据
car_controls.is_manual_gear = False
car_controls.steering = udp.command[0] / 1000
if udp.command[1] < 0:
car_controls.is_manual_gear = True
car_controls.manual_gear = -1
car_controls.throttle = udp.command[1] / 1000
client.setCarControls(car_controls)
# 将车辆初始化处理,后续如果需要重新控制则需要再次调用`enableApiControl`或`armDisarm
# client.reset()
# client.enableApiControl(False) # 取消仿真器API控制
'''
# get state of the car
car_state = client.getCarState() # 获取车辆状态坐标统一是NED坐标使用国际单位。
# <CarState> { 'gear': 0,
# 'handbrake': False,
# 'kinematics_estimated': <KinematicsState> { 'angular_acceleration': <Vector3r> { 'x_val': 0.0,
# 'y_val': 0.0,
# 'z_val': 0.0},
# 'angular_velocity': <Vector3r> { 'x_val': 0.0,
# 'y_val': 0.0,
# 'z_val': 0.0},
# 'linear_acceleration': <Vector3r> { 'x_val': 0.0,
# 'y_val': 0.0,
# 'z_val': 0.0},
# 'linear_velocity': <Vector3r> { 'x_val': 0.0,
# 'y_val': 0.0,
# 'z_val': -0.0},
# 'orientation': <Quaternionr> { 'w_val': 1.0,
# 'x_val': 3.1523319194093347e-05,
# 'y_val': 0.0,
# 'z_val': 0.0},
# 'position': <Vector3r> { 'x_val': 0.0,
# 'y_val': -2.2888182229507947e-06,
# 'z_val': 0.23400458693504333}},
# 'maxrpm': 7500.0,
# 'rpm': 0.0,
# 'speed': 0.0,
# 'timestamp': 1597385459957857300}
print("Speed %d, Gear %d" % (car_state.speed, car_state.gear))'''
'''controls = airsim.CarControls() # 获得车辆控制数据
# <CarControls> { 'brake': 0,
# 'gear_immediate': True,
# 'handbrake': False,
# 'is_manual_gear': False,
# 'manual_gear': 0,
# 'steering': 0,
# 'throttle': 0}
'''
''' # 前进配置car_controls之后使用setCarControls设置状态
# 这允许您设置油门(throttle),转向(steering),手制动(handbrake)和自动或手动档位(auto or manual gear)
car_controls.throttle = 0.5
car_controls.steering = 0
client.setCarControls(car_controls)
print("Go Forward")
time.sleep(3) # let car drive a bit
# 前进+右转
car_controls.throttle = 0.5
car_controls.steering = 1
client.setCarControls(car_controls)
print("Go Forward, steer right")
time.sleep(3) # let car drive a bit
# 倒车,记得要复原
car_controls.throttle = -0.5
car_controls.is_manual_gear = True
car_controls.manual_gear = -1
car_controls.steering = 0
client.setCarControls(car_controls)
print("Go reverse, steer right")
time.sleep(3) # let car drive a bit
car_controls.is_manual_gear = False # change back gear to auto
car_controls.manual_gear = 0
# 急刹
car_controls.brake = 1
client.setCarControls(car_controls)
print("Apply brakes")
time.sleep(3) # let car drive a bit
car_controls.brake = 0 # remove brake
# 将车辆初始化处理,后续如果需要重新控制则需要再次调用`enableApiControl`或`armDisarm`.'''

View File

@ -0,0 +1,112 @@
import socket
import struct
import time
class Udp:
def __init__(self):
self.receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.receive.bind(('192.168.31.87', 9876))
self.receive.setblocking(False)
self.command = [0, 0]
self.states = []
self.buffer = []
def receive(self):
try:
data, send_address = self.receive.recvfrom(19)
except BlockingIOError:
data = ''
send_address = ()
self.buffer += data
while len(self.buffer) >= 5:
if self.buffer[0] != calculate_header_lrc(self.buffer):
del self.buffer[0]
continue
packet_data_length = self.buffer[2]
packet_length = packet_data_length + 5
if len(self.buffer) < packet_length:
break
if (self.buffer[3] | self.buffer[4] << 8) != calculate_crc16_ccitt(self.buffer[5:], packet_data_length):
del self.buffer[0]
continue
packet_id = self.buffer[1]
packet_data = self.buffer[5:packet_length]
'''if (packet_data[0] | packet_data[1] << 8) != usvid: # 数据过滤
del self.buffer[:packet_length]
continue'''
if packet_id == 0 and packet_data_length == 14: # Command
data = struct.unpack('<hh', bytes(packet_data[10:packet_data_length]))
self.command[0] = data[0]
self.command[1] = data[1]
del self.buffer[:packet_length]
continue
def send(self):
data_bytes = struct.pack('<Hddddffffffffffff', 0, time.time(), self.states[0] / 100000, self.states[1] / 100000,
self.states[2] / 100000,
self.states[3], self.states[4], self.states[5], self.states[6], self.states[7],
self.states[8], self.states[9], self.states[10], self.states[11], self.states[12],
self.states[13], self.states[14])
crc16 = calculate_crc16_ccitt(data_bytes, len(data_bytes))
header_bytes = calculate_header_bytes(10, 82, crc16) # 回应包id=10包长82
send_data = header_bytes + data_bytes
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
send_socket.bind(('192.168.31.87', 6789))
send_socket.sendto(send_data, ('192.168.31.234', 9876))
send_socket.close()
def calculate_header_bytes(usv_id, length, crc16):
header_list = [0, usv_id, length, crc16 & 0xff, crc16 // 0x100]
header_list[0] = calculate_header_lrc(header_list)
return struct.pack('<BBBBB', header_list[0], header_list[1], header_list[2], header_list[3], header_list[4])
def calculate_header_lrc(data_list):
return ((((data_list[1] + data_list[2] + data_list[3] + data_list[4]) & 0xff) ^ 0xff) + 1) & 0xff
def calculate_crc16_ccitt(data_list, length):
crc16_ccitt = 0xffff
crc16_ccitt_list = [0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0]
for i in range(length):
crc16_ccitt = ((crc16_ccitt << 8 & 0xffff) ^ crc16_ccitt_list[
((crc16_ccitt >> 8) & 0xffff) ^ data_list[i]]) & 0xffff
return crc16_ccitt

View File

@ -19,7 +19,7 @@ class GroundControlStation:
self.send_status(usv)
def receive_decode(self, usv):
self.buffer += self.gcs.read(256)
self.buffer += self.gcs.read(128)
while len(self.buffer) >= 5:
if self.buffer[0] != calculate_header_lrc(self.buffer):

View File

@ -25,7 +25,7 @@ class Navigation:
self.navigation = serial.Serial(usv.settings.navigation_com, usv.settings.navigation_baudrate, timeout=0,
write_timeout=0)
if usv.settings.navigation_type == 'airsim':
ip_port = (usv.settings.usv_ip, usv.settings.airsim_port)
ip_port = (usv.settings.usv_ip, usv.settings.airsim_receive_port)
self.receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.receive.bind(ip_port)
self.receive.setblocking(False)
@ -113,17 +113,16 @@ class Navigation:
pass
def airsim_decode(self, usv):
buffer = []
try:
data, send_add = self.receive.recvfrom(82)
data, send_address = self.receive.recvfrom(87)
except BlockingIOError:
data = ""
send_add = ()
send_address = ()
if send_add == (usv.settings.airsim_ip, usv.settings.airsim_port):
buffer += data
if send_address == (usv.settings.airsim_ip, usv.settings.airsim_send_port):
self.buffer += data
while len(buffer) >= 5:
while len(self.buffer) >= 5:
if self.buffer[0] != calculate_header_lrc(self.buffer):
del self.buffer[0]
continue
@ -141,7 +140,7 @@ class Navigation:
packet_id = self.buffer[1]
packet_data = self.buffer[5:packet_length]
if (packet_data[0] | packet_data[1] << 8) != usv.control.pid['id']: # 数据过滤
if (packet_data[0] | packet_data[1] << 8) != usv.settings.usv_id: # 数据过滤
del self.buffer[:packet_length]
continue

View File

@ -37,14 +37,14 @@ class RemoteControlUnit:
self.encode()
self.sbus.write(self.packet.tobytes())
else:
data_bytes = struct.pack('<Bdhh', usv.settings.usv_id, time.time(), usv.control.data['rudder'],
data_bytes = struct.pack('<Hdhh', usv.settings.usv_id, time.time(), usv.control.data['rudder'],
usv.control.data['thrust'])
crc16 = calculate_crc16_ccitt(data_bytes, len(data_bytes))
header_bytes = calculate_header_bytes(0, 16, crc16) # 回应包id=0包长16
header_bytes = calculate_header_bytes(0, 14, crc16) # 回应包id=0包长14
send_data = header_bytes + data_bytes
ip_port = (usv.settings.airsim_ip, usv.settings.airsim_port)
send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
send.sendto(send_data, ip_port)
send.bind((usv.settings.usv_ip,usv.settings.airsim_send_port))
send.sendto(send_data, (usv.settings.airsim_ip, usv.settings.airsim_receive_port))
send.close()
def decode(self, ):

View File

@ -24,7 +24,8 @@ class Settings:
config.set('navigation', 'navigation_com', 'COM51')
config.set('navigation', 'navigation_baudrate', '115200')
config.set('navigation', 'airsim_ip', '192.168.31.86')
config.set('navigation', 'airsim_port', '9876')
config.set('navigation', 'airsim_receive_port', '9876')
config.set('navigation', 'airsim_send_port', '6789')
config.add_section('gcs')
config.set('gcs', 'gcs_com', 'COM52')
@ -48,7 +49,8 @@ class Settings:
self.navigation_type = config.get('navigation', 'navigation_type')
self.navigation_baudrate = eval(config.get('navigation', 'navigation_baudrate'))
self.airsim_ip = config.get('navigation', 'airsim_ip')
self.airsim_port = eval(config.get('navigation', 'airsim_port'))
self.airsim_receive_port = eval(config.get('navigation', 'airsim_receive_port'))
self.airsim_send_port = eval(config.get('navigation', 'airsim_send_port'))
self.gcs_com = config.get('gcs', 'gcs_com')
self.gcs_disconnect_time_allow = eval(config.get('gcs', 'gcs_disconnect_time_allow'))