-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[cob_sick_s300] fix bug reading not all scan points #317
Changes from 7 commits
f5afc07
59c486f
57ae0bc
3cfe7bb
f7a40b4
d6a3303
15024ba
0c68fa8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# cob_sick_s300 | ||
This package implements a driver for the Sick S300 Safety laser scanners. | ||
It provides an implementation for both, the old (1.40) and the new (2.10) protocol. | ||
Thus, the old Sick S300 Professional CMS as well as the new Sick S300 Expert are supported. | ||
|
||
However, it does not cover the full functionality of the protocol: | ||
- It only handle distance measurements properly | ||
- It only handles no or only one configured measurement range field properly | ||
- It does not handle I/O-data or reflector data | ||
(though it reads the reflector marker field in the distance measurements) | ||
|
||
See http://wiki.ros.org/cob_sick_s300 for more details. | ||
|
||
## S300 Configuration | ||
Here are a few notes about how to best configure the S300: | ||
- Configure the RS422 output to 500kBaud (otherwise, the scanner only provides a lower frequency) | ||
- Configure the scanner to Continuous Data Output | ||
- Send data via one telegram | ||
- Only configure distances, no I/O or reflector data (otherwise, the scanner only provides a lower frequency). | ||
- Configuration of the measurement ranges | ||
- For protocol 1.40: only configure one measurement range field with the full range (-45° to 225°) with all values. | ||
- For protocol 2.10: do not configure a measurement range field | ||
(otherwise, the scanner only provides a lower frequency). | ||
- If you want to only use certain measurement ranges, do this on the ROS side using e.g. the `cob_scan_filter` | ||
located in this package as well. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,14 +55,36 @@ | |
|
||
#include <arpa/inet.h> | ||
|
||
/* | ||
* S300 header format in continuous mode: | ||
* | ||
* | 00 00 00 00 | 4 byte reply header | ||
* | 00 00 | data block number (fixed for continuous output) | ||
* | xx xx | size of data telegram in 16-bit data words | ||
* | FF xx | coordination flag and device address (07 in most cases, but 08 for slave configured scanners) | ||
* | xx xx | protocol version (02 01 for old protocol,.otherwise 03 01) | ||
* | 0x 00 | status: 00 00 = normal, 01 00 = lockout | ||
* | xx xx xx xx | scan number (time stamp) | ||
* | xx xx | telegram number | ||
* | BB BB | ID of output (AAAA=I/O, BBBB=range measruements, CCCC=reflector measurements) | ||
* | 11 11 | number of configures measurement field (1111, 2222, 3333, 4444 or 5555) | ||
* ... data | ||
* | xx xx | CRC | ||
* | ||
* in this parser, user_data_ denotes all but the first 20 bytes (up to and including telegram number above) | ||
* and the last two bytes (CRC) | ||
* | ||
*/ | ||
|
||
|
||
class TelegramParser { | ||
|
||
#pragma pack(push,1) | ||
union TELEGRAM_COMMON1 { | ||
struct { | ||
uint32_t reply_telegram; | ||
uint16_t trigger_result; | ||
uint16_t size; | ||
uint16_t size; // in 16bit=2byte words | ||
uint8_t coordination_flag; | ||
uint8_t device_addresss; | ||
}; | ||
|
@@ -211,10 +233,14 @@ class TelegramParser { | |
TELEGRAM_COMMON2 tc2_; | ||
TELEGRAM_COMMON3 tc3_; | ||
TELEGRAM_DISTANCE td_; | ||
int last_offset_; | ||
int size_field_start_byte_, crc_bytes_in_size_, user_data_size_; | ||
public: | ||
|
||
TelegramParser() : last_offset_(0) {} | ||
TelegramParser() : | ||
size_field_start_byte_(0), | ||
crc_bytes_in_size_(0), | ||
user_data_size_(0) | ||
{} | ||
|
||
bool parseHeader(const unsigned char *buffer, const size_t max_size, const uint8_t DEVICE_ADDR, const bool debug) | ||
{ | ||
|
@@ -229,47 +255,87 @@ class TelegramParser { | |
ntoh(tc1_); | ||
if(debug) print(tc1_); | ||
|
||
if(tc1_.size*2+JUNK_SIZE>(int)max_size) { | ||
if(debug) std::cout<<"invalid header size"<<std::endl; | ||
return false; | ||
} | ||
|
||
tc2_ = *((TELEGRAM_COMMON2*)(buffer+sizeof(TELEGRAM_COMMON1))); | ||
tc3_ = *((TELEGRAM_COMMON3*)(buffer+(sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)))); | ||
|
||
TELEGRAM_TAIL tt; | ||
uint16_t crc; | ||
|
||
int full_data_size = 0; | ||
|
||
// The size reported by the protocol varies depending on the calculation which is different depending | ||
// on several factors. | ||
// The calculation is described on pp. 70-73 in: | ||
// https://www.sick.com/media/dox/1/91/891/Telegram_listing_S3000_Expert_Anti_Collision_S300_Expert_de_en_IM0022891.PDF | ||
// | ||
// Also, the size is reported as 16bit-words = 2 bytes... | ||
// | ||
// For the old protocol/compatability mode: | ||
// "The telegram size is calculated ... starting with the ... 5. byte ... up to and including the ... CRC." | ||
if(tc2_.protocol_version==0x102) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it make sense to have some kind of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, probably yes? However, then we could just throw a warning and stop the node. There is no other way to do this. New protocol number is Should I add this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, do we have a chance to "try" the protocols supported and then exit in case it is an unsupported protocol? How would we detect whether the protocol is not supported? Would "total message length" be an indicator? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, total message length is not an indicator for the protocol. This changes depending on what you configure in the laser scanner itself. I could throw an exception when there is an unsupported protocol, and "try" in the ROS (or the driver) part. What would you prefer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Detecting if the protocol is not supported could IMO only be done via checking the protocl number. However, this node could use some more error handling in this parser :-) However, this would then also be another issue... |
||
last_offset_ = -10; | ||
tt = *((TELEGRAM_TAIL*) (buffer+(2*tc1_.size+JUNK_SIZE-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_)) ); | ||
ntoh(tt); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, 2*tc1_.size-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_); | ||
|
||
if(tc2_.protocol_version!=0x102) { | ||
if(tt.crc!=crc) { | ||
last_offset_ = 0; | ||
if(debug) std::cout<<"CRC failed!\ntrying offset of "<<std::dec<<last_offset_<<std::endl; | ||
tt = *((TELEGRAM_TAIL*) (buffer+(2*tc1_.size+JUNK_SIZE-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_)) ); | ||
{ | ||
size_field_start_byte_ = 4; // start at 5th byte (started numbering at 0) | ||
crc_bytes_in_size_ = 2; // include 2 bytes CRC | ||
|
||
// the user_data_size is the size of the actual payload data in bytes, | ||
//i.e. all data except of the CRC and the first two common telegrams | ||
user_data_size_ = | ||
2*tc1_.size - | ||
(sizeof(TELEGRAM_COMMON1) + sizeof(TELEGRAM_COMMON2) - size_field_start_byte_ + crc_bytes_in_size_); | ||
full_data_size = sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_+sizeof(TELEGRAM_TAIL); | ||
|
||
tt = *((TELEGRAM_TAIL*) (buffer+(sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_)) ); | ||
ntoh(tt); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, full_data_size-JUNK_SIZE-sizeof(TELEGRAM_TAIL)); | ||
} | ||
// Special handling for the new protocol, as the settings cannot be fully deduced from the protocol itself | ||
// Thus, we have to try both possibilities and check against the CRC... | ||
else | ||
{ | ||
// If NO I/O or measuring fields are configured: | ||
// "The telegram size is calculated ... starting with the ... 9. byte ... up to and including the ... CRC." | ||
size_field_start_byte_ = 8; // start at 9th byte (started numbering at 0) | ||
crc_bytes_in_size_ = 2; // include 2 bytes CRC | ||
user_data_size_ = | ||
2*tc1_.size - | ||
(sizeof(TELEGRAM_COMMON1) + sizeof(TELEGRAM_COMMON2) - size_field_start_byte_ + crc_bytes_in_size_); | ||
full_data_size = sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_+sizeof(TELEGRAM_TAIL); | ||
|
||
tt = *((TELEGRAM_TAIL*) (buffer+(sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_)) ); | ||
ntoh(tt); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, full_data_size-JUNK_SIZE-sizeof(TELEGRAM_TAIL)); | ||
|
||
if(tt.crc!=crc) | ||
{ | ||
// If any I/O or measuring field is configured: | ||
// "The telegram size is calculated ... starting with the ... 13. byte ... up to and including the | ||
// last byte ... bevore (sic!) the CRC." | ||
size_field_start_byte_ = 12; // start at 13th byte (started numbering at 0) | ||
crc_bytes_in_size_ = 0; // do NOT include 2 bytes CRC | ||
user_data_size_ = | ||
2*tc1_.size - | ||
(sizeof(TELEGRAM_COMMON1) + sizeof(TELEGRAM_COMMON2) - size_field_start_byte_ + crc_bytes_in_size_); | ||
full_data_size = | ||
sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_+sizeof(TELEGRAM_TAIL); | ||
|
||
tt = *((TELEGRAM_TAIL*) (buffer+(sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_)) ); | ||
ntoh(tt); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, 2*tc1_.size-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, full_data_size-JUNK_SIZE-sizeof(TELEGRAM_TAIL)); | ||
} | ||
} | ||
|
||
if(tt.crc!=crc) { | ||
last_offset_ = -6; | ||
if(debug) std::cout<<"CRC failed!\ntrying offset of "<<std::dec<<last_offset_<<std::endl; | ||
tt = *((TELEGRAM_TAIL*) (buffer+(2*tc1_.size+JUNK_SIZE-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_)) ); | ||
ntoh(tt); | ||
crc = createCRC((uint8_t*)buffer+JUNK_SIZE, 2*tc1_.size-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_); | ||
} | ||
if( (sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_+sizeof(TELEGRAM_TAIL)) > (int)max_size) | ||
{ | ||
if(debug) std::cout<<"invalid header size"<<std::endl; | ||
return false; | ||
} | ||
|
||
|
||
if(tt.crc!=crc) { | ||
if(debug) { | ||
print(tc2_); | ||
print(tc3_); | ||
print(tt); | ||
std::cout<<"at "<<std::hex<<(2*tc1_.size+JUNK_SIZE-sizeof(TELEGRAM_TAIL)+sizeof(TELEGRAM_COMMON2)+last_offset_)<<std::endl; | ||
std::cout<<"at "<<std::dec<<(sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_)<<std::hex<<std::endl; | ||
std::cout<<"invalid CRC: "<<crc<<" ("<<tt.crc<<")"<<std::endl; | ||
} | ||
return false; | ||
|
@@ -282,7 +348,7 @@ class TelegramParser { | |
case DISTANCE: | ||
if(debug) std::cout<<"got distance"<<std::endl; | ||
|
||
td_ = *((TELEGRAM_DISTANCE*)(buffer+sizeof(tc1_)+sizeof(tc2_)+sizeof(tc3_))); | ||
td_ = *((TELEGRAM_DISTANCE*)(buffer+sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+sizeof(TELEGRAM_COMMON3))); | ||
ntoh(td_); | ||
//print(td_); | ||
break; | ||
|
@@ -307,22 +373,24 @@ class TelegramParser { | |
} | ||
|
||
int getCompletePacketSize() const { | ||
return 2*tc1_.size + sizeof(tc1_) + JUNK_SIZE + last_offset_; | ||
return sizeof(TELEGRAM_COMMON1)+sizeof(TELEGRAM_COMMON2)+user_data_size_+sizeof(TELEGRAM_TAIL); | ||
} | ||
|
||
void readDistRaw(const unsigned char *buffer, std::vector<int> &res) const | ||
void readDistRaw(const unsigned char *buffer, std::vector<int> &res, bool debug) const | ||
{ | ||
res.clear(); | ||
if(!isDist()) return; | ||
|
||
size_t num_points = (2*tc1_.size - (sizeof(tc1_)+sizeof(tc2_)+sizeof(tc3_)+sizeof(td_)+sizeof(TELEGRAM_TAIL)-JUNK_SIZE-last_offset_)); | ||
//std::cout<<"num_points: "<<std::dec<<num_points/sizeof(TELEGRAM_S300_DIST_2B)<<" "<<num_points<<" "<<tc1_.size<<std::endl; | ||
size_t i=0; | ||
for(; i<num_points; ) { | ||
TELEGRAM_S300_DIST_2B dist = *((TELEGRAM_S300_DIST_2B*) (buffer+(sizeof(tc1_)+sizeof(tc2_)+sizeof(tc3_)+sizeof(td_)+i)) ); | ||
size_t num_points = | ||
(user_data_size_ - sizeof(TELEGRAM_COMMON3) - sizeof(TELEGRAM_DISTANCE)) / sizeof(TELEGRAM_S300_DIST_2B); | ||
if (debug) std::cout << "Number of points: " << std::dec << num_points << std::endl; | ||
for(size_t i=0; i<num_points; ++i) { | ||
TELEGRAM_S300_DIST_2B dist = | ||
*((TELEGRAM_S300_DIST_2B*) (buffer + (sizeof(TELEGRAM_COMMON1) + sizeof(TELEGRAM_COMMON2) + | ||
sizeof(TELEGRAM_COMMON3) + sizeof(TELEGRAM_DISTANCE) + | ||
i * sizeof(TELEGRAM_S300_DIST_2B))) ); | ||
//for distance only: res.push_back((int)dist.distance); | ||
res.push_back((int)dist.val16); | ||
i += sizeof(TELEGRAM_S300_DIST_2B); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -188,7 +188,7 @@ class NodeClass | |
} | ||
else | ||
{ | ||
ROS_WARN("No params for the Sick S300 fieldset were specified --> will using default, but it's deprecated now, please adjust parameters!!!"); | ||
//ROS_WARN("No params for the Sick S300 fieldset were specified --> will using default, but it's deprecated now, please adjust parameters!!!"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You know how I don't like commenting warnings... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do. However, this is a newly introduced parameter which should deprecate the old one. But this is buggy/not properly implemented and undocumented. See issue #318 The warning pops up every time you use a laserscanner without the new parameter layout and it took me quite some time to get the syntax right. It is also not yet used with cob (at least, I did not find anything). Thus, instead of telling people they have a deprecated parameter set, we should first make sure this one is working correctly. Also, there is another issue with this parameter as it could leed to ambiguities. I'll gladly discuss this in person... |
||
|
||
//setting defaults to be backwards compatible | ||
ScannerSickS300::ParamType param; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handle*s*