我对一个错误感到困惑,因为一个结构的成员有时会被一个与它无关的库函数调用所覆盖。
在看似随机数量的Navagator :: update()调用之后,GPS.parse(GPS.lastNMEA())行将覆盖导航器对象的路点变量指向的内容。
这个错误很奇怪,我有可能在某个地方放错了指针吗?
// TransAt.ino
// ---------------------
#include "motors.h"
#include "navagator.h"
Navagator* nav;
Waypoint w = { 43.36, -75.1 };
uint32_t timer = millis();
void setup()
{
Serial.begin(115200);
Serial.println("MIT TransAt Vehicle Debugging");
nav = &Navagator(&w, 1);
Serial.print("\Setup 1 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
Serial.print("\nSetup 2 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
}
void loop()
{
Serial.print("\nOuter Loop 1 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
nav->update();
Serial.print("\nOuter Loop 2 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
//delay(100000);
if (timer > millis()) timer = millis();
if (millis() - timer > 2000) {
Serial.print("\nLoop 2 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
timer = millis(); // reset the timer
nav->diagnostic();
Serial.print("\nLoop 3 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
int course = nav->getCourse();
Serial.println("\nCourse: "); Serial.println(course);
Serial.print("\nLoop 4 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
}
Serial.print("\nLoop 5 Loc: "); Serial.print(nav->waypoints->lat);
Serial.print(", "); Serial.println(nav->waypoints->lon);
}
-
// navagator.h
// ---------------------
#ifndef _NAVAGATOR_h
#define _NAVAGATOR_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Adafruit_GPS.h>
struct Waypoint {
double lat, lon;
};
class Navagator {
public:
Navagator(Waypoint* w, int);
int getCourse(); // Returns course to next waypoint (compass bearings 0-360 with 0 true North)
void update(); // Updates navagator (Call this frequently in the main loop)
void diagnostic();
Waypoint* waypoints; // Array of waypoints to pass through
private:
int pointCount, next; // Number of waypoints, index of next waypoint
int course; // Last calculated course to waypoint
bool checkWaypoint();
void updateWaypoints();
void startGPS();
void updateGPS();
};
double degreesToRadians(double);
double radiansToDegrees(double);
int calculateCourse(double lat1, double lon1, double lat2, double lon2);
double calculateCoordinateDistance(double, double, double, double);
double getLatitude();
double getLongitude();
#endif
-
//
// Library for navigator guiding the TransAt vehicle
//
// Note: The navigator uses 0-360 compass bearings with 0 equivalent to true North and assumes 1 degree of precision
// All distances are measured in km
// The course is the ideal angle between the vehicle and a waypoint
// navagator.cpp
// -------------------
#include "navagator.h"
#include <math.h>
#define EARTH_RADIUS 6371 // in km
#define WAYPOINT_RADIUS 5 // in km
#include <Adafruit_GPS.h>
#define GPSSerial Serial3
Adafruit_GPS GPS(&GPSSerial);
Navagator::Navagator(Waypoint* w, int count) {
waypoints = w;
pointCount = count;
next = 0; // start with first waypoint
course = 90; // Notice that initial course is due East
startGPS();
}
// Returns course between 0-360 degrees, where 0 is true North
int Navagator::getCourse() {
if (!GPS.fix) // Without a fix, continue along last course.
return course;
// Get coordinates (In decimal degrees)
double waypointLon = waypoints->lon;
double waypointLat = waypoints->lat;
double currentLon = getLongitude();
double currentLat = getLatitude();
Serial.print("Waypoint Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
// Update course
course = calculateCourse(currentLat, currentLon, waypointLat, waypointLon);
return course;
}
// Passed coordinates in decimal degrees
// Returns course in integer compass bearings
int calculateCourse(double lat1, double lon1, double lat2, double lon2) {
// From https://www.movable-type.co.uk/scripts/latlong.html
// Convert to radians
lat1 = degreesToRadians(lat1);
lon1 = degreesToRadians(lon1);
lat2 = degreesToRadians(lat2);
lon2 = degreesToRadians(lon2);
double y = sin(lon2 - lon1) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
int course = ((int)radiansToDegrees(atan2(y, x)) + 360) % 360; // We convert the -pi to pi result of atan2 to 360 degree compass bearings
return course;
}
// Call this from the main loop to keep navigation up to date
void Navagator::update() {
Serial.print("Update 1 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
updateGPS();
Serial.print("Update 2 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
updateWaypoints();
Serial.print("Update 3 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
}
void Navagator::updateWaypoints() {
if (checkWaypoint()) { // If we've reached a waypoint
if (next + 1 < pointCount) // If there are more waypoints,
next++; // Move to the next waypoint (Else, keep seeking the last waypoint)
}
}
// Returns true if we've reached the next waypoint, else false
bool Navagator::checkWaypoint() {
if (!GPS.fix) // Without a fix, assume we haven't reached a waypoint
return false;
// Get coordinates
double waypointLon = (waypoints + next)->lon;
double waypointLat = (waypoints + next)->lat;
double currentLon = getLongitude();
double currentLat = getLatitude();
// Return if distance between them is less than the waypoint
return calculateCoordinateDistance(currentLat, currentLon, waypointLat, waypointLon) < WAYPOINT_RADIUS;
}
// Takes coordinates in decimal degrees
// Returns distance between in km
double calculateCoordinateDistance(double lat1, double lon1, double lat2, double lon2) {
// Halversine Formula https://www.movable-type.co.uk/scripts/latlong.html
// Convert to radians
lat1 = degreesToRadians(lat1);
lon1 = degreesToRadians(lon1);
lat2 = degreesToRadians(lat2);
lon2 = degreesToRadians(lon2);
// Find distance on unit sphere
double a = pow(sin((lat2 - lat1) / 2), 2) + cos(lat1) * cos(lat2) * pow(sin((lon2 - lon1) / 2), 2); // For right triangle with sides a, b, and c
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
return EARTH_RADIUS * c;
}
double degreesToRadians(double degrees) {
return degrees * M_PI / 180;
}
double radiansToDegrees(double radians) {
return radians * 180 / M_PI;
}
// Provides decimal degree latitude with ~1km of precision
double getLatitude() {
double lat = GPS.latitude_fixed;
if (GPS.lat == 'S') { // Note we're doing a character comparision here so the == is appropriate
lat = -lat; // Southern latitudes are negative
}
return lat / 10000000.0;
}
// Provides decimal degree latitude with ~1km of precision
double getLongitude() {
double lon = GPS.longitude_fixed;
if (GPS.lon == 'W') { // Note we're doing a character comparision here so the == is appropriate
lon = -lon; // Western lattitudes are negative
}
return lon / 10000000.0;
}
// Configures and begins reading GPS
void Navagator::startGPS() {
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // Turn on RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate (In final version, these rates should be lowered and the interupt used less frequently to conserve clock cycles)
delay(1000);
// Create interrupt
OCR0A = 0xAF; // We'll use comparison register A (OCR0A) for the ATMega's ~1Hz Timer0 (When Timer0 == OCR0A == 0xAF, the interupt will fire)
TIMSK0 |= _BV(OCIE0A); // enable compare A interrupts
// Note: TIMSK0 is a macro for the 'Timer Interrupt Mask Register' and OCIE0A is the bit mask specifing 'Timer/Counter Output Compare Match A Interrupt'
// Wait for GPS to get fix
// Actually we can't do that
// Trying to loop here produces weird behavior where nothing is printed to Serial or we never get any data from the gps
// Possibly try to figure out what is going on later?
}
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
GPS.read(); // read any new GPS data (We still have to parse completed sentences in the main loop)
}
// Parses new NMEA sentences
void Navagator::updateGPS() {
Serial.print("UpdateGPS 1 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
if (GPS.newNMEAreceived())
Serial.print("**UpdateGPS 2 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
Serial.println(GPS.parse(GPS.lastNMEA()));
Serial.print("UpdateGPS 3 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
}
void Navagator::diagnostic() {
Serial.print("\nNav 1 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
Serial.println("\n\nNavagation Diagnostic");
Serial.print("Time: ");
Serial.print(GPS.hour, DEC); Serial.print(':');
Serial.print(GPS.minute, DEC); Serial.print(':');
Serial.print(GPS.seconds, DEC); Serial.print('.');
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
if (GPS.fix) {
Serial.print("Location: ");
Serial.print(getLatitude()); Serial.print(GPS.lat); // Decimal Degrees (1/10,000,000 of a degree)
Serial.print(", ");
Serial.print(getLongitude()); Serial.println(GPS.lon); // Decimal Degrees (1/10,000,000 of a degree)
Serial.print("Speed (knots): "); Serial.println(GPS.speed);
Serial.print("Angle: "); Serial.println(GPS.angle);
Serial.print("Altitude: "); Serial.println(GPS.altitude);
Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
}
Serial.print("Waypoint: "); Serial.println(next);
Serial.print("\nNav 2 Loc: "); Serial.print(waypoints->lat);
Serial.print(", "); Serial.println(waypoints->lon);
}
-
输出
...
0
UpdateGPS 3 Loc: 43.36, -75.10
Update 2 Loc: 43.36, -75.10
Update 3 Loc: 43.36, -75.10
Outer Loop 2 Loc: 43.36, -75.10
Loop 5 Loc: 43.36, -75.10
Outer Loop 1 Loc: 43.36, -75.10
Update 1 Loc: 43.36, -75.10
UpdateGPS 1 Loc: 43.36, -75.10
**UpdateGPS 2 Loc: 43.36, -75.10
1
UpdateGPS 3 Loc: 0.00, 0.00
Update 2 Loc: 0.00, 0.00
Update 3 Loc: 0.00, 0.00
Outer Loop 2 Loc: 0.00, 0.00
Loop 5 Loc: 0.00, 0.00
Outer Loop 1 Loc: 0.00, 0.00
Update 1 Loc: 0.00, 0.00
UpdateGPS 1 Loc: 0.00, 0.00
0.00, 0.00
1
UpdateGPS 3 Loc: 0.00, 0.00
Update 2 Loc: 0.00, 0.00
Update 3 Loc: 0.00, 0.00
Outer Loop 2 Loc: 0.00, 0.00
Loop 5 Loc: 0.00, 0.00
Outer Loop 1 Loc: 0.00, 0.00
Update 1 Loc: 0.00, 0.00
UpdateGPS 1 Loc: 0.00, 0.00
0.00, 0.00
1
...
答案 0 :(得分:2)
这一行有一个问题:
nav = &Navagator(&w, 1);
创建Navigator
对象,然后立即销毁。它的一生就是那个陈述,所以你要记下那些不会出现的事情的地址。因此,如果您使用nav
,程序将显示未定义的行为,假设它仍然指向某个有效的地方。
由于nav
是指向Navigator
的指针,并且您当前的程序将此声明为全局变量(有问题,但这是另一个故事),所以这是使用std::unique_ptr<Navigator>
。这将允许动态创建Navigator
,并在std::unique_ptr
超出范围时(将在您的程序终止时)自动删除。
例如:
#include <memory>
//...
std::unique_ptr<Navigator> nav;
然后在setup()
函数中:
nav = std::make_unique<Navigator>(&w, 1);
然后,如果您想要访问基础nav
指针,请使用get()
提供的std::unique_ptr
函数:
Navigator* ptr = nav.get();
答案 1 :(得分:1)
非常感谢。
我以为我正在将导航分配到一个实例,但实际上我将它指向一个临时对象,这是我所有艰难的来源。
我可以使用new来创建对象,但我听说这不适用于微控制器,因此我会在全局范围内创建对象以避免此问题。