Advertisement
Guest User

nmea_to_navsat.py

a guest
Nov 11th, 2024
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.64 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import rclpy
  4. from rclpy.node import Node
  5. from std_msgs.msg import String
  6. from sensor_msgs.msg import NavSatFix, Imu
  7. from nav_msgs.msg import Odometry
  8. from geometry_msgs.msg import Twist
  9. import pynmea2
  10. import math
  11. import tf_transformations
  12.  
  13. class NMEAToNavSatFixNode(Node):
  14. def __init__(self):
  15. super().__init__('nmea_to_navsat_creating_odom_updated')
  16.  
  17. # Variáveis para odometria
  18. self.x_ = 0.0
  19. self.y_ = 0.0
  20. self.yaw_imu = 0.0 # Yaw integrado do IMU
  21. self.last_cog = None # COG do GPS em graus
  22.  
  23. self.wheelbase = 5.950 # Distância entre os eixos em metros
  24.  
  25. # Variáveis de velocidade e aceleração
  26. self.current_speed_kmh = 0.0
  27. self.current_speed = 0.0
  28. self.last_speed = 0.0
  29. self.last_time = self.get_clock().now()
  30. self.yaw_rate = 0.0
  31.  
  32. # Inicializa o buffer para o filtro de média móvel
  33. self.filter_buffers = {
  34. 'speed': [],
  35. 'steering_angle': []
  36. }
  37.  
  38. # Assinantes
  39. self.subscription_nmea = self.create_subscription(
  40. String,
  41. '/nmea_sentence_topic',
  42. self.nmea_listener_callback,
  43. 10)
  44.  
  45. self.subscription_imu_raw = self.create_subscription(
  46. Imu,
  47. '/imu_raw',
  48. self.imu_raw_callback,
  49. 10)
  50.  
  51. self.subscription_speed_steer = self.create_subscription(
  52. Twist,
  53. '/speed_steer_feedback',
  54. self.speed_steer_callback,
  55. 10)
  56.  
  57. # Publicadores
  58. self.publisher_navsat = self.create_publisher(NavSatFix, '/real_gps/fix', 10)
  59. self.publisher_imu = self.create_publisher(Imu, '/imu', 10) # Publica o IMU corrigido
  60. self.odom_pub = self.create_publisher(Odometry, '/odom', 10)
  61.  
  62. # Variáveis para armazenar as últimas mensagens
  63. self.latest_imu_raw_msg = None
  64. self.latest_speed_steer_msg = None
  65.  
  66. # Variável para controlar a impressão dos logs
  67. self.create_timer(1.0, self.log_status)
  68.  
  69. def nmea_listener_callback(self, msg):
  70. try:
  71. if msg.data.startswith('$GPRMC'):
  72. nmea_obj = pynmea2.parse(msg.data)
  73.  
  74. # Verifica se os campos necessários estão presentes e válidos
  75. if nmea_obj.status != 'A':
  76. self.get_logger().warning('NMEA data invalid or not fixed.')
  77. return
  78.  
  79. if not nmea_obj.latitude or not nmea_obj.longitude:
  80. self.get_logger().warning('NMEA data missing latitude or longitude.')
  81. return
  82.  
  83. # Cria mensagem NavSatFix
  84. navsatfix_msg = NavSatFix()
  85. navsatfix_msg.header.stamp = self.get_clock().now().to_msg()
  86. navsatfix_msg.header.frame_id = 'base_footprint'
  87. navsatfix_msg.latitude = nmea_obj.latitude
  88. navsatfix_msg.longitude = nmea_obj.longitude
  89. navsatfix_msg.altitude = float('nan') # Altitude como NaN
  90.  
  91. # Ajustar a covariância do GPS para refletir a precisão
  92. navsatfix_msg.position_covariance = [
  93. 0.01, 0.0, 0.0,
  94. 0.0, 0.01, 0.0,
  95. 0.0, 0.0, 9999.0
  96. ]
  97. navsatfix_msg.position_covariance_type = NavSatFix.COVARIANCE_TYPE_DIAGONAL_KNOWN
  98.  
  99. # Publica a mensagem NavSatFix
  100. self.publisher_navsat.publish(navsatfix_msg)
  101.  
  102. # Obtém COG e velocidade
  103. try:
  104. speed_knots = float(nmea_obj.spd_over_grnd)
  105. speed_kmh = speed_knots * 1.852 # Converte de nós para km/h
  106. cog = float(nmea_obj.true_course) # COG em graus
  107.  
  108. # Atualiza o COG se a velocidade for acima do limiar
  109. if speed_kmh > 2.5:
  110. # Normaliza o COG para [0, 360)
  111. cog_normalized = cog % 360
  112.  
  113. # Atualiza last_cog
  114. self.last_cog = cog_normalized
  115.  
  116. # Converte COG para yaw em radianos
  117. yaw_rad = self.cog_to_yaw(cog_normalized)
  118. self.yaw_imu = yaw_rad # Atualiza yaw_imu com o COG do GPS
  119.  
  120. except (ValueError, AttributeError):
  121. self.get_logger().warning('Invalid spd_over_grnd or true_course in NMEA data.')
  122.  
  123. except pynmea2.ParseError as e:
  124. self.get_logger().error(f'Erro ao analisar a sentença NMEA: {msg.data}')
  125. except Exception as e:
  126. self.get_logger().error(f'Erro ao processar NMEA: {e}')
  127.  
  128. def imu_raw_callback(self, imu_raw_msg):
  129. self.latest_imu_raw_msg = imu_raw_msg
  130. self.process_data()
  131.  
  132. def speed_steer_callback(self, speed_steer_msg):
  133. self.latest_speed_steer_msg = speed_steer_msg
  134. self.process_data()
  135.  
  136. def process_data(self):
  137. # Certifica-se de que recebeu ambas as mensagens
  138. if self.latest_imu_raw_msg is None or self.latest_speed_steer_msg is None:
  139. return
  140.  
  141. try:
  142. current_time = self.get_clock().now()
  143.  
  144. # Atualiza a velocidade atual
  145. self.current_speed_kmh = self.latest_speed_steer_msg.linear.x # Velocidade em km/h
  146. self.current_speed = self.current_speed_kmh / 3.6 # Converte km/h para m/s
  147.  
  148. # Ângulo de direção (steering angle) em radianos
  149. steering_angle_rad = self.latest_speed_steer_msg.angular.z # Assumindo que está em radianos
  150.  
  151. # Filtragem de média móvel na velocidade
  152. self.current_speed = self.apply_moving_average_filter(self.current_speed, 'speed')
  153.  
  154. # Filtragem de média móvel no steering angle
  155. steering_angle_rad = self.apply_moving_average_filter(steering_angle_rad, 'steering_angle')
  156.  
  157. # Cálculo do yaw rate baseado no modelo Ackermann
  158. if abs(steering_angle_rad) > 1e-6:
  159. tan_steering = math.tan(steering_angle_rad)
  160. if tan_steering != 0.0:
  161. turning_radius = self.wheelbase / tan_steering
  162. self.yaw_rate = self.current_speed / turning_radius
  163. else:
  164. self.yaw_rate = 0.0
  165. else:
  166. self.yaw_rate = 0.0
  167.  
  168. # Calcula dt
  169. dt = (current_time - self.last_time).nanoseconds * 1e-9
  170. if dt <= 0 or dt > 1:
  171. dt = 0.02 # Valor padrão
  172.  
  173. self.last_time = current_time
  174.  
  175. # Integra o yaw rate para atualizar yaw_imu
  176. self.yaw_imu += self.yaw_rate * dt
  177. self.yaw_imu = self.normalize_angle(self.yaw_imu)
  178.  
  179. # Calcula a aceleração linear
  180. if dt > 0:
  181. linear_acceleration = (self.current_speed - self.last_speed) / dt
  182. else:
  183. linear_acceleration = 0.0
  184.  
  185. self.last_speed = self.current_speed
  186.  
  187. # Atualiza o IMU (orientação)
  188. quaternion = tf_transformations.quaternion_from_euler(0, 0, self.yaw_imu)
  189. imu_msg = Imu()
  190. imu_msg.header.stamp = current_time.to_msg()
  191. imu_msg.header.frame_id = self.latest_imu_raw_msg.header.frame_id
  192. imu_msg.orientation.x = quaternion[0]
  193. imu_msg.orientation.y = quaternion[1]
  194. imu_msg.orientation.z = quaternion[2]
  195. imu_msg.orientation.w = quaternion[3]
  196.  
  197. # Copia os dados de angular_velocity
  198. imu_msg.angular_velocity = self.latest_imu_raw_msg.angular_velocity
  199. imu_msg.angular_velocity_covariance = self.latest_imu_raw_msg.angular_velocity_covariance
  200.  
  201. # Define a aceleração linear calculada
  202. imu_msg.linear_acceleration.x = linear_acceleration
  203. imu_msg.linear_acceleration.y = 0.0
  204. imu_msg.linear_acceleration.z = 0.0
  205.  
  206. # Ajustar a covariância da orientação do IMU
  207. imu_msg.orientation_covariance = [
  208. 0.1, 0.0, 0.0,
  209. 0.0, 0.1, 0.0,
  210. 0.0, 0.0, 0.1
  211. ]
  212.  
  213. # Defina angular_velocity e linear_acceleration como desconhecidas (-1)
  214. imu_msg.angular_velocity_covariance = [
  215. -1.0, 0.0, 0.0,
  216. 0.0, -1.0, 0.0,
  217. 0.0, 0.0, -1.0
  218. ]
  219. imu_msg.linear_acceleration_covariance = [
  220. -1.0, 0.0, 0.0,
  221. 0.0, -1.0, 0.0,
  222. 0.0, 0.0, -1.0
  223. ]
  224.  
  225. # Publica a mensagem IMU corrigida
  226. self.publisher_imu.publish(imu_msg)
  227.  
  228. # Calcula a mudança na posição usando yaw_imu
  229. x_dot = self.current_speed * math.cos(self.yaw_imu)
  230. y_dot = self.current_speed * math.sin(self.yaw_imu)
  231.  
  232. self.x_ += x_dot * dt
  233. self.y_ += y_dot * dt
  234.  
  235. # Cria mensagem de odometria
  236. odom_msg = Odometry()
  237. odom_msg.header.stamp = current_time.to_msg()
  238. odom_msg.header.frame_id = "odom"
  239. odom_msg.child_frame_id = "base_footprint"
  240.  
  241. # Posição e orientação
  242. odom_msg.pose.pose.position.x = self.x_
  243. odom_msg.pose.pose.position.y = self.y_
  244. odom_msg.pose.pose.position.z = 0.0 # Movimentação 2D
  245.  
  246. odom_msg.pose.pose.orientation = imu_msg.orientation
  247.  
  248. # Velocidades linear e angular
  249. odom_msg.twist.twist.linear.x = self.current_speed
  250. odom_msg.twist.twist.angular.z = self.yaw_rate
  251.  
  252. # Define as covariâncias para reduzir a influência da odometria
  253.  
  254. odom_msg.pose.covariance = [
  255. 2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  256. 0.0, 2.0, 0.0, 0.0, 0.0, 0.0,
  257. 0.0, 0.0, 9999.0, 0.0, 0.0, 0.0,
  258. 0.0, 0.0, 0.0, 9999.0, 0.0, 0.0,
  259. 0.0, 0.0, 0.0, 0.0, 9999.0, 0.0,
  260. 0.0, 0.0, 0.0, 0.0, 0.0, 0.1
  261. ]
  262. odom_msg.twist.covariance = [
  263. 2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  264. 0.0, 2.0, 0.0, 0.0, 0.0, 0.0,
  265. 0.0, 0.0, 9999.0, 0.0, 0.0, 0.0,
  266. 0.0, 0.0, 0.0, 9999.0, 0.0, 0.0,
  267. 0.0, 0.0, 0.0, 0.0, 9999.0, 0.0,
  268. 0.0, 0.0, 0.0, 0.0, 0.0, 0.1
  269. ]
  270.  
  271. # Publica a odometria
  272. self.odom_pub.publish(odom_msg)
  273.  
  274. except Exception as e:
  275. self.get_logger().error(f'Erro em process_data: {e}')
  276.  
  277. def apply_moving_average_filter(self, new_value, key, window_size=5):
  278. buffer = self.filter_buffers[key]
  279. buffer.append(new_value)
  280. if len(buffer) > window_size:
  281. buffer.pop(0)
  282. return sum(buffer) / len(buffer)
  283.  
  284. def cog_to_yaw(self, cog_deg):
  285. yaw_rad = math.radians(90 - cog_deg)
  286. yaw_rad = self.normalize_angle(yaw_rad)
  287. return yaw_rad
  288.  
  289. def normalize_angle(self, angle):
  290. return (angle + math.pi) % (2 * math.pi) - math.pi
  291.  
  292. def log_status(self):
  293. self.get_logger().info(f'Orientação (Yaw): {math.degrees(self.yaw_imu):.2f} graus')
  294. self.get_logger().info(f'Velocidade: {self.current_speed_kmh:.2f} km/h')
  295.  
  296. def main(args=None):
  297. rclpy.init(args=args)
  298. node = NMEAToNavSatFixNode()
  299. try:
  300. rclpy.spin(node)
  301. except KeyboardInterrupt:
  302. pass
  303. finally:
  304. node.destroy_node()
  305. rclpy.shutdown()
  306.  
  307. if __name__ == '__main__':
  308. main()
  309.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement